Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • covid
  • fourier_coeff
  • headers
  • master
  • multiple_files
  • revampProbStat
  • untagged-0a69f730a9edf8f452c2
  • untagged-1146cc237dc62ad4adac
  • untagged-185a5c9790d5fd86d5a7
  • untagged-2023d0fb6c34f29165ee
  • untagged-45a5d503565c4f0dc073
  • untagged-5f91b9934a0d64190e08
  • untagged-67162b0c997bec772454
  • untagged-82bd404cbb7da09ef714
  • untagged-942bdedb39bd9d9a9db2
  • untagged-9d1a8d01c160be73c2d7
  • untagged-a7d0fd1e09f98f58b2e7
  • untagged-b5c0979b02d61124836f
  • untagged-c23b343a32e6ba6b41ef
  • untagged-d08ea895726d1693fe75
  • untagged-da21599a55453b349309
  • untagged-e4eb7c83718bffcd6dc3
  • untagged-f970fb8b2d5aa387c7e1
  • v1.0.0
  • v1.0.1
  • v1.0.2
  • v1.0.3
  • v1.0.4
  • v1.0.5
29 results

Target

Select target project
  • orestis.malaspin/math_tech_info
  • jerome.chetelat/math_tech_info
  • julien.borel/math_tech_info
  • xavier.perret/math_tech_info
  • ilias.nhairi/math_tech_info
  • julien.seemulle/math_tech_info
  • michael.elkharro/math_tech_info
  • fabien.lometti/math_tech_info
  • guillaum.pin/math_tech_info
  • quentin.rod/math_tech_info
  • simon.cirilli/math_tech_info
11 results
Select Git revision
  • covid
  • fourier_coeff
  • headers
  • master
  • multiple_files
  • patch-1
  • revampProbStat
  • untagged-0a69f730a9edf8f452c2
  • untagged-1146cc237dc62ad4adac
  • untagged-185a5c9790d5fd86d5a7
  • untagged-2023d0fb6c34f29165ee
  • untagged-45a5d503565c4f0dc073
  • untagged-5f91b9934a0d64190e08
  • untagged-67162b0c997bec772454
  • untagged-82bd404cbb7da09ef714
  • untagged-942bdedb39bd9d9a9db2
  • untagged-9d1a8d01c160be73c2d7
  • untagged-a7d0fd1e09f98f58b2e7
  • untagged-b5c0979b02d61124836f
  • untagged-c23b343a32e6ba6b41ef
  • untagged-d08ea895726d1693fe75
  • untagged-da21599a55453b349309
  • untagged-e4eb7c83718bffcd6dc3
  • untagged-f970fb8b2d5aa387c7e1
  • v1.0.0
  • v1.0.1
  • v1.0.2
  • v1.0.3
  • v1.0.4
  • v1.0.5
30 results
Show changes
Showing
with 1487 additions and 2 deletions
#ifndef _GFX_H_
#define _GFX_H_
#include <stdint.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
#define MAKE_COLOR(r,g,b) ((uint32_t)b|((uint32_t)g<<8)|((uint32_t)r<<16))
#define COLOR_GET_B(color) (color & 0xff)
#define COLOR_GET_G(color) ((color >> 8) & 0xff)
#define COLOR_GET_R(color) ((color >> 16) & 0xff)
#define COLOR_BLACK 0x00000000
#define COLOR_RED 0x00FF0000
#define COLOR_GREEN 0x0000FF00
#define COLOR_BLUE 0x000000FF
#define COLOR_WHITE 0x00FFFFFF
#define COLOR_YELLOW 0x00FFFF00
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned char uchar;
struct gfx_context_t {
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Texture *texture;
uint32_t *pixels;
int width;
int height;
};
extern void gfx_putpixel(struct gfx_context_t *ctxt, int x, int y, uint32_t color);
extern void gfx_clear(struct gfx_context_t *ctxt, uint32_t color);
extern struct gfx_context_t* gfx_create(char *text, uint width, uint height);
extern void gfx_destroy(struct gfx_context_t *ctxt);
extern void gfx_present(struct gfx_context_t *ctxt);
extern SDL_Keycode gfx_keypressed();
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "gfx.h"
static void gfx_clear_rect(struct gfx_context_t *ctxt, int x0, int x1, int y0, int y1) {
for (int x = x0; x < x1; ++x) {
for (int y = y0; y < y1; ++y) {
gfx_putpixel(ctxt, x, y, COLOR_BLACK);
}
}
}
static double map(double x, double lambda) {
return lambda * x * (1.0 - x);
}
int main() {
const double lambda_min = 3;
const double lambda_max = 4;
const int N = 1920;
const int M = 1000;
double dl = (lambda_max - lambda_min) / N;
struct gfx_context_t *ctxt = gfx_create("Example", N, M);
if (!ctxt) {
fprintf(stderr, "Graphics initialization failed!\n");
return EXIT_FAILURE;
}
gfx_clear(ctxt, COLOR_BLACK);
double prop = 0.2;
int num_points = M / 10;
int max_iter = 10000;
for (int i_lambda = 0; i_lambda < N; ++i_lambda) {
double x = 0.5;
for (int i = 0; i < max_iter; ++i) {
double lambda = lambda_min + i_lambda * dl;
x = map(x, lambda);
if (i > max_iter - num_points) {
gfx_putpixel(ctxt, i % (num_points / 2), (M - 1) - (M - 1) * x * prop, COLOR_WHITE);
gfx_clear_rect(ctxt, (i + 1) % (num_points / 2), (i + 2) % (num_points / 2), (1 - prop) * (M-1), (M - 1));
gfx_putpixel(ctxt, i_lambda, (M - 1) * (1 - x), COLOR_WHITE);
// printf("x(%d, %g) = %g\n", i, i_lambda * dl, x);
// usleep((unsigned int)1000);
// if (i % 512 == 0) {
// gfx_present(ctxt);
// }
}
}
gfx_clear_rect(ctxt, (num_points / 2 - 1), (num_points / 2) + 2, (1 - prop) * (M-1), (M - 1));
gfx_present(ctxt);
// printf("\n");
}
while (gfx_keypressed() != SDLK_ESCAPE) {
}
gfx_destroy(ctxt);
return EXIT_SUCCESS;
}
STYLES := ../css/tufte-css/tufte.css \
../css/pandoc.css \
../css/pandoc-solarized.css \
../css/tufte-extra.css
OPTIONS = --filter=pandoc-numbering
OPTIONS += --filter=pandoc-crossref
PDFOPTIONS = --pdf-engine pdflatex
PDFOPTIONS += --highlight-style kate
PDFOPTIONS += --number-sections
PDFOPTIONS += --template=./default.latex
HTMLOPTIONS += -t html5
HTMLOPTIONS += -c ../../css/styling.css
HTMLOPTIONS += --self-contained
HTMLOPTIONS += --mathjax=../../MathJax.js
MD=$(wildcard *.md)
HTML=$(MD:%.md=%.html)
PDF=$(MD:%.md=%.pdf)
all: $(HTML) $(PDF)
%.pdf: %.md Makefile
pandoc -s $(OPTIONS) $(PDFOPTIONS) -o $@ $<
%.html: %.md Makefile
pandoc -s $(OPTIONS) $(HTMLOPTIONS) -o $@ $<
clean:
rm -rf *.html *.pdf
---
# author:
# - Orestis Malaspinas
title: Travail pratique sur les équations différentielles
autoSectionLabels: true
autoEqnLabels: false
eqnPrefix:
- "éq."
- "éqs."
chapters: false
numberSections: false
chaptersDepth: 1
sectionsDepth: 3
lang: fr
documentclass: article
papersize: A4
cref: false
pandoc-numbering:
- category: exercice
urlcolor: blue
---
\newcommand{\real}{\mathbb{R}}
# Rappel théorique : approximation numérique d'équations différentielles ordinaires
En général il n'existe pas de solutions analytiques pour les équations
différentielles d'un intérêt pratique en ingénierie, c'est pourquoi on
est obligé d'utiliser des méthodes numériques pour approximer la
solution. Nous présentons ici une famille de méthodes numériques : les
méthodes de Runge-Kutta qui permettent d'approximer les solutions
d'équations différentielles ordinaires
$$
\frac{\mathrm{d}y}{\mathrm{d}t} = F(y,t), \quad y(t_0)=y_0,
$$
où $F$ est une fonction qui dépend de $y$ et de $t$, et où $y_0\in\real$ est la condition initiale de l'équation différentielle.
Par exemple, dans le cas de l'équation différentielle
$$
y'(t)=y(t)+8t^2+1,
$$
$F(y,t)=y(t)+8t^2+1$.
Afin d'avoir une solution unique, il est nécessaire de donner une condition
initiale à une équation différentielle, de la forme $y(t_0)=y_0$. Ici on pourrait par exemple avoir
$$
y(t=0)=0.
$$
## Les méthodes de Runge-Kutta
Les méthodes de Runge--Kutta sont une famille de méthodes explicites
pour résoudre des équations différentielles ordinaires. Elles ont la
forme général suivante
$$
y_{n+1}=y_0+\delta t\sum_{i=1}^s b_ik_i,
$$ où
$\delta t$ est le pas de temps, et
$$
y_n\equiv y(t_n),
$$
avec $t_n=t_0+n\delta t$. La valeur des $k_i$ dépend de la précision de
l'algorithme (et donc de la valeur du nombre "d'étages" $s$) et ils sont
donnés de façon itérative par $$\begin{aligned}
k_1&=F(y_n,t_n),\\
k_2&=F(y_n+a_{21}\delta t k_1,t_n+c_2\delta t),\\
&\cdots\\
k_s&=F(y_n+a_{s1}\delta t k_1+a_{s2}\delta t k_2+\cdots +a_{s,s-1}\delta t k_{s-1},t_n+c_s \delta t).\end{aligned}$$
et où les $a_{ij}$, $b_i$ et $c_i$ sont propres de nouveaux au nombre
d'étages $s$ de la méthode. Pour $s=1$, on a simplement $c_1=0$, $b_1=1$
et donc il vient $$y_{n+1}=y_n+\delta t F(y_n,t_n).$$ Cette méthode
s'appelle également méthode d'Euler explicite (vous l'avez peut-être
reconnue). Pour $s=2$, on obtient $c_1=0$, $c_2=1/2$, $a_{21}=1/2$,
$b_1=0$, et $b_2=1$. On a donc
$$
y_{n+1}=y_n+\delta t F\left(y_n+\frac{\delta t}{2}F(y_n,t_n),t_n+\frac{\delta t}{2}\right).
$$
Cette méthode s'appelle également méthode de point du milieu (elle est
proche de la méthode du point du milieu pour les intégrales).
Finalement, une méthode bien connue pour les méthode de Runge--Kutta est celle d'ordre 4
$$
y_{n+1}=y_n+\frac{1}{6}(k_1+2(k_2+k_3)+k_4),
$$
\begin{align}
k_1&=\delta tF(y_n, t_n),\\
k_2&=\delta tF\left(y_n+\frac{k_1}{2}, t_n+\frac{\delta t}{2}\right),\\
k_3&=\delta tF(y_n+\frac{k_2}{2}, t_n+\frac{\delta t}{2}),\\
k_4&=\delta tF(y_n+k_3, t_n+\delta t).
\end{align}
## L'équation de Lorenz
L'équation de Lorenz est un système d'équations différentielles
ordinaires agissant sur une variable vectorielle à trois composantes
$\vec y(t)=(y_x(t),y_y(t),y_z(t))$
$$\begin{aligned}
\frac{\mathrm{d}y_x}{\mathrm{d}t}&=\sigma(y_y-y_x),\\
\frac{\mathrm{d}y_y}{\mathrm{d}t}&=y_x(\rho-y_z)-y_y,\\
\frac{\mathrm{d}y_z}{\mathrm{d}t}&=y_yy_x-\beta y_z,
\end{aligned}$$
où les constantes sont données par $\beta=8/3$, $\sigma=10$, et $\rho=28$.
![Solution dans le plan $x-z$ de l'équation de Lorenz pour une solution
initiale donnée (image tirée de
Wikipedia).](../../figs/1024px-Lorenz_system_r28_s10_b2-6666.png){#fig_lorenz
width="50%"}
La solution de l'équation de Lorenz est dite *chaotique*. Cela signifie
que si nous considérons deux conditions initiales $\vec y^1$ et $\vec y^2$ qui
sont très proches. Elles s'éloigneront l'une de l'autre
exponentiellement vite au cours du temps.
Par ailleurs, comme on peut le constater sur la figure
[1](#fig_lorenz){reference-type="ref" reference="fig_lorenz"} la
solution a une structure de "papillon". Cette structure est retrouvée
indépendemment de la condition initiale (on aura toujours cette forme de
solution) bien que la position à un temps donné ne soit pas la même pour
deux conditions initiales différentes.
# Travail à effectuer
## Écriture des solveurs
Écrire un solveur pour la méthode d'Euler explicite (méthode de
Runge--Kutta à un étage), un pour la méthode de Runge--Kutta à deux
étages et un pour la méthode de Runge--Kutta d'ordre 4.
## Équation de Lorenz
Afin de valider les solveurs, appliquer les trois solveurs à l'équation de Lorenz et reproduire une
figure approchant celle de l'énoncé (faire un graphique éventuellement
tri-dimmensionnel de la trajectoire obtenue pour une condition initiale
quelconque).
## Orbites périodiques
L'attracteur de Lorenz contient des trajectoires périodiques sur
lesquelles le système revient au point initial après un certain temps.
Ces trajectoires sont dites "instables", comme toute autre trajectoire
de ce système : il suffit qu'on dévie un tout petit peu de la
trajectoire, et on finit par s'en éloigner complètement. Le point
suivant se trouve (dans la limite de la précision numérique fournie) sur
une trajectoire périodique de période $t_{max}=1.5586522$:
$y_0=(-0.9101673912,-1.922121396,18.18952097)$. Prendre ce point comme
valeur initiale, et faites évoluer le système avec un pas de temps donné
(par exemple $\delta t = 0.001$). Combien de temps arrivez-vous à rester
sur l'orbite périodique avec chacun des solveurs? Tracez les deux
trajectoires sur une figure 3D pour les comparer.
# Modélisation d'épidémies
Une fois les solveurs validés vous vous attaquerez à la tâche d'utiliser vos solveurs
pour résoudre un vrai problème d'actualité.
## Le modèle SEIR
Afin de modéliser la propagation d'une épidémie, nous allons utiliser le modèle SEIR, qui est un modèle compartimental où $S$ est la population susceptible d'être infectée, $E$ la population exposée (pas encore infectieuse) qui correspond à la population où la population est en incubation, $I$ la population infectieuse, et finalement $R$ la population rétablie.
La dynamique de ce système est caractérisée par la population initiale de chaque catégorie:
$$
S(t=0)=S_0,\quad E(t=0)=E_0,\quad I(t=0)=I_0,\quad R(t=0)=R_0,
$$
et par quatre équation différentielles ordinaires
\begin{align}
S'(t)&=-\frac{\mathcal{R}_0}{T_{inf}}I(t)\frac{S(t)}{N},\\
E'(t)&=\frac{\mathcal{R}_0}{T_{inf}}I(t)\frac{S(t)}{N}-\frac{1}{T_{inc}}E(t),\\
I'(t)&=\frac{1}{T_{inc}}E(t)-\frac{1}{T_{inf}}I(t),\\
R'(t)&=\frac{1}{T_{inf}}I(t),
\end{align}
où $\mathcal{R}_0$ est taux de reproduction de base, $T_{inf}$ est le temps où un individu est infectieux, et $T_{inc}$ est le temps d'incubation de la maladie. La taux de reproduction de base peut être remplacé par
le taux de reproduction effectif, $\mathcal{R}_t=\mathcal{R}_0\frac{S(t)}{N}$ qui représente
la susceptibilité effective au temps $t$.
Ce modèle peut être amélioré pour inclure d'autres termes (par exemple des termes reliés aux trajets de gens entrant ou sortant dans la population, ainsi que ternir compte des morts naturelles, etc). Pour le moment nous nous contentons de cette version simplifiée qui pourra être rendue encore plus réaliste par la suite.
## Travail à effectuer
L'utilisation que vous ferez de votre modèle ne dépendra que de vous. Essayez de concevoir des scénarios différents de réaction des pouvoirs publics et simulez les!
# Évaluation
Me cassez pas les pieds avec vos évaluations!
OPTIONS = --filter=pandoc-numbering
OPTIONS += --filter=pandoc-crossref
PDFOPTIONS = --highlight-style kate
PDFOPTIONS += --pdf-engine pdflatex
PDFOPTIONS += --number-sections
PDFOPTIONS += --template=./default.latex
HTMLOPTIONS += -t html5
HTMLOPTIONS += -c css/prism.css
HTMLOPTIONS += --self-contained
HTMLOPTIONS += --mathjax=MathJax.js
MD=$(wildcard *.md)
HTML=$(MD:%.md=%.html)
PDF=$(MD:%.md=%.pdf)
all: $(HTML) $(PDF)
%.pdf: %.md Makefile
sed -e 's/language-python/python/g' -e 's/language-bash/bash/g' $< > $*_tex.md
pandoc -s $(OPTIONS) $(PDFOPTIONS) -o $@ $<
rm $*_tex.md
%.html: %.md Makefile
pandoc -s $(OPTIONS) $(HTMLOPTIONS) -o $@ $<
clean:
rm -rf *.html *.pdf
var fileref=document.createElement('script')
fileref.setAttribute("type","text/javascript")
fileref.setAttribute("src", "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML")
document.getElementsByTagName("head")[0].appendChild(fileref)
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+python */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 1em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+python */
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(g){var c=/\blang(?:uage)?-([\w-]+)\b/i,a=0,C={manual:g.Prism&&g.Prism.manual,disableWorkerMessageHandler:g.Prism&&g.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof M?new M(e.type,C.util.encode(e.content),e.alias):Array.isArray(e)?e.map(C.util.encode):e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++a}),e.__id},clone:function t(e,n){var r,a,i=C.util.type(e);switch(n=n||{},i){case"Object":if(a=C.util.objId(e),n[a])return n[a];for(var l in r={},n[a]=r,e)e.hasOwnProperty(l)&&(r[l]=t(e[l],n));return r;case"Array":return a=C.util.objId(e),n[a]?n[a]:(r=[],n[a]=r,e.forEach(function(e,a){r[a]=t(e,n)}),r);default:return e}}},languages:{extend:function(e,a){var t=C.util.clone(C.languages[e]);for(var n in a)t[n]=a[n];return t},insertBefore:function(t,e,a,n){var r=(n=n||C.languages)[t],i={};for(var l in r)if(r.hasOwnProperty(l)){if(l==e)for(var o in a)a.hasOwnProperty(o)&&(i[o]=a[o]);a.hasOwnProperty(l)||(i[l]=r[l])}var s=n[t];return n[t]=i,C.languages.DFS(C.languages,function(e,a){a===s&&e!=t&&(this[e]=i)}),i},DFS:function e(a,t,n,r){r=r||{};var i=C.util.objId;for(var l in a)if(a.hasOwnProperty(l)){t.call(a,l,a[l],n||l);var o=a[l],s=C.util.type(o);"Object"!==s||r[i(o)]?"Array"!==s||r[i(o)]||(r[i(o)]=!0,e(o,t,l,r)):(r[i(o)]=!0,e(o,t,null,r))}}},plugins:{},highlightAll:function(e,a){C.highlightAllUnder(document,e,a)},highlightAllUnder:function(e,a,t){var n={callback:t,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};C.hooks.run("before-highlightall",n);for(var r,i=n.elements||e.querySelectorAll(n.selector),l=0;r=i[l++];)C.highlightElement(r,!0===a,n.callback)},highlightElement:function(e,a,t){for(var n,r,i=e;i&&!c.test(i.className);)i=i.parentNode;i&&(n=(i.className.match(c)||[,""])[1].toLowerCase(),r=C.languages[n]),e.className=e.className.replace(c,"").replace(/\s+/g," ")+" language-"+n,e.parentNode&&(i=e.parentNode,/pre/i.test(i.nodeName)&&(i.className=i.className.replace(c,"").replace(/\s+/g," ")+" language-"+n));var l={element:e,language:n,grammar:r,code:e.textContent},o=function(e){l.highlightedCode=e,C.hooks.run("before-insert",l),l.element.innerHTML=l.highlightedCode,C.hooks.run("after-highlight",l),C.hooks.run("complete",l),t&&t.call(l.element)};if(C.hooks.run("before-sanity-check",l),l.code)if(C.hooks.run("before-highlight",l),l.grammar)if(a&&g.Worker){var s=new Worker(C.filename);s.onmessage=function(e){o(e.data)},s.postMessage(JSON.stringify({language:l.language,code:l.code,immediateClose:!0}))}else o(C.highlight(l.code,l.grammar,l.language));else o(C.util.encode(l.code));else C.hooks.run("complete",l)},highlight:function(e,a,t){var n={code:e,grammar:a,language:t};return C.hooks.run("before-tokenize",n),n.tokens=C.tokenize(n.code,n.grammar),C.hooks.run("after-tokenize",n),M.stringify(C.util.encode(n.tokens),n.language)},matchGrammar:function(e,a,t,n,r,i,l){for(var o in t)if(t.hasOwnProperty(o)&&t[o]){if(o==l)return;var s=t[o];s="Array"===C.util.type(s)?s:[s];for(var g=0;g<s.length;++g){var c=s[g],u=c.inside,h=!!c.lookbehind,f=!!c.greedy,d=0,m=c.alias;if(f&&!c.pattern.global){var p=c.pattern.toString().match(/[imuy]*$/)[0];c.pattern=RegExp(c.pattern.source,p+"g")}c=c.pattern||c;for(var y=n,v=r;y<a.length;v+=a[y].length,++y){var k=a[y];if(a.length>e.length)return;if(!(k instanceof M)){if(f&&y!=a.length-1){if(c.lastIndex=v,!(x=c.exec(e)))break;for(var b=x.index+(h?x[1].length:0),w=x.index+x[0].length,A=y,P=v,O=a.length;A<O&&(P<w||!a[A].type&&!a[A-1].greedy);++A)(P+=a[A].length)<=b&&(++y,v=P);if(a[y]instanceof M)continue;N=A-y,k=e.slice(v,P),x.index-=v}else{c.lastIndex=0;var x=c.exec(k),N=1}if(x){h&&(d=x[1]?x[1].length:0);w=(b=x.index+d)+(x=x[0].slice(d)).length;var j=k.slice(0,b),S=k.slice(w),E=[y,N];j&&(++y,v+=j.length,E.push(j));var _=new M(o,u?C.tokenize(x,u):x,m,x,f);if(E.push(_),S&&E.push(S),Array.prototype.splice.apply(a,E),1!=N&&C.matchGrammar(e,a,t,y,v,!0,o),i)break}else if(i)break}}}}},tokenize:function(e,a){var t=[e],n=a.rest;if(n){for(var r in n)a[r]=n[r];delete a.rest}return C.matchGrammar(e,t,a,0,0,!1),t},hooks:{all:{},add:function(e,a){var t=C.hooks.all;t[e]=t[e]||[],t[e].push(a)},run:function(e,a){var t=C.hooks.all[e];if(t&&t.length)for(var n,r=0;n=t[r++];)n(a)}},Token:M};function M(e,a,t,n,r){this.type=e,this.content=a,this.alias=t,this.length=0|(n||"").length,this.greedy=!!r}if(g.Prism=C,M.stringify=function(a,t,e){if("string"==typeof a)return a;if(Array.isArray(a))return a.map(function(e){return M.stringify(e,t,a)}).join("");var n={type:a.type,content:M.stringify(a.content,t,e),tag:"span",classes:["token",a.type],attributes:{},language:t,parent:e};if(a.alias){var r=Array.isArray(a.alias)?a.alias:[a.alias];Array.prototype.push.apply(n.classes,r)}C.hooks.run("wrap",n);var i=Object.keys(n.attributes).map(function(e){return e+'="'+(n.attributes[e]||"").replace(/"/g,"&quot;")+'"'}).join(" ");return"<"+n.tag+' class="'+n.classes.join(" ")+'"'+(i?" "+i:"")+">"+n.content+"</"+n.tag+">"},!g.document)return g.addEventListener&&(C.disableWorkerMessageHandler||g.addEventListener("message",function(e){var a=JSON.parse(e.data),t=a.language,n=a.code,r=a.immediateClose;g.postMessage(C.highlight(n,C.languages[t],t)),r&&g.close()},!1)),C;var e=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return e&&(C.filename=e.src,C.manual||e.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(C.highlightAll):window.setTimeout(C.highlightAll,16):document.addEventListener("DOMContentLoaded",C.highlightAll))),C}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
Prism.languages.markup={comment:/<!--[\s\S]*?-->/,prolog:/<\?[\s\S]+?\?>/,doctype:/<!DOCTYPE[\s\S]+?>/i,cdata:/<!\[CDATA\[[\s\S]*?]]>/i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&amp;/,"&"))}),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^<!\[CDATA\[|\]\]>$/i;var n={"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:s}};n["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var i={};i[a]={pattern:RegExp("(<__[\\s\\S]*?>)(?:<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\s*|[\\s\\S])*?(?=<\\/__>)".replace(/__/g,a),"i"),lookbehind:!0,greedy:!0,inside:n},Prism.languages.insertBefore("markup","cdata",i)}}),Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup;
!function(s){var e=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?[\s\S]*?(?:;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:RegExp("url\\((?:"+e.source+"|.*?)\\)","i"),selector:RegExp("[^{}\\s](?:[^{};\"']|"+e.source+")*?(?=\\s*\\{)"),string:{pattern:e,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var a=s.languages.markup;a&&(a.tag.addInlined("style","css"),s.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:a.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:s.languages.css}},alias:"language-css"}},a.tag))}(Prism);
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/};
Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.])\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+)n?|\d+n|NaN|Infinity)\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee][+-]?\d+)?/,function:/[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/-[-=]?|\+[+=]?|!=?=?|<<?=?|>>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/,greedy:!0,inside:{interpolation:{pattern:/\${[^}]+}/,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.js=Prism.languages.javascript;
Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},"string-interpolation":{pattern:/(?:f|rf|fr)(?:("""|''')[\s\S]+?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:{{)*){(?!{)(?:[^{}]|{(?!{)(?:[^{}]|{(?!{)(?:[^{}])+})+})+}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|rb|br)?("""|''')[\s\S]+?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|rb|br)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^\s*)@\w+(?:\.\w+)*/i,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:True|False|None)\b/,number:/(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python;
\documentclass[$if(fontsize)$$fontsize$,$endif$$if(lang)$$babel-lang$,$endif$$if(papersize)$$papersize$paper,$endif$$for(classoption)$$classoption$$sep$,$endfor$]{$documentclass$}
$if(beamerarticle)$
\usepackage{beamerarticle} % needs to be loaded first
$endif$
$if(fontfamily)$
\usepackage[$for(fontfamilyoptions)$$fontfamilyoptions$$sep$,$endfor$]{$fontfamily$}
$else$
\usepackage{lmodern}
$endif$
$if(linestretch)$
\usepackage{setspace}
\setstretch{$linestretch$}
$endif$
\usepackage{amssymb,amsmath,bm}
\usepackage{ifxetex,ifluatex}
\usepackage{fixltx2e} % provides \textsubscript
\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex
\usepackage[$if(fontenc)$$fontenc$$else$T1$endif$]{fontenc}
\usepackage[utf8]{inputenc}
$if(euro)$
\usepackage{eurosym}
$endif$
\else % if luatex or xelatex
$if(mathspec)$
\ifxetex
\usepackage{mathspec}
\else
\usepackage{unicode-math}
\fi
$else$
\usepackage{unicode-math}
$endif$
\defaultfontfeatures{Ligatures=TeX,Scale=MatchLowercase}
$for(fontfamilies)$
\newfontfamily{$fontfamilies.name$}[$fontfamilies.options$]{$fontfamilies.font$}
$endfor$
$if(euro)$
\newcommand{\euro}{}
$endif$
$if(mainfont)$
\setmainfont[$for(mainfontoptions)$$mainfontoptions$$sep$,$endfor$]{$mainfont$}
$endif$
$if(sansfont)$
\setsansfont[$for(sansfontoptions)$$sansfontoptions$$sep$,$endfor$]{$sansfont$}
$endif$
$if(monofont)$
\setmonofont[Mapping=tex-ansi$if(monofontoptions)$,$for(monofontoptions)$$monofontoptions$$sep$,$endfor$$endif$]{$monofont$}
$endif$
$if(mathfont)$
$if(mathspec)$
\ifxetex
\setmathfont(Digits,Latin,Greek)[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$}
\else
\setmathfont[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$}
\fi
$else$
\setmathfont[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$}
$endif$
$endif$
$if(CJKmainfont)$
\usepackage{xeCJK}
\setCJKmainfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKmainfont$}
$endif$
\fi
% use upquote if available, for straight quotes in verbatim environments
\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
% use microtype if available
\IfFileExists{microtype.sty}{%
\usepackage[$for(microtypeoptions)$$microtypeoptions$$sep$,$endfor$]{microtype}
\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
}{}
\PassOptionsToPackage{hyphens}{url} % url is loaded by hyperref
$if(verbatim-in-note)$
\usepackage{fancyvrb}
$endif$
\usepackage[unicode=true]{hyperref}
$if(colorlinks)$
\PassOptionsToPackage{usenames,dvipsnames}{color} % color is loaded by hyperref
$endif$
\hypersetup{
$if(title-meta)$
pdftitle={$title-meta$},
$endif$
$if(author-meta)$
pdfauthor={$author-meta$},
$endif$
$if(keywords)$
pdfkeywords={$for(keywords)$$keywords$$sep$, $endfor$},
$endif$
$if(colorlinks)$
colorlinks=true,
linkcolor=$if(linkcolor)$$linkcolor$$else$Maroon$endif$,
citecolor=$if(citecolor)$$citecolor$$else$Blue$endif$,
urlcolor=$if(urlcolor)$$urlcolor$$else$Blue$endif$,
$else$
pdfborder={0 0 0},
$endif$
breaklinks=true}
\urlstyle{same} % don't use monospace font for urls
$if(verbatim-in-note)$
\VerbatimFootnotes % allows verbatim text in footnotes
$endif$
$if(geometry)$
\usepackage[$for(geometry)$$geometry$$sep$,$endfor$]{geometry}
$endif$
$if(lang)$
\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex
\usepackage[shorthands=off,$for(babel-otherlangs)$$babel-otherlangs$,$endfor$main=$babel-lang$]{babel}
$if(babel-newcommands)$
$babel-newcommands$
$endif$
\else
\usepackage{polyglossia}
\setmainlanguage[$polyglossia-lang.options$]{$polyglossia-lang.name$}
$for(polyglossia-otherlangs)$
\setotherlanguage[$polyglossia-otherlangs.options$]{$polyglossia-otherlangs.name$}
$endfor$
\fi
$endif$
$if(natbib)$
\usepackage{natbib}
\bibliographystyle{$if(biblio-style)$$biblio-style$$else$plainnat$endif$}
$endif$
$if(biblatex)$
\usepackage[$if(biblio-style)$style=$biblio-style$,$endif$$for(biblatexoptions)$$biblatexoptions$$sep$,$endfor$]{biblatex}
$for(bibliography)$
\addbibresource{$bibliography$}
$endfor$
$endif$
$if(listings)$
\usepackage{listings}
$endif$
$if(lhs)$
\lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{}
$endif$
$if(highlighting-macros)$
$highlighting-macros$
$endif$
$if(tables)$
\usepackage{longtable,booktabs}
% Fix footnotes in tables (requires footnote package)
\IfFileExists{footnote.sty}{\usepackage{footnote}\makesavenoteenv{long table}}{}
$endif$
$if(graphics)$
\usepackage{graphicx,grffile}
\makeatletter
\def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi}
\def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi}
\makeatother
% Scale images if necessary, so that they will not overflow the page
% margins by default, and it is still possible to overwrite the defaults
% using explicit options in \includegraphics[width, height, ...]{}
\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio}
$endif$
$if(links-as-notes)$
% Make links footnotes instead of hotlinks:
\renewcommand{\href}[2]{#2\footnote{\url{#1}}}
$endif$
$if(strikeout)$
\usepackage[normalem]{ulem}
% avoid problems with \sout in headers with hyperref:
\pdfstringdefDisableCommands{\renewcommand{\sout}{}}
$endif$
$if(indent)$
$else$
\IfFileExists{parskip.sty}{%
\usepackage{parskip}
}{% else
\setlength{\parindent}{0pt}
\setlength{\parskip}{6pt plus 2pt minus 1pt}
}
$endif$
\setlength{\emergencystretch}{3em} % prevent overfull lines
\providecommand{\tightlist}{%
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
$if(numbersections)$
\setcounter{secnumdepth}{$if(secnumdepth)$$secnumdepth$$else$5$endif$}
$else$
\setcounter{secnumdepth}{0}
$endif$
$if(subparagraph)$
$else$
% Redefines (sub)paragraphs to behave more like sections
\ifx\paragraph\undefined\else
\let\oldparagraph\paragraph
\renewcommand{\paragraph}[1]{\oldparagraph{#1}\mbox{}}
\fi
\ifx\subparagraph\undefined\else
\let\oldsubparagraph\subparagraph
\renewcommand{\subparagraph}[1]{\oldsubparagraph{#1}\mbox{}}
\fi
$endif$
$if(dir)$
\ifxetex
% load bidi as late as possible as it modifies e.g. graphicx
$if(latex-dir-rtl)$
\usepackage[RTLdocument]{bidi}
$else$
\usepackage{bidi}
$endif$
\fi
\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex
\TeXXeTstate=1
\newcommand{\RL}[1]{\beginR #1\endR}
\newcommand{\LR}[1]{\beginL #1\endL}
\newenvironment{RTL}{\beginR}{\endR}
\newenvironment{LTR}{\beginL}{\endL}
\fi
$endif$
% set default figure placement to htbp
\makeatletter
\def\fps@figure{htbp}
\makeatother
$for(header-includes)$
$header-includes$
$endfor$
$if(title)$
\title{$title$$if(thanks)$\thanks{$thanks$}$endif$}
$endif$
$if(subtitle)$
\providecommand{\subtitle}[1]{}
\subtitle{$subtitle$}
$endif$
$if(author)$
\author{$for(author)$$author$$sep$ \and $endfor$}
$endif$
$if(institute)$
\providecommand{\institute}[1]{}
\institute{$for(institute)$$institute$$sep$ \and $endfor$}
$endif$
\date{$date$}
\begin{document}
$if(title)$
\maketitle
$endif$
$if(abstract)$
\begin{abstract}
$abstract$
\end{abstract}
$endif$
$for(include-before)$
$include-before$
$endfor$
$if(toc)$
{
$if(colorlinks)$
\hypersetup{linkcolor=$if(toccolor)$$toccolor$$else$black$endif$}
$endif$
\setcounter{tocdepth}{$toc-depth$}
\tableofcontents
}
$endif$
$if(lot)$
\listoftables
$endif$
$if(lof)$
\listoffigures
$endif$
$body$
$if(natbib)$
$if(bibliography)$
$if(biblio-title)$
$if(book-class)$
\renewcommand\bibname{$biblio-title$}
$else$
\renewcommand\refname{$biblio-title$}
$endif$
$endif$
\bibliography{$for(bibliography)$$bibliography$$sep$,$endfor$}
$endif$
$endif$
$if(biblatex)$
\printbibliography$if(biblio-title)$[title=$biblio-title$]$endif$
$endif$
$for(include-after)$
$include-after$
$endfor$
\end{document}
---
author:
- Mathématiques en technologie de l'information
title: Travail pratique - Les transformées de Fourier
autoSectionLabels: false
autoEqnLabels: true
eqnPrefix:
- "éq."
- "éqs."
chapters: true
numberSections: false
chaptersDepth: 1
sectionsDepth: 3
lang: fr
documentclass: article
papersize: A4
cref: false
urlcolor: blue
toc: false
include-before: <script src="css/prism.js"></script>
---
\newcommand{\dd}{\mathrm{d}}
\newcommand{\real}{\mathbb{R}}
\newcommand{\integer}{\mathbb{Z}}
\renewcommand{\natural}{\mathbb{N}}
\newcommand{\complex}{\mathbb{C}}
# Objectif
Implémenter de façon naïve les transformées de Fourier discrètes
en deux dimensions avec pour but:
1. Tenter de comprendre leur utilité en traitement d'image.
2. Tenter de comprendre leur utilité en compression d'image.
# Les transformées de Fourier en plusieurs dimensions
Nous avons vu pendant le cours la définition de la transformée de Fourier discrète
en une dimension donc nous n'allons pas trop insister sur le sujet ici
mais reproduire le résultat principal.
Soit un signal $f[n]$ discret et de longueur $N$ ($n$ va de $0$ à $N-1$),
nous pouvons écrire sa transformée, $\hat{f}[k]$ ($k$ allant de $0,..,N-1$) de Fourier comme[^1]
$$
{\hat{f}}[k]=\sum_{n=0}^{N-1} f[n] e^{-\frac{2\pi i n k}{N}}.
$$
A l'inverse, on peut calculer la transformées de Fourier discrète inverse de $\hat{f}[k]$ comme
$$
f[n]=\frac{1}{N}\sum_{k=0}^{N-1} {\hat{f}}[k] e^{\frac{2\pi i k n}{N}},
$$
ce qui permet de déterminer le signal d'origine à partir de sa transformée de Fourier.
On peut généraliser la transformée de Fourier pour des signaux en plus d'une dimension.
Ici, on va s'intéresser aux signaux bidimensionnels. Un tel signal peut représenter
par exemple les niveaux de gris d'une image. On a que la transformée de Fourier
pour un signal qui dépend de deux coordonnées, $f[n_1,n_2]$, $n_1=0,..,N_1-1$, $n_2=0,..,N_2-1$,
a une transformée de Fourier discrète, $\hat{f}[k_1,k_2]$, $k_1=0,..,N_1-1$ et $k_2=0,..,N_2-1$, donnée par
$$
{\hat{f}}[k_1,k_2]=\sum_{n_1=0}^{N_1-1} e^{-\frac{2\pi i n_1 k_1}{N_1}}\left(\sum_{n_2=0}^{N_2-1} f[n_1,n_2] e^{-\frac{2\pi i n_2 k_2}{N_2}}\right).
$$
De cette formule, on peut déduire que faire la transformée de Fourier en deux dimensions
n'est rien d'autre que faire la transformée de Fourier uni-dimensionnelle dans chacune des dimensions
séparément.
De même la transformée de Fourier inverse à deux dimensions s'écrit
$$
f[n_1,n_2]=\frac{1}{N_1\cdot N_2}\sum_{k_1=0}^{N_1-1} e^{\frac{2\pi i n_1 k_1}{N_1}}\left(\sum_{k_2=0}^{N_2-1} {\hat{f}}[k_1,k_2] e^{\frac{2\pi i n_2 k_2}{N_2}}\right).
$$
Souvenez-vous qu'on a vu que la transformée de Fourier discrète et son inverse peuvent s'écrire sous la forme
d'un produit matrice vecteur. Cela peut vous aider à rendre votre code plus performant en
utilisant les fonctionnalités de la librairie `numpy`.
On peut donc faire une analyse de Fourier d'une image presque aussi facilement qu'on le ferait pour un signal unidimensionnel.
# Travail à réaliser
Dans ce travail, vous avez un cahier des charges relativement peu précis. Vous devez vous débrouiller pour atteindre un
but fixé, mais le chemin est assez peu balisé.
C'est à dessein. On peut discuter oralement de chaque partie si vous voulez vous assurer de ce que vous devez faire, mais je ne veux
pas trop vous cadrer et vous laisser vous débrouiller.
En résumé, il faudra à l'aide des transformées de Fourier discrètes filtrer une image. Puis écrire un algorithme
permettant de la compresser/décompresser (avec pertes).
## Implémenter les fonctions `tfd()` et `tfdi()`
Il s'agit d'écrire une fonction vous même pour calculer la transformée de Fourier discrète à une, puis à deux dimensions (`tfd()` et `tfd2()` respectivement). Pensez
à écrire un programme pour **valider** vos transformées de Fourier.
Pour ce faire utilisez des fonctions dont vous pouvez facilement calculer
analytiquement les transformées de Fourier (les sinus/cosinus s'y prêtent particulièrement).
Vous pouvez également comparer vos résultats avec les fonction `fft()`{.language-python} et `fft2()`{.language-python} de python.
Dans un deuxième temps implémentez les transformées de Fourier inverses
en une et deux dimensions (`itfd()` et `itfd2()` respectivement). Assurez-vous que ces fonctions marchent bien. Un
bon test est que `tfdi(tfd(signal)) == signal`{.language-python}.
En d'autres termes, la transformée de Fourier inverse de la transformée
de Fourier d'un signal, doit donner le signal lui-même.
Vous pouvez également comparer vos résultats avec les fonctions `ifft()`{.language-python}
et `ifft2()`{.language-python} de `numpy`.
## Filtrage de bruit
Sur `cyberlearn`, vous trouverez dans un premier temps **une seule image**
très bruitée nommée `cache.png`. Tapie dans le bruit, une personnalité est cachée prête à bondir.
Il s'agit ici de trouver laquelle.
Vous devrez donc utiliser vos fonctions `tfd2()`{.language-python} et `tfdi2()`{.language-python} pour filtrer les images.
Histoire de vous familiariser avec le filtrage, créez un signal
simple contenant deux fréquences. Un exemple pourrait être
d'échantillonner
$$
f(t)=2.3\cdot \sin(2\pi t) + 0.1\cdot \sin(10\pi t),
$$
pour $t=[0,1]$. Quelle est la période de ce signal? Calculez la transformée de Fourier de ce signal,
puis mettez à zéro le coefficient correspondant à la plus haute fréquence dans le résultat obtenu.
Faites ensuite la transformée de Fourier inverse, du signal
avec un seul des pics. Voilà, si tout s'est bien passé vous venez de
filtrer la haute fréquence de votre signal.
Maintenant que vous êtes une professionnelle du filtrage, passons
la vitesse supérieure. Chargez l'image que vous trouvez sur `cyberlearn`.
Normalement, il s'agit d'une image en niveaux de gris encodées sur 16 bits
avec que du bruit dessus (on voit pas grand chose...).
Calculez la transformée de Fourier discrète à deux dimensions de ce signal. Inspectez le signal (à l'aide de la fonction `plot_surface()` qui se trouve dans `from mpl_toolkits.mplot3d`
par exemple). Comme pour le signal uni-dimensionnel, filtrez les hautes fréquences (mettez les amplitudes à zéro) au delà d'une certaine fréquence[^2]. En deux dimensions, cela revient à
mettre à zéro les $\hat{f}[k_1,k_2]$ $k_1>k_{1,\mathrm{min}}$ ou $k_2>k_{2,\mathrm{min}}$, où $k_{1,\mathrm{min}}$ et $k_{2,\mathrm{min}}$
sont les fréquences à partir desquelles vous voulez filtrer.
Puis, calculez la transformée de Fourier inverse. Si vous avez filtré
correctement votre image, un visage devrait apparaître. Savez-vous de qui il s'agit?
Lorsque vous aurez terminé cette première étape, plus d'images seront déposées sur `cyberlearn` et il faudra
également les filtrer et déterminer les noms des gens s'y trouvant.
## Compression d'image
Sur `cyberlearn` vous trouverez une image nomée `transmetropolitan.pgm`[^3]. Il faudra la lire,
et à l'aide de vos transformées de Fourier la compresser et l'écrire sur le disque.
A vous de définir un format de compression avec pertes.
Vous devrez également fournir l'utilitaire pour la décompresser et l'afficher.
Écrivez un code assez générique pour que l'utilisatrice puisse choisir un degré de compression
allant par exemple de 0 à 9: 0 étant pas compressé et 9 compressé au maximum (quel que soit ce maximum).
Votre compression étant écrite à l'aide de nombres à virgule flottante double précision,
ils prennent plus de place que des entiers 8 bits (comme c'est le cas pour les fichiers PGM).
Afin d'obtenir une conversion, il faut également transformer les amplitudes en entiers 8 bits
avant de les écrire. A l'inverse lorsque vous lirez votre image compressée,
il faut transformer vos entiers 8 bits en nombres à virgule flottante.
# Rendu
La note est une combinaison de la note du code et du rapport.
Il faut rendre un rapport de quelques pages (quelques: **plus petit** que 5).
Ce rapport doit être bref et expliquer votre travail.
Je veux un minimum de mathématiques et un maximum d'explications
de haut niveau sur comment fonctionne votre filtrage et votre compression/décompression.
Vous **devez** faire ce travail par groupe de 2 et aucune exception ne sera faite.
Vous devez rendre le rapport et le code sur `cyberlearn` le tout dans une archive aux noms des deux
personnes du groupe. Je devrais pouvoir exécuter
vos codes pour voir quels sont les visages que vous découvrez et comment vous comprimez
vos images.
# Conseils et remarques
Les librairies `python` que j'ai essayées pour faire moi-même ce TP sont:
1. `numpy` pour les manipulations de matrices et le calcul de TFR (les fonctions `fft.ifft2()`{.language-python} et `fft.fft2()`{.language-python}).
2. `imageio` pour la manipulation d'images (en particulier `imread()`{.language-python} et `imwrite()`{.language-python}).
3. `matplotlib` pour la visualisation des images et ds spectres (en particulier `pyplot.imshow()`{.language-python} et `plot_surface()`{.language-python} respectivement).
Vous n'êtes pas obligés de les utiliser. Vous pouvez également utiliser n'importe quel autre language pour effectuer ce TP.
[^1]: On représente $f[n]$ et $\hat{f}[k]$ comme des listes (ou vecteurs) de longueur $N$.
[^2]: Attention le spectre est "à double" donc n'oubliez pas de garder les parties symétriques également.
[^3]: Pour les fans de bande dessinée je vous recommande la lecture de ces comics.
\ No newline at end of file
STYLES := ../css/tufte-css/tufte.css \
../css/pandoc.css \
../css/pandoc-solarized.css \
../css/tufte-extra.css
OPTIONS = --filter=pandoc-numbering
OPTIONS += --filter=pandoc-crossref
PDFOPTIONS = --pdf-engine pdflatex
PDFOPTIONS += --highlight-style kate
PDFOPTIONS += --number-sections
PDFOPTIONS += --template=./default.latex
HTMLOPTIONS += -t html5
HTMLOPTIONS += -c ../../css/styling.css
HTMLOPTIONS += --self-contained
HTMLOPTIONS += --mathjax=../../MathJax.js
MD=$(wildcard *.md)
HTML=$(MD:%.md=%.html)
PDF=$(MD:%.md=%.pdf)
all: $(HTML) $(PDF)
%.pdf: %.md Makefile
pandoc -s $(OPTIONS) $(PDFOPTIONS) -o $@ $<
%.html: %.md Makefile
pandoc -s $(OPTIONS) $(HTMLOPTIONS) -o $@ $<
clean:
rm -rf *.html *.pdf
......@@ -15,7 +15,7 @@
\title{Travail pratique sur les intégrales}
% \author{Orestis Malaspinas}
\date{A rendre pour le 09.12.2016}
\date{A rendre pour le 5.12.2018}
\begin{document}
\maketitle
......
---
# author:
# - Orestis Malaspinas
# - Michaël El Kharroubi (update)
title: Travail pratique sur les intégrales
autoSectionLabels: true
autoEqnLabels: false
eqnPrefix:
- "éq."
- "éqs."
chapters: false
numberSections: false
chaptersDepth: 1
sectionsDepth: 3
lang: fr
documentclass: article
papersize: A4
cref: false
pandoc-numbering:
- category: exercice
urlcolor: blue
---
\newcommand{\dd}{\mathrm{d}}
\newcommand{\real}{\mathbb{R}}
\newcommand{\vectwo}[2]{\begin{pmatrix}#1 \\ #2 \end{pmatrix}}
\newcommand{\vecthree}[3]{\begin{pmatrix}#1 \\ #2 \\ #3 \end{pmatrix}}
\newcommand{\mat}[1]{\underline{\underline{#1}}}
\newcommand{\mattwo}[4]{\begin{pmatrix}
#1 & #2 \\
#3 & #4
\end{pmatrix}}
# But du travail et rendu
Le but de ce travail pratique est d'implémenter les méthodes numériques de calcul d'intégrales que nous avons vues en cours,
afin de les comprendre de façon un peu plus approfondie. Puis, il faudra utiliser ces méthodes pour calculer
des convolutions afin de filtrer un signal.
Le noyau de calcul de votre travail pratique doit être réalisé en C et il doit être compilable avec
un `Makefile`. Pour la réalisation de graphiques vous êtes libres d'utiliser
l'outil de votre choix (la librairie matplotlib par exemple). Certaines visualisations
peuvent être faites à l'aide de la librairie SDL pour obtenir un bonus.
Vous devrez rendre un petit rapport (environ 10 pages) qui explique ce que vous avez fait et dans quel but. Il devra contenir
en premier lieu une introduction générale à votre travail qui peut être comprise par n'importe qui.
Puis une courte introduction théorique (rappelant les formules et le but du travail), une partie expliquant dans les grandes lignes
des algorithmes (pas de copier-coller du code, pas de captures d'écran non plus sous peine de sanctions terribles).
La partie importante est celle contenant les résultats obtenus et leur discussion. Finalement, il faut inclure
une conclusion résumant votre travail.
Le travail doit être effectué par groupes de deux (oui c'est une obligation).
N'oubliez pas de mentionner les deux noms sur le rapport et dans le code.
Je dois pouvoir exécuter le code
afin de pouvoir reproduire les résultats présentés dans le rapport (un petit *readme* pour les instructions est le bienvenu).
Je dois aussi pouvoir définir ma propre fonction à intégrer de façon simple.
Le rapport et le code doivent être déposés sur `Cyberlearn` et `gitedu` respectivement.
La note sera une combinaison entre le code rendu et le rapport (moitié/moitié).
# Intégration numérique
## Méthodes d'intégration
Dans un premier temps, le but est donc d'écrire un code où l'utilisateur spécifie une fonction $f(x)$
(il écrira lui-même le code de la dite fonction) qu'on
suppose ``gentille'' (pas besoin de vérifier
si elle est bien définie partout par exemple), un intervalle $[a,b]$, et
un nombre de subdivisions $N$. Le code devra rendre la valeur numérique obtenue pour l'intégrale de la fonction
\begin{equation}
I(a,b,N,f(x)) \cong \int_a^bf(x)\dd x
\end{equation}
pour deux méthodes vues en cours (méthode du rectangle à gauche, et méthode du trapèze)[^1].
Dans le cas de la méthode du rectangle à gauche on a
\begin{equation}
I(a,b,N,f(x))=\sum_{i=0}^{N-1} f(a+i\delta x)\delta x,\quad \delta x=\frac{b-a}{N}.
\end{equation}
## Validation
Lorsqu'on calcule numériquement une intégrale, on souhaite que la valeur de celle-ci *converge*. C'est-à-dire
que plus $N$ est grand, plus la valeur de l'approximation est bonne. Pour vérifier que cela se passe avec nos
méthodes d'intégration, vous devrez effectuer une étude de l'erreur de vos méthodes d'intégration numériques.
Pour ce faire, nous choisissons une fonction $f(x)$
dont la primitive est simple à calculer
\begin{equation}
f(x)=-\frac{2x-1}{\exp(x^2-x+2)},
\end{equation}3
et un intervalle sur lequel la fonction est bien définie. Choisissons ici $[a,b]$ avec $a=1$ et $b=5$.
On peut donc calculer l'intégrale exactement (analytiquement) et on notera ce résultat exact $I$
\begin{equation}
I=\int_a^b-\frac{2x-1}{\exp(x^2-x+2)}\mathrm{d}x.
\end{equation}
Puis, il faut calculer l'erreur commise par l'évaluation de la fonction $I(a,b,N,f(x))$
pour $N=5, 10, 50, 100, 500, 1000$ pour chacune des méthodes que vous avez implémentées ci-dessus.
L'erreur $E(N)$ se calcule de la façon suivante
\begin{equation}
E(N)=\left|\frac{I-I(a,b,N,f(x))}{I}\right|
\end{equation}
Ces résultats devront être illustrés sous forme de graphique ($E$ en fonction de $N$ en échelle log-log).
Que constatez-vous? Pouvez-vous mesurer le taux de décroissance de l'erreur (a.k.a. l'ordre de l'erreur)?
# Convolutions et filtrage
Nous voulons à présent essayer de voir comment utiliser la convolution pour filtrer un signal simple.
## Convolution en 1 dimension
### La convolution continue
Le signal que nous souhaitons filtrer est définit par la fonction $s(x)$
\begin{equation}
s(x)=\sin(2\pi\omega_1 x)+\sin(2\pi\omega_2 x).
\end{equation}
La fonction de filtrage que nous allons utiliser est définie par $f(x)$
\begin{equation}
f(x)=\left\{\begin{array}{ll}
\frac{1}{\psi},&\mbox{ si }x\in[-\psi/2,\psi/2]\\
0,&\mbox{ sinon.}
\end{array}\right.
\end{equation}
Afin de se familiariser un peu avec ces deux fonctions, dessiner les
pour différentes valeur de $\omega_1$, $\omega_2$, et $\psi$.
Puis, calculer analytiquement (à la main avec du papier et un crayon[^2]) la convolution $(f\ast s)(x)$ (rappelez vous qu'on
a fait des choses similaires en cours).
Ensuite, on souhaite filtrer $s$ à l'aide de $f$. Pour cela, on veut "enlever" totalement
la partie $\omega_1$ (ou $\omega_2$) de $f\ast s$. En choisissant astucieusement $\psi$
on se rend compte que cela est plus simple qu'il n'y paraît!
Utiliser ces relations pour illustrer le filtrage de $s(x)$ pour différentes valeurs de $\omega_1$ et $\omega_2$.
### La convolution discrète
Utiliser une des méthodes implémentées dans le chapitre précédent pour calculer numériquement
le filtrage de $s(x)$ par la fonction $f(x)$ pour différentes valeurs de $\omega_1$, $\omega_2$ et $\psi$ (essayer par exemple de reproduire
les résultats de la section précédente). Puis, répétez l'opération avec une autre fonction de filtrage
$h(x)$ définie par
\begin{equation}
h(x)=\frac{1}{\sqrt{2\pi\psi}}\exp(-x^2/(2\psi)).
\end{equation}
Voyez-vous des différences?
## Convolution en 2 dimensions
### Théorie
Dans le cadre de ce tp, nous allons nous concentrer sur la convolution discrète d'un signal discret en deux dimensions. Pour représenter notre signal discret en deux dimensions, nous pouvons utiliser des matrices. Par exemple :
\begin{equation}
\label{eqn:mat_exemple}
\mat{A}=\begin{pmatrix}
a_{-1,-1} & a_{-1,0} & a_{-1,1}\\
a_{0,-1} & a_{0,0} & a_{0,1}\\
a_{1,-1} & a_{1,0} & a_{1,1}
\end{pmatrix} ,\quad \mat{B}=\begin{pmatrix}
b_{-2,-2} & b_{-2,-1} & b_{-2,0} & b_{-2,1} & b_{-2,2}\\
b_{-1,-2} & b_{-1,-1} & b_{-1,0} & b_{-1,1} & b_{-1,2}\\
b_{0,-2} & b_{0,-1} & b_{0,0} & b_{0,1} & b_{0,2}\\
b_{1,-2} & b_{1,-1} & b_{1,0} & b_{1,1} & b_{1,2}\\
b_{2,-2} & b_{2,-1} & b_{2,0} & b_{2,1} & b_{2,2}
\end{pmatrix}
\end{equation}
Une image matricielle peut être interprétée comme un signal discret en deux dimensions.
Pour rappel, la formule du produit de convolution en 1 dimension d'un signal discret est :
\begin{equation}
(s\ast u)[t] =\sum_{n=-N}^{+N} s[n]\cdot u[t-n]
\end{equation}
Lorsque l'on rajoute une nouvelle dimension la formule devient, avec $-M$ l'indice de ligne le plus petit de la matrice $\mat{A}$, et $+M$ le plus grand, ainsi que $-N$, $+N$ pour les indices de colonne :
\begin{equation}
(\mat{A}\ast \mat{B})[i,j] =\sum_{m=-M}^{+M}\sum_{n=-N}^{+N} \mat{A}[m, n]\cdot \mat{B}[i-m,j-n]
\end{equation}
Si on reprend par exemple les matrices $\mat{A}$ et $\mat{B}$ de l'équation \ref{eqn:mat_exemple}, et qu'on choisit le centre (1,1), on obtient :
\begin{equation}
\begin{aligned}
(\mat{A}\ast \mat{B})[1,1] =\sum_{m=-1}^{+1}\sum_{n=-1}^{+1} \mat{A}[m, n]\cdot \mat{B}[1-m,1-n]\\
(\mat{A}\ast \mat{B})[1,1] = a_{-1,-1}\cdot b_{2,2}+\dots +a_{1,1}\cdot b_{0,0}\\
\end{aligned}
\end{equation}
Si l'on essaye de calculer $(\mat{A}\ast \mat{B})[2,2]$, on découvre qu'il nous faut des valeurs qui ne sont pas dans notre matrice, comme par exemple, $b_{3,3}$. Voici 3 solutions différentes pour définir nos valeurs manquantes :
- Les valeurs en dehors de la matrice sont nulles, $b_{3,3} = 0$.
- Recopier la valeur du voisin le plus proche, $b_{3,3} = b_{2,2}$.
- Définir que notre signal est périodique, on a donc $b_{3,3} = b_{-2,-2}$.
### Exercice
Au risque de se répéter, rappelons quelques contraintes administratives.
Vous allez devoir implémenter une solution de traitement d'images en nuances de gris basé sur la convolution de signal en deux dimensions
par groupe de deux. Le language imposé est le C. Vous devrez rendre le code sur `gitedu` ainsi qu'un rapport succinct sur `Cyberlearn` (moins de 10 pages par groupe).
Pour commencer, vous devrez implémenter un outil permettant de lire des images au format PGM
(n'hésitez pas à réutiliser celui que vous avez déjà implémenté en programmation séquentielle).
Vous utiliserez le format binaire pour stocker la valeur de vos pixels.
Votre programme devra respecter les spécifications du format PGM ([http://netpbm.sourceforge.net/doc/pgm.html](http://netpbm.sourceforge.net/doc/pgm.html)).
Pour visualiser votre image, vous pouvez à choix l'afficher avec la librairie SDL (bonus sur la note finale) ou alors la sauvegarder au format PGM, et utiliser un outil compatible (par ex: ImageMagick).
Lorsque vous appliquerez les différents filtres, si vous tombez sur des valeurs inférieures à 0 ou supérieur à votre valeur maximale (par ex: $2^{16}-1$); vous mettrez la valeur cohérente la plus proche, 0 si $<$ 0 et $max$ si $>$ $max$.
#### Partie 1
Calculez à la main le produit de convolution de ces deux matrices, en utilisant la méthode de votre choix pour la gestion des bords :
\begin{equation*}
\mat{A}=\begin{pmatrix}
0 & 1 & 0\\
-1 & 0 & -1\\
0 & 1 & 0
\end{pmatrix} ,\quad \mat{B}=\begin{pmatrix}
1 & 2 & 3\\
4 & 5 & 6\\
7 & 8 & 9
\end{pmatrix}
\end{equation*}
#### Partie 2
Appliquez les 5 filtres ci-dessous en faisant le produit $\mat{F_n}\ast \mat{\mathcal{I}}$, où $\mat{\mathcal{I}}$ est l'image "part2.pgm" jointe à l'énoncé. Expliquez avec vos mots l'effet de ces filtres, est essayant d'être le plus descriptif possible (évitez les phrases de 3 mots).
\begin{equation*}
\mat{F_0} = \begin{pmatrix}
0 & 0 & 0\\
0 & 1 & 0\\
0 & 0 & 0
\end{pmatrix}
\mat{F_1} = \frac{1}{25}\begin{pmatrix}
1 & 1 & 1 & 1 & 1\\
1 & 1 & 1 & 1 & 1\\
1 & 1 & 1 & 1 & 1\\
1 & 1 & 1 & 1 & 1\\
1 & 1 & 1 & 1 & 1
\end{pmatrix}
\mat{F_2} = \frac{1}{256}\begin{pmatrix}
1 & 4 & 6 & 4 & 1\\
4 & 16 & 24 & 16 & 4\\
6 & 24 & 36 & 24 & 6\\
4 & 16 & 24 & 16 & 4\\
1 & 4 & 6 & 4 & 1
\end{pmatrix}
\end{equation*}
\begin{equation*}
\mat{F_3} = \begin{pmatrix}
0 & -1 & 0\\
-1 & 5 & -1\\
0 & -1 & 0
\end{pmatrix}
\mat{F_4} = \begin{pmatrix}
0 & -1 & 0\\
-1 & 4 & -1\\
0 & -1 & 0
\end{pmatrix}
\end{equation*}
\newpage
#### Partie 3
Récupérez sur cyberlearn l'image nommée `part3_<n>.pgm`, où `n` est votre numéro de groupe. Cette image a été fortement bruitée, heureusement (quelle chance vraiment :)), le bruit est périodique, et peut être supprimé à l'aide d'un filtre moyenneur.
\begin{equation*}
\mat{F} = \frac{1}{9}\begin{pmatrix}
1 & 1 & 1\\
1 & 1 & 1\\
1 & 1 & 1
\end{pmatrix}
\end{equation*}
Pour débruiter l'image vous devez lui appliquer le filtre $\mat{F}$. Afin d'éviter les problèmes liés à la gestion des bords, vous n'afficherez que la partie interne de l'image filtrée. Autrement dit, vous supprimerez les 2 premières ainsi que les 2 dernières lignes et colonnes (si votre image fait 100x100, vous garderez le centre de taille 96x96).
Si vous obteniez malgré tout une image totalement grise, vous pourriez utiliser la fonction suivante pour normaliser les valeurs de votre matrice $x'=(2^n-1)\cdot\frac{x-min(\mathcal{I})}{max(\mathcal{I})-min(\mathcal{I})}$[^3] pour obtenir une image n bits. Cette fonction permet de maximiser l'écart entre les différents niveaux de gris qui composent votre image, ce qui a pour effet d'accentuer les différences.
[^1]: Vous retrouverez les formules dans le polycopié.
[^2]: Exceptionnellement un stylo est également toléré.
[^3]: $x'$ la nouvelle valeur de votre pixel, $x$ l'ancienne valeur de votre pixel, $\mathcal{I}$ la matrice de votre image.
\ No newline at end of file
OPTIONS = --filter=pandoc-numbering
OPTIONS += --filter=pandoc-crossref
PDFOPTIONS = --highlight-style kate
PDFOPTIONS += --pdf-engine pdflatex
PDFOPTIONS += --number-sections
PDFOPTIONS += --template=./default.latex
HTMLOPTIONS += -t html5
HTMLOPTIONS += -c ../../css/styling.css
HTMLOPTIONS += --self-contained
HTMLOPTIONS += --mathjax=MathJax.js
MD=$(wildcard *.md)
HTML=$(MD:%.md=%.html)
PDF=$(MD:%.md=%.pdf)
all: $(HTML) $(PDF)
%.pdf: %.md Makefile
sed -e 's/language-python/python/g' -e 's/language-bash/bash/g' $< > $*_tex.md
pandoc -s $(OPTIONS) $(PDFOPTIONS) -o $@ $<
rm $*_tex.md
%.html: %.md Makefile
pandoc -s $(OPTIONS) $(HTMLOPTIONS) -o $@ $<
clean:
rm -rf *.html *.pdf
var fileref=document.createElement('script')
fileref.setAttribute("type","text/javascript")
fileref.setAttribute("src", "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML")
document.getElementsByTagName("head")[0].appendChild(fileref)
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+python */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 1em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+python */
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(g){var c=/\blang(?:uage)?-([\w-]+)\b/i,a=0,C={manual:g.Prism&&g.Prism.manual,disableWorkerMessageHandler:g.Prism&&g.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof M?new M(e.type,C.util.encode(e.content),e.alias):Array.isArray(e)?e.map(C.util.encode):e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++a}),e.__id},clone:function t(e,n){var r,a,i=C.util.type(e);switch(n=n||{},i){case"Object":if(a=C.util.objId(e),n[a])return n[a];for(var l in r={},n[a]=r,e)e.hasOwnProperty(l)&&(r[l]=t(e[l],n));return r;case"Array":return a=C.util.objId(e),n[a]?n[a]:(r=[],n[a]=r,e.forEach(function(e,a){r[a]=t(e,n)}),r);default:return e}}},languages:{extend:function(e,a){var t=C.util.clone(C.languages[e]);for(var n in a)t[n]=a[n];return t},insertBefore:function(t,e,a,n){var r=(n=n||C.languages)[t],i={};for(var l in r)if(r.hasOwnProperty(l)){if(l==e)for(var o in a)a.hasOwnProperty(o)&&(i[o]=a[o]);a.hasOwnProperty(l)||(i[l]=r[l])}var s=n[t];return n[t]=i,C.languages.DFS(C.languages,function(e,a){a===s&&e!=t&&(this[e]=i)}),i},DFS:function e(a,t,n,r){r=r||{};var i=C.util.objId;for(var l in a)if(a.hasOwnProperty(l)){t.call(a,l,a[l],n||l);var o=a[l],s=C.util.type(o);"Object"!==s||r[i(o)]?"Array"!==s||r[i(o)]||(r[i(o)]=!0,e(o,t,l,r)):(r[i(o)]=!0,e(o,t,null,r))}}},plugins:{},highlightAll:function(e,a){C.highlightAllUnder(document,e,a)},highlightAllUnder:function(e,a,t){var n={callback:t,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};C.hooks.run("before-highlightall",n);for(var r,i=n.elements||e.querySelectorAll(n.selector),l=0;r=i[l++];)C.highlightElement(r,!0===a,n.callback)},highlightElement:function(e,a,t){for(var n,r,i=e;i&&!c.test(i.className);)i=i.parentNode;i&&(n=(i.className.match(c)||[,""])[1].toLowerCase(),r=C.languages[n]),e.className=e.className.replace(c,"").replace(/\s+/g," ")+" language-"+n,e.parentNode&&(i=e.parentNode,/pre/i.test(i.nodeName)&&(i.className=i.className.replace(c,"").replace(/\s+/g," ")+" language-"+n));var l={element:e,language:n,grammar:r,code:e.textContent},o=function(e){l.highlightedCode=e,C.hooks.run("before-insert",l),l.element.innerHTML=l.highlightedCode,C.hooks.run("after-highlight",l),C.hooks.run("complete",l),t&&t.call(l.element)};if(C.hooks.run("before-sanity-check",l),l.code)if(C.hooks.run("before-highlight",l),l.grammar)if(a&&g.Worker){var s=new Worker(C.filename);s.onmessage=function(e){o(e.data)},s.postMessage(JSON.stringify({language:l.language,code:l.code,immediateClose:!0}))}else o(C.highlight(l.code,l.grammar,l.language));else o(C.util.encode(l.code));else C.hooks.run("complete",l)},highlight:function(e,a,t){var n={code:e,grammar:a,language:t};return C.hooks.run("before-tokenize",n),n.tokens=C.tokenize(n.code,n.grammar),C.hooks.run("after-tokenize",n),M.stringify(C.util.encode(n.tokens),n.language)},matchGrammar:function(e,a,t,n,r,i,l){for(var o in t)if(t.hasOwnProperty(o)&&t[o]){if(o==l)return;var s=t[o];s="Array"===C.util.type(s)?s:[s];for(var g=0;g<s.length;++g){var c=s[g],u=c.inside,h=!!c.lookbehind,f=!!c.greedy,d=0,m=c.alias;if(f&&!c.pattern.global){var p=c.pattern.toString().match(/[imuy]*$/)[0];c.pattern=RegExp(c.pattern.source,p+"g")}c=c.pattern||c;for(var y=n,v=r;y<a.length;v+=a[y].length,++y){var k=a[y];if(a.length>e.length)return;if(!(k instanceof M)){if(f&&y!=a.length-1){if(c.lastIndex=v,!(x=c.exec(e)))break;for(var b=x.index+(h?x[1].length:0),w=x.index+x[0].length,A=y,P=v,O=a.length;A<O&&(P<w||!a[A].type&&!a[A-1].greedy);++A)(P+=a[A].length)<=b&&(++y,v=P);if(a[y]instanceof M)continue;N=A-y,k=e.slice(v,P),x.index-=v}else{c.lastIndex=0;var x=c.exec(k),N=1}if(x){h&&(d=x[1]?x[1].length:0);w=(b=x.index+d)+(x=x[0].slice(d)).length;var j=k.slice(0,b),S=k.slice(w),E=[y,N];j&&(++y,v+=j.length,E.push(j));var _=new M(o,u?C.tokenize(x,u):x,m,x,f);if(E.push(_),S&&E.push(S),Array.prototype.splice.apply(a,E),1!=N&&C.matchGrammar(e,a,t,y,v,!0,o),i)break}else if(i)break}}}}},tokenize:function(e,a){var t=[e],n=a.rest;if(n){for(var r in n)a[r]=n[r];delete a.rest}return C.matchGrammar(e,t,a,0,0,!1),t},hooks:{all:{},add:function(e,a){var t=C.hooks.all;t[e]=t[e]||[],t[e].push(a)},run:function(e,a){var t=C.hooks.all[e];if(t&&t.length)for(var n,r=0;n=t[r++];)n(a)}},Token:M};function M(e,a,t,n,r){this.type=e,this.content=a,this.alias=t,this.length=0|(n||"").length,this.greedy=!!r}if(g.Prism=C,M.stringify=function(a,t,e){if("string"==typeof a)return a;if(Array.isArray(a))return a.map(function(e){return M.stringify(e,t,a)}).join("");var n={type:a.type,content:M.stringify(a.content,t,e),tag:"span",classes:["token",a.type],attributes:{},language:t,parent:e};if(a.alias){var r=Array.isArray(a.alias)?a.alias:[a.alias];Array.prototype.push.apply(n.classes,r)}C.hooks.run("wrap",n);var i=Object.keys(n.attributes).map(function(e){return e+'="'+(n.attributes[e]||"").replace(/"/g,"&quot;")+'"'}).join(" ");return"<"+n.tag+' class="'+n.classes.join(" ")+'"'+(i?" "+i:"")+">"+n.content+"</"+n.tag+">"},!g.document)return g.addEventListener&&(C.disableWorkerMessageHandler||g.addEventListener("message",function(e){var a=JSON.parse(e.data),t=a.language,n=a.code,r=a.immediateClose;g.postMessage(C.highlight(n,C.languages[t],t)),r&&g.close()},!1)),C;var e=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return e&&(C.filename=e.src,C.manual||e.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(C.highlightAll):window.setTimeout(C.highlightAll,16):document.addEventListener("DOMContentLoaded",C.highlightAll))),C}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
Prism.languages.markup={comment:/<!--[\s\S]*?-->/,prolog:/<\?[\s\S]+?\?>/,doctype:/<!DOCTYPE[\s\S]+?>/i,cdata:/<!\[CDATA\[[\s\S]*?]]>/i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&amp;/,"&"))}),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^<!\[CDATA\[|\]\]>$/i;var n={"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:s}};n["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var i={};i[a]={pattern:RegExp("(<__[\\s\\S]*?>)(?:<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\s*|[\\s\\S])*?(?=<\\/__>)".replace(/__/g,a),"i"),lookbehind:!0,greedy:!0,inside:n},Prism.languages.insertBefore("markup","cdata",i)}}),Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup;
!function(s){var e=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?[\s\S]*?(?:;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:RegExp("url\\((?:"+e.source+"|.*?)\\)","i"),selector:RegExp("[^{}\\s](?:[^{};\"']|"+e.source+")*?(?=\\s*\\{)"),string:{pattern:e,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var a=s.languages.markup;a&&(a.tag.addInlined("style","css"),s.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:a.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:s.languages.css}},alias:"language-css"}},a.tag))}(Prism);
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/};
Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.])\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+)n?|\d+n|NaN|Infinity)\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee][+-]?\d+)?/,function:/[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/-[-=]?|\+[+=]?|!=?=?|<<?=?|>>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/,greedy:!0,inside:{interpolation:{pattern:/\${[^}]+}/,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.js=Prism.languages.javascript;
Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},"string-interpolation":{pattern:/(?:f|rf|fr)(?:("""|''')[\s\S]+?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:{{)*){(?!{)(?:[^{}]|{(?!{)(?:[^{}]|{(?!{)(?:[^{}])+})+})+}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|rb|br)?("""|''')[\s\S]+?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|rb|br)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^\s*)@\w+(?:\.\w+)*/i,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:True|False|None)\b/,number:/(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python;