### A Pluto.jl notebook ###
# v0.14.7

using Markdown
using InteractiveUtils

# â•”â•�â•¡ 84e1cf74-d1ec-4a62-b6eb-8724a7845c2e
using LinearAlgebra

# â•”â•�â•¡ e5bd718d-1c46-4341-adee-17efddb0d199
begin
	using PlutoUI
end

# â•”â•�â•¡ 8e7c9226-ddee-11eb-3e73-3112eb059eb7
md"# Pensamiento matemÃ¡tico para la programaciÃ³n en Julia

Â¡Bienvenidos al **curso introductorio a Julia para matemÃ¡ticos**!. Como dice el tÃ­tulo, este curso estarÃ¡ de principio a fin orientado para matemÃ¡ticos, dentro de lo que es capaz de contenerse dicho concepto en el manifiesto sencillo de un estilo inusual de programaciÃ³n. 

## Enteros en Julia

Para comenzar a ilustrar el punto de ello, consideremos lo que en Julia (y mucho otros lenguajes de programaciÃ³n) llaman **tipos**, desde otra perspectiva mÃ¡s adecuada:"

# â•”â•�â•¡ 49d3d127-c964-4494-94c2-c7f0123ba0d9
Integer <: Real

# â•”â•�â•¡ debb0d4f-f029-4067-920c-cc049cd83adc
Real <: Integer

# â•”â•�â•¡ 8db92bb9-1a1d-4398-9fc6-72734976c816
md"Lo anterior se entiende como la declaraciÃ³n comÃºn en matemÃ¡ticas de que los nÃºmeros enteros son un **subconjunto** de los nÃºmeros reales, pero es cÃ³digo de Julia... y esos de arriba son 'tipos'."

# â•”â•�â•¡ 205e451b-cabb-4d52-b041-eeb5ce0bacbc
typeof(Integer), typeof(Real)

# â•”â•�â•¡ 4b567441-f60e-4a6f-a4ea-ef961762e7f0
md"Todos los objetos en julia tienen un tipo, el cual puede ser encontrado con la funciÃ³n `typeof`. No obstante, Julia tiene la peculiaridad de que sus tipos son realmente **conjuntos** en el sentido tradicional matemÃ¡tico. 

Por ejemplo, si bien `Integer` representa $1$-a-$1$ el concepto tradicional de nÃºmero entero, Ã©ste es un **concepto abstracto** que es mejor entendido por humanos que por mÃ¡quinas (por ahora...) y *no tiene una representaciÃ³n **concreta** en la computadora*.

Las computadoras actuales entienden la informaciÃ³n en tÃ©rminos de representaciones de **bits** (cualquier cosa que pueda representar un estado de encendido, 1, y de apagado, 0. ComÃºnmente transistores). Por ello, tenemos distintos tipos de nÃºmeros enteros representables en la computadora"

# â•”â•�â•¡ beda1f98-d17f-4915-819d-24a1dbe3b837
Int64.isbitstype, Int32.isbitstype,  Int16.isbitstype


# â•”â•�â•¡ cae300fc-5dbc-447f-8f16-5e4d2ec98633
Integer.isbitstype

# â•”â•�â•¡ 5e771978-db46-409f-a91e-d66d6182340c
bitstring(Int16(5))

# â•”â•�â•¡ 4b60ab09-19ce-4501-b8da-20a667fdce52
Int64.isconcretetype, Int32.isconcretetype, Int16.isconcretetype

# â•”â•�â•¡ 627c5ca8-eed8-4dae-bf28-1c41996e70f6
md"Y todos estos 'enteros restringidos a ser representados de cierta manera' se puedne entender como subconjunto de `Integer`:"

# â•”â•�â•¡ b145a130-cc8e-4431-bfd2-ed85130b95a5
Int64 <: Integer, Int32 <: Integer, Int16 <: Integer

# â•”â•�â•¡ 1cb50db6-6207-4a84-a91d-1a41129f707c
md"No obstante, entre ellos no cumplen ninguna relaciÃ³n de contenencia:"

# â•”â•�â•¡ bb11b4d8-fef8-4f34-8bf6-e6a6ceb551c5
Int32 <: Int64, Int16 <: Int64

# â•”â•�â•¡ 349b1f42-bb9f-4770-a7d5-dd8bc78a318f
md"Para aclararlo, lo anterior debe enterse como:

- `Integer` = $\mathbb{Z}$


- `Int16` = $\{x \in \mathbb{Z}\ |\ x = \sum_{n = 0}^{16} m_k 2^{k} \text{ y } x \text{ se identifica con } (m_1, \ldots, m_{16})\in \{0,1\}^{16}\}$


- `Int32` = $\{x \in \mathbb{Z}\ |\ x = \sum_{n = 0}^{32} m_k 2^{k} \text{ y } x \text{ se identifica con } (m_1, \ldots, m_{32})\in \{0,1\}^{32}\}$


- ... etc."

# â•”â•�â•¡ b79180ce-d566-4039-b40f-b52ce1fd91f1
md"Es decir, podemos tener un nÃºmero entero (pertenece a `Integer`) que puede pertenecer a `Int8` si se determina una cadena de **ocho** $0$s y $1$s (los $m_k$) y se representa como tal, y pertenecer por separado a `int16` si en su lugar se determina una cadena de **dieciseis** $0$s y $1$s y se identifica con dicha tupla en su lugar. 

Como a continuaciÃ³n podemos ver:"

# â•”â•�â•¡ 2d2d9b97-5522-446b-8c8b-aff0c20b0d8c
bitstring(Int8(10))

# â•”â•�â•¡ 9b502ab8-20ca-46e5-977e-80b0adc19bfd
bitstring(Int16(10))

# â•”â•�â•¡ 9e3c684b-4039-4eb2-b633-619f9bd4d851
md"La cadena selecta en ambos casos para representar al nÃºmero 10 es porsupuesto la expanciÃ³n binaria del nÃºmero. Pero noten que son considerados objetos diferentes ya que estÃ¡n sometido a condiciones distintas."

# â•”â•�â•¡ d3e10fbc-f1b6-44b2-b40f-77d1d065d680
Int8(10) === Int8(10) # la triple igualdad verifica igualdad de objetos 

# â•”â•�â•¡ 82da2de9-d202-43d1-ab7d-c6c7f8e3ee55
Int8(10) === Int16(10)

# â•”â•�â•¡ 919fc101-078d-4e00-9fbe-3be7c677572e
md"Aunque aun es posible verificar que son ambos representaciones distintas de un mismo objeto. La doble igualdad verifica igualdad independiente de la representaciÃ³n si es posible. Si no, hace lo mismo que `===`"

# â•”â•�â•¡ 4a3e5c65-69da-428d-bce5-7f1348868657
Int8(10) == Int16(10) # La doble igualdad de valores, o una mÃ¡s "abstracta"

# â•”â•�â•¡ 28529472-2bda-44f1-9b17-8d9674fdd5f7
# Int8(200) == Int64(200) # Esto da error por que 200 es muy grande para 8 bits

# â•”â•�â•¡ 76bee316-0bfd-44f7-ab57-976a4ba56c11
typemax(Int8)

# â•”â•�â•¡ 244cfe8d-ad8e-49d2-8a28-9abbfed36d96
md"
En pocas palabras, Ambos `Int16` e `Int8` son subconjuntos de `Integer`, pero entre ellos, formalmente, no podemos hacer una relaciÃ³n a pesar que de forma intuitiva es claro que es el mismo nÃºmero (como hemos visto que se puede verificar con `==`)

Para esto, Julia utiliza la funciÃ³n `convert`.
"

# â•”â•�â•¡ aefc90c8-a2eb-465f-9584-3f2224bfe5b6
typeof(convert(Int16, 10))

# â•”â•�â•¡ 350ab8e1-d0c2-4a5f-a8a1-c51f2d4f429a
convert(Int8, 10) |> typeof

# â•”â•�â•¡ e5db8570-7dc0-48e3-85bd-34feaacdc4a8
md"la funciÃ³n `convert` covierte su segundo argumento (que puede ser cualquier elemento de `Integer`. En este caso, `10` es un `Int64` por defecto) y lo lleva a su representaciÃ³n dentro del conjunto especificado en el primer argmento, o como dirÃ­a un programador: Lo convierte al *tipo del primer argumento*

Notemos ademÃ¡s el uso del sÃ­mbolo `|>`, llamado **pipe**, para llevar el resultado de `convert(Int8, 10)` hacia ser evaluado en `typeof`. Es decir, esto fue equivalente a haber hecho:"

# â•”â•�â•¡ da194735-2d45-4af7-bd9c-3b24acb73e3a
typeof(convert(Int8, 10))

# â•”â•�â•¡ 48706abb-afb7-4533-bd8a-ac914b5ea692
md"Como ilustraciÃ³n, podemos realizar operaciones como la siguiente:"

# â•”â•�â•¡ ba33bfcc-1bee-4ada-b97f-0c88466d75a2
10 |> Int8 |> bitstring |> length

# â•”â•�â•¡ 22578cb2-8609-4504-8c24-06635967ccae
length(bitstring(Int8(10)))

# â•”â•�â•¡ 18430d41-e129-4614-aa63-4992db146a15
md"
## MÃ¡s sobre tipos/conjuntos en Julia
En general, para todo tipo (conjunto) en Julia, podemos inspeccionar sus subtipos (subconjuntos) de la siguiente forma:"

# â•”â•�â•¡ 63df9020-91d3-48ea-8842-bff38784a5c2
subtypes(Integer)

# â•”â•�â•¡ 03911b4f-7f2f-4052-ba88-a329fd43306a
md"Arriba vemos una primera jerarquÃ­a de divisiÃ³n de `Integer` en subconjuntos. Por ahora, inspeccionemos el llamado `Signed`:"

# â•”â•�â•¡ c90082b1-be11-4239-8818-bc4057743fe9
subtypes(Signed)

# â•”â•�â•¡ d67877ec-bd4e-4148-bb80-e138e29e760b
md"Notamos que los tipos anteriormente utilizados se encuentran como subconjuntos de `Signed`, el cual es subconjunto de `Integer`. `Signed` formalmente se puede definir como:

`Signed` $= \{x \in \mathbb{Z}\ |\ \text{tenemos una nociÃ³n de signo para }x \}$

Lo cual puede sonar bastante abstracto tanto para la computadora como para nosotros (incluso algo vago). Esto es porque tipos como `Signed` (e `Integer`, `Unsiged`, etc.) existen con finalidad de organizar conceptos mÃ¡s que definir objetos concretos. 

Esto lo podemos enfatizar al observar que:"

# â•”â•�â•¡ 91c7ca22-ff23-4c6f-a325-69e02aa7c05e
Signed.abstract, Integer.abstract, Int64.abstract

# â•”â•�â•¡ 86c92f6a-aaeb-4d83-bb5c-66e7e2b523e0
md"AquÃ­ visitamos el concepto de **tipo abstracto** y **tipo concetro** en Julia. Los tipos concretos son aquellos conjuntos que contienen elementos que estrictamente puedan ser representados en una computadora (ya sea como cadena de bits o de alguna manera mÃ¡s sofisticada). Mientras que los tipos abstractos, no necesitan poder ser representados, pues solo pretenden definir conceptos Ãºtiles para la organizaciÃ³n y el humano lector.

Por otra parte, como caso especial de los tipos concretos, tenemos los 'tipos bits', que son tipos concretos que son representables como cadena de bits. El resto de tipos concretos ocupan dos o mÃ¡s cadenas y conexiones mÃ¡s complicadas para ser representadas, aunque siempre siendo posible."

# â•”â•�â•¡ 552a2916-d4d7-4a8a-8ce0-04099dac3e5b
Int64.isbitstype, Integer.isbitstype

# â•”â•�â•¡ 00cf2b2f-db7b-4aca-b66a-f5e245c0a9b8
subtypes(Int64)

# â•”â•�â•¡ 57d98383-9aa4-4ea8-abd5-aa5f983482f8
typeintersect(Int64, Int32)

# â•”â•�â•¡ 4180a87e-d0f6-473f-b96c-525230ac2153
md"Un ejemplo de un conjunto se elementos que puede ser representado en memoria de la computadora (tipo concreto) pero no como una sencilla cadena de bits es el vector con entradas de tipo `Int64`:"

# â•”â•�â•¡ 7a9b78f3-f872-4f35-b00d-cbbbef4f386c
Vector{Int64}.isconcretetype, Vector{Int64}.isbitstype

# â•”â•�â•¡ fb5917ee-edf4-4b2f-91dc-827219c50974
md"Hablaremos mÃ¡s sobre vectores mÃ¡s adelante. Por los momentos, observemos otros tipos de nÃºmeros que tenemos en Julia:"

# â•”â•�â•¡ 423ab2d4-d048-4573-b2cb-3558ff265a62
Number.abstract

# â•”â•�â•¡ 5bfb0f96-39d1-4db0-81f6-8988fa613afe
Number |> subtypes

# â•”â•�â•¡ 88e6559a-cad0-4e49-afc1-af31c4cc0ad2
Real |> subtypes

# â•”â•�â•¡ dc28040c-b779-420c-979f-9f525112d481
1//2 |> typeof 

# â•”â•�â•¡ 2eab181d-03e1-49f2-adeb-233a769087ee
(2+5im)//(6) |> typeof

# â•”â•�â•¡ a50634b5-e6fa-4b15-b80d-352815d9eb1b
AbstractFloat |> subtypes

# â•”â•�â•¡ c65b82c6-a851-42e7-bda9-9873a995fc6e
Ï€ |> typeof

# â•”â•�â•¡ 5e6dc109-b34b-4402-8c63-a1f90782e530
2.0 |> typeof

# â•”â•�â•¡ 5512c6a1-3cad-474d-be76-672ce48903c4
2.04 |> bitstring

# â•”â•�â•¡ 3c3600a9-e57a-4ac2-b996-b2395ab62b0a
md"Este es un vistazo de la jerarquÃ­a de conjuntos bajo el conjunto abstracto `Number`. La forma correcta de pensar en `Number` es que es el conjunto de toda entidad *'que se comporta como nÃºmero'*. Esto podrÃ­a incluir, por ejemplo, a los cuaterniones, a los p-Ã¡dicos, a los multivectores o cualquier objeto que *'tenga las operaciones y funciones que un nÃºmero tendrÃ­a'*. En particular, incluye a los nÃºmeros complejos como vemos en el diagrama.

Bajo la misma linea de pensamiento, un `AbstractFloat` contiene 'todos las entidades que se comportan como flotantes', siendo ejemplos `Float64`, `Float32`, etc. pero, como veremos luego, tambiÃ©n tenemos permitido definir nuestros propios tipos. Esto permite a Julia ser **extensible**. 

![diagrama_tipos](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Julia-number-type-hierarchy.svg/1920px-Julia-number-type-hierarchy.svg.png)"

# â•”â•�â•¡ 1d64b3df-f57a-4e4b-abf7-e6cbf99b7778
md"
Para culminar la discusiÃ³n de los tipos en Julia como conjuntos, observemos que tambiÃ©n tenemos la nociÃ³n se uniÃ³n e intersecciÃ³n de tipos

## Operaciones sobre conjuntos
### UniÃ³n de conjuntos

Tenemos uniÃ³n de conjuntos:
"

# â•”â•�â•¡ d83268e9-28ae-46f6-8f86-3e6fa739094b
Union{Integer, AbstractFloat}

# â•”â•�â•¡ 00fd02e1-56af-4e68-869a-0938563cfeaf
md"Notemos que la uniÃ³n entre `Integer` y `Real` es `Real` ya que tenemos que `Integer <: Real` "

# â•”â•�â•¡ eff7d1af-6dc3-448e-bbd7-35c0556ebb5f
Union{Integer, Real}

# â•”â•�â•¡ 83e3bdfb-f94a-4b37-99c2-48692b96066e
md"Igualmente para la siguiente lista de conjuntos, todos contenidos en `Integer`"

# â•”â•�â•¡ 8e18d4b0-918d-49b7-b0a3-ab542dc5cdd1
Union{Integer, Bool, Int64, Signed, BigInt}

# â•”â•�â•¡ b8679619-0a4c-40e9-a34a-f8b574fa3f7d
md"Por otro lado, tenemos el caso de los conjuntos `Bool`, `Signed`, `Unsigned` que juntos conforman `Integer` y su uniÃ³n deberÃ­a ser igual a `Integer`, pero esto no es decidido automÃ¡ticamente:"

# â•”â•�â•¡ a079a501-ca65-49e5-8380-8677aa3c3bb2
Integer |> subtypes

# â•”â•�â•¡ ab150d7b-0323-44c2-bee9-904024b36a7f
Union{Bool, Signed, Unsigned}

# â•”â•�â•¡ 3f326e0d-0426-41bf-ba42-3359b7adab38
md"Esto es para dar consistencia ante uniones que solo son de un par de conjuntos que pueden o no estar relacionados:"

# â•”â•�â•¡ 4e1851ce-10e7-4b60-a9ac-9ba3aad98d15
Union{Float64, Integer}

# â•”â•�â•¡ 529d74b9-d016-4b10-af11-0dd9f8478ff3
md"Si uno insiste en obtener **el conjunto mÃ¡s pequeÃ±o que contiene la uniÃ³n en cuestiÃ³n** podemos lograrlo utilizando la funciÃ³n `typejoin`:"

# â•”â•�â•¡ 8e5c7acd-0d1c-4ece-915e-e0f26288ee7b
typejoin(Bool, Signed, Unsigned)

# â•”â•�â•¡ 292ba510-2ad2-4ac9-a0af-e5375540f1cb
typejoin(Float64, Integer)

# â•”â•�â•¡ acc308af-6c79-49e6-9568-4d1dca3d69e6
md"Esto se puede pensar tambiÃ©n como *encontrar el ancestro mÃ¡s cercano en comÃºn. Gran parte del funcionamiento de Julia utiliza estas nociones como veremos mÃ¡s adelante.

### El conjunto vacÃ­o y el universal

Tenemos igual la nociÃ³n del conjunto vacÃ­o, expresado en Julia como la uniÃ³n de ningÃºn conjunto, el cual vemos que no tiene ningÃºn subconjunto y la relaciÃ³n de subconjunto da `true` para cualquier conjunto, incluso los concretos y con Ã©l mismo:"

# â•”â•�â•¡ 8e697f02-fdbd-4bdc-9e79-61eec5ec89a4
Union{}

# â•”â•�â•¡ ff621270-7f72-49b6-8f61-8d6a477d0cb1
subtypes(Union{})

# â•”â•�â•¡ c24e66fa-eca8-4acb-8e09-9101ff8677ad
Int64 |> subtypes

# â•”â•�â•¡ be7598e9-4beb-4338-be16-0aae10987051
Union{} <: Integer, Union{} <: Int64, Union{} <: Union{}

# â•”â•�â•¡ f8b37afc-816c-4050-a7f6-8585859bd3d4
md"En caso de uniones con el conjunto vacÃ­o, da el mismo resultado a que si no hubiese estado presente, tal como esperariamos:"

# â•”â•�â•¡ 3af58fab-af03-40c9-8807-fcc34fa2066b
Union{Union{}, Integer}

# â•”â•�â•¡ ee56b35e-31f8-4cce-a58a-002c8eb4368e
Union{Union{}, Float64, Float32}

# â•”â•�â•¡ 6cfb227b-27a5-44a7-b5c5-2564bc8e6c23
md"Similarmente para `typejoin`s:"

# â•”â•�â•¡ dd74d84d-ba49-4b28-b76d-dfceb7e4f826
typejoin(Union{}, Float64)

# â•”â•�â•¡ 34cebccc-7717-4bd0-8641-7c670f0f6c7c
md"El conjunto universal en Julia es denotado por `Any`, en el espÃ­ritu de consistencia con otros lenguajes de programaciÃ³n. Este tiene las propiedades esperadas de un conjunto universal que contiene a todos los demÃ¡s:"

# â•”â•�â•¡ 57674351-c24d-4744-9dca-6fb6ff7c8276
Any # El conjunto universal

# â•”â•�â•¡ 142555da-2602-43ca-8cf2-15acbe5bc2d1
Any <: Any, Real <: Any, Number <: Any

# â•”â•�â•¡ 55966f3d-7c69-4e54-af99-42d6b971b926
Union{Any, Real, Number}

# â•”â•�â•¡ b65df0ea-60f3-47a5-b6c4-81d88b850063
typejoin(Any, Real, Number)

# â•”â•�â•¡ fcb025f9-fb22-4ced-a3e6-025d74159fa1
Any |> supertypes

# â•”â•�â•¡ 51ecb052-af9c-4627-a13c-7cd7d899aa16
subtypes(Any)

# â•”â•�â•¡ c006f66b-62b9-42dd-bf03-81ded6f626dd
length(subtypes(Any))

# â•”â•�â•¡ 1e3a07bd-c825-4a0f-a3a8-d2d774aea576
md"
Como podemos ver, hay una gran cantidad de conjuntos que son directos subconjuntos de `Any` y eso es sin contar a todos los subconjuntos de estos $(length(subtypes(Any))) subconjuntos directos.

### Intersecciones

Las uniones en Julia son altamente utilizadas, como veremos mÃ¡s adelante, para definir los posibles **dominios** en que actuarÃ¡n nuestras funciones o donde existirÃ¡n parÃ¡metros de nuestros tipos propios. 

Las intersecciones no son comÃºnmente utilizadas pero igual tenemos una interfaz para manipularlas:"

# â•”â•�â•¡ 3b63818b-4ce3-4347-a0b2-5680590d185e
typeintersect(Real, Integer)

# â•”â•�â•¡ 3ca8523e-988e-4ef4-8f4e-3edb43958b70
typeintersect(AbstractFloat, Integer)

# â•”â•�â•¡ 329ef148-9988-41df-8225-ac48942ce8ce
typeintersect(Union{Float64, Int64, Float32}, AbstractFloat)

# â•”â•�â•¡ 3bf72263-4afd-41ea-a41f-3f9976c2c87e
typeintersect(typeintersect(typeintersect(Union{Int32, Bool, Char}, Union{Char, Any}), Any), Union{Unsigned, Signed})

# â•”â•�â•¡ 3fa33c74-3adf-4f73-8ebf-d8e8cbd8d453
'ğŸ�°' |> typeof

# â•”â•�â•¡ 72bee22a-a78a-4171-a016-8b4348ff39b9
md"Vemos que tenemos las propiedades esperadas de una operaciÃ³n de intersecciÃ³n de conjuntos. 

## Â¿QuÃ© sucede con las otras operaciones?

Claramente tenemos otras posibles operaciones de conjuntos como el complemento, pero resulta que Ã©stas existirÃ­an en Julia por capricho mÃ¡s que por utilidad si se implementasen (a menos que se les encuentre un uso real).

Por ello, no tenemos una operaciÃ³n para el complemento de un conjunto, especialmente dado que la intersecciÃ³n es rara vez utilizada. 

## IntroducciÃ³n a Funciones
### Funciones matemÃ¡ticas y multiple dispatch

Ya entendiendo los bÃ¡sicos de los tipos/conjuntos en Julia, podemos hablar directamente de las funciones. En el mismo espÃ­ritu purista matemÃ¡tico, veamos que las funciones en Julia pueden ser definidas como estamos acostumbrados en matemÃ¡ticas.

Considere a:

$\begin{aligned} g: \mathbb{R} &\longrightarrow \ \ \mathbb{R} \\ x &\longmapsto \sin(x) \end{aligned}$"

# â•”â•�â•¡ 981f14ad-dcfc-4009-bb0a-fcbfd2657648
g = x::Real -> sin(x)::Real

# â•”â•�â•¡ 8152c80b-d4e1-4b66-93e8-be42ede8e1dd
g(Ï€/2)

# â•”â•�â•¡ 598b080a-f71f-43d6-a0c4-3e7566c18bd9
md"Â¿QuÃ© pasa si intentamos evaluar algo que no sea un real?"

# â•”â•�â•¡ f3c6ecf9-28b4-4f73-881b-93f4bc197a07
#g("hola")

# â•”â•�â•¡ 9a078de5-7adb-45c8-8604-c5fbdd93983e
#g(1 + im)

# â•”â•�â•¡ c58c2532-9433-4f3b-bed8-bff7314ad635
md"Esta es una de las muchas maneras en que uno puede definir funciones en Julia. En particular, esta es conocida como una *funciÃ³n anÃ³nima* dado que, aunque le pusimos el nombre de `g`, realmente no ocupaba dicho nombre:"

# â•”â•�â•¡ 63fecdc6-b0eb-4d58-b398-5eeef42464ea
x::Real -> sin(x)

# â•”â•�â•¡ aae483ec-f49e-4735-bb55-5f3e9f4c202c
md"igualmente puede ser evaluada."

# â•”â•�â•¡ 16c32600-e92e-46fb-b98d-7627b2544a25
(x::Real -> sin(x))(Ï€/2)

# â•”â•�â•¡ 89285103-c01a-45b3-a307-b5eb7e91745d
md"
Notemos ademÃ¡s que si no especificamos su codominio, Julia automÃ¡ticamente colocarÃ¡ un `Any` explÃ­cito

El tema del codominio merece un poco mÃ¡s de discusiÃ³n. Es muy comÃºn en matemÃ¡ticas preocuparnos sobre el codominio ya que nos define nociones como funciones sobreyectivas o propiedades de dimensiÃ³n del rango sobre el del codominio, etc.

No obstante, al momento de programar nuestras funciones, esta no es una preocupaciÃ³n que se mantenga en mente y por ende es comÃºn no colocarlo y permitir que sea `Any` a menos que queramos explÃ­citamos **anotarle al compilador** el tipo de salida (esto se discutirÃ¡ a profundidad luego)

Otra forma de definir funciones, quizÃ¡ mÃ¡s tradicional en notaciÃ³n, es:"

# â•”â•�â•¡ b187d275-730e-43cf-85a9-fed53e984d69
f(x::Real) = x^2

# â•”â•�â•¡ ba03c65f-a538-4624-a672-ef47722b4a33
md"Â¿Pero quÃ© pasa si yo tengo dos funciones donde deseo hacer abuso de notaciÃ³n y llamarles `f` a ambas, para que se entienda acorde al contexto. O bien, puede ser que una versiÃ³n necesite ciertas modificaciones para funcionar para cierto tipo de objetos, etc.

En Julia tenemos el concepto de **multiple dispatch** que traducido podrÃ­a entenderse como *'entrega mÃºltiple' o *'envÃ­o mÃºltiple'*, entendiÃ©ndose como que una sola funciÃ³n `f` puede ser *entregada* o *entendida* bajo diferentes definiciones dependiendo del tipo de objeto utilizado. Obsvemos:"

# â•”â•�â•¡ b664d583-ec0d-4713-bbd3-c0c5a5a70222
f(z::Complex) = real(z)

# â•”â•�â•¡ aa74c32c-8e5f-42ea-b762-9f4a369d0fcd
f(x::Integer) = x - 5

# â•”â•�â•¡ a1e2f72a-a8f6-4048-b5e5-f922259dfc67
f(x::Int64) = âˆšx

# â•”â•�â•¡ e3ea8720-26d4-46b6-8f5b-a3b66bba7910
f(2)

# â•”â•�â•¡ 94554311-81d4-497e-8add-461dba63ab43
f(2.5)

# â•”â•�â•¡ 8cce1773-0227-4dbf-bc7e-ea2ac29a3225
f(Ï€)

# â•”â•�â•¡ a5efac21-a4f7-4531-945e-8c0f5003ec0c
f(3+im)

# â•”â•�â•¡ 44465bbb-4df1-4465-8f97-cd89d36d7f50
f(5)

# â•”â•�â•¡ 98348a61-0fb0-46e5-a795-b76e2f3533b3
f(2.0) 

# â•”â•�â•¡ eb9d82e1-3637-4f84-adbe-0111716f0993
md"Pero.. Â¿No es `Real <: Complex` y entonces la definiciÃ³n reciente deberÃ­a aplicar tambiÃ©n para `Real`? Â¿No habrÃ¡ conflicto?

Primero que nada, un poco de terminologÃ­a: 
- A lo que le hemos estado llamando **funciones** realmente se les conoce como **mÃ©todos**
- MÃºltiples mÃ©todos estÃ¡n contenidos dentro de lo que conocemos como **funciÃ³n genÃ©rica**, que podrÃ­amos pensar es el nombre en sÃ­ mientras que los mÃ©todos son las diferentes *entregas* del multiple dispatch.

Dicho eso, cuando Julia debe evaluar una funciÃ³n, lo que sucede detrÃ¡s de cÃ¡maras es una bÃºsqueda a lo largo de todos los mÃ©todos de la funciÃ³n genÃ©rica que estamos llamando, yendo de mÃ¡s a menos particular.

Esto quiere decir que, incluso si existe una definiciÃ³n de `f` para nÃºmeros complejos, si existe una para nÃºmeros reales, la de nÃºmeros reales va a ser utilizada al momento de llamarse con un elemento de `Real`.

Por otro lado, si existiese una definiciÃ³n `f(x::Float64)`, entonces al ser llamado por un `Float64`, usarÃ­amos esa, pero al ser llamado con un `Float32`, al no existir `f(::Float32)`, el mÃ©todo a ser llamado serÃ­a el *mÃ¡s particular aplicable*, en este caso: `f(x::Real)`.

### Otras formas de definir funciones

Introducimos brevemente otras syntaxis para definir funciones, unas que son mÃ¡s similares a cÃ³mo se definen en otros lenguajes. Ya hemos platicado de funciones anÃ³nimas:"

# â•”â•�â•¡ 7101ef15-8b80-49f9-a8e4-8f2432827e73
x -> x + 3

# â•”â•�â•¡ e05a113e-1caf-4784-aa53-7f40c47a870f
md"Como mencionado anteriormente, las funciones anÃ³nimas tienen nombres e identidades automÃ¡ticamente generadas. AdemÃ¡s, comienzan con `#` que es el caracter de comentarios en Julia, por lo que no podrÃ­amos asignarle mÃ¡s mÃ©todos a dicha funciÃ³n genÃ©rica. 

En conclusiÃ³n, el multiple dispatch no funciona con funciones anÃ³nimas, pero sÃ­ con la notaciÃ³n de `f(x) = x`. Otra notaciÃ³n posible es:"

# â•”â•�â•¡ b05d7c05-ac71-4870-9e1c-790e3cceecff
function miFunciÃ³n(texto::String)
	return "Â¡Hola! El texto recibido fue: $(texto)"
end

# â•”â•�â•¡ abc74d08-dc46-4398-97ec-406ef8608a35
'ğŸ�°'

# â•”â•�â•¡ 93a038b8-019e-457b-b92a-83fa2bb01db2
miFunciÃ³n("montaÃ±a")

# â•”â•�â•¡ c558b408-9cef-4848-83f5-03a6948ce0f6
5 â‰  5.0 

# â•”â•�â•¡ 7b81bfb5-38ee-45bf-a97f-3a39c2009270
5 â‰¥ 5

# â•”â•�â•¡ 7b71d775-4540-4e36-8695-a8e40db9fbf6
Ï€ â‰ˆ Ï€ - âˆšeps(Float64)

# â•”â•�â•¡ 23ad64bd-ddcf-4544-aa50-45c1f62b4801
âˆšeps(Float64)

# â•”â•�â•¡ c8ec4b83-f54b-4117-bd29-9317e63be499
1.0 + eps(Float64)

# â•”â•�â•¡ e596a83c-e13b-40b3-8a67-b2acb019f2f7
(f âˆ˜ f)(10)

# â•”â•�â•¡ b934d565-f9ba-4be4-a3b3-177a07282a7a
[1,2] âˆª [2,4] 

# â•”â•�â•¡ 62371834-034d-4d3b-939f-d8e8d69c8a21
âˆ©

# â•”â•�â•¡ f20630d8-3def-44a0-a875-2832d11855c8
[1,0,0] Ã— [0,0,1]

# â•”â•�â•¡ f1d27a48-4924-4ed3-bf27-59de5b312918
md"aquÃ­ han ocurrido varias cosas que valen la pena comentar.

- Es la primera vez utilizando un elemento del conjunto `String`. Estos son todos las cadenas de caracteres en formato utf8 que puede manejar Julia. Esto quiere decir que podemos utilizar tildes, emoticones, etc. 
  
  Esto es de hecho verdad incluso para nombres de funciones


- Para decidir la salida de la funciÃ³n se utiliza la palabra clave reservada `return` y en este caso hemos retornado un nuevo string.


- Este string contiene en su interior una declaraciÃ³n de la forma `$()` donde dentro de los parÃ©ntesis hemos colocado el argumento de la funciÃ³n. A esto se le llama *interpolaciÃ³n de cadenas de caracteres*. Lo que le pasamos a `$()` no ocupa ser un string, si no cualquier cosa que queramos que Julia evalÃºa y coloque en nuestro texto. Miremos:"

# â•”â•�â•¡ 1688a976-9412-4632-96bb-f3352a6104b1
"2 + 2 = $(2+2)"

# â•”â•�â•¡ 2f515623-3173-4273-b761-11111baa71f7
"f(2) = $(f(2))"

# â•”â•�â•¡ 943d00c7-5dec-4e13-84ee-3ad55b676f69
md"Por supuesto, el cuerpo de las funciones con la sintaxis anterior puede ser mucho mÃ¡s extenso de lo que mostramos y en el futuro veremos ejemplos de ello.

Una Ãºltima forma de definir funciones es:"

# â•”â•�â•¡ 0bd992a7-d333-4235-8bc8-554a76ef04e5
otraForma(texto::String, n) = begin
	resultado = ""
	for i âˆˆ 1:n
		resultado *= texto
	end
	return resultado
end

# â•”â•�â•¡ 2d484489-4786-489e-86c7-53b669878935
md"Ejemplo de uso de scope local con la keyword `let`"

# â•”â•�â•¡ eaef40eb-df5f-42ce-868f-e3200f0c5365
let x = 5
	y = 20
	# --- Hice cÃ¡lculos
	x + y
end

# â•”â•�â•¡ 744e7ab3-fcfe-488e-8805-812135425e75
otraForma("Hola", 4)

# â•”â•�â•¡ 832b7bc6-0341-4560-8660-d1862351944d
otraForma("Hola", 2.0)

# â•”â•�â•¡ 01e94a33-76af-40ad-9846-dad66cedb37e
"Hola " * "Â¿CÃ³mo estÃ¡n?"

# â•”â•�â•¡ 49a83604-134d-48a2-bf95-d8a6cb946da7
md"De nuevo, hagamos un desglose de lo que sucediÃ³ arriba:

- Esta es la primera vez que utilizamos mÃ¡s de un argumento en una funciÃ³n. Es claro que de la misma forma podemos tener mÃºltiples argumentos para funciones en las sintaxis anteriores, incluso para funciones anÃ³nimas haciendo: 

  ```Jula
  (x,y,z) -> x+y-z
  ```

- La linea `resultado = \"\"` denota la asignaciÃ³n de la cadena vacÃ­a de caracteres, `\"\"`, hacia la variable `resultado`

- La linea `for i âˆˆ 1:n` es el inicio de la declaraciÃ³n de un **ciclo de repeticiÃ³n de tipo for**. Hablaremos en detalle de esto posteriormente en caso que no los conozcan.

- La linea `resultado *= texto` es una forma corta de representar a `resultado = resultado*texto`. Es decir, estamos asignando a la variable `resultado` el valor que resulta de multiplicar al valor antiguo de `resultado` y el valor de `texto`.

  Â¿CÃ³mo funciona esto? Â¿MultiplicaciÃ³n entre texto? Esto no es tan poco comÃºn en lenguajes de programaciÃ³n. Se llama **concatenaciÃ³n de texto** y es usualmente represetado con una suma (como en Python), aunque tiene mÃ¡s sentido utilizar la multiplicaciÃ³n `*` ya que esta operaciÃ³n es **no conmutativa** y de hecho forma un monoide con `\"\"` como elemento identidad. Miremos:

"

# â•”â•�â•¡ 0b0446dc-ca43-409e-bc3d-5eaaca17c117
nombre = "Luis"

# â•”â•�â•¡ 510167e1-e4d3-48b0-a4cb-c42e728a1db2
"Â¡Hola, " * nombre * " Â¿CÃ³mo estÃ¡s?"

# â•”â•�â•¡ 73e2a929-bb80-48c7-8fc7-ae861796068b
md"Por Ãºltimo Â¿QuÃ© diferencia hay entre las formas de definir funciones?

Apartando el caso especial de las funciones anÃ³nimas, no existe diferencia entre las otras tres presentadas mÃ¡s que la comodidad de escribirlas dependiendo la situaciÃ³n. definitivamente la segunda es la mÃ¡s comÃºn:

```Julia
function nombre_funciÃ³n(arg1, arg2, ...)
	# Cuerpo de la funciÃ³n
end
```

AdemÃ¡s de tradicional y consistente con otros lenguajes. La Ãºltima presentada puede de hecho verse como un efecto secundario de lo que llamamos **bloques begin-end** que tienen la forma 

```Julia
begin
	# instrucciones realizadas
end
```

Que en Julia no tiene mayor propÃ³sito mÃ¡s que organizar instrucciones que ameritan estar juntas en un solo 'bloque' (puesto a que no afecta el **scope** de las variables. MÃ¡s adelante se discutirÃ¡ mÃ¡s sobre scopes).

Entonces, una funciÃ³n definida de la forma

```Julia
nombre_funciÃ³n(arg1, arg2, ...) = begin
	# instrucciones realizadas
end
```

podrÃ­a pensarse como una variable que contiene todas las instrucciones del bloque begin-end, el cual sÃ­ *entrega pertenencia de la funciÃ³n* a `nombre_funciÃ³n` a diferencia de las funciones anÃ³nimas.
"

# â•”â•�â•¡ aba32638-d45f-4bf4-8892-b4afe7837dc5
md"## MÃ¡s ejemplos de Multiple Dispatch

Para complementar lo anterior, miremos el siguiente ejemplo que ilustra el poder de multiple dispatch y cÃ³mo esto nos permite hacer cosas que en otros lenguajes de programaciÃ³n serÃ­a muy difÃ­cil o a veces imposible de replicar exactamente:

Llamemos la librerÃ­a de Ã¡lgebra lineal que estÃ¡ en la librerÃ­a estandar de Julia:
"

# â•”â•�â•¡ afb0c0c7-7a0d-4292-958c-02e085cece41
md"Esta trae muchas funciones de Ã¡lgebra lineal numÃ©rica. Pero cuando decimos *muchas funciones* realmente nos referimos a *muchos mÃ©todos de algunas funciones genÃ©ricas*

Notemos: Tenemos por ejemplo el caso de la funciÃ³n genÃ©rica `factorize` que, tal cual como es comÃºn en julia, tiene un nombre sencillo que expresa un verbo asociado a su acciÃ³n. `factorize` tiene mÃ©todos que le permiten *reaccionar* al tipo de matriz que tiene como argumento y utilizar tÃ©cnicas distintas de factorizaciÃ³n:"

# â•”â•�â•¡ 1a044da9-8fe8-447e-8761-255d81920644
factorize

# â•”â•�â•¡ bf1226a6-78dd-4611-840b-93859b9fa7a9
factorize([2 -1 0; -1 2 -1; 0 -1 2])

# â•”â•�â•¡ 270f04a2-bcd6-4122-9710-620993257535
factorize([1 2 3; 
  		   2 1 4;
		   3 4 1])

# â•”â•�â•¡ 7d435b4f-dbab-48fc-9ebb-504ccfeaad62
md"## Tipos compuestos, estudio de caso: Vectores

Los vectores en Julia son un caso especial del tipo `AbstractArray{T,N}`. Esto es lo que llamamos un **tipo paramÃ©trico**. Es decir, es un conjunto que para ser completamente especificado, depende de parÃ¡metros. En este caso `T` (el tipo de las entradas del arreglo) y `N` (el nÃºmero de dimensiones o grados de libertad). 

Realmente podemos pensar tambiÃ©n en los tipos paramÃ©tricos como conjuntos que son **uniones de muchos conjuntos a lo largo de todos los posibles valores que tomen sus parÃ¡metros**. Es decir,

`AbstractArray{T,N}` = $\displaystyle \bigcup_{t \in T,\ n \in N}$ `AbstractArray{t,n}`

Esto se formalizarÃ¡ en breve cuando hablemos de estos tipos de datos llamados **UnionAll**. Por los momentos, miremos el ejemplo de un vector:"

# â•”â•�â•¡ 74976ad7-aa26-4a24-b25f-48165851153d
[1, 2, 3]

# â•”â•�â•¡ 544ebf57-936c-4cc1-b94e-a17d1110e59a
[1, 2, 3] |> typeof

# â•”â•�â•¡ 43ca5d5b-2e10-41c0-81ce-42cc344deb00
[1.0, 2.0, 3.0] |> typeof

# â•”â•�â•¡ ec7c769e-4e7b-4857-aa28-7cdf0d732378
[1, 2, 3.0] |> typeof

# â•”â•�â•¡ 86c2ebb9-9380-404a-8397-0efd2233ba96
md"TambiÃ©n podemos definir los vectores de esta manera:"

# â•”â•�â•¡ 18a99b10-f9d0-4294-98b9-267cfe3fe2aa
[1 2 3]

# â•”â•�â•¡ f5ff1497-7553-4c45-85c2-34aacf32c2d8
md"... Excepto que como pueden notar, el tipo del objeto es diferente. Esto es por que hemos definido realmente una **matriz fila**. Los vectores en Julia son, por defecto, isomorfos a las matrices columna. Intentemos definir de esos:"

# â•”â•�â•¡ 32fb002e-4ecc-4e6b-bee3-98cd46f7a3d4
[1; 2; 3] |> typeof

# â•”â•�â•¡ 21698e47-4fae-423f-b38c-e4160a946507
md"Â¡Perfecto! FuncionÃ³. Ahora, el caso general es cuando tenemos una matriz con varias columnas y filas:"

# â•”â•�â•¡ 1a46f05b-7e83-4986-b2de-3f57ae7126a0
[1 2 3; 2 3 4; 3 4 5]

# â•”â•�â•¡ 3a7c72b3-f98d-4e8a-a426-409e9bc2273a
md"### Operaciones sobre vectores"

# â•”â•�â•¡ 3cd266c7-cd8c-4bda-855e-168afcf9ad36
vâƒ— = [1.0, 2.0, 3.0]

# â•”â•�â•¡ 53f3e8da-7681-4908-b9c9-51c7ecda9b46
md"## UnionAll: Tipos paramÃ©tricos"

# â•”â•�â•¡ d96d6da8-7505-451a-9d68-2ae5c70ce55a
mitipo = Union{Vector{T}} where {T <: Integer};

# â•”â•�â•¡ a2550528-734f-4658-a649-06c654b79151
Vector{Int64} <: mitipo

# â•”â•�â•¡ c400e7ba-0969-4733-a173-5fa505d3c30d
Vector{Bool} <: mitipo

# â•”â•�â•¡ 60e54e9a-2fc6-49b9-ab43-bc4e5278eb77
Vector{Float64} <: mitipo

# â•”â•�â•¡ 4714ca10-b741-472b-8f06-3a944a63b10e
md"## Structs: tipos compuestos propios"

# â•”â•�â•¡ c72240ff-3853-4cb5-90f8-d609517a0c5e
md"## SÃ­mbolos en Julia"

# â•”â•�â•¡ 1d70aaf6-3485-40c5-8f2d-f12af111c42a
md"
Julia es capaz de entender lenguaje simbÃ³lico:
"

# â•”â•�â•¡ 88552435-a511-48d2-9306-ab3c1dd5e2b2
:x, :y  

# â•”â•�â•¡ 2cb0c754-7bea-4e0d-8817-a7abe2b3f897
md"Julia es un lenguaje opcionalmente tipado, similar a cuando el ser humano puede **inferir** lo que significa algo sin que se nos diga, dependiendo dle contexto, y de no ser posible inferir en el momento, podemos inferir cuando tengamos mÃ¡s informaciÃ³n:"

# â•”â•�â•¡ 497d436a-380f-48be-8336-32639a3dc565


# â•”â•�â•¡ 9f086353-b88b-444e-bbe5-426d2758a9e0
TableOfContents(title = "Contenidos", aside = true)

# â•”â•�â•¡ 101c6fab-780e-4c04-b495-1801bace7850


# â•”â•�â•¡ 66f87d6d-fde8-474a-a5ed-9957ef857327


# â•”â•�â•¡ 339377d9-a16d-4f21-a1ea-c4f6bdc1e41f


# â•”â•�â•¡ 60e43ecc-4bfb-4b4d-9993-e2ee739b6deb


# â•”â•�â•¡ 9eaad4fe-0eaf-4003-9ec3-5de0a756d4da
hint(texts::AbstractArray) = Markdown.MD(Markdown.Admonition("warning", "Nota", texts))

# â•”â•�â•¡ c78463d1-b682-48a4-b270-2fbe6c51acf6
hint(text::Union{AbstractString, Markdown.MD}) = hint([text])

# â•”â•�â•¡ 9938ab35-34c9-4578-aa01-25968e7a5812
hint(md"
Es importante notar que esto es posible ya que `==` es **una definida funciÃ³n realmente** mientras que `===` es una funciÃ³n que existe entendida a nivel de compilaciÃ³n del lenguaje:
	
```Julia
=== # Se le conoce como built-in function
```
	
```Julia
== # Tenemos 220 definiciones, cada una para diferentes tipos de objetos.
```	
	
	")

# â•”â•�â•¡ 3dcea53d-bb7f-480c-9aba-e9f460ea3bbd
hint(md"Es valioso aclarar que las funciones anÃ³nimas son *realmente siempre definidas sin nombre*, solo que podemos guardarlas en variables. Esto es fundamentalmente distinto a que la funciÃ³n tenga nombre como veremos cuando hablemos de **Multiple Dispatch**. 

Podemos pensarlo como que su nombre *real* serÃ¡ algo como `#793817` (o cualquier otra cosa realmente) y cuando hacemos algo como 

```julia
g = x -> sin(x)	
``` 
	
estamos guardando el objeto `#793817`, que pertenece al conjunto/tipo `Function` dentro de la variable con nombre `g`. Pero `g` podrÃ­a luego asignÃ¡rsele otra cosa y la funciÃ³n `#793817` seguirÃ­a existiendo independiente de `g` (hasta que el *recolector de basura* lo recoja. MÃ¡s de ello luego.)")

# â•”â•�â•¡ Cell order:
# â•Ÿâ”€8e7c9226-ddee-11eb-3e73-3112eb059eb7
# â• â•�49d3d127-c964-4494-94c2-c7f0123ba0d9
# â• â•�debb0d4f-f029-4067-920c-cc049cd83adc
# â•Ÿâ”€8db92bb9-1a1d-4398-9fc6-72734976c816
# â• â•�205e451b-cabb-4d52-b041-eeb5ce0bacbc
# â•Ÿâ”€4b567441-f60e-4a6f-a4ea-ef961762e7f0
# â• â•�beda1f98-d17f-4915-819d-24a1dbe3b837
# â• â•�cae300fc-5dbc-447f-8f16-5e4d2ec98633
# â• â•�5e771978-db46-409f-a91e-d66d6182340c
# â• â•�4b60ab09-19ce-4501-b8da-20a667fdce52
# â•Ÿâ”€627c5ca8-eed8-4dae-bf28-1c41996e70f6
# â• â•�b145a130-cc8e-4431-bfd2-ed85130b95a5
# â•Ÿâ”€1cb50db6-6207-4a84-a91d-1a41129f707c
# â• â•�bb11b4d8-fef8-4f34-8bf6-e6a6ceb551c5
# â•Ÿâ”€349b1f42-bb9f-4770-a7d5-dd8bc78a318f
# â•Ÿâ”€b79180ce-d566-4039-b40f-b52ce1fd91f1
# â• â•�2d2d9b97-5522-446b-8c8b-aff0c20b0d8c
# â• â•�9b502ab8-20ca-46e5-977e-80b0adc19bfd
# â•Ÿâ”€9e3c684b-4039-4eb2-b633-619f9bd4d851
# â• â•�d3e10fbc-f1b6-44b2-b40f-77d1d065d680
# â• â•�82da2de9-d202-43d1-ab7d-c6c7f8e3ee55
# â•Ÿâ”€919fc101-078d-4e00-9fbe-3be7c677572e
# â• â•�4a3e5c65-69da-428d-bce5-7f1348868657
# â• â•�28529472-2bda-44f1-9b17-8d9674fdd5f7
# â• â•�76bee316-0bfd-44f7-ab57-976a4ba56c11
# â•Ÿâ”€9938ab35-34c9-4578-aa01-25968e7a5812
# â•Ÿâ”€244cfe8d-ad8e-49d2-8a28-9abbfed36d96
# â• â•�aefc90c8-a2eb-465f-9584-3f2224bfe5b6
# â• â•�350ab8e1-d0c2-4a5f-a8a1-c51f2d4f429a
# â•Ÿâ”€e5db8570-7dc0-48e3-85bd-34feaacdc4a8
# â• â•�da194735-2d45-4af7-bd9c-3b24acb73e3a
# â•Ÿâ”€48706abb-afb7-4533-bd8a-ac914b5ea692
# â• â•�ba33bfcc-1bee-4ada-b97f-0c88466d75a2
# â• â•�22578cb2-8609-4504-8c24-06635967ccae
# â•Ÿâ”€18430d41-e129-4614-aa63-4992db146a15
# â• â•�63df9020-91d3-48ea-8842-bff38784a5c2
# â•Ÿâ”€03911b4f-7f2f-4052-ba88-a329fd43306a
# â• â•�c90082b1-be11-4239-8818-bc4057743fe9
# â•Ÿâ”€d67877ec-bd4e-4148-bb80-e138e29e760b
# â• â•�91c7ca22-ff23-4c6f-a325-69e02aa7c05e
# â•Ÿâ”€86c92f6a-aaeb-4d83-bb5c-66e7e2b523e0
# â• â•�552a2916-d4d7-4a8a-8ce0-04099dac3e5b
# â• â•�00cf2b2f-db7b-4aca-b66a-f5e245c0a9b8
# â• â•�57d98383-9aa4-4ea8-abd5-aa5f983482f8
# â•Ÿâ”€4180a87e-d0f6-473f-b96c-525230ac2153
# â• â•�7a9b78f3-f872-4f35-b00d-cbbbef4f386c
# â•Ÿâ”€fb5917ee-edf4-4b2f-91dc-827219c50974
# â• â•�423ab2d4-d048-4573-b2cb-3558ff265a62
# â• â•�5bfb0f96-39d1-4db0-81f6-8988fa613afe
# â• â•�88e6559a-cad0-4e49-afc1-af31c4cc0ad2
# â• â•�dc28040c-b779-420c-979f-9f525112d481
# â• â•�2eab181d-03e1-49f2-adeb-233a769087ee
# â• â•�a50634b5-e6fa-4b15-b80d-352815d9eb1b
# â• â•�c65b82c6-a851-42e7-bda9-9873a995fc6e
# â• â•�5e6dc109-b34b-4402-8c63-a1f90782e530
# â• â•�5512c6a1-3cad-474d-be76-672ce48903c4
# â•Ÿâ”€3c3600a9-e57a-4ac2-b996-b2395ab62b0a
# â•Ÿâ”€1d64b3df-f57a-4e4b-abf7-e6cbf99b7778
# â• â•�d83268e9-28ae-46f6-8f86-3e6fa739094b
# â•Ÿâ”€00fd02e1-56af-4e68-869a-0938563cfeaf
# â• â•�eff7d1af-6dc3-448e-bbd7-35c0556ebb5f
# â•Ÿâ”€83e3bdfb-f94a-4b37-99c2-48692b96066e
# â• â•�8e18d4b0-918d-49b7-b0a3-ab542dc5cdd1
# â•Ÿâ”€b8679619-0a4c-40e9-a34a-f8b574fa3f7d
# â• â•�a079a501-ca65-49e5-8380-8677aa3c3bb2
# â• â•�ab150d7b-0323-44c2-bee9-904024b36a7f
# â•Ÿâ”€3f326e0d-0426-41bf-ba42-3359b7adab38
# â• â•�4e1851ce-10e7-4b60-a9ac-9ba3aad98d15
# â•Ÿâ”€529d74b9-d016-4b10-af11-0dd9f8478ff3
# â• â•�8e5c7acd-0d1c-4ece-915e-e0f26288ee7b
# â• â•�292ba510-2ad2-4ac9-a0af-e5375540f1cb
# â•Ÿâ”€acc308af-6c79-49e6-9568-4d1dca3d69e6
# â• â•�8e697f02-fdbd-4bdc-9e79-61eec5ec89a4
# â• â•�ff621270-7f72-49b6-8f61-8d6a477d0cb1
# â• â•�c24e66fa-eca8-4acb-8e09-9101ff8677ad
# â• â•�be7598e9-4beb-4338-be16-0aae10987051
# â•Ÿâ”€f8b37afc-816c-4050-a7f6-8585859bd3d4
# â• â•�3af58fab-af03-40c9-8807-fcc34fa2066b
# â• â•�ee56b35e-31f8-4cce-a58a-002c8eb4368e
# â•Ÿâ”€6cfb227b-27a5-44a7-b5c5-2564bc8e6c23
# â• â•�dd74d84d-ba49-4b28-b76d-dfceb7e4f826
# â•Ÿâ”€34cebccc-7717-4bd0-8641-7c670f0f6c7c
# â• â•�57674351-c24d-4744-9dca-6fb6ff7c8276
# â• â•�142555da-2602-43ca-8cf2-15acbe5bc2d1
# â• â•�55966f3d-7c69-4e54-af99-42d6b971b926
# â• â•�b65df0ea-60f3-47a5-b6c4-81d88b850063
# â• â•�fcb025f9-fb22-4ced-a3e6-025d74159fa1
# â• â•�51ecb052-af9c-4627-a13c-7cd7d899aa16
# â• â•�c006f66b-62b9-42dd-bf03-81ded6f626dd
# â•Ÿâ”€1e3a07bd-c825-4a0f-a3a8-d2d774aea576
# â• â•�3b63818b-4ce3-4347-a0b2-5680590d185e
# â• â•�3ca8523e-988e-4ef4-8f4e-3edb43958b70
# â• â•�329ef148-9988-41df-8225-ac48942ce8ce
# â• â•�3bf72263-4afd-41ea-a41f-3f9976c2c87e
# â• â•�3fa33c74-3adf-4f73-8ebf-d8e8cbd8d453
# â•Ÿâ”€72bee22a-a78a-4171-a016-8b4348ff39b9
# â• â•�981f14ad-dcfc-4009-bb0a-fcbfd2657648
# â• â•�8152c80b-d4e1-4b66-93e8-be42ede8e1dd
# â•Ÿâ”€598b080a-f71f-43d6-a0c4-3e7566c18bd9
# â• â•�f3c6ecf9-28b4-4f73-881b-93f4bc197a07
# â• â•�9a078de5-7adb-45c8-8604-c5fbdd93983e
# â•Ÿâ”€c58c2532-9433-4f3b-bed8-bff7314ad635
# â• â•�63fecdc6-b0eb-4d58-b398-5eeef42464ea
# â•Ÿâ”€aae483ec-f49e-4735-bb55-5f3e9f4c202c
# â• â•�16c32600-e92e-46fb-b98d-7627b2544a25
# â•Ÿâ”€3dcea53d-bb7f-480c-9aba-e9f460ea3bbd
# â•Ÿâ”€89285103-c01a-45b3-a307-b5eb7e91745d
# â• â•�b187d275-730e-43cf-85a9-fed53e984d69
# â• â•�e3ea8720-26d4-46b6-8f5b-a3b66bba7910
# â• â•�94554311-81d4-497e-8add-461dba63ab43
# â• â•�8cce1773-0227-4dbf-bc7e-ea2ac29a3225
# â•Ÿâ”€ba03c65f-a538-4624-a672-ef47722b4a33
# â• â•�b664d583-ec0d-4713-bbd3-c0c5a5a70222
# â• â•�a5efac21-a4f7-4531-945e-8c0f5003ec0c
# â• â•�aa74c32c-8e5f-42ea-b762-9f4a369d0fcd
# â• â•�a1e2f72a-a8f6-4048-b5e5-f922259dfc67
# â• â•�44465bbb-4df1-4465-8f97-cd89d36d7f50
# â• â•�98348a61-0fb0-46e5-a795-b76e2f3533b3
# â•Ÿâ”€eb9d82e1-3637-4f84-adbe-0111716f0993
# â• â•�7101ef15-8b80-49f9-a8e4-8f2432827e73
# â•Ÿâ”€e05a113e-1caf-4784-aa53-7f40c47a870f
# â• â•�b05d7c05-ac71-4870-9e1c-790e3cceecff
# â• â•�abc74d08-dc46-4398-97ec-406ef8608a35
# â• â•�93a038b8-019e-457b-b92a-83fa2bb01db2
# â• â•�c558b408-9cef-4848-83f5-03a6948ce0f6
# â• â•�7b81bfb5-38ee-45bf-a97f-3a39c2009270
# â• â•�7b71d775-4540-4e36-8695-a8e40db9fbf6
# â• â•�23ad64bd-ddcf-4544-aa50-45c1f62b4801
# â• â•�c8ec4b83-f54b-4117-bd29-9317e63be499
# â• â•�e596a83c-e13b-40b3-8a67-b2acb019f2f7
# â• â•�b934d565-f9ba-4be4-a3b3-177a07282a7a
# â• â•�62371834-034d-4d3b-939f-d8e8d69c8a21
# â• â•�f20630d8-3def-44a0-a875-2832d11855c8
# â•Ÿâ”€f1d27a48-4924-4ed3-bf27-59de5b312918
# â• â•�1688a976-9412-4632-96bb-f3352a6104b1
# â• â•�2f515623-3173-4273-b761-11111baa71f7
# â•Ÿâ”€943d00c7-5dec-4e13-84ee-3ad55b676f69
# â• â•�0bd992a7-d333-4235-8bc8-554a76ef04e5
# â•Ÿâ”€2d484489-4786-489e-86c7-53b669878935
# â• â•�eaef40eb-df5f-42ce-868f-e3200f0c5365
# â• â•�744e7ab3-fcfe-488e-8805-812135425e75
# â• â•�832b7bc6-0341-4560-8660-d1862351944d
# â• â•�01e94a33-76af-40ad-9846-dad66cedb37e
# â•Ÿâ”€49a83604-134d-48a2-bf95-d8a6cb946da7
# â• â•�0b0446dc-ca43-409e-bc3d-5eaaca17c117
# â• â•�510167e1-e4d3-48b0-a4cb-c42e728a1db2
# â•Ÿâ”€73e2a929-bb80-48c7-8fc7-ae861796068b
# â•Ÿâ”€aba32638-d45f-4bf4-8892-b4afe7837dc5
# â• â•�84e1cf74-d1ec-4a62-b6eb-8724a7845c2e
# â•Ÿâ”€afb0c0c7-7a0d-4292-958c-02e085cece41
# â• â•�1a044da9-8fe8-447e-8761-255d81920644
# â• â•�bf1226a6-78dd-4611-840b-93859b9fa7a9
# â• â•�270f04a2-bcd6-4122-9710-620993257535
# â•Ÿâ”€7d435b4f-dbab-48fc-9ebb-504ccfeaad62
# â• â•�74976ad7-aa26-4a24-b25f-48165851153d
# â• â•�544ebf57-936c-4cc1-b94e-a17d1110e59a
# â• â•�43ca5d5b-2e10-41c0-81ce-42cc344deb00
# â• â•�ec7c769e-4e7b-4857-aa28-7cdf0d732378
# â•Ÿâ”€86c2ebb9-9380-404a-8397-0efd2233ba96
# â• â•�18a99b10-f9d0-4294-98b9-267cfe3fe2aa
# â•Ÿâ”€f5ff1497-7553-4c45-85c2-34aacf32c2d8
# â• â•�32fb002e-4ecc-4e6b-bee3-98cd46f7a3d4
# â•Ÿâ”€21698e47-4fae-423f-b38c-e4160a946507
# â• â•�1a46f05b-7e83-4986-b2de-3f57ae7126a0
# â•Ÿâ”€3a7c72b3-f98d-4e8a-a426-409e9bc2273a
# â• â•�3cd266c7-cd8c-4bda-855e-168afcf9ad36
# â•Ÿâ”€53f3e8da-7681-4908-b9c9-51c7ecda9b46
# â• â•�d96d6da8-7505-451a-9d68-2ae5c70ce55a
# â• â•�a2550528-734f-4658-a649-06c654b79151
# â• â•�c400e7ba-0969-4733-a173-5fa505d3c30d
# â• â•�60e54e9a-2fc6-49b9-ab43-bc4e5278eb77
# â• â•�4714ca10-b741-472b-8f06-3a944a63b10e
# â• â•�c72240ff-3853-4cb5-90f8-d609517a0c5e
# â•Ÿâ”€1d70aaf6-3485-40c5-8f2d-f12af111c42a
# â• â•�88552435-a511-48d2-9306-ab3c1dd5e2b2
# â•Ÿâ”€2cb0c754-7bea-4e0d-8817-a7abe2b3f897
# â• â•�497d436a-380f-48be-8336-32639a3dc565
# â•Ÿâ”€e5bd718d-1c46-4341-adee-17efddb0d199
# â•Ÿâ”€9f086353-b88b-444e-bbe5-426d2758a9e0
# â•Ÿâ”€101c6fab-780e-4c04-b495-1801bace7850
# â•Ÿâ”€66f87d6d-fde8-474a-a5ed-9957ef857327
# â•Ÿâ”€339377d9-a16d-4f21-a1ea-c4f6bdc1e41f
# â•Ÿâ”€60e43ecc-4bfb-4b4d-9993-e2ee739b6deb
# â•Ÿâ”€9eaad4fe-0eaf-4003-9ec3-5de0a756d4da
# â•Ÿâ”€c78463d1-b682-48a4-b270-2fbe6c51acf6
