domingo, 28 de diciembre de 2008

JavaScript

Para practicar la puesta en práctica de las recomendaciones de Douglas Crockford respecto a como programar en JavaScript, me decidí por hacer una demostración en SVG del famoso "Arnold's Cat Map". Este mapeo consiste simplemente en aplicar repetidas veces a las coordenadas de un conjunto de puntos inicial la transformación

(x, y) → (x + y, x + 2y) mod 1 .

El "mod 1" es lo que indica que trabajamos sobre el toroide ℝ2/ℤ2.



Si bien el mapa aplicado a valores reales es caótico y mezcla fuertemente el conjunto de puntos, se observa un fenómeno de recurrencia en las variantes discretas del problema (tal como la implementada). Para este caso particular en el que la imagen es de 32 X 32 pixels, la recurrencia se observa en el paso 24.

Nota de implementación: la imagen fue tomada de Wikipedia y editada y transformada a formato "JSON" utilizando PIL.

lunes, 22 de diciembre de 2008

Porqué los espejos intercambian derecha e izquierda?

Se cree comúnmente que los espejos "intercambian la derecha y la izquierda". Por ejemplo, la imagen especular de una persona diestra escribirá, aparentemente, con la mano izquierda. Esto nos llevaría a pensar que el espejo trata en forma diferente a las direcciones horizontales que a las direcciones verticales (porque no observamos que el espejo invierta "arriba y abajo").

Pero esto no es lo que en realidad "el espejo hace". La reflexión invierte las posiciones en la dirección perpendicular al espejo. Es por alguna razón psicológica que esto se observa como una rotación de 180° alrededor de un eje vertical compuesta con una inversión de derecha e izquierda.

La razón que se me ocurre para esto es que inconsciente observamos al reflejo como otra persona física; como físicamente las personas pueden rotarse pero no reflejarse, la visualizamos como una versión rotada de nosotros mismos con sus lados derecho e izquierdo intercambiados. 

Esto plantea la pregunta de porqué visualizamos una reflexión derecha/izquierda pero no la reflexión adelante/atrás. Si bien no se me ocurre una respuesta directa a esta pregunta, probablemente se deba a la cuasi-simetría externa de las personas respecto al intercambio derecha/izquierda.

sábado, 20 de diciembre de 2008

Lattice Boltzmann (actualización)

Todavía no encontré el tiempo para dar una breve explicación de Lattice Boltzmann, pero logré mejorar notablemnete la eficiencia de la simulación. Puede hacerse checkout de la última "versión" hasta el momento en http://lattice-boltzmann-sdl.googlecode.com/svn/tags/0.03/.

A continuación muestro dos imágenes de la simulación, que está corriendo a unos 30 FPS en un solo core de un Intel Core Duo T2600 @ 2.16 GHz. Corresponden a la colisión de dos esferas de fluido en forma ligeramente oblicua.


miércoles, 17 de diciembre de 2008

The Emily Project

La calidad de los renders sigue mejorando...



Actualización: El video normal no le hace justicia; puede verse el video en alta definición.

domingo, 14 de diciembre de 2008

La subasta del dólar

Desde el punto de vista formal se define a un juego como un conjunto de jugadores, cada uno de los cuales dispone de una serie de posibles acciones a tomar, y una forma de determinar cuanta recompensa recibe cada uno de los jugadores en base a las acciones que eligen todos ellos. Este formalismo no solo puede modelizar juegos como el ta-te-ti o el ajedrez, sino que puede aplicarse al análisis de situaciones hipotéticas tales como el conocido "dilema del prisionero".

Uno de los juegos que fueron definidos de este modo es el conocido como la "subasta del dolar" y fue creado por Martin Shubik en 1971 para dar un ejemplo simple del conocido proceso de escalada en los conflictos. El juego es muy simple: se subasta un dólar entre dos jugadores pero ambos deben pagar sus ofertas, lo que altera radicalmente el desarrollo de la subasta.

Supongamos que la oferta mínima y el incremento mínimo de las ofertas se establece en 0.05. Entonces, a primera vista, parece un excelente retorno ofrecer 0.05 a cambio del dólar; pero también lo será para el otro jugador ofrecer 0.10 y así el proceso puede seguir hasta que las ofertas hasta 0.95. Pero la fase más interesante ocurre a partir de este momento...

Supongamos que el jugador "A" hizo la última oferta de 0.95 después de que "B" hubiera ofrecido 0.90. Entonces "B" tiene dos opciones: puede aceptar perder 0.90 o puede ofrecer 1.00 y "salir hecho". El mismo análisis puede hacerlo "A" en caso de realizar "B" su oferta de 1.00: puede perder 0.95 u ofrecer 1.05 y perder 0.05... Esto lleva a una competencia en la que las pérdidas son potencialmente ilimitadas.

Uno podría pensar que este juego carece de relevancia práctica en si y que solo interesa como modelo de otras competencias que suceden en la realidad, pero eso sería incorrecto. Pueden verse más detalles acerca de Swoopo y su modelo de negocios en múltiples sitios. Quién dice que la teoría de juegos no es redituable? :-D

lunes, 8 de diciembre de 2008

Lattice Boltzmann (con SDL!)

Estoy trabajando en un nuevo proyecto personal: realizar una simulación de fluidos mediante el método conocido como Lattice Boltzmann y usando SDL para los gráficos. Si bien el objetivo último es lograr una buena performance, actualmente solo está funcionando la implementación "lenta" que voy a usar con propósitos comparativos (a unos 8 FPS con simple precisión en un Core Duo @ 2.16 GHz).

Hoy fue una experiencia bastante frustrante: empleé horas buscando un error en mi implementación de Lattice Boltzmann para descubrir finalmente que el problema estaba en las condiciones iniciales que estaba empleando... Más específicamente, no estaba cumpliendo con la condición CFL :-S

En las próximas semanas probablemente agregue algo más acerca de Lattice Boltzmann y, con suerte, tenga nuevos avances del proyecto.

miércoles, 3 de diciembre de 2008

Lissajous II

(Continúa a este post.)

Es bien conocido que puede escribirse a todo número real como el producto de un número real de módulo menor que 1 y una potencia entera de un número mayor que 1. Por ejemplo, tomando base 10, tenemos:

234.435 = 0.234435 ∙ 103
3.14159... = 0.314159... ∙ 101
1024 = 0.1024 ∙ 104

Claramente podemos observar que el primer dígito del número aparece como el primer decimal en esta forma de representación. Utilizando algunas funciones tales como

{x} = la parte fraccionaria de x, igual a x - ⌊x⌋,

podemos expresar esto como

pd(x) = ⌊10{log10 x}⌋,

donde pd(x) es la función que devuelve el primer dígito del número (esta expresión está limitada a numeros positivos, pero son los que interesan en nuestro caso). Esto nos indica que el primer dígito será:

1 si 0 ≤ {log10 x} <  log10 2
2 si  log10 2 ≤ {log10 x} <  log10 3
...
9 si  log10 9 ≤ {log10 x} <  1 

Por lo tanto, la determinación de la primera cifra de la expansión decimal de un número se reduce a encontrar en cual de estos intervalos cae la parte fraccionarial de su logaritmo en base 10. Si aplicamos esto a las potencias de dos tenemos que

 {log10 2n} = {n log10 2}.

Por consiguiente, la determinación de los posibles valores de los primeros dígitos de las potencias de dos se reduce a encontrar los sectores del intervalo [0, 1) donde caen las partes fraccionarias de los múltiplos de log10 2. Encontrar cuáles son estos sectores y cuál es la conexión de todo esto con las figuras de Lissajous quedará para el próximo post por razones de tiempo :-)

martes, 25 de noviembre de 2008

Lissajous I

Es bien conocido que las representaciones decimales de las potencias de diez comienzan con un dígito '1' y terminan con un dígito '0' (exceptuando el caso trivial de 10 = 1; se supondrán exponentes no nulos en el resto del post). Lo mismo sucederá, cualquiera sea el valor de n, con las representaciones en base n de las potencias de n.

Lo que no es tan claro es el comportamiento de los dígitos en una base dada, por ejemplo 10, de las potencias de un número distinto a la base. Si consideramos el caso de las potencias de 2 expresadas en base 10, podemos ver que terminarán en un dígito par ya que son números pares. Pensando un poco más, podemos ver que nunca terminarán en '0', ya que para ello deberían ser múltiplos de 10 y, por lo tanto, múltiplos de 5. Pero ciertamente quedaría en duda qué otros valores particulares podrán tomar los últimos dígitos...

Los que memorizamos potencias de dos como parte de nuestra actividad académica/profesional :-) podemos ver que:

21 = 2
22 = 4
24 = 16
23 = 8

y que, por lo tanto, todos los otros dígitos pares aparecen al final de las potencias de dos. Pero este es un proceso de enumeración exhaustiva, claramente insatisfactorio a la hora de obtener entendimiento de un proceso.

La primera incógnita que quedará planteada para el próximo post es determinar "que regla" siguen estos dígitos y, de mayor interés, qué regla siguen los dígitos más significativos; por ejemplo, puede una potencia de dos empezar con 9? El otro interrogante será ver que conexión tiene esto con las figuras de Lissajous...

sábado, 15 de noviembre de 2008

Solución y otras cosas

A continuación se muestra la solución al problema planteado en el último post. Para verla hacer click aquí.


El programa eventualmente se detiene, pero después de unas 22040 iteraciones del loop externo. Por lo tanto, para fines prácticos, puede considerarse como un loop infinito... Es claro que devuelve cero, ya que tuvo que salir del loop externo para poder terminar. A continuación se describe con algo más de detalle el funcionamiento del programa.

Es claro que el único punto confuso es la acción de la línea "while (!++*p++);", el resto del programa es bastante normal. Para interpretarla, puede verse teniendo en cuenta la prioridad de los operadores que es equivalente a realizar "while (!(++(*(p++))));". Esto corresponde a:
  1. Incrementar p devolviendo su valor original, que podemos llamar p'.
  2. Incrementar el valor apuntado por p'.
  3. Si el valor apuntado por p' es ahora 0, volver al paso 1. En caso contrario, salir.
Como buffer se inicializa con ceros al ser variable global y p apunta inicialmente a buffer[0], tenemos que en la primera iteración del loop externo:
  • (1) p <-- &buffer[1]
  • (2) buffer[0] <-- 1
  • (3) Sale porque buffer[0] != 0
En la segunda iteración tendremos:
  • (1) p <-- &buffer[1]
  • (2) buffer[0] <-- 2
  • (3) Sale porque buffer[0] != 0
Esto continuará hasta que buffer[0] sea 255. En ese momento sucederá lo siguiente:
  • (1) p <-- &buffer[1]
  • (2) buffer[0] <-- 0
  • (3) Vuelve a "1" porque buffer[0] == 0
  • (1') p <-- &buffer[2]
  • (2') buffer[1] <-- 1
  • (3') Sale porque buffer[1] != 0
Si se observa el comportamiento en detalle, puede verse que tenemos un contador en el que cada posición de buffer actúa como un dígito base 256. Para salir es necesario que al finalizar el loop interno p sea igual a q. Pero esto implicaría que se acaba de incrementar a buffer[255], lo que no sucederá hasta que hayan ocurrido 256255 == 28*255 == 22040 iteraciones del loop externo.


En otro área completamente diferente, encontraron en México una caverna llena de extraordinarios cristales de yeso hidratado (sulfato de calcio):


En el sitio de National Geographic pueden observarse otras fotos espectaculares y más detalles sobre el descubrimiento (por ejemplo, el porqué de los trajes naranjas :-). (Via Robin Hanson.)

lunes, 10 de noviembre de 2008

C Puzzle

El problema es determinar si el siguiente programa:
  • termina de ejecutarse;
  • en caso de hacerlo, cuanto demora y qué devuelve al sistema operativo.

static unsigned char buffer[256];

int main(void)
{
unsigned char *p, *q;
q = (p = buffer) + sizeof(buffer);
while (q - p)
{
p = buffer;
while (!++*p++);
}
return p - q;
}

Las respuestas a estas preguntas en unos días... :-D

domingo, 2 de noviembre de 2008

Detectando dígitos en un 8051

Uno de los problemas que aparecieron en un examen de Labo de Micros reciente (que le tomaron a mi hermano) indicaba hacer una "función" en assembly 8051 tal que detectara si el valor que se le pasaba era un dígito. Más especificamente, debían volver con C en 1 si y solo si el valor no era un dígito.

Yo siempre había pensado el problema de la forma obvia, algo así como:

no_es_digito:
clr C
subb A, #'0'
jc no_es_dig_end
subb A, #10
cpl C
no_es_dig_end:
ret

Pero a mi hermano le dijeron que podía hacerse con cinco instrucciones, lo que me hizo pensar en más detalle... hasta que vi que la resta no era la única solución. Eso me llevó al siguiente código:

no_es_digito:
add A, #(256 - '0')
add A, #(256 - 10)
ret

El primer add lleva, en forma modular, el valor desde el rango ASCII '0' ... '9' al rango 0x00 ... 0x09. En base a eso es simple ver que el segundo add dará como resultado C = 1 solo si el valor es 0x0a o mayor. Por lo tanto, solo dará C = 1 si el caracter pasado originalmente no cae en el rango '0' ... '9'.

No creo que pueda hacerse con dos instrucciones, ya que las instrucciones que alteran C en base al contenido del acumulador son aritméticas (según recuerdo!) y solo pueden "detectar" valores mayores o menores a uno especificado... pero el desafío queda abierto :-D

sábado, 18 de octubre de 2008

Sistemas de ecuaciones booleanos

Hace unos días un compañero de la facultad (hola Guille!) me comentaba sobre una clase de problemas que suelen aparecer en los parciales de Matemática Discreta (al menos en la FIUBA). Recordaba que estos problemas consistían en encontrar una función booleana que de como resultado 1 si y solo si los valores de sus parámetros satisfacen un sistema de ecuaciones booleano y eso me hizo recordar un "método" que había desarrollado cuando la había cursado (aunque creo que no tuve ocasión de usarlo en el parcial).

Nota: Las "barras" que se suelen utilizarse para indicar complemento no se expresan fácilmente en HTML. Esto puede resolverse utilizando el operador "¬". Las otras dos operaciones pueden expresarse por "∧" (AND) y "∨" (OR). Por simplicidad, podemos asumir que "∧" tiene prioridad sobre "∨".

Para empezar podemos demostrar que dada una variable y satisfaciendo x y = 0 y xy = 1 sabemos que y = ¬x (o sea la unicidad del complemento). Una forma es viendo que:

(1) xy = 1 (Hipótesis)
(2) ¬x ∧ (xy) = ¬x ∧ 1 (Aplico la misma operación a ambos miembros)
(3) ¬x ∧ (xy) = ¬x (Definición de 1)
(4) ¬xx ∨ ¬xy = ¬x (Propiedad distributiva)
(5) 0 ∨ ¬xy = ¬x (Definición de complemento)
(6) xy = 0 (Hipótesis)
(7) xy ∨ ¬xy = ¬x (Reemplazando 6 en 5)
(8) (x ∨ ¬x) ∧ y = ¬x (Propiedad distributiva)
(9) 1 ∧ y = ¬x (Definición de complemento)
(10) y = ¬x (Definición de 1)

Otro "lema" que es útil indica que dados x e y tales que xy = 1, puede decirse que x = 1 y que y = 1. Una forma de demostrarlo es la siguiente:

(1) xy = 1 (Hipótesis)
(2) xyx¬y = 1 ∨ x¬y (Aplico la misma operación a ambos miembros)
(3) xyx¬y = 1 (Propiedad absorbente)
(4) x ∧ (y¬y) = 1 (Propiedad distributiva)
(5) x ∧ 1 = 1 (Definición de complemento)
(6) x = 1 (Definición de 1)

Por simetría podemos ver que y = 1 y podemos evitar demostrar la propiedad absorbente para poder llegar alguna vez al tema a tratar :-)

Ahora, podemos ver a todo sistema de ecuaciones como una serie de igualdades entre funciones que requieren satisfacerse. Por ejemplo:

f1(x, y, ...) = g1(x, y, ...)
f2(x, y, ...) = g2(x, y, ...)
f3(x, y, ...) = g3(x, y, ...)
...

Ahora, como sabemos que x y = 0 y xy = 1 implican x = ¬y y que xy = 1 implica que tanto x como y son iguales a 1, podemos expresar una igualdad arbitraria a = b como ¬(a ∧ ¬b) ∧ (a ∨ ¬b) = 1 aplicando lo siguiente:

Ida

(1) a = b (Hipótesis)
(2) a = ¬(¬b) (Propiedad involutiva del complemento)
(3) a ∧ ¬b = 0 (Definición de complemento)
(4) a ∨ ¬b = 1 (Definición de complemento)
(5) ¬(a ∧ ¬b) = ¬0 (Aplico la misma operación a ambos miembros)
(6) ¬(a ∧ ¬b) = 1 (Aplico ¬0 = 1)
(7) ¬(a ∧ ¬b) ∧ (a ∨ ¬b) = 1 (Aplico ∧ miembro a miembro sobre 4 y 6)

Vuelta

(1) ¬(a ∧ ¬b) ∧ (a ∨ ¬b) = 1 (Hipótesis)
(2) ¬(a ∧ ¬b) = 1 (Propiedad demostrada anteriormente)
(3) a ∨ ¬b = 1 (De 1 por propiedad demostrada anteriormente)
(4) ¬¬(a ∧ ¬b) = ¬1 (Aplico la misma operación a ambos miembros)
(5) a ∧ ¬b = ¬1 (Propiedad involutiva del complemento)
(6) a ∧ ¬b = 0 (Aplico ¬1 = 0)
(7) a = ¬¬b (Aplico otra propiedad demostrada anteriormente sobre 3 y 6)
(8) a = b (Propiedad involutiva del complemento)

Si omitimos que no demostramos ni la propiedad involutiva del complemento ni que ¬0 = 1 (las demostraciones son simples, quedan como ejercicio para el lector :-), sabemos como transformar una ecuación arbitraria en una igualada a 1. Ahora podemos aplicar que xy = 1 implica que x = y = 1 para expresar el sistema de ecuaciones anterior como:

¬(f1(x, y, z, ...) ∧ ¬g1(x, y, z, ...)) ∧
(f1(x, y, z, ...) ∨ ¬g1(x, y, z, ...)) ∧
¬(f2(x, y, z, ...) ∧ ¬g2(x, y, z, ...)) ∧
(f2(x, y, z, ...) ∨ ¬g2(x, y, z, ...)) ∧
¬(f3(x, y, z, ...) ∧ ¬g3(x, y, z, ...)) ∧
(f3(x, y, z, ...) ∨ ¬g3(x, y, z, ...)) ∧
... = 1

Esto cumple en forma automática el objetivo del problema aunque puede ser algo tedioso...

sábado, 2 de agosto de 2008

Me pasó otra vez...

...Google insiste en confundirme con un bot. No se que es lo que confundirá a sus algoritmos de detección, aunque sospecho de mi tendencia a abrir múltiples pestañas cuando veo una lista de links :-)


Por si fuera poco, el CAPTCHA también me clasificó como "no humano", ya que realmente me resultaba difícil ver que es lo que decía. Tengo la sospecha de que navegar por Internet en el futuro va a ser realmente interesante...

viernes, 18 de julio de 2008

spice3f5 para Visual C++

Acabo de agregar a la página de mi proyecto de Tesis una versión de spice3f5 compilable con Microsoft Visual C++ 2008. Esto permite realizar fácilmente análisis de una netlist cualquiera (aunque no incluye manual de uso :-)

miércoles, 2 de julio de 2008

Un problema de probabilidad

En un juego, se presentan a una persona dos sobres. Sabe que uno de ellos contiene una cantidad desconocida de dinero 'A', y que el otro contiene el doble de esa cantidad, pero el jugador desconoce "cuál es cual". Se le dice que puede llevarse solo uno de los sobres y que debe llevarse el último sobre que abra.

La pregunta es: después de haber elegido y abierto un sobre, le conviene cambiarlo por el otro?

La respuesta intuitiva es "no", ya que ver el contenido del sobre no nos permite discriminar entre el caso en que elegimos el sobre con la cantidad A y el caso en que elegimos el sobre con al cantidad 2A. Al no tener evidencia proveniente de ver el contenido del sobre, el intercambio nos sería indiferente.

Ahora supongamos que calculamos el valor esperado del contenido del sobre no abierto, al que denominaremos "sobre 2". Si llamamos B al contenido del sobre abierto, el sobre 2 puede tener contenido B/2 o 2B. Asumiendo la indiferencia mencionada anteriormente, el valor esperado del contenido del sobre 2 sería:

E(contenido sobre 2) = 1/2 B/2 + 1/2 2B = B/4 + B = 5/4 B

Esto implicaría que siempre nos sería conveniente realizar el intercambio!

Más detalles sobre el problema y sus implicaciones en Wikipedia...

miércoles, 30 de abril de 2008

Factories en C++

Las implementaciones normales de factories en C++ suelen introducir la necesidad de modificar la clase Factory o su inicialización para cada clase que se agrega. Los intentos de evadir esta centralización utilizando la inicialización estática suelen ser algo como lo que aparece en el ejemplo factory_mal (es claro que hay algo mal :-) .

Este ejemplo utiliza la inicialización de variables globales para realizar el registro de los factory methods. En muchos casos esto parece funcionar, de hecho me acaba de funcionar al probarlo, pero contiene un error grave: asume un orden de inicialización de objetos estáticos entre distintos archivos (bueno, "unidades de traducción").

El problema en que no puede suponerse que los distintos Registrators vayan a inicializarse después que Factory::cm_. Si los Registrators se inicializan primero, Factory::registrate() accederá a un map no inicializado, probablemente llevando a un segmentation fault.

La solución a esto es la misma que la aplicada en el Singleton de Meyers: utilizar una variable estática dentro de un método. De este modo podemos garantizar que el objeto estará inicializado cuando sea utilizado.

Obviamente esta solución no maneja los problemas con múltiples threads, ni arregla mágicamente los problemas de diseño que puedan ser provocados por el uso indebido de singletons, pero soluciona el problema de la inicialización.

sábado, 5 de abril de 2008

Relatividad en 6 minutos

(Vía John Walker)
Sí, un video musical por Max Tegmark! No puedo imaginarme algo así en la FIUBA aunque, en realidad, tampoco lo imaginaba en el MIT :-D



El paper con la letra...

jueves, 3 de abril de 2008

Un robot distinto

BigDog: un robot que parece "útil". Acostumbrado a ver la forma vacilante en que suelen caminar los robots me impresionó mucho la estabilidad y adaptabilidad que despliega BigDog. Tal vez hay razones para el optimismo en la robótica :-)

miércoles, 19 de marzo de 2008

Muere Arthur Clarke a los 90 años

Probablemente, su idea más conocida es la que apareció en esta tapa de diario, allá por el año 1946 y en una revista en el año 1945 con más detalle. Un anticipo de DirectTV... aunque con tripulaciones yendo a cambiar las válvulas :-)

Probablemente esta mezcla de aciertos y desaciertos sea inherente a tratar de predecir tecnologías futuras; como dice la frase, "es difícil hacer predicciones, sobre todo acerca del futuro". Probablemente allá por el año 2200 se reirán de algunas de las predicciones que se hacen hoy día acerca de la nanotecnología.

lunes, 17 de marzo de 2008

Proyecto para Labo de Micros

Acá está todo el software del proyecto que hicimos (junto con Mariano Beiró) para la materia Laboratorio de Microcomputadoras: un juego de Ta-Te-Ti capaz de conectarse a cualquier televisor (con audio incluido!). Una de las cosas más particulares que tenía el software era el uso de un tabla comprimida con Huffman para determinar que jugada realizar en base a la posición del tablero. Algunas fotos:



La imagen proyectada sobre una PC cuando hicimos la presentación (como la salida era video compuesto la tomaba sin problemas).



Una vista de la plaqueta principal.

domingo, 16 de marzo de 2008

Graficando funciones discontinuas con Mathematica

Mientras estaba tratando de ilustrar el comportamiento de las funciones racionales elípticas dentro de la Tesis, me encontré con uno de los problemas del comando Plot[] del Mathematica. Este comando, al ir conectando puntos donde evalúa a la función, termina dibujando una línea en donde debería haber una discontinuidad. Por ejemplo, si uno ejecuta el comando Plot[1/(x-1),{x,-3,3}] obtiene el siguiente resultado:












Puede verse claramente que hay una línea vertical en x = 1.

Después de encontrar en la Web varias opciones que no me resultaban satisfactorias, tales como actualizar a la versión 6 o dividir el gráfico en segmentos con dominios donde la función es continua (estaba trabajando con una función con 4 polos), decidí acotar el valor de la función y reemplazar los valores superiores con valores indeterminados.

Esta solución aplicada al problema de mostrar correctamente al gráfico anterior quedaría como:

f[x_]:=1/(x-1);
fp[x_]:=Module[{},
ret=f[x];
If[Abs[ret]>30,Indeterminate,ret]];
Off[Plot::plnr];
Plot[fp[x],{x,-3,3}];
On[Plot::plnr];












Los comandos Off[] y On[] desactivan y activan respectivamente la emisión del error Plot::plnr, producido por la función Plot[] al encontrarse con valores no numéricos.

En otros temas, más allá de las funciones elípticas, dos interesantes fotos de objetos de oro sólido (particularmente apropiados con el oro a más de $100000 el kg :-)
  1. Una pepita de oro de más de 4 kg.
  2. Un ladrillo de 220 kg de oro (más de $22000000 en la actualidad).
Una cosa a destacar es que 220 kg de oro solo tienen un volumen de 11.4 litros, ya que el oro es uno de los elementos más densos (si, más que el plomo). O sea que podríamos meter $22000000 en un cubo de 23 cm de arista... aunque sería algo difícil de levantar.