David Madore's WebLog: 2010-03

This WebLog is bilingual, some entries are in English and others are in French. A few of them have a version in either language. Other than that, the French entries are not translations of the English ones or vice versa. Of course, if you understand only English, the English entries ought to be quite understandable without reading the French ones.

Ce WebLog est bilingue, certaines entrées sont en anglais et d'autres sont en français. Quelques-unes ont une version dans chaque langue. À part ça, les entrées en français ne sont pas des traductions de celles en anglais ou vice versa. Bien sûr, si vous ne comprenez que le français, les entrées en français devraient être assez compréhensibles sans lire celles en anglais.

Note that the first entry comes last! / Notez que la première entrée vient en dernier !

Index of all entries / Index de toutes les entréesXML (RSS 1.0) • Recent comments / Commentaires récents

Entries published in March 2010 / Entrées publiées en mars 2010:

(samedi)

La Loi du Sommeil de David

J'avais déjà constaté ça empiriquement depuis bien longtemps, mais je crois avoir réussi à dégager assez précisément la façon dont fonctionne mon sommeil. En général, je dors plutôt bien. C'est-à-dire, si j'ai un lit raisonnablement confortable et que je n'ai pas de besoin particulier de me réveiller, je fais de bonnes nuits, j'ai besoin de sept ou huit heures de sommeil mais je peux me contenter d'un peu moins sans être trop dérangé, bref, tout va bien. Le problème, c'est la règle suivante, qui est un peu hallucinante :

Loi du Sommeil de David : Si, à un moment donné, je suis réveillé, et qu'il est prévisible que je doive me lever dans moins de x heures (c'est-à-dire, que je ne pourrai plus dormir après), alors j'ai beaucoup de mal à m'endormir ; ici, x dépend du temps que j'ai déjà dormi, mais il est toujours compris entre 3½ (quand j'ai déjà dormi) et 9 (quand je n'ai pas dormi).

Autrement dit, si je me couche, disons, à 1h du matin, et que je n'ai pas de raison particulière de mettre un réveil, alors je me lèverai typiquement vers 8h ou 9h du matin. En revanche, si, toujours en me couchant à 1h du matin, je sais que je devrai me lever à 8h, même si sept heures de sommeil a priori me suffisent, cela me stressera si bien que j'aurai énormément de mal à m'endormir, je n'y arriverai que vers 2h ou 3h du matin et, du coup, je n'aurai effectivement pas assez dormi. Ceci ne dépend pas de la façon dont je serai réveillé (par un réveil, par mon poussinet qui me fait des bisous dans le cou, ou par n'importe quel autre moyen) : ce qui m'empêche de dormir est la certitude que je devrai être réveillé, pas la façon dont je le serai. Pas non plus le fait que je serai réveillé : si, par exemple, je me couche à 1h et que mon poussinet doit partir à 6h du matin pour prendre son train pour Bordeaux[#] et que je sais qu'il me réveillera en partant, si je sais que je peux dormir autant que je veux ensuite, ça ne pose pas de problème : le poussinet me réveille, mais je ne dois pas me lever, donc je n'angoisse pas, donc je dors quand même bien (et je peux très bien dormir de 1h à 6h et de 6h30 à 8h et être raisonnablement frais à 8h). Par contre, si le poussinet doit partir à 6h et que moi je dois me lever à 9h, c'est une catastrophe : quand le poussinet me réveille à 6h, il reste moins de 3½ heures pendant lesquelles je peux dormir, donc je n'y arrive pas, donc je me réveille, de fait à 6h, et comme la loi ci-dessus s'applique récursivement, finalement, il faudrait que je me couche à 21h pour avoir un sommeil correct.

Tout cela est terriblement contrariant, et le poussinet se moque de moi, mais je n'ai pas trouvé de moyen d'éviter le phénomène : dès que je sais que je dois me lever à une certaine heure, ma capacité à me rendormir rapidement après un petit réveil accidentel est anéantie. J'ai de la chance d'avoir un travail où les horaires sont très flexibles ! Il n'y a que les jours où à la fois j'ai un cours le matin et où le poussinet doit prendre un train encore plus tôt, qui posent vraiment problème. Néanmoins, ce matin, nous devions nous lever à une heure plus que décente, mais j'ai été réveillé successivement par le facteur qui livrait un colis, par un voisin qui jouait de la perceuse, par le chat d'un autre voisin qui miaulait à notre porte, et par un coup de téléphone publicitaire : si à chaque fois la certitude de devoir me lever prochainement ne m'avait pas empêché de me rendormir rapidement, j'aurais passé une nuit bien meilleure.

[#] Pour ceux qui s'inquiéteraient pour lui : le poussinet, lui, il dormira dans le train, il y arrive très bien. La règle du sommeil du poussinet est qu'il a besoin de 8h de sommeil, mais qu'il peut les prendre n'importe comment et n'importe quand. C'est plus simple !

(jeudi)

Quels sont mes langages de programmation préférés ?

L'avantage des langages de programmation, par opposition aux langues naturelles, c'est qu'on peut très rapidement devenir hautement polyglotte : il m'est arrivé de devoir écrire ou modifier un programme dans un langage de programmation dont je ne connaissais que le nom, et de me débrouiller passablement bien au bout de seulement quelques heures. (Il est vrai, cependant, que les langages les plus intéressants sont ceux qui demandent quelques efforts à apprendre parce qu'ils ne se contentent pas de changer de syntaxe par rapport à un autre langage, ils vous forcent à penser autrement, ce qui est toujours un enrichissement mental. Un peu comme quand on découvre que deux mots qui sont confondus dans notre langue maternelle sont différencés dans une autre langue naturelle et que cela nous apprend une distinction à laquelle on n'aurait pas pensé.) L'inconvénient, c'est que l'embarras du choix peut être angoissant ; et que plus on connaît de langages plus on se dit que c'est dommage qu'il n'en existe pas un qui combine l'idée X du langage A et les idées Y et Z du langage B, avec aussi quelques trucs de C, et on finit par envisager d'inventer son propre langage et d'ajouter une petite niche à une tour de Babel déjà bien assez compliquée.

Une autre chose, c'est que les langages de programmation ont tendance à avoir leurs zélotes, qui insistent pour vous dire qu'il faut absolument que vous l'appreniez et que vous vous en serviez pour faire votre café le matin, et que leur langage il est plus beau, plus logique, plus cohérent, plus simple, plus lisible, plus concis, plus rapide à l'exécution, plus efficace à coder voire tout ça à la fois. Les zélotes sont fatigants : j'ai tendance à avoir un avis nuancé sur tous les langages de programmation que j'utilise et je soupçonne toujours les gens qui s'enthousiasment trop devant un langage donné d'être d'une mauvaise foi absolue ou de mauvais programmeurs. Bizarrement, il y a des langages qui sont plus des langages à zélotes que d'autres (et ça ne semble ni très corrélé aux meilleurs langages ni aux moins bons) : j'ai l'impression qu'il y a beaucoup plus de zélotes Python ou Haskell que de zélotes C ou JavaScript.

Bref, je ne crois pas avoir de langage préféré. Le langage dans lequel je me retrouve bien souvent à coder, c'est le C, parce qu'il s'avère que telle bibliothèque n'est disponible que pour le C, ou que ses bindings pour tous les autres langages ne sont pas bien à jour, ou pas parfaits, ou trop mal documentés, ou toute autre combinaison de ce genre. Ce n'est presque jamais le choix que j'aurais fait naturellement, même si je suis loin de détester le C. Programmer en C peut donner l'impression satisfaisante qu'on comprend ce qui se passe, qu'on évite les baguettes magiques : ça a un côté bio-sans-pesticides-préserve-la-mémoire jusqu'à ce qu'on s'en lasse parce que vraiment on perd son temps avec des détails insignifiants comme écrire une demi-douzaine de lignes pour faire un remplacement dans une chaîne de caractères.

Le langage dans lequel je code le plus efficacement, c'est indiscutablement le Perl. La puissance du Perl, c'est dans tout ce qu'on peut faire en une seule ligne avec lui. Quand je veux faire un remplacement global dans tout un arbre de fichiers, j'utilise Perl (et le Shell) ; quand je veux transformer les bookmarks de mon navigateur en un fichier HTML à ma sauce, j'utilise Perl ; quand je veux transformer une suite de caractères Unicode en leurs noms pour démystifier tel ou tel symbole bizarre, j'utilise Perl ; quand je veux extraire des données utiles d'une page Web, faire des statistiques sur un fichier ou un tableau de nombres (ou les traiter pour les fournir à un outil pour faire des graphes) ; bref, pour tout ce qui ressemble à des traitements de textes, de chaînes de caractères, Perl est l'arme absolue, et la possibilité d'écrire énormément de choses en une seule ligne, exécutée directement dans un terminal, est vraiment précieuse. Je me sers aussi de Perl pour des vrais programmes, par exemple le système de commentaires de ce blog, ou le programme qui me sert à générer des calendriers, ou encore celui qui convertit les fragments littéraires gratuits que j'écris depuis un format HTML vers un joli document XeTeX. Ce que j'aime surtout avec Perl, c'est qu'il est tout sauf religieux : il n'essaie pas de vous imposer une approche particulière des problèmes, il vous laisse faire ce que vous voulez et il vous donne toutes les armes pour ça. Évidemment, ça peut produire des programmes illisibles (et il est vrai que le Perl devient vite illisible), mais je trouve que c'est un reproche injuste à faire au Perl que de mettre sur son dos les défauts des programmeurs qu'il ne châtie pas. Ce qui me pose surtout problème avec Perl, en fait, c'est qu'il a l'air complètement moribond depuis que son créateur a imaginé de créer Perl 6, un nouveau langage dont le rapport avec le Perl actuel (Perl 5) est assez ténu, et qui ressemble plus à une blague maintenant — une blague qui dure depuis dix ans, qui a plein d'idées intéressantes mais dont il n'est pas sorti un seul truc utilisable et c'est prévu pour continuer comme ça pour tout l'avenir envisageable.

J'aime beaucoup moins Python que Perl : la principale raison est celle que j'ai déjà expliquée : Perl n'est pas un langage qui essaie de vous imposer sa façon de voir les choses — Python, si. Je n'ai pas de problème avec le fait que la syntaxe Python dépende de l'indentation (il est whitespace-sensitive : si vous ajoutez ou retirez des espaces, ça peut changer l'effet programme) ; par contre, j'ai un sérieux problème avec le fait que ce soit la seule façon d'organiser les blocs et que, par conséquent, on ne puisse quasiment rien coder en une seule ligne en Python (ou, en tout cas, pas sans astuces pénibles) : Python aurait très bien pu adopter l'approche pragmatique de Haskell (où l'identation est effectivement prise en compte, mais si on n'aime pas ce fait, il y a toujours moyen de forcer telle ou telle interprétation avec des accolades explicites), mais apparemment l'aspect religieux l'a emporté. Ce n'est pas catastrophique (contrairement à ce que dirons certains zélotes anti-Python, qui sont aussi fastidieux que les zélotes pro-Python), mais c'est révélateur d'un état d'esprit gênant. De même, le langage impose une distinction (assez gratuite dans un langage de haut niveau, surtout qu'il prétend avoir récupéré des idées de la programmation fonctionnelle) entre instructions et expressions, en omettant volontairement un mécanisme qui permettrait de transformer une instruction en expression (en Perl, on peut utiliser do{...} pour transformer n'importe quelle suite d'instructions en une expression susceptible d'être utilisée comme valeur), et quitte à rendre quasiment inutilisables le fonctions anonymes que Python permet quand même. Cette limitation n'est pas ce qui m'ennuie le plus : ce qui m'ennuie le plus est qu'on ose la justifier par autoriser la transformation d'une instruction en expression (ou les fonctions anonymes important des instructions) encouragerait les programmeurs à écrire du mauvais code — un langage de programmation n'est pas là pour faire la morale aux programmeurs, il est là pour leur donner ce qu'ils veulent, y compris de quoi se tirer une balle dans le pied si c'est ça qu'ils veulent (et la tradition d'Unix, et de Perl, a toujours été de fournir un abondant arsenal pour les programmeurs qui veulent se tirer des balles dans le pied ☺ ; plus sérieusement, le programmeur est quand même mieux placé que le concepteur du langage, aussi malin soit-il, pour décider ce qui est le mieux dans le contexte précis de son programme). Après, il est indéniable que Python a des fonctionnalités très intéressantes : les itérateurs et coroutines, par exemple ; ou simplement, le fait que les entiers soient illimités par défaut. Par rapport à Perl, aussi, il gagne sur les exceptions (beaucoup plus agréables en Python que l'affreux $@ de Perl), mais il perd sur les références (les références du Perl, il faut peut-être un moment pour les comprendre, mais ensuite c'est vraiment élégant et logique).

Haskell devrait, logiquement, être mon langage préféré, parce que c'est un langage d'une grande beauté pour les mathématiciens. J'en pense effectivement beaucoup de bien, mais dans la pratique je trouve qu'il souffre de deux défauts. Le premier est purement psychologique : le langage est tellement pur qu'on se sent obligé de faire les choses de façon pure, et on finit par perdre beaucoup de temps parce qu'on ne veut pas céder à la façon évidente de faire les choses qu'on aurait choisie dans tout autre langage que Haskell (par exemple, on se sent obligé de dégager des fonctions d'ordre supérieur, de ne jamais utiliser de unsafePerformIO ni de IORef, et ainsi de suite… cela peut représenter un boulot supplémentaire considérable). L'autre est plus réel : l'évaluation paresseuse a tendance à être tellement insidieuse qu'on peut perdre un temps fou à comprendre pourquoi tel ou tel truc est atrocement lent, parce qu'on avait complètement oublié que telle variable n'allait être évaluée que bien après le moment où on le pensait. Et saupoudrer le code de `seq`, de $! et de constructeurs stricts, c'est vraiment horrible. Malgré cela, Haskell reste un langage vraiment unique, et pour faire des calculs mathématiques ou combinatoires j'y ai souvent recours. Et, pour le coup, le genre de choses qu'on peut faire avec des one-liners, ce ne sont pas tant des manipulations de chaînes de caractères comme en Perl, ce sont plutôt des calculs de suites récurrentes ou des choses comme ça, où les listes paresseuses montrent toute leur puissance (par exemple, pour calculer le nombre d'itérations pour atteindre 1 dans le problème de Collatz en partant des entiers de 1 à 100, cela s'écrit take 100 $ let ev n | n`mod`2==0 = n`div`2 ; ev n = 3*n+1 in map (Maybe.fromJust . List.elemIndex 1 . iterate ev) [1..] ; le Haskell n'est souvent pas beaucoup plus clair que le Perl, mais pour des raisons beaucoup plus profondes).

Un langage pour lequel je suis très partagé, c'est JavaScript. Par certains côtés, c'est une horreur : la bibliothèque standard est pourrie, les règles de conversion automatique sont tellement magiques qu'on n'y comprend plus rien (exemple aléatoire : []==![] s'évalue en true, ce qui est quand même violent), il y a des chausse-trapes partout (ce quiz n'est pas mauvais pour s'en rendre compte). D'un autre côté, JavaScript a certains aspects très satisfaisants : le mélange entre l'impératif, l'orienté objet et le fonctionnel, tout ça avec une syntaxe à la C, me semble vraiment bien réalisé ; et l'orienté objet à la sauce prototypage, finalement, j'aime beaucoup, et ça permet de faire des choses très puissantes. Un peu dans le même genre, il y a Lua, que j'aime assez bien (et je suis content qu'il ait été choisi comme langage d'extension pour étendre TeX), même s'il faut admettre que sa bibliothèque standard est, disons, minimaliste (enfin, c'est un peu le principe d'un langage d'extension). Il y avait Pike que j'avais repéré également, dans un genre probablement semblable.

Je suis aussi partagé pour Java, mais pour des raisons presque diamétralement opposées à JavaScript : la bibliothèque standard est très intéressante, mais je ne suis pas trop convaincu par le modèle de programmation orienté objet à fond, où les fonctions n'ont pas la citoyenneté de première classe (voire, pas d'existence du tout) sauf comme méthode d'un objet. (Ceci étant, les fanatiques anti-Java sont tellement pénibles qu'ils me donnent envie d'aimer le langage malgré ses quelques défauts.) Je ne connais pas C#, mais il est possible qu'il réalise un meilleur compromis.

Parmi les autres langages dont au moins certaines fonctionnalités ou idées me semblent intéressantes, je pourrais citer Scheme (ou une autre variante de Lisp), ou bien Smalltalk. Mais il y a quantité d'autres langages dont je me dis qu'ils pourraient être intéressants et que ça m'intéresserait d'en apprendre un peu un jour : Ruby, Erlang, Mercury, Dylan, Forth et Scala me viennent à l'esprit. Par contre, je n'ai aucune envie d'apprendre quoi que ce soit de C++ : tout ce que j'en ai vu m'a surtout donné envie de fuir (c'est-à-dire des programmes qui cassent à chaque mise à jour de gcc parce que l'interprétation de la norme n'arrête pas de changer), mais je ne vais pas m'étendre là-dessus parce que je trouve assez pénibles les gens qui disent uniformément du mal d'un langage quelconque pour ne pas m'y jeter moi-même, surtout s'agissant de quelque chose que je ne connais pas. De même, je ne commenterai pas sur mon absence d'envie d'apprendre le PHP.

J'en profite pour signaler cette page, qui recense la façon d'écrire la fonction 91 de McCarthy dans un certain nombre de langages. Ça peut donner envie d'en éviter certains. 😉

(lundi)

Fragment littéraire gratuit #130 (au club)

Franck aimait l'ambiance dans laquelle se déroulait ce rituel absurde. La salle sans fenêtres éclairée de néons alternativement blancs et roses. La musique : Radio FG (autrefois Fréquence Gaie, maintenant Fucking Good Music) proclame I've got the power!, mais au bout des écouteurs qu'on remarque accrochés à de nombreuses oreilles on devine un iPod qui doit jouer autre chose. De temps en temps couvre la musique le cri presque bestial de celui qui a dû réussir un exploit. L'odeur, aussi : la transpiration des corps de cette clientèle surtout masculine, écœurante au début, mais qu'on s'étonne d'oublier aussi vite. Des écrans affichent des dépêches AFP souvent vieilles de deux ou trois jours. Les gens font semblant de s'ignorer : un quinquagénaire se concentre sur sa lecture de La Tribune, un sportif rythme ses mouvements d'après la musique, trois jeunes se reposent en se racontant leur journée, tous paraissent indifférent à ce qui les entoure ; en vérité, Franck le sait bien, des coup d'œils furtifs s'échangent, pour apprécier le physique d'un autre, ou pour jauger son niveau à l'échelle impitoyable du curseur placé sur le tas de fonte.

(dimanche)

Mon poussinet veut reverse-engineerer les tarifs de la SNCF

On entend régulièrement les gens se plaindre que la grille des tarifs de la SNCF est incompréhensible : mon poussinet, qui voyage énormément en train et qui est à peu près aussi geek que moi, a décidé de prendre les choses en main et d'essayer de déterminer — quitte à interroger massivement le site voyages-sncf.com — les règles (et, si possible, les formules exactes) qui sont utilisées pour calculer le prix d'un billet. Il a par exemple déterminé le nombre de niveaux de réduction possibles offerts (par contingents de places) pour un billet au tarif loisir ou avec une carte de réduction commerciale.

La SNCF offre elle-même quelques indices, notamment dans le document Tarifs voyageurs. Voyez en particulier le volume 6, Recueil des prix, à partir de la page 114 dans le PDF, et spécifiquement le tableau de la page 118 numérotée 6:5, qui définit un prix de base général pour les voyages dans les trains autres que les TGV (tableau qui est essentiellement une approximation affine par morceaux d'une constante fois la puissance 0.79 de la distance ; mon poussinet a déterminé que pour appliquer le tableau en question il faut d'abord arrondir au centime inférieur puis arrondir au décime supérieur, ce qui est un peu étrange, mais enfin). Restent bien des mystères, que mon poussinet aimerait bien percer : comment sont calculés les prix des trajets réalisés en TGV ne circulant pas à grande vitesse (ne circulant pas à grande vitesse, parce que les prix de base des trajets TGV circulant à grande vitesse sont donnés par un tableau ad hoc), et surtout, comment sont calculés les prix des billets avec changement (si on va de A à C en changeant à B, quelle formule donne le prix du billet en fonction de ceux de A à B et de B à C et éventuellement des distances concernées). On a trouvé des formules qui marchent très bien (pour les billets avec changement : ajouter les puissances 1/0.79 des prix et reprendre la puissance 0.79 de cette somme), mais rien qui ne marche exactement. C'est rageant.

La SNCF devrait fournir tous ces détails. (Plutôt que des phrases qui ne veulent rien dire comme le prix de chaque segment ou de chaque ensemble homogène de segments est affecté d'un coefficient intégrant, sur l'ensemble du trajet […], la dégressivité de prix contenue dans la formule de calcul du tarif de base général (page 27 numérotée 2:7 du PDF précédemment mentionné) ; affirmation incompréhensible qui rend d'autant plus saugrenue la précision absurde de la phrase suivante : Le prix est arrondi au décime d'euro supérieur à chaque étape du calcul.) Et surtout, il y a un certain énervement geek à voir qu'ils appliquent des règles visiblement très compliquées comme approximation de règles simples (ne serait-ce que faire une approximation affine par morceaux par tableau de la fonction puissance 0.79, c'est quand même idiot).

(lundi)

xml:lang et le DOM

La râlante du jour (dans le fil de la quête envisagée hier) :

Le W3C standardise en XML l'usage de l'attribut xml:lang pour indiquer la langue du contenu d'un élément (la langue est implicitement héritée par tous les descendants de la balise). J'en fais un usage systématique[#] quand je tape du texte en n'importe quelle variante de XML (par exemple, si je dois citer The Lord of the Rings dans ce blog, j'écris : <cite xml:lang="en">The Lord of the Rings</cite> (et mon moteur de blog ajoutera un lang="en" en plus du xml:lang="en" pour compatibilité HTML, mais peu importe ici). Très bien.

Le W3C standardise un modèle d'objets (DOM) pour accéder à un document XML. Énormément de bibliothèques se basent dessus pour les manipulations, et il n'est pas trop déplaisant à utiliser. Tant mieux.

Mais ce DOM ignore complètement les spécificités de l'attribut xml:lang : aucune fonction n'est prévue pour interroger la langue d'un élément du DOM, ni pour copier/déplacer/supprimer un élément en préservant sa langue et celle de tous les descendants (c'est-à-dire en ajoutant un attribut xml:lang si nécessaire, ou en le retirant s'il est devenu superflu au nouvel emplacement). Il faut tout faire à la main. Moins bien. Beaucoup moins bien.

La façon évidente de procéder consiste en début de traitement à propager systématiquement l'attribut xml:lang à tous les descendants de n'importe quel nœud, à manipuler le document ainsi transformé, et à expurger les attributs xml:lang inutiles à la fin du traitement. Malheureusement, cette façon évidente est lente, très lente. Pas bien du tout, ça.

Alors, à chaque déplacement d'une balise, il faut se farcir une réflexion pénible : Est-ce que je suis en train de casser potentiellement un héritage de xml:lang, là ?

Bref, des petites crottes de ragondin, comme aime le dire mon ami David Monniaux.

[#] Dans le fol espoir, par exemple, qu'un jour il existe un correcteur orthographique en tenant compte (c'est-à-dire, capable de corriger un mélange aléatoire de français, d'anglais et de je ne sais quoi, dans n'importe quelle instance de XML, en prenant le bon dictionnaire pour chaque passage). Ou qu'un moteur de recherche sache en faire un usage intelligent. Je sais, je suis naïf, c'est touchant.

(dimanche)

Je voudrais changer le moteur de ce blog

Le moteur de ce blog est une abomination sans nom. Il s'agit d'un programme C, que j'ai écrit il y a sept ans, qui parse un fichier source XML unique que j'édite et qui contient toutes les entrées, pour produire les fichiers HTML statiques rassemblant les entrées de chaque mois ainsi que celui contenant les 20 dernières entrées et celui reprenant mes fragments littéraires gratuits ou encore l'index de toutes les entrées. Le même programme sert à générer un certain nombre d'autres pages de ce site Web (mais pas toutes, parce que j'ai un bon nombre de couches de vieilleries empilées les unes sur les autres ☹ — avec au moins trois styles de présentation différents, plus quelques pages qui sont exceptionnelles pour une raison ou une autre).

Je n'ai pas voulu utiliser un moteur de contenu|blog standard, d'abord parce qu'en 2003 le choix n'était pas terrible, mais aussi parce que je tiens à avoir des pages HTML statiques et pas générées à chaque requête, ou parce que je déteste le PHP, ou simplement parce que je suis control-freak. Je me suis dit qu'avoir mon propre moteur me donnerait plus de flexibilité pour me créer des nouveaux tags dans le source, pour organiser mon contenu comme je le veux, ou ce genre de choses. Enfin, ça c'était la théorie, et ça n'a pas marché du tout.

J'ai écrit le moteur en C pour des raisons d'efficacité : la machine qui fait le traitement était initialement une machine de l'ENS complètement à bout de souffle, et toutes les autres solutions que j'avais regardées (notamment des choses sur Perl) étaient abominablement lentes. Quant à XSLT, sur lequel j'étais parti, il n'était décidément pas adapté (déjà, seule la version 1 existait en 2003, mais le moindre test, comme pour vérifier que les entrées étaient bien numérotées correctement, devenait une insupportable prise de tête).

Mais même en C, l'efficacité est devenue un problème. Mon programme ne génère qu'un fichier mensuel par appel : pour générer tous les 86 fichiers de sortie (84 mois plus trois fichiers particuliers), il faut donc l'appeler 86 fois, et à chaque fois il doit parser complètement le fichier d'entrée qui fait maintenant 5.5Mo. Ça fait l'équivalent de 470Mo de XML à traiter : même si le serveur qui fait le traitement est un peu plus rapide que celui avec lequel j'ai commencé, ça prend presque une minute, que je n'ai pas envie d'attendre à chaque fois que je compile pour vérifier que tout s'affiche bien comme je veux. J'ai adopté la moyenne mesure consistant à recompiler à chaque modification seulement les fichiers des trois derniers mois (plus l'index et le fichier des 20 dernières entrées), et à recompiler une fois par mois (ou quand j'en éprouve le besoin particulièrement) la totalité du HTML, des fois que j'aurais fait des corrections de fautes d'orthographes dans des entrées un peu anciennes. Ce n'est vraiment pas satisfaisant (mais détecter si une entrée ancienne a changé n'est pas facilement possible). Et surtout, ça m'a empêché de créer d'autre « catégorie » que celle des fragments littéraires gratuits (savoir quels fichiers recompiler deviendrait de plus en plus abominable). En l'état actuel du programme, le modifier pour produire plusieurs fichiers en sortie par un seul appel serait d'une difficulté inextricable.

Et je ne parle pas des maux de tête au sujet du fil RDF, qui est une abomination dans une abomination (un autre programme C qui partage une partie du code source du premier) : je sais qu'on m'a demandé mille fois de mettre des heures dedans, et/ou de mettre le début de mes entrées plutôt que juste leur titre, mais avec le programme que j'ai c'est juste trop difficile.

Ajouter à ça que la compilation du programme C moteur de blog lui-même est très lente (il y a un fichier source qui est assez énorme). Donc, même sans compter que toute manipulation de chaînes de caractères en C s'apparente au plaisir de résoudre une équation différentielle à la règle à calcul : ajouter le moindre tag que je pourrais utiliser dans le source est vraiment trop chiant. Du coup, finalement, je fais presque tout à la main (par exemple, les notes en bas d'entrée, elles ne sont pas formatées automatiquement comme elles devraient l'être). Tout ça fait que j'ai envie de tout mettre à la poubelle.

Je viens de passer la journée à me demander ce que je veux utiliser à la place. Je pense que je m'oriente vers une architecture en Perl, utilisant la XML::LibXML (même si j'ai déjà eu des soucis avec), et vers une compilation en deux parties, une partie consistant à insérer les entrées dans une base de données SQL (ce qui permettra, par exemple, de savoir plus facilement celles qui ont changé) et la seconde consistant à générer les fichiers HTML à partir de différents modèles qui font appel à cette base de données.

(mercredi)

Le groupe de Galois de x^5−5x+12

J'ai beau connaître la théorie de Galois depuis longtemps, et l'avoir enseignée pendant trois ans à l'ENS, je continue à trouver ça assez magique. Il y a quelque chose de vraiment difficile à admettre, intuitivement, dans le fait que certaines équations ont des groupes de Galois plus petits que ce qu'ils pourraient être : et même quand on connaît bien la théorie, et qu'on sait calculer les groupes de Galois en pratique, on a l'impression de ne pas vraiment avoir de réponse satisfaisante à la question, mais pourquoi, au juste, cette équation décide-t-elle de ne pas faire comme ses petites copines ? Ça n'arrange pas, en plus, que la majorité des cours sur la théorie de Galois vous laissent sans la moindre idée de comment au juste on peut calculer un groupe de Galois en pratique (surtout quand il est petit).

Je peux essayer d'illustrer ça par l'exemple du polynôme f=x5−5x+12 (sur ℚ), que j'ai commencé à utiliser comme exemple dans un texte que j'écrivais aujourd'hui : qu'est-ce qui peut bien faire que ses racines décident de ne pas être rigoureusement interchangeables comme dans les équations les plus générales (car c'est ça que signifie le fait d'avoir un groupe de Galois maximal — tout le groupe symétrique sur les racines) ?

On peut en avoir une mesure expérimentale en regardant ce qui se passe si on réduit ce polynôme modulo différents nombres premiers p (c'est-à-dire qu'on le regarde comme un polynôme à coefficients dans 𝔽p=ℤ/pℤ) et qu'on cherche à le factoriser : modulo les 10000 premiers nombres premiers, si on excepte p=2 (le polynôme f devient x·(x+1)4) et p=5 (le polynôme devient (x+2)5) où le polynôme a des facteurs multiples (ce sont les nombres premiers dits ramifiés), il y a 4016 nombres premiers (7, 11, 13, 19, 23, 37…) modulo lesquels f est irréductible, il y en a 5021 (3, 17, 29, 31, 43, 61…) tels qu'il se factorise en un facteur linéaire et deux de degré 2 (par exemple, modulo 3, le polynôme f devient x·(x²+x+2)·(x²+2x+2)), et enfin il y a 961 nombres premiers (127, 157, 197, 223, 251, 331…) tels que le polynôme f se décompose totalement en cinq facteurs linéaires (par exemple, modulo 127, le polynôme f devient (x+19)·(x+65)·(x+81)·(x+93)·(x+123)). Autrement dit, il y a en gros 40%, 50% et 10% des nombres premiers modulo lesquels on a des degrés de factorisations 5, 1+2+2 et 1+1+1+1+1 respectivement ; et aucune autre factorisation ne se produit (ou en tout cas, ne semble se produire si on regarde les petits nombres premiers). On est censé trouver ça surprenant, parce qu'un polynôme général de degré 5 (qui a pour groupe de Galois le groupe symétrique 𝔖5, par exemple x5−5x+11), il a ces trois factorisations modulo p dans des proportions respectives 20%, 12.5% et ~0.83%, mais surtout, il y a d'autres factorisations possibles (1+4 modulo 25% des nombres premiers, 2+3 et 1+1+3 modulo ~16.7% chacun, et 1+1+1+2 dans ~8.3% des cas) : donc la conséquence, ou la détection, expérimentale du petit groupe de Galois de f=x5−5x+12, c'est que sa réduction modulo p n'admet pas toutes les factorisations possibles, ou avec des proportions inattendues pour celles qui sont possibles (et notamment, le scindage complet en facteurs linéaires a lieu beaucoup plus souvent que dans le cas général). Le théorème qui sous-tend ces statistiques, c'est le théorème de Čebotarëv (qui implique que les proportions des différentes partitions en degrés pour la factorisation des réductions d'un polynôme sur ℚ sont, asymptotiquement, les proportions des différentes répartitions en cycles des éléments de son groupe de Galois agissant sur les racines) ; les proportions observées pour f doivent faire soupçonner comme groupe de Galois le groupe diédral du pentagone, car on cherche un groupe de permutations sur 5 éléments, dont le nombre d'éléments est multiple de 5 et approximativement 10 (l'inverse de la proportion de p modulo lesquels f se décompose complètement), et qui a 40% d'éléments sans points fixes et 50% d'éléments avec exactement un point fixe. Mais tout ceci ne permet de conclure qu'heuristiquement.

Comme f=x5−5x+12 est irréductible (par exemple parce qu'il l'est modulo 7), ses racines sont au moins une fois interchangeables (=le groupe de Galois opère transitivement dessus) : on peut appeler a une de ses racines. On peut se fixer les idées en disant que a est l'unique racine réelle (celle qui vaut environ −1.842085). Le polynôme s'écrit alors f=(xa)·(x4+ax3+a2x2+a3x+a4−5). Là où ce polynôme est surprenant, c'est que le second facteur se décompose plus loin : f=(xa(x2+¼(−a4a3a2+3a+4)x+¼(−a4a3a2−5a+8))·(x2+¼(a4+a3+a2+a−4)x+½(−a3a−2)). On peut vérifier cette expression en développant patiemment le produit (et en utilisant de façon répétée le fait que f(a)=0) ; la présence des deux facteurs quadratiques garantit d'ores et déjà que f vu modulo n'importe quel nombre premier p ne pourra jamais avoir de factorisation dont les facteurs auraient degrés 1+1+3, par exemple (i.e., deux facteurs linéaires et un de degré 3), et l'existence de nombres premiers modulo lesquels ces facteurs quadratiques sont irréductibles (3, par exemple) garantit qu'ils sont irréductibles tout court. Mais ce n'est pas tout : on voit qu'une fois qu'on a choisi (identifié, nommé, distingué) une racine a de f, les quatre autres ne sont plus interchangeables — elles viennent en deux paires différenciées, à savoir, celles qui sont racines de x2+¼(−a4a3a2+3a+4)x+¼(−a4a3a2−5a+8), et celles qui sont racines de x2+¼(a4+a3+a2+a−4)x+½(−a3a−2). Par exemple, dans les complexes, si j'ai choisi a≈−1.842085, alors les racines du premier facteur quadratique seront environ 1.272897+0.719799i et 1.272897−0.719799i, que je voudrai noter respectivement a1 et a4 pour des raisons qui apparaîtront plus tard, et les racines du second facteur quadratique seront environ −0.351854+1.709561i et −0.351854−1.709561i, que je voudrai noter a2 et a3 respectivement.

Cette fois, on a bien prouvé les choses suivantes sur le groupe de Galois : qu'il opère transitivement sur les 5 racines, et que le sous-groupe fixant une racine a quelconque opère sur les 4 autres en les séparant en deux orbites de 2 éléments chacune. On peut se convaincre en examinant toutes sortes de cas que le groupe diédral du pentagone est le seul groupe de permutations sur cinq objets qui réponde à ces conditions. On peut cependant être plus explicite : si je note a0=a une des cinq racines de f, et a1 une des deux racines de x2+¼(−a4a3a2+3a+4)x+¼(−a4a3a2−5a+8), alors en posant a2=[(a04a03+a02a0−4)a1+(−a04+a03a02+a0−4)]/8, on peut vérifier (de nouveau, en développant de façon fastidieuse, si on ne trouve pas mieux) que a2 est aussi racine de f (et plus précisément de x2+¼(a4+a3+a2+a−4)x+½(−a3a−2)) ; et il en va de même de a3=[(−a04+a03a02+a0+4)a1+(−a04−3a03a02−3a0+12)]/8 ; enfin, la dernière racine de f est alors a4=−a1+¼(a04+a03+a02−3a0−4) d'après l'équation vérifiée par a1 : ceci montre qu'une fois choisies les racines a0 (une parmi cinq) et a1 (une parmi deux), toutes les autres sont complètement déterminées, donc le groupe de Galois a dix éléments. Et avec ces conventions, si on permute cycliquement les cinq racines a0, a1, a2, a3, a4, alors les mêmes relations sont satisfaite, de même que si, à a0 fixé, on échange a1 et a4 et a2 et a3 : on s'est donc convaincu que le groupe de Galois de f est le groupe diédral du pentagone, le pentagone en question étant celui formé par les aj (abstraitement, car dans ℂ ce n'est pas du tout un pentagone régulier).

Pour résumer, donc, si j'essaie de jouer à bouger les racines de f entre elles, je peux en mettre une première n'importe où, je peux mettre une seconde parmi deux possibles (si les racines étaient adjacentes sur le pentagone abstrait a0, a1, a2, a3, a4, elles doivent le rester), et une fois que c'est fait toutes les autres tombent forcément en place.

Parmi les petites identités remarquables qui tombent de la théorie de Galois, on peut remarquer que comme le groupe diédral du pentagone a un sous-groupe d'indice 2 (les rotations du pentagone), il doit y avoir une extension quadratique de ℚ dans celle engendrée par les racines de f : c'est bien le cas, et spécifiquement, √−10=(a02+1)a1+¼(−a04+a03a02+5a0+8) est laissé invariant par les éléments du groupe de Galois qui permutent cycliquement les cinq racines, et envoyé en son opposé par les éléments qui laissent fixe une racine. Du coup, si on veut, toutes les racines de f peuvent s'exprimer à partir d'une seule racine a et de cette quantité √−10. On pourrait aussi jouer à exprimer explicitement toutes les racines de f avec des radicaux : on va dire que je laisse ça en exercice au lecteur, qui pourra s'inspirer de l'exercice 5 de cette feuille d'exercices.

PS (2010-03-04T15:25+0100) : D'autres exemples de groupes de Galois un peu rigolos qui ne soient ni trop petits ni trop gros pour être intéressants : x7−7x+3 (groupe simple à 168 éléments, PGL2(𝔽7)≅PGL3(𝔽2)) ; x8−16x+28 (groupe d'ordre 1344 extension du précédent par (ℤ/2ℤ)3) ; x8−5x−5 (groupe d'ordre 1152 extension de (ℤ/2ℤ) par le produit de deux copies de 𝔖4) ; x9−9x2+9x−10 (groupe d'ordre 1296 produit en couronne de 3 copies de 𝔖3 avec action de 𝔖3, i.e., stabilisateur d'un système de 3 blocs de 3).

(lundi)

Richard Dawkins, et comment Dieu agi(rai)t sur le monde

Je regardais récemment une vidéo de Richard Dawkins discutant avec la créationniste Wendy Wright. La raison pour laquelle je la signale n'est pas pour l'intérêt du fond du débat : il n'y en a guère — outre qu'elles se ressemblent toutes, les discussions avec les créationnistes n'ont vraiment pas grand intérêt pour commencer, on sait d'avance que cette dame ne sera pas convaincue[#], elle répète des phrases quasiment par cœur sans faire la moindre attention à ce que son interlocuteur lui dit, bref, c'est le prototype du dialogue de sourds. Et d'ailleurs, honnêtement, j'ai vu Dawkins meilleur dans son argumentation. Cette vidéo est déjà peut-être plus intéressante comme test de patience, parce que la Wendy Wright en question est tellement insupportablement horripilante avec son air souriant faux-cul quand elle dit it's very demeaning to say that we only believe what we believe because we've been told that qu'il faut au moins une ceinture noire de patriarcat zen 3e dan pour réussir à ne pas craquer en l'écoutant, et mon admiration pour Dawkins, là, est surtout qu'il a réussi l'exploit de ne pas lui foutre une paire de baffes[#2]. Et globalement, cette vidéo est fascinante pour ce qui est de se rendre compte de la façon dont les gens arrivent à se mettre des œillères et à faire preuve de la mauvaise foi la plus spectaculaire (on le sait déjà, bien sûr, par des écrits, mais c'est toujours plus spectaculaire quand on entend quelqu'un parler, et comme je le disais, cette dame a la mauvaise foi particulièrement rayonnante).

(On voit ici le phénomène typique de ce dont je parlais dans mon entrée précédente : je n'ai rigoureusement rien dit à part que Dawkins parle à une dame insupportable, et il m'a quand même fallu pas loin de 250 mots pour ne rien dire. Damnèd. Et la suite est bien pire !)

*

Mais une autre chose qui m'a intéressé dans cette vidéo (que j'ai regardé intégralement pour entraîner mes nerfs à supporter la bêtise, c'est un exercice important pour un enseignant ☺), c'est la façon dont Dawkins, à plusieurs reprises, ébauche un argument — ou plutôt, une exhortation — du type : mais ne voyez-vous pas que l'évolution par la sélection naturelle est quelque chose de merveilleusement élégant, et que ça pourrait être un témoignage à la grandeur de Dieu d'avoir choisi quelque chose d'aussi subtil et minimaliste pour arriver à Ses fins plutôt que d'agiter une baguette de sorcier et d'intervenir directement dans sa création. Et, de fait (fait remarquer Dawkins), un certain nombre de croyants, même spécifiquement de chrétiens, qui admettent que l'évolution au sens darwinien peut être le moyen choisi par Dieu pour Son Plan.

Je suis amusé de voir que (même si cela semble contredire des choses qu'il écrit dans The God Delusion[#3]) Dawkins semble rejoindre un peu cette idée que j'ai souvent avancée moi-même en discutant avec des croyants, et que je développerais ainsi : si on veut croire en Dieu, il faut croire que Dieu a quelque chose d'un mathématicien, Il a inventé des lois de la physique d'une très grande élégance et simplicité, et s'Il souhaite intervenir dans le monde, Il ne va pas faire une exception à ces lois qui par définition sont parfaites et sont Sa volonté. L'idée d'un Dieu qui consent occasionnellement — parcimonieusement — à suspendre l'application des lois de l'Univers pour faire des miracles à la faveur d'une prière individuelle me semble, quitte à risquer d'être vexant, assez enfantine. C'est peut-être le principe même de croire en Dieu que de croire qu'Il sert à faire de la magie de temps en temps et qu'Il attribue assez d'importance à l'humanité pour y consentir, mais ce que j'essaie surtout de dire c'est qu'il n'y a pas besoin de supposer que cela passe par une dérogation des lois de la physique : un Dieu censément omniscient et infiniment habile peut tout faire sans jamais déroger aux règles qu'Il se serait fixé — il pourrait « intervenir » dans l'Univers en déplaçant un seul quark au moment du big bang pour avoir exactement les conséquences souhaitées au moment souhaité (savoir s'Il modifie ce quark rétroactivement ou si ce quark a toujours été comme ça est une question vide de sens : Dieu est censé exister en-dehors du temps de toute façon, donc pour Lui le présent, le passé et l'avenir sont la même donnée).

Plusieurs fois j'ai évoqué cette idée de Dieu avec des croyants (chrétiens, spécifiquement), donc, mais tous l'ont catégoriquement rejetée : peut-être parce qu'elle rend Dieu trop distant ou trop dispensable (Celui qui met toute la machine en route et la regarde tourner parce que cette machine est parfaite) ; peut-être parce que l'idée du déterminisme de la physique semble contredire le dogme du libre-arbitre de l'homme[#4] ; peut-être parce que j'ai tort de penser que celui qui s'intéresse à une religion s'intéresse nécessairement à la métaphysique[#5] ; ou peut-être parce que c'est un dangereux premier pas vers l'athéisme (ou, pire encore, vers une région floue entre le théisme et l'athéisme).

Quelque part, c'est dommage : c'est une idée que je trouve intellectuellement séduisante, qui permet justement de rendre continue toute la lignée des croyances entre Dieu existe, il est conscient et s'intéresse à nous et Dieu n'existe pas, l'Univers n'a pas de but ou de raison à part ce que nous voulons y voir, mais les croyants des religions traditionnelles ne veulent pas s'engager là-dedans, et les athées n'ont pas à le faire. Il est vrai que ce n'est pas à moi, qui suis athée, d'expliquer aux croyants comment leur Dieu devrait être, ou que c'est de mauvais goût quand on est Dieu de jouer[#6] à l'archimage.

Si vous êtes sages, un jour je montrerai comment on peut fabriquer une religion rigolote (mais sans dieu[#7]) en poussant beaucoup plus loin l'idée que j'ai esquisée. (Mise à jour : c'est ici.)

*

[#] Je ne suis pas sûr que le débat rationnel puisse jamais convaincre qui que ce soit sur ce genre de questions, mais si jamais un créationniste peut être convaincu qu'il a tort, ce ne sera pas quelqu'un qui ait une position « officielle » qu'il risquerait de perdre, comme la présidente des Concerned Women for America.

[#2] Heureusement, d'ailleurs, parce que ça aurait eu toutes sortes de répercussions déplaisantes. Mais la morale est qu'on ne devrait pas chercher à imiter Dawkins à moins d'être soi-même, non seulement vaguement compétent en biologie (pour discuter à ce niveau, une connaissance très vague doit suffire), mais aussi, patriarche zen ceinture noire 3e dan, donc.

[#3] Où il dénonce l'hypocrisie du compromis des non-overlapping magisteria (selon lequel la religion et la science parlent de choses différentes et n'ont pas de raison de se contredire) : Dawkins répond en substance que toutes les religions font au moins certaines affirmations qui ont un sens scientifique précis et seraient, au moins en théorie, susceptibles d'être contredites par l'expérience. Il en conclut que la seule façon d'être croyant et de faire quand même de la science est d'être prêt à reculer les frontières de sa foi à chaque fois que la science progresse, de façon à garder cette illusion de magistères qui ne se recoupent pas.

[#4] Je dis semble contredire parce que je pense qu'il n'y a pas de contradiction entre déterminisme et libre-arbitre, dans la mesure où la notion de libre-arbitre a un sens pour commencer (ce qui n'est pas clair) : de toute façon, il n'y a qu'un Univers (et on ne peut pas rejouer le passé), donc la question de savoir s'il est prédestiné est assez creuse. Cette entrée est assez longue comme ça pour que je ne veuille pas poursuivre cette ligne de pensée, mais en tout cas il me semble que d'éminents théologiens chrétiens ont été de l'avis qu'il n'y a pas de contradiction entre le fait que Dieu sache tout l'avenir du monde et le fait que l'Homme soit libre — donc du même coup je ne vois pas pourquoi l'Homme ne pourrait pas être libre dans un Univers déterministe dont Dieu aurait fixé les règles.

[#5] Une amie avec qui j'en ai parlé m'a répondu en substance que la métaphysique est une sorte de branlette intellectuelle (ce sont mes termes, pas les siens…) et que ce n'est pas ce qui intéresse le croyant, car le but de la religion est de nous dire comment (bien) agir, pas de réfléchir à la création ou au but de l'Univers, ni même de se concentrer sur les aspects pittoresques de certaines dénominations de la religion chrétienne (ce sont ses termes) comme savoir si Marie était effectivement vierge ou si Jésus s'incarne vraiment en petits morceaux de pain circulaires. Je cromprends et je sympathise avec son point de vue, mais dans ce cas j'ai aussi un avis sur la façon de bien agir, qui rejoint sans doute assez largement ce qu'elle pense : je ne vois pas bien l'intérêt d'avancer la croyance (véritablement métaphysique) que l'Univers a été fabriqué par un Créateur conscient pour arriver à une éthique raisonnable, à moins qu'on y croie vraiment.

[#6] Il faut dire que la raison nº1 pour laquelle je serais bien incapable d'être chrétien, même si j'étais théiste, c'est que je ne comprends fichtrement rien aux motivations de leur Dieu, qui fait décidément les choses les plus étranges à mes yeux (je ne parle pas seulement des petits morceaux de pain circulaires ni du fait d'être descendu sur Terre dans une obscure province romaine pour y mener un programme à la logique incompréhensible — mais, pour commencer, de l'idée de créer des hommes, de leur donner un libre-arbitre et de les juger ensuite pour toute l'éternité sur ce qu'ils font pendant une durée finie). La réponse standard est que les voies du Seigneur sont impénétrables, mais enfin, Il est censé nous avoir fait à Son image et on est censé avoir mangé l'arbre d'un certain fruit, donc globalement ça ne devrait pas être si incompréhensible que ça (surtout que sur d'autres choses, Il s'explique assez bien).

[#7] Peut-être que le terme de religion est alors abusif, mais j'ai tendance à appeler religion tout ce qui fournit une réponse à la question métaphysique de la vie, de l'univers, et de tout le reste (et notamment de leur sens), qui ait plus de sens que 42 ou que il n'y en a pas (ou ce n'est pas intéressant, on ne sait pas quelle est la question). En particulier, tout ce qui tend à vouloir faire croire que l'homme a une place spéciale dans l'Univers autre que parce qu'il s'en donne une.

Continue to older entries. / Continuer à lire les entrées plus anciennes.


Entries by month / Entrées par mois:

2017 Jan 2017 Feb 2017 Mar 2017 Apr 2017 May 2017 Jun 2017 Jul 2017
2016 Jan 2016 Feb 2016 Mar 2016 Apr 2016 May 2016 Jun 2016 Jul 2016 Aug 2016 Sep 2016 Oct 2016 Nov 2016 Dec 2016
2015 Jan 2015 Feb 2015 Mar 2015 Apr 2015 May 2015 Jun 2015 Jul 2015 Aug 2015 Sep 2015 Oct 2015 Nov 2015 Dec 2015
2014 Jan 2014 Feb 2014 Mar 2014 Apr 2014 May 2014 Jun 2014 Jul 2014 Aug 2014 Sep 2014 Oct 2014 Nov 2014 Dec 2014
2013 Jan 2013 Feb 2013 Mar 2013 Apr 2013 May 2013 Jun 2013 Jul 2013 Aug 2013 Sep 2013 Oct 2013 Nov 2013 Dec 2013
2012 Jan 2012 Feb 2012 Mar 2012 Apr 2012 May 2012 Jun 2012 Jul 2012 Aug 2012 Sep 2012 Oct 2012 Nov 2012 Dec 2012
2011 Jan 2011 Feb 2011 Mar 2011 Apr 2011 May 2011 Jun 2011 Jul 2011 Aug 2011 Sep 2011 Oct 2011 Nov 2011 Dec 2011
2010 Jan 2010 Feb 2010 Mar 2010 Apr 2010 May 2010 Jun 2010 Jul 2010 Aug 2010 Sep 2010 Oct 2010 Nov 2010 Dec 2010
2009 Jan 2009 Feb 2009 Mar 2009 Apr 2009 May 2009 Jun 2009 Jul 2009 Aug 2009 Sep 2009 Oct 2009 Nov 2009 Dec 2009
2008 Jan 2008 Feb 2008 Mar 2008 Apr 2008 May 2008 Jun 2008 Jul 2008 Aug 2008 Sep 2008 Oct 2008 Nov 2008 Dec 2008
2007 Jan 2007 Feb 2007 Mar 2007 Apr 2007 May 2007 Jun 2007 Jul 2007 Aug 2007 Sep 2007 Oct 2007 Nov 2007 Dec 2007
2006 Jan 2006 Feb 2006 Mar 2006 Apr 2006 May 2006 Jun 2006 Jul 2006 Aug 2006 Sep 2006 Oct 2006 Nov 2006 Dec 2006
2005 Jan 2005 Feb 2005 Mar 2005 Apr 2005 May 2005 Jun 2005 Jul 2005 Aug 2005 Sep 2005 Oct 2005 Nov 2005 Dec 2005
2004 Jan 2004 Feb 2004 Mar 2004 Apr 2004 May 2004 Jun 2004 Jul 2004 Aug 2004 Sep 2004 Oct 2004 Nov 2004 Dec 2004
2003 May 2003 Jun 2003 Jul 2003 Aug 2003 Sep 2003 Oct 2003 Nov 2003 Dec 2003