Sunday, January 2, 2011

Dark Fortran: EQUIVALENCE / Fortran oscuro: EQUIVALENCE

Lately I've been very busy at work translating a library, written in old Fortran 77, into C++.
Besides finding a lot of goto statements and really "creative" ways of passing arguments that change the control flow of functions, I've come across several pretty funny sentences that I've never seen before, such as, for instance, EQUIVALENCE.
Googling a bit I found a site that has turned very useful:
Forbidden / Esoteric / Obsolete Statements
In this site, you can find out the origins of many of this Fortran statements, often present in old Fortran 77 legacy code, and what they are for. In addition to this, whenever possible, alternatives to these sentences are proposed which are more compatible with good programming practices.

---------------------------------------------------------------------------------------------

Últimamente he estado bastante liado en el trabajo reescribiendo en C++ algunas librerías bastante antiguas que estaban programadas en Fortran 77.
Además de infinitud de sentencias goto, y formas "creativas" de pasarle opciones a funciones, me encontré con algunas sentencias bastante curiosas que nunca había visto, como, por ejemplo, EQUIVALENCE.
Buscando en Google encontré un sitio que me ha resultado muy útil:,
Forbidden / Esoteric / Obsolete Statements
En él se explica el origen y funcionamiento de ciertas sentencias de Fortran 77 que uno puede encontrar (desgraciadamente, con frecuencia) en códigos heredados que tengan cierta antiguedad.
Además de esto, siempre que existan, proponen alternativas compatibles con buenas prácticas de programación.

Saturday, March 27, 2010

Usando dSFMT

Antes de empezar el Master de Física Computacional y Aplicada sólo había usado generadores de números aleatorios escritos en Fortran.
En la asignatura Simulación con Dinámica Molecular y Monte Carlo tuve que hacer una simulación del modelo de Ising para la que necesitaba un buen generador escrito en C.
Googleando un poco acabé encontrando el Mersenne Twister desarrollado en 1997 por Makoto Matsumoto y Takuji Nishimura, que, según Wikipedia:
... provides for fast generation of very high-quality pseudorandom numbers, having been designed specifically to rectify many of the flaws found in older algorithms.
Ahora para el proyecto final del máster estoy utilizando la versión 2.1 en C del Double precision SIMD-oriented Fast Mersenne Twister (dSFMT) que es una variante del Mersenne Twister que introdujeron Mutsuo Saito y Makoto Matsumoto en el 2006. La librería se puede descargar de la página web de los inventores.

Esta variante es mucho más rápida que el MT en la mayoría de las plataformas. Además cuenta con la ventaja de que proporciona directamente numeros reales de doble precisión.

Existen implementaciones de SFMT en otros lenguajes (aún no de dSFMT) que han sido realizadas por voluntarios.

Lo último que han hecho ha sido una versión del MT para GPUs.

Funciones con un número indeterminado de argumentos en C

En el proyecto de fin de master utilicé listas de argumentos variables para poder pasar diferentes sistemas de ecuaciones diferenciales a la función con la que calculo exponentes de Lyapunov.

Como ese código es demasiado largo para ponerlo aquí, pondré las pequeñas pruebas que hice para aprender.

Se puede encontrar una explicación muy buena de las funciones variádicas (funciones que pueden aceptar un número indeterminado de argumentos) en un apartado del apéndice A del manual de The GNU C Library.
En este manual hay un ejemplo en el que se le pasa a una función un número indeterminado de enteros para que esta devuelva su suma.
Este es el ejemplo del manual con los comentarios traducidos y resaltando las líneas más importantes:
#include 
#include 

int suma (int count,...)
{
    va_list ap; //Lista de parámetros
    int i, sum;

    /* Inicializa la lista de argumentos */
    va_start (ap, count);         

    sum = 0;
    for (i = 0; i < count; i++)
    {
        /* Obtiene el siguiente argumento. */
        sum += va_arg (ap, int);    
    }
    
    /* Limpia la lista */
    va_end (ap);       
    
    return sum;
}

int main(void)
{
    /* Esta llamada imprime 16. */
    printf ("%d\n", suma (3, 5, 5, 6));

    /* Esta llamada imprime 19. */
    printf ("%d\n", suma (10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
    
    return (0);
}
En este ejemplo se debe destacar lo siguiente:
  • Para poder pasar un número indeterminado de argumentos se debe incluir la cabecera stdarg.h de la librería estándar de C.
  • En la lista de argumentos se usan tres puntos para indicar el comienzo de los argumentos variables.
  • Se debe declarar una variable de tipo va_list que se encargará de contener la lista de argumentos.
  • Se utiliza la macro va_start para inicializar la lista indicándole cuál es el último argumento fijo.
  • Se utiliza la macro va_arg para extraer los argumentos de la lista indicándole cuál es su tipo de datos. 
  • Se utiliza la macro va_end para limpiar la memoria.  
Este es el resultado de ejecutar el ejemplo anterior:
$ ./test1 
16
55
En este otro ejemplo usé el mismo procedimiento para pasar un número indeterminado de argumentos variables con tipos de datos definidos por el usuario:
#include  
#include 

typedef struct s_Fecha
{
    unsigned int anyo;
    unsigned int mes;
    unsigned int dia;
}t_Fecha;

t_Fecha setFecha(unsigned short dia, unsigned short mes,unsigned short anyo);
void imprimeFecha(t_Fecha fecha);

/* Imprime varias fechas */
void imprimeFechas(unsigned int numFechas, ...)
{
    va_list ap;
    int i;

    /* Se inicializa la lista de argumentos */
    va_start (ap, numFechas);         

    for (i = 0; i < numFechas; i++)
    {
        // Se saca el siguiente argumento de la lista
        imprimeFecha(va_arg (ap, t_Fecha));
    }
    
    printf("\n");
    
    //Se limpia la memoria
    va_end (ap);          
    
    return;
}

int main(void)
{
    t_Fecha fecha1, fecha2, fecha3;
    
    fecha1=setFecha(1,1,2009);
    fecha2=setFecha(2,2,2009);
    fecha3=setFecha(3,3,2009);
    
    /* Esta llamada imprime 1/1/2009 */
    imprimeFechas(1, fecha1);

    /* Esta llamada imprime 1/1/2009 2/2/2009*/
    imprimeFechas(2, fecha1, fecha2);
    
    /* Esta llamada imprime 1/1/2009 2/2/2009 3/3/2009*/
    imprimeFechas(3, fecha1, fecha2, fecha3);

    return (0);
}

/* Devuelve una variable de tipo t_Fecha */
t_Fecha setFecha(unsigned short dia, unsigned short mes,unsigned short anyo)
{
    t_Fecha fecha;
    
    fecha.dia=dia;
    fecha.mes=mes;
    fecha.anyo=anyo;
    
    return fecha;
}

/*Imprime una fecha*/
void imprimeFecha(t_Fecha fecha)
{
    printf("%d/%d/%d ",fecha.dia,fecha.mes,fecha.anyo);
    return;
}
Este es el resultado:
$ ./test2 
1/1/2009 
1/1/2009 2/2/2009 
1/1/2009 2/2/2009 3/3/2009 

Agenda mensual con calendario en PHP (y V)

Por último la clase Mes.
Las clases van contenidas unas dentro de otras a modo de "matrioska".
La clase Mes es la que conecta con la BD.
/* Clase Mes */
class Mes
{
    private $anyo;    
    private $mes;
    private $numDiasMes;
    private $numSemanasMes;
    private $semanas;

    
    /* Constructor */
    public function __construct($anyo, $mes)  
    {
        //Inicializo los miembros
        $this->setAnyo($anyo);
        $this->setMes($mes);
        $this->numDiasMes = Fechas::getFinMes($anyo,$mes);
        $this->crearSemanasMes();  
        $this->addEventos();
    }       
    
    /* Métodos de acceso */
    public function setAnyo($anyo) 
    {
        $this->anyo = $anyo;
    }

    public function getAnyo() 
    {
        return $this->anyo;
    }
    
    public function setMes($mes) 
    {
        $this->mes = $mes;
    }

    public function getMes() 
    {
        return $this->mes;
    }

    public function getNumDiasMes() 
    {
        return $this->numDiasMes;
    }

    public function getNumSemanasMes() 
    {
        return $this->numSemanasMes;
    }
   
    public function getSemanas() 
    {
        return $this->semanas;
    }
    
    /* Métodos */
    
    /* Genera las semanas de un mes */
    private function crearSemanasMes()
    {   
        /*Se inicializan las variables antes del bucle*/
        
        $i = 0;
        
        /*Dia en que nos encontramos al recorrer la agenda*/
        $diaActual = 1; //Se inicializa a 1
        
        /*Mientras no hayamos entrado en otro mes*/
        while( $diaActual <= $this->numDiasMes )
        {
            /*Se crean las semanas*/  
            $this->semanas[$i] = new Semana();
            
            $this->semanas[$i]->creaDiasSemana($diaActual, $this->anyo,
                                               $this->mes,$this->numDiasMes);
            
            $i = $i + 1;
        }  
        
        /*Se guarda el número de semanas del mes*/
        $this->numSemanasMes = $i;
       
    }
    
    /* Añade un evento a la agenda */
    public function addEvento($dia, $evento) 
    {
        $numSemanaIni = Fechas::getSemanaAnyo($this->anyo, $this->mes, 1);
        
        $numSemana = Fechas::getSemanaAnyo($this->anyo, $this->mes, $dia);
        
        //Si la primera semana del año es la 59
        if(53==$numSemanaIni)
        {
            $numSemanaIni = 0;
            
            //Si además el evento es esa semana
            if(53==$numSemana)
            {
                $numSemana = 0;    
            }
        }
        
        $indiceSemana = $numSemana - $numSemanaIni;
        
        $diaSemana = Fechas::getDiaSemana($this->anyo, $this->mes, $dia);
        
        $this->semanas[$indiceSemana]->setEvento($evento, $diaSemana);
    }
    
    /* Obtiene un evento de la agenda */
    public function getEvento($dia) 
    {
        $numSemanaIni = Fechas::getSemanaAnyo($this->anyo, $this->mes, 1);
    
        $numSemana = Fechas::getSemanaAnyo($this->anyo, $this->mes, $dia);
        
        //Si la primera semana del año es la 59
        if(53==$numSemanaIni)
        {
            $numSemanaIni = 0;
            
            //Si además el evento es esa semana
            if(53==$numSemana)
            {
                $numSemana = 0;    
            }
        }
        
        $indiceSemana = $numSemana - $numSemanaIni;
        
        $diaSemana = Fechas::getDiaSemana($this->anyo, $this->mes, $dia);           
        
        return $this->semanas[$indiceSemana]->getEvento($diaSemana);
    }
    
    /* Muestra un evento de la agenda */
    public function muestraEvento($dia) 
    {
        $numSemanaIni = Fechas::getSemanaAnyo($this->anyo, $this->mes, 1);
    
        $numSemana = Fechas::getSemanaAnyo($this->anyo, $this->mes, $dia);
        
        //Si la primera semana del año es la 59
        if(53==$numSemanaIni)
        {
            $numSemanaIni = 0;
            
            //Si además el evento es esa semana
            if(53==$numSemana)
            {
                $numSemana = 0;    
            }
        }
        
        $indiceSemana = $numSemana - $numSemanaIni;
        
        $diaSemana = Fechas::getDiaSemana($this->anyo, $this->mes, $dia);           
        
        $this->semanas[$indiceSemana]->muestraEvento($diaSemana);
    }
    
    /* Muestra el HTML de un mes */
    public function muestraCalendario()
    {   
        $nombreMeses = array('Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio',
                             'Agosto','Septiembre','Octubre','Noviembre','Diciembre');
        $nombreDias = array('Lun','Mar','Mie','Jue','Vie','Sab','Dom');                                  
        
        
        /*Se obtiene el nombre del mes*/
        $nombreMes = $nombreMeses[$this->mes - 1];
        
        
        /*Se colocan los botones*/
        printf("<table class=\"tablaBotones\"><tr><td>
                <input type=\"submit\" name=\"restaMes\" value=\"<<\" onClick=\"verMesAnterior()\">
                </td><td class=\"caption_text\">%s %d</td><td>
                <input type=\"submit\" name=\"sumaMes\" value=\">>\" onClick=\"verMesSiguiente()\">
                </td></tr><table>\n", $nombreMes, $this->anyo);
                
        /*Se abre la tabla*/
        printf("<table>\n");
        
        /*Se abre el cuerpo de la tabla*/ /*Se abre la tabla*/
        printf("<tbody>\n");
        /*Se crea la primera fila de la tabla*/
        printf("<tr>\n");
        
        /*La primera casilla va vacía*/
        printf("<th class=\"dia\"></th>\n");
        
        /*Se generan las casilla de los días de la semana*/
        for($i=0;$i<7;$i++)
        {   
           /*Se escribe en el HTML el texto del día de la semana */
           printf("<th class=\"dia\"> %s </th>\n", $nombreDias[$i]);     
        }
     
        printf("</tr>\n");

        /* Semanas */
        $this->muestraSemanas();
        
        /*Se cierra el cuerpo de la tabla*/
        printf("</tbody>\n");
        
        /*Se cierra la tabla*/
        printf("</table>\n");

    }
    
    /* Genera el HTML de todas las semanas del mes */
    public function muestraSemanas()
    {
        foreach($this->semanas as $semana)
        {
            $semana->muestraSemana();
        }
    }
    
    /* Añade al calendario del mes los eventos que están en la BD */
    public function addEventos()
    {
        require_once 'login.php';
        
        //Se intenta conectar con la BD
        $db_server = mysql_connect($db_hostname,$db_username,$db_password);
                
        //Si ha habido errores se aborta y se informa al usuario
        if(!$db_server)
        {
            die( "No se pudo conectar con MySQL: " . mysql_error() );
        }
        
        mysql_select_db($db_database)
            or die( "No se pudo selecionar la BD: " . mysql_error() );
        
        //Consulta SQL
        $query = "select id_evento, nombre, DATE_FORMAT(fecha_hora,'%d-%m-%y'),
                  TIME_FORMAT(fecha_hora,'%H:%k'), descripcion, lugar, DAY(fecha_hora)
                  from calendariov where MONTH(fecha_hora) = '$this->mes'
                  and YEAR(fecha_hora) = '$this->anyo'";
        
        $result = mysql_query($query);

        //Si hubo fallos se informa al usuario
        if(!$result)
        {
            die("Fallo en el acceso a la BD: " . mysql_error());
        }

        //Si todo fue bien
        $numEventos = mysql_num_rows($result);

        for($j = 0;$j < $numEventos; ++$j)
        {
            $row = mysql_fetch_row($result);
                      
            //Se inserta el evento en el calendario
            $this->addEvento( (int) $row[6], new Evento($row[0], $row[1], $row[2], 
                                                        $row[3], $row[4], $row[5]));
        }

        mysql_close($db_server);

    }
            
}  

?>     

Agenda mensual con calendario en PHP (IV)

Esta es la clase Semana.
/* Clase Semana */
class Semana
{
    /* Miembros */
    private $dias;
    private $numSemanaAnyo;
    
    /* Métodos de acceso */
    public function getNumSemanaAnyo() 
    {
        return $this->numSemanaAnyo;
    }
    
    public function getDias() 
    {
        return $this->dias;
    }
    
    /* Metodos */
    
    /* Genera los días de una semana */
    public function creaDiasSemana(&$diaActual, $anyo, $mes, $finMes)
    {          
        /*Se crea la semana*/
        $this->numSemanaAnyo = Fechas::getSemanaAnyo($anyo, $mes, $diaActual);
         
        $diaSemana = Fechas::getDiaSemana($anyo, $mes, $diaActual);
        
        /*Se crea el dia actual*/
        $diaActAux = $diaActual; 
        
        /*Se calcula lo que hay que retroceder*/
        $atras = $diaSemana - 1;
        
        /*Retrocedemos*/
        for($i = 0; $i < $atras; $i++)
        {
            /*Se crea un nuevo día*/
            $this->dias[$diaSemana - 2 - $i] = new Dia($diaSemana - 1 - $i,0,false);
        }
             
        /*Se calcula lo que hay que avanzar*/
        $adelante= 7 - $diaSemana + 1;
        
        /*Avanzamos*/
        for($i = 0; $i < $adelante; $i++)
        {   
            /*Se comprueba si el día está dentro del mes*/
            $perteneceAlMes = ( $diaActAux <= $finMes );

            /*Se crea un nuevo día*/
            $this->dias[$diaSemana - 1 + $i] = new Dia($diaSemana + $i, $diaActAux, $perteneceAlMes);
            
            /*Se incrementa el valor del día actual si se está dentro del mes*/    
            $diaActAux = $diaActAux + 1;
        }
         
        /*Se actualiza el día actual de forma que la próxima semana empiece en Lunes*/
        $diaActual = $diaActual + $adelante;
        return;
    }
    
    public function setEvento($evento, $diaSemana)
    {
        $this->dias[$diaSemana - 1]->setEvento($evento);
    }
    
    public function getEvento($diaSemana)
    {
        return $this->dias[$diaSemana - 1]->getEvento();
    }
    
    public function muestraEvento($diaSemana)
    {
        $this->dias[$diaSemana - 1]->muestraEvento();
    }
    
    /* Muestra el HTML correspondiente a una semana */
    public function muestraSemana()
    {
        /*Se abre la fila*/
        printf("<tr>\n");
        
        /*La primera columna lleva el número de semana*/
        printf("<td class=\"semana\"> %d </td>\n", $this->numSemanaAnyo);
        
        /*Se genera el HTML de los días*/
        foreach($this->dias as $dia)
        {
            $dia->muestraDia();
        }
        
        /*Se cierra la fila*/
        printf("</tr>\n"); 
    }
}