diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml
new file mode 100644
index 0000000..d07081b
--- /dev/null
+++ b/.github/workflows/static.yml
@@ -0,0 +1,48 @@
+name: Deploy Sphinx documentation to GitHub Pages
+
+on:
+ push:
+ branches: ["Dev"]
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v4
+
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.x'
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install sphinx sphinx_rtd_theme myst-parser furo
+
+ - name: Build Sphinx documentation
+ run: |
+ sphinx-build -b html ./docs/source ./docs/html
+
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./docs/html # Dossier contenant la doc HTML générée
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.gitignore b/.gitignore
index f7275bb..ae030b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
venv/
+*.env
diff --git a/README.md b/README.md
index 40ea76a..e5c34f1 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
-
+
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..926e290
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,9 @@
+## 🛠️ To-Do List
+
+- [x] Générer la doc avec Sphinx
+- [x] Déployer la documentation en ligne
+- [ ] Création du jeu F1F
+- [ ] Commande administrateur complète
+- [ ] Gestions des data
+- [ ] Tester le bot à spa en commité réduit (DEV / STAFF / BOOSTEUR)
+- [ ] Supprimé la commande ban du bot
\ No newline at end of file
diff --git a/app/.env b/app/.env
deleted file mode 100644
index 66619df..0000000
--- a/app/.env
+++ /dev/null
@@ -1 +0,0 @@
-DISCORD_TOKEN_F1F = MTMzNTkzODIzNDU5NjcyMDc1MQ.GWn40f.JO91FWe8InPBMej2wiV-bA8NAiFFD1_yirdW7g
\ No newline at end of file
diff --git a/app/__pycache__/admin_command.cpython-313.pyc b/app/__pycache__/admin_command.cpython-313.pyc
new file mode 100644
index 0000000..d764dd7
Binary files /dev/null and b/app/__pycache__/admin_command.cpython-313.pyc differ
diff --git a/app/__pycache__/api.cpython-313.pyc b/app/__pycache__/api.cpython-313.pyc
new file mode 100644
index 0000000..1401bc6
Binary files /dev/null and b/app/__pycache__/api.cpython-313.pyc differ
diff --git a/app/__pycache__/classement.cpython-313.pyc b/app/__pycache__/classement.cpython-313.pyc
index db67291..7937c49 100644
Binary files a/app/__pycache__/classement.cpython-313.pyc and b/app/__pycache__/classement.cpython-313.pyc differ
diff --git a/app/__pycache__/config.cpython-313.pyc b/app/__pycache__/config.cpython-313.pyc
index 1f13749..1452c02 100644
Binary files a/app/__pycache__/config.cpython-313.pyc and b/app/__pycache__/config.cpython-313.pyc differ
diff --git a/app/__pycache__/error_embed.cpython-313.pyc b/app/__pycache__/error_embed.cpython-313.pyc
index f0c6544..67c7e16 100644
Binary files a/app/__pycache__/error_embed.cpython-313.pyc and b/app/__pycache__/error_embed.cpython-313.pyc differ
diff --git a/app/__pycache__/f1api.cpython-313.pyc b/app/__pycache__/f1api.cpython-313.pyc
new file mode 100644
index 0000000..d3c634e
Binary files /dev/null and b/app/__pycache__/f1api.cpython-313.pyc differ
diff --git a/app/__pycache__/pronos.cpython-313.pyc b/app/__pycache__/pronos.cpython-313.pyc
new file mode 100644
index 0000000..c77d786
Binary files /dev/null and b/app/__pycache__/pronos.cpython-313.pyc differ
diff --git a/app/__pycache__/tools.cpython-313.pyc b/app/__pycache__/tools.cpython-313.pyc
index cb07e72..c74be39 100644
Binary files a/app/__pycache__/tools.cpython-313.pyc and b/app/__pycache__/tools.cpython-313.pyc differ
diff --git a/app/_app.py b/app/_app.py
index 3974cfd..848d74e 100644
--- a/app/_app.py
+++ b/app/_app.py
@@ -1,18 +1,22 @@
from discord import app_commands
-
-from config import bot, tree, logger, discord, TOKEN
-from qualif import pronos_qualif_logic, visualisation_pronos_qualif_logic, modify_qualif_logic
-from race import pronos_course_logic, visualisation_pronos_course_logic, modify_course
-from scrapping_result import scrapping_result_course, scrapping_result_qualif
-from tools import help, clear_slash
-from error_embed import chat_you_dont_have_perm
-from classement import embed_classement, calculer_classement
-from read_docs import read_the_docs
+import asyncio
+from config import os, bot, tree, logger,discord, TOKEN
+from tools import help, clear_slash,start_Session,Wait,presentation_bot
+import error_embed as embed
+import classement as ldb
+from admin_command import ban
+import pronos as pr
+import json
+import f1api
+from datetime import timedelta,datetime
@bot.event
async def on_ready():
- await tree.sync()
+ global command_enabled
+ command_enabled = False
+ global auto
+ auto = False
print(f"✅ Connecté en tant que {bot.user} !")
logger.info("BOT LANCER")
@@ -55,110 +59,234 @@ async def helping_tools(interaction: discord.Interaction):
# _______________________________________________________________________________________________________________________________
-@tree.command(name="pronos_course", description="Pronostiques Course de Formule 1")
-@app_commands.describe(premier="Le premier", deuxieme="Le deuxième", troisieme="Le troisième", meilleur_tour="Meilleur Tour")
-async def prono_race(interaction: discord.Interaction, premier: str, deuxieme: str, troisieme: str, meilleur_tour: str):
- await pronos_course_logic(interaction, premier, deuxieme, troisieme, meilleur_tour)
+@tree.command(name="pronos_course", description="Enregistre tes pronos ou modifie les si tu l'a déja fait par le passé(max 1 fois)")
+@app_commands.describe(premier="Le premier", deuxieme="Le deuxième", troisieme="Le troisième", best_lap="Meilleur Tour")
+async def submit(interaction: discord.Interaction, premier: str, deuxieme: str, troisieme: str, best_lap: str):
+ await interaction.response.defer(ephemeral=True)
+ if (command_enabled):
-# _______________________________________________________________________________________________________________________________
+ if(pr.pronos(interaction.user.id, str(interaction.user),
+ premier, deuxieme, troisieme, best_lap)):
+ await interaction.followup.send("Ton prono a bien été pris en compte", ephemeral=True)
+ else:
+ await embed.Error(interaction, "Tu ne peux modifier ton pronostic qu'une fois")
+
+
+ else:
+ await embed.Error(interaction, "Il y a une heure pour tout faire, et celle ci n'est pas pour les pronos. Par conséquent ton prono n'a pas pu être enregistré. Si tu veux être notifié des prochaines sessions, utilise /role")
-@tree.command(name="pronos_qualifs", description="Pronostiques Qualification de Formule 1")
-@app_commands.describe(premier="Le premier", deuxieme="Le deuxième", troisieme="Le troisième")
-async def pronostique_qualification(interaction: discord.Interaction, premier: str, deuxieme: str, troisieme: str):
- await pronos_qualif_logic(interaction, premier, deuxieme, troisieme)
# _______________________________________________________________________________________________________________________________
-
-@tree.command(name="visualisation_pronos_course", description="Voir mon pronostique de course")
-async def voir_pronos_course(interaction: discord.Interaction):
- await visualisation_pronos_course_logic(interaction)
+@tree.command(name="visualisation", description="Te montre tes pronos")
+async def visu(interaction : discord.Interaction):
+ await interaction.response.defer(ephemeral=True)
+ await pr.visualisation(interaction)
# _______________________________________________________________________________________________________________________________
-
-@tree.command(name="visualisation_pronos_qualif", description="Voir mon pronostique de qualif")
-async def voir_pronos_qualif(interaction: discord.Interaction):
- await visualisation_pronos_qualif_logic(interaction)
-
+@tree.command(name="leaderboard", description="Affiche le clasement des membres")
+async def leaderboard(interaction: discord.Interaction):
+ await interaction.response.defer()
+ embed = ldb.Leaderboard()
+ await interaction.followup.send(content=None,embed=embed)
+
# _______________________________________________________________________________________________________________________________
-
-
-@tree.command(name="modify_course", description="Modifie ton pronostique pour la course")
-@app_commands.describe(premier="Le premier", deuxieme="Le deuxième", troisieme="Le troisième", meilleur_tour="Meilleur Tour")
-async def modifier_pronos(interaction: discord.Interaction, premier: str, deuxieme: str, troisieme: str, meilleur_tour: str):
- # 👈 Appel ici
- await modify_course(interaction, premier, deuxieme, troisieme, meilleur_tour)
-
-# _______________________________________________________________________________________________________________
-
-
-@tree.command(name="modify_qualif", description="Modifie ton pronostique pour la qualification")
-@app_commands.describe(premier="Le premier", deuxieme="Le deuxième", troisieme="Le troisième")
-async def modifier_qualif(interaction: discord.Interaction, premier: str, deuxieme: str, troisieme: str):
- await modify_qualif_logic(interaction, premier, deuxieme, troisieme)
-
-# _______________________________________________________________________________________________________________
-
-
-@tree.command(name="admin_result_reasearch_course", description="AC-001")
-async def recup_valeur(interaction: discord.Interaction):
+
+@tree.command(name="admin_ban", description="AC-03")
+@app_commands.describe(member= "Membre à bannnir", reason = "Raison du bannissement" , article = "Le ou les articles qu'il a enfreint")
+async def bannissement(interaction: discord.Interaction, member: discord.Member, reason: str, article: str):
+ if interaction.user.guild_permissions.ban_members:
+ await interaction.response.defer(ephemeral=True)
+ await ban(interaction, member, reason, article)
+ logger.info(f"{interaction.user.name} à banni {member}")
+
+ else:
+ await embed.chat_you_dont_have_perm(interaction)
+
+# _______________________________________________________________________________________________________________________________
+
+@tree.command(name="admin_open", description="Ouvre une session de pronostics pour un temp donné")
+@app_commands.describe(duration="temps en heures")
+async def create(interaction: discord.Interaction, duration: float):
if interaction.user.guild_permissions.administrator:
+ global auto
+ if (not auto):
+ await interaction.response.defer()
+ global task
+ global command_enabled
+ command_enabled = True
+ task = asyncio.create_task(
+ start_Session(interaction, duration))
+ await task
+ command_enabled = False
+ else:
+ await interaction.response.defer(ephemeral=True)
+ await embed.Error(interaction, "Cette commande n'est pas disponible en mode auto")
+ else:
await interaction.response.defer(ephemeral=True)
+ await interaction.followup.send(embed=await embed.permError(interaction))
+
+# _______________________________________________________________________________________________________________________________
- try:
- embed = await scrapping_result_course(interaction)
- await interaction.followup.send(embed=embed, ephemeral=True)
- except Exception as e:
- logger.error(f"Erreur pendant le scraping : {e}")
- await interaction.followup.send(
- content="❌ Une erreur est survenue lors de la récupération des résultats.",
- ephemeral=True
- )
+@tree.command(name="admin_close", description="Ferme la session de pronos")
+async def close(interaction: discord.Interaction):
+ await interaction.response.defer(ephemeral=True)
+ if interaction.user.guild_permissions.administrator:
+ global auto
+ if (not auto):
+ global command_enabled
+ if (command_enabled):
+ task.cancel()
+ try:
+ await task
+ except asyncio.CancelledError:
+ pass
+ command_enabled = False
+ await interaction.followup.send("Les pronos ont bien été fermés")
+ else:
+ await embed.Error(interaction, "Il semble qu'aucune session pronos est actuellement ouverte")
+ else:
+ await embed.Error(interaction, "Cette commande n'est pas disponible en mode manuel")
else:
- await chat_you_dont_have_perm(interaction)
-
-# _______________________________________________________________________________________________________________
+ await interaction.followup.send(embed=await embed.permError(interaction))
+# _______________________________________________________________________________________________________________________________
-@tree.command(name="admin_result_reasearch_qualif", description="AC-002")
-async def recup_valeur_qualif(interaction: discord.Interaction):
+@tree.command(name="session", description="Configure manuellement la session des pronos récupère ses résultats, et update le leaderboard")
+@app_commands.describe(saison="Année de la saison", location="Nom du Circuit", type="Type de l'événement(Q pour qualif, ou R pour course)")
+async def session(interaction: discord.Interaction, saison: int, location: str, type: str):
+ interaction.response.defer()
if interaction.user.guild_permissions.administrator:
- await interaction.response.defer(ephemeral=True)
-
+ await interaction.response.send_message("⏳ Configuration en cours...", ephemeral=True)
+ session = {
+ "Saison": saison,
+ "Location": location,
+ "Session": type.upper()
+ }
+ os.makedirs('data', exist_ok=True)
+ with open('data/Session.json', 'w', encoding='utf-8') as f:
+ json.dump(session, f, ensure_ascii=False, indent=4)
try:
- embed = await scrapping_result_qualif(interaction)
- await interaction.followup.send(embed=embed, ephemeral=True)
+ f1api.getResults()
except Exception as e:
- logger.error(f"Erreur pendant le scraping : {e}")
- await interaction.followup.send(
- content="❌ Une erreur est survenue lors de la récupération des résultats.",
- ephemeral=True
- )
+ await interaction.followup.send(f" Erreur : {str(e)}")
+ return
+ ldb.saveResults()
+ os.remove('data/Pronos.json')
+ await interaction.followup.send("Le Leaderboard est à jour")
else:
- await chat_you_dont_have_perm(interaction)
-
+ await interaction.followup.send(embed=await embed.permError(interaction))
+
# _______________________________________________________________________________________________________________________________
+@tree.command(name="admin_status", description="Retourne le status du bot")
+async def status(interaction: discord.Interaction):
+ await interaction.response.defer(ephemeral=True)
+ if interaction.user.guild_permissions.administrator:
+ global auto
+ if (auto):
+ await interaction.followup.send("Le bot est en mode auto", ephemeral=True)
+ else:
+ await interaction.followup.send("Le bot est en mode manuel", ephemeral=True)
+ else:
+ await interaction.followup.send(embed=await embed.permError(interaction))
+
+# _______________________________________________________________________________________________________________________________
-@tree.command(name="classement", description="Affiche le classement des pronostics")
-async def classement(interaction: discord.Interaction):
+@tree.command(name="admin_stop", description="Stop le fonctionnement auto du bot")
+async def stop(interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
-
- output_file_path = "../data/Classement_Qualif.xlsx"
+ if interaction.user.guild_permissions.administrator:
+ global auto
+ global task
+ if (auto):
+ task.cancel()
+ try:
+ await task
+ except asyncio.CancelledError:
+ pass
+ auto = False
+ await interaction.followup.send("Le bot est repassé en mode manuel")
+ else:
+ await embed.Error(interaction, "Cette commande n'est pas disponible en mode manuel")
+ else:
+ await interaction.followup.send(embed=await embed.permError(interaction), ephemeral=True)
- resultats = read_the_docs(interaction, "../data/resultats_qualif.json")
- pronostics = read_the_docs(
- interaction, "../data/Result_Qualif_Pronos_F1F_DEMO.xlsx")
+# _______________________________________________________________________________________________________________________________
- if resultats is None or pronostics is None:
- await interaction.followup.send("Erreur : Fichier(s) manquant(s)", ephemeral=True)
- return
- classement_df = calculer_classement(resultats, pronostics, output_file_path)
- classement = classement_df.values.tolist() # convertit en liste de tuples
- embed = embed_classement(classement)
+async def auto_mod(interaction: discord.Interaction):
+ global command_enabled
+ global auto
+ auto = True
+ while (True):
+ f1api.getNextEvent()
+ await interaction.followup.send("Le mode auto à bien été lancé", ephemeral=True)
+ time = Wait()
+ logger.info(str(time))
+ if (time > 0):
+ await asyncio.sleep(time)
+ logger.info("C'est l'heure")
+ command_enabled = True
+ await asyncio.sleep(timedelta(hours=5).total_seconds())
+ logger.info("c'est fini")
+ command_enabled = False
+ f1api.getResults()
+ ldb.saveResults()
+ if (os.path.exists("data/Pronos.json")):
+ os.remove("data/Pronos.json")
+ elif (time <= -timedelta(hours=5).total_seconds()):
+ break
+ else:
+ logger.info("C'est l'heure")
+ command_enabled = True
+ await asyncio.sleep(timedelta(hours=5).total_seconds()+time)
+ logger.info("C'est fini")
+ command_enabled = False
+ f1api.getResults()
+ ldb.saveResults()
+ if (os.path.exists("data/Pronos.json")):
+ os.remove("data/Pronos.json")
+@tree.command(name="admin_launch", description="Lance le fonctionnement auto du bot")
+async def launch(interaction: discord.Interaction):
+ if interaction.user.guild_permissions.administrator:
+ await interaction.response.defer(ephemeral=True)
+ global task
+ task = asyncio.create_task(auto_mod(interaction))
+ await task
+ else:
+ await interaction.response.defer(ephemeral=True)
+ await interaction.followup.send(embed=await embed.permError(interaction), ephemeral=True)
+
+# _______________________________________________________________________________________________________________________________
- await interaction.followup.send(embed=embed, ephemeral=True)
+@bot.event
+async def on_message(message: discord.Message):
+ # Nos ID pour pouvoir etre les seuls à pouvoir lancer la commande
+ owners_id = [1256311918319112294, 1200489866165747722,]
+ if (not message.author.bot):
+ if message.content == "!sync":
+ if message.author.id not in owners_id:
+ logger.warning(
+ f"{str(message.author.id)} alias {message.author.display_name} a tenté de sync le bot")
+ await message.delete()
+ else:
+ synced = await tree.sync()
+ try:
+ await message.author.send(f"🔄 {len(synced)} commandes slash synchronisées.")
+ except discord.Forbidden:
+ message.channel.send("Je n'ai pas pu t'envoyer de dm mais "+len(
+ synced)+" commandes ont été correctement synchronisées")
+ asyncio.sleep(5)
+ pass
+ await message.delete()
+
+# _______________________________________________________________________________________________________________________________
+@tree.command(name="presentation", description="Laisse moi me présenter et aide moi à trouver mon nom !")
+async def presentation(interaction: discord.Interaction):
+ await interaction.response.defer(ephemeral=True)
+ await interaction.followup.send(f"{interaction.user.mention}, va voir tes MP !", ephemeral=False)
+ await presentation_bot(interaction)
bot.run(TOKEN)
diff --git a/app/admin_command.py b/app/admin_command.py
new file mode 100644
index 0000000..2d9c251
--- /dev/null
+++ b/app/admin_command.py
@@ -0,0 +1,24 @@
+from config import EMBED_COLOR_RED, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT, EMBED_IMAGE
+from error_embed import chat_Oops
+import discord
+
+async def ban(interaction: discord.Interaction, member: discord.Member, reason:str, article: str):
+ try:
+ await discord.Member.ban(reason=reason, article=article)
+
+ embed = discord.Embed(
+ title=f"Le membre {member} à été banni!",
+ description="Zoup dehors",
+ color=EMBED_COLOR_RED
+ )
+
+ embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
+ embed.set_thumbnail(url=interaction.user.display_avatar.url)
+ embed.set_image(url=EMBED_IMAGE)
+
+ await interaction.response.send_message(embed=embed, ephemeral=False)
+
+ except ValueError:
+ chat_Oops(interaction)
+
+
\ No newline at end of file
diff --git a/app/classement.py b/app/classement.py
index bab5969..18f0abd 100644
--- a/app/classement.py
+++ b/app/classement.py
@@ -1,86 +1,168 @@
+import json
+import os
+from thefuzz import fuzz
import discord
from config import logger, EMBED_COLOR_RED, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT, EMBED_IMAGE
-import pandas as pd
-def calculer_classement(resultats, pronostics_df, output_file_path):
- classement = []
-
- # Convertir JSON en dict position -> nom
- resultats_dict = {int(pos): nom for pos, nom in resultats}
-
- for _, row in pronostics_df.iterrows():
- pseudo = row["Pseudo"]
- score = 0
-
- # Si tout est bon → +5 points
- if (
- row.get("Premier", "").strip().lower() == resultats_dict.get(1, "").strip().lower() and
- row.get("Deuxième", "").strip().lower() == resultats_dict.get(2, "").strip().lower() and
- row.get("Troisième", "").strip().lower(
- ) == resultats_dict.get(3, "").strip().lower()
- ):
- score += 5
+def saveResults():
+
+ try:
+ with open('../data/Results.json', 'r', encoding='utf-8') as f:
+ results = json.load(f)
+ with open('../data/Pronos.json', 'r', encoding='utf-8') as f:
+ pronos = json.load(f)
+
+ except ValueError:
+ logger.info("Files cannot be accessed, are you sure pronostics exist ?")
+ return
+
+ try:
+ with open('data/Barem.json', 'r', encoding='utf-8') as f:
+ barem = json.load(f)
+ except ValueError:
+ logger.info("Problem with Barem")
+ return
+
+ Premier = results['1']
+ Second = results['2']
+ Troisième = results['3']
+
+ for key in pronos.keys():
+ points = 0
+ complex = False
+ logger.info(key)
+ Premier_Pronos = False
+ Second_Pronos = False
+ Troisième_Pronos = False
+
+ def match_Position(entry: str, target: str, threshold: int) -> bool:
+ parts = entry.strip().split(' ', 1)
+
+ if (len(parts) == 2):
+ prenom, nom = parts
+ else:
+ prenom = nom = parts[0]
+
+ target_prenom, target_nom = target.strip().split(' ', 1)
+ return fuzz.ratio(target_prenom.lower(), prenom.lower()) >= 90 or fuzz.ratio(target_nom.lower(), nom.lower()) >= threshold
+
+ Premier_Pronos = match_Position(pronos[key]['1'], Premier, 90)
+ Second_Pronos = match_Position(pronos[key]['2'], Second, 90)
+ Troisième_Pronos = match_Position(pronos[key]['3'], Troisième, 90)
+
+ try:
+ if Premier_Pronos and Second_Pronos and Troisième_Pronos:
+ logger.info("Tout juste pour "+pronos[key]["Pseudo"])
+ points += barem["allCorrect"]
+ complex = True
+
+ elif Premier_Pronos and Second_Pronos:
+ logger.info("Le premier et le deuxième sont bon pour " +
+ pronos[key]['Pseudo']+" + "+str(barem["first2Correct"])+" Points")
+ points += barem["first2Correct"]
+ complex = True
+
+ elif Second_Pronos and Troisième_Pronos:
+
+ logger.info(
+ "Le premier est bon"+pronos[key]['Pseudo']+" + "+str(barem["last2Correct"])+" Points")
+ points += barem["last2Correct"]
+ complex = True
+
+ elif (Troisième_Pronos and Premier_Pronos):
+
+ logger.info("Le premier et le dernier sont bons pour " +
+ pronos[key]['Pseudo']+" + "+str(barem["1and3Correct"])+" Points")
+ points += barem["1and3Correct"]
+ complex = True
+
+ except KeyError as e:
+ logger.info(str(e) + "is missing")
+ pass
+ except TypeError as e:
+ logger.info(e)
+ pass
+
+ try:
+ if (Premier_Pronos and not complex):
+ logger.info("Le premier est bon pour " +
+ pronos[key]['Pseudo']+" + "+str(barem["firstCorrect"])+" Points")
+ points += barem["firstCorrect"]
+
+ if (Second_Pronos and not complex):
+ logger.info("Le deuxième est bon pour " +
+ pronos[key]['Pseudo']+" + "+str(barem["secondCorrect"])+" Points")
+ points += barem["secondCorrect"]
+
+ if (Troisième_Pronos and not complex):
+ logger.info("Le troisième est bon pour " +
+ pronos[key]['Pseudo']+" + "+str(barem["thirdCorrect"])+" Points")
+ points += barem["thirdCorrect"]
+
+ except KeyError as e:
+ logger.info(str(e) + "is missing")
+ return
+
+ except TypeError as e:
+ logger.info(e)
+ return
+
+ file_path = 'data/Leaderbord.json'
+
+ if not os.path.exists(file_path):
+ pointsPronos = {
+ key: {
+ "Pseudo": pronos[key]["Pseudo"],
+ "Points": points
+ }
+ }
else:
- # Comparaison Premier
- if "Premier" in row and 1 in resultats_dict:
- if row["Premier"].strip().lower() == resultats_dict[1].strip().lower():
- score += 1
- elif row["Premier"].strip().lower() in [
- resultats_dict[2].strip().lower(
- ), resultats_dict[3].strip().lower()
- ]:
- score += 0.5
-
- # Comparaison Deuxième
- if "Deuxième" in row and 2 in resultats_dict:
- if row["Deuxième"].strip().lower() == resultats_dict[2].strip().lower():
- score += 1
- elif row["Deuxième"].strip().lower() in [
- resultats_dict[1].strip().lower(
- ), resultats_dict[3].strip().lower()
- ]:
- score += 0.5
-
- # Comparaison Troisième
- if "Troisième" in row and 3 in resultats_dict:
- if row["Troisième"].strip().lower() == resultats_dict[3].strip().lower():
- score += 1
- elif row["Troisième"].strip().lower() in [
- resultats_dict[1].strip().lower(
- ), resultats_dict[2].strip().lower()
- ]:
- score += 0.5
-
- # Ajouter au classement
- classement.append((pseudo, score))
-
- # Trier par score décroissant
- classement.sort(key=lambda x: x[1], reverse=True)
-
- # Création du DataFrame à partir du classement calculé
- colonnes = ["Pseudo", "Score"]
- df_classement = pd.DataFrame(classement, columns=colonnes)
-
- # Sauvegarde dans le fichier Excel
- df_classement.to_excel(output_file_path, index=False)
-
- logger.info(f"✅ Classement sauvegardé dans : {output_file_path}")
-
- return df_classement
-
-# _______________________________________________________________________________________________________________________________
-
-
-def embed_classement(classement):
+ with open(file_path, 'r', encoding='utf-8') as f:
+ pointsPronos = json.load(f)
+ if key not in pointsPronos:
+ pointsPronos[key] = {
+ "Pseudo": pronos[key]["Pseudo"],
+ "Points": points
+ }
+ else:
+ pointsPronos[key]["Pseudo"] = pronos[key]["Pseudo"]
+ pointsPronos[key]["Points"] += points
+
+ with open(file_path, 'w', encoding='utf-8') as f:
+ json.dump(pointsPronos, f, ensure_ascii=False, indent=4)
+
+
+def Leaderboard():
+
+ try:
+ with open("data/Leaderbord.json", 'r', encoding='utf-8') as f:
+ pointsPronos = json.load(f)
+ pronosLeaderboard = dict(
+ sorted(pointsPronos.items(), key=lambda item: item[1]['Points'], reverse=True))
+
+ except FileNotFoundError:
+ logger.info("The file doesn't exist")
+ return
+
+ except KeyError:
+ logger.info("Erreur dans la lecture des points")
+ return
+
embed = discord.Embed(
title="🏆 Classement des Pronostics",
color=EMBED_COLOR_RED
)
- for i, (pseudo, score) in enumerate(classement, start=1):
- embed.add_field(name=f"{i}ᵉ - {pseudo}",
- value=f"Score : **{score}**", inline=False)
-
+ pseudos, points = zip(*[(val['Pseudo'], val['Points'])
+ for val in pronosLeaderboard.values()])
+ pseudos = list(pseudos)
+ points = list(points)
+ position = 1
+ for i in range(len(pseudos)):
+ embed.add_field(name=f"{position}ᵉ - {pseudos[i]}",
+ value=f"Score : **{points[i]}**", inline=False)
+ if (i+1 < len(pseudos) and points[i] != points[i+1]):
+ position += 1
embed.set_footer(text=EMBED_FOOTER_TEXT)
embed.set_image(url=EMBED_IMAGE)
embed.set_thumbnail(url=EMBED_THUMBNAIL)
diff --git a/app/config.py b/app/config.py
index 81101d7..275c48a 100644
--- a/app/config.py
+++ b/app/config.py
@@ -26,7 +26,7 @@
logger = logging.getLogger("F1F_Bot")
logger.setLevel(LOG_LEVEL)
-log_path = "../log/app.log"
+log_path = "log/app.log"
# Crée le dossier si nécessaire
os.makedirs(os.path.dirname(log_path), exist_ok=True)
@@ -46,7 +46,7 @@
EMBED_THUMBNAIL = "https://cdn.discordapp.com/attachments/1339299411360088226/1367477935392428083/Votre_texte_de_paragraphe_12.png?ex=6871ac52&is=68705ad2&hm=a63fd375a9f30130247df80b936c43e1d93b3a5b16c3415f7a63cac72614058e&"
EMBED_IMAGE = "https://cdn.discordapp.com/attachments/1339299411360088226/1368544420504272987/Design_sans_titre_2.png?ex=68719910&is=68704790&hm=46bd1e4a625f33cc26d5e029888bef5c265732b842de241daa62662206f13885&"
-EMBED_FOOTER_TEXT = "Bot créé par F1F Team"
+EMBED_FOOTER_TEXT = "Bot créé par F1F Team (BETA)"
EMBED_FOOTER_ICON = EMBED_THUMBNAIL
@@ -57,3 +57,9 @@
URL_BASE = "https://fr.motorsport.com/"
URL_RESULT_COURSE = "https://fr.motorsport.com/f1/results/2025"
URL_RESULT_QUALIF = "https://fr.motorsport.com/f1/results/2025/gp-de-grande-bretagne-653245/?st=Q3"
+
+# === Environment variable ===
+
+PRONOS_ID=1395093933176131755
+GUILD_ID = 1394054995523010761
+
diff --git a/app/error_embed.py b/app/error_embed.py
index 62fb0e5..0445207 100644
--- a/app/error_embed.py
+++ b/app/error_embed.py
@@ -1,4 +1,4 @@
-from config import EMBED_COLOR_RED, EMBED_IMAGE, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT
+from config import logger, EMBED_COLOR_RED, EMBED_IMAGE, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT, PRONOS_ID
import discord
@@ -14,3 +14,75 @@ async def chat_you_dont_have_perm(interaction: discord.Interaction):
embed.set_image(url=EMBED_IMAGE)
await interaction.response.send_message(embed=embed, ephemeral=True)
+
+async def chat_Oops(interaction: discord.Interaction):
+ embed = discord.Embed(
+ title=f"Désolé {interaction.user} !",
+ description="Oops ca ne fonctionne pas!",
+ color=EMBED_COLOR_RED
+ )
+
+ embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
+ embed.set_thumbnail(url=interaction.user.display_avatar.url)
+ embed.set_image(url=EMBED_IMAGE)
+
+ await interaction.response.send_message(embed=embed, ephemeral=True)
+
+async def info_embed(message: str, interaction: discord.Interaction):
+ role = interaction.guild.get_role(PRONOS_ID)
+ embed = discord.Embed(
+ title="Infos",
+ description=message,
+ color=EMBED_COLOR_RED
+ )
+
+ embed.set_footer(text=EMBED_FOOTER_TEXT)
+ embed.set_image(url=EMBED_IMAGE)
+ embed.set_thumbnail(url=EMBED_THUMBNAIL)
+ await interaction.followup.send(
+ content=f"||{role.mention}||",
+ ephemeral=False,
+ embed=embed
+ )
+
+
+async def permError(interaction: discord.Interaction):
+ logger.warning(
+ f"{str(interaction.user.id)} alias {interaction.user} a tenté de lancer une commande sans en avoir les droits")
+ embed = discord.Embed(
+ title="❌ Tu n'a pas les permissions pour cette commande",
+ color=EMBED_COLOR_RED
+ )
+ embed.set_footer(text=EMBED_FOOTER_TEXT)
+ embed.set_image(url=EMBED_IMAGE)
+ embed.set_thumbnail(url=EMBED_THUMBNAIL)
+ return embed
+
+
+async def Error(interaction: discord.Interaction, erreur: str):
+ embed = discord.Embed(
+ title="❌ Oups, une erreur est survenue",
+ description=erreur,
+ color=EMBED_COLOR_RED,
+ )
+ embed.set_footer(text=EMBED_FOOTER_TEXT)
+ embed.set_image(url=EMBED_IMAGE)
+ embed.set_thumbnail(url=EMBED_THUMBNAIL)
+ await interaction.followup.send(
+ ephemeral=True,
+ embed=embed
+ )
+
+async def no_prono(interaction: discord.Interaction):
+ embed = discord.Embed(
+ title="❌ Oups, on dirait que tu n'as pas fais de pronos",
+ description=f"{interaction.user.name} utilise la fonction pronos !",
+ color=EMBED_COLOR_RED,
+ )
+ embed.set_footer(text=EMBED_FOOTER_TEXT)
+ embed.set_image(url=EMBED_IMAGE)
+ embed.set_thumbnail(url=EMBED_THUMBNAIL)
+ await interaction.followup.send(
+ ephemeral=True,
+ embed=embed
+ )
\ No newline at end of file
diff --git a/app/api.py b/app/f1api.py
similarity index 70%
rename from app/api.py
rename to app/f1api.py
index bd4c068..5eeecb5 100644
--- a/app/api.py
+++ b/app/f1api.py
@@ -2,6 +2,7 @@
from datetime import datetime, timezone
import pandas as pd
import json
+from config import logger
def getNextEvent():
@@ -9,35 +10,35 @@ def getNextEvent():
calendar = f1.get_event_schedule(year)
time = datetime.now(timezone.utc)
for row in calendar.itertuples():
- if (pd.isna(row.Session4Date)):
- print("Pas de Date pour la qualif à "+row.Location)
+ if (pd.isna(row.Session4DateUtc)):
+ logger.info("Pas de Date pour la qualif à "+row.Location)
elif (time > row.Session4Date):
- print(row.Location + " est passé (Qualif)")
+ logger.info(row.Location + " est passé (Qualif)")
else:
- print("Prochaine Session Qualif à "+row.Location)
+ logger.info("Prochaine Session Qualif à "+row.Location)
session = {
"Round": str(row.Index),
"Country": row.Country,
"Location": row.Location,
"Session": 'Q',
- "Date": time.strftime("%d/%m,%H:%M:%S"),
+ "Date": row.Session4DateUtc.strftime("%d/%m/%Y,%H:%M:%S"),
"Saison": datetime.now(timezone.utc).year
}
with open('data/Session.json', 'w', encoding='utf-8') as f:
json.dump(session, f, ensure_ascii=False, indent=4)
break
- if (pd.isna(row.Session5Date)):
- print("Pas de Date pour la course à "+row.Location)
+ if (pd.isna(row.Session5DateUtc)):
+ logger.info("Pas de Date pour la course à "+row.Location)
elif (time > row.Session5Date):
- print(row.Location + " est passé (Course)")
+ logger.info(row.Location + " est passé (Course)")
else:
- print("Prochaine Course à "+row.Location)
+ logger.info("Prochaine Course à "+row.Location)
session = {
- "Round": str(row.Index),
+ "Round": row.Index,
"Country": row.Country,
"Location": row.Location,
"Session": 'R',
- "Date": time.strftime("%d/%m,%H:%M:%S"),
+ "Date": row.Session5DateUtc.strftime("%d/%m/%Y,%H:%M:%S"),
"Saison": datetime.now(timezone.utc).year
}
with open('data/Session.json', 'w', encoding='utf-8') as f:
@@ -50,7 +51,8 @@ def getResults():
data = json.load(f)
session = f1.get_session(data['Saison'], data["Location"], data["Session"])
if not session.f1_api_support:
- print("L'api n'est pas disponible") # Rajouter une erreur sur discord
+ # Rajouter une erreur sur discord
+ logger.info("L'api n'est pas disponible")
return 2
session.load()
try:
@@ -65,11 +67,7 @@ def getResults():
}
except ValueError:
# Aussi rajouter une erreur sur discord
- print("Value are not avalaible yet")
+ logger.info("Value are not avalaible yet")
return 1
with open('data/Results.json', 'w', encoding='utf-8') as f:
json.dump(result, f, ensure_ascii=False, indent=4)
-
-
-getNextEvent()
-getResults()
diff --git a/app/pronos.py b/app/pronos.py
index 9abf70c..f66adc3 100644
--- a/app/pronos.py
+++ b/app/pronos.py
@@ -1,9 +1,11 @@
-# Gestion des pronostiques, écriture et lecture
+# Gestion des pronostiques, écriture et lecture
import json
import os
+import discord
+from config import logger
-def pronos_qualif_logic(id, pseudo, premier: str, second: str, troisieme: str, bt: str):
+def pronos(id: int, pseudo: str, premier: str, second: str, troisieme: str, bt: str):
file_path = 'data/Pronos.json'
if not os.path.exists(file_path):
pronos_database = {
@@ -12,7 +14,8 @@ def pronos_qualif_logic(id, pseudo, premier: str, second: str, troisieme: str, b
"1": premier,
"2": second,
"3": troisieme,
- "Best Lap": bt
+ "Best Lap": bt,
+ "Modif":False
}
}
else:
@@ -24,16 +27,85 @@ def pronos_qualif_logic(id, pseudo, premier: str, second: str, troisieme: str, b
"1": premier,
"2": second,
"3": troisieme,
- "Best Lap": bt
+ "Best Lap": bt,
+ "Modif":False
}
else:
- print("Modif")
- pronos_database[str(id)["Pseudo"]] = pseudo
- pronos_database[str(id)["1"]] = premier
- pronos_database[str(id)["2"]] = second
- pronos_database[str(id)["3"]] = troisieme
+ if(pronos_database[str(id)]["Modif"] == True):
+ return False
+ else:
+
+ pronos_database[str(id)]["Pseudo"] = pseudo
+ pronos_database[str(id)]["1"] = premier
+ pronos_database[str(id)]["2"] = second
+ pronos_database[str(id)]["3"] = troisieme
+ pronos_database[str(id)]["Best Lap"] = bt
+ pronos_database[str(id)]["Modif"] = True
+
+
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(pronos_database, f, ensure_ascii=False, indent=4)
+ return True
+
+async def visualisation(interaction: discord.Interaction):
+
+ file_path = "data/Pronos.json"
+ with open(file_path, 'r', encoding='utf-8') as f:
+ pronos_database = json.load(f)
+
+ user = interaction.user.id
+
+ if str(user) in pronos_database:
+ premier=pronos_database[str(user)]["1"]
+ deuxieme=pronos_database[str(user)]["2"]
+ troisieme=pronos_database[str(user)]["3"]
+ best_lap=pronos_database[str(user)]["Best Lap"]
+ modif=pronos_database[str(user)]["Modif"]
+
+ embed = discord.Embed(
+ title = f"🐐 Merci pour vos pronos {interaction.user} !",
+ description="Voici tes pronostiques : ",
+ color=discord.Color.red()
+ )
+
+ embed.add_field(name="Ton Premier 🥇 :", value=f"{premier}", inline=False)
+ embed.add_field(name="Ton Deuxième 🥈 :", value=f"{deuxieme}", inline=False)
+ embed.add_field(name="Ton Troisième 🥉 :", value=f"{troisieme}", inline=False)
+ embed.add_field(name="Ton Meilleur Tour ⏱️ :", value=f"{best_lap}", inline=False)
+ if(modif):
+ value="❌ Tu as déja modifié ton pronostic par le passé"
+ else:
+ value="✅ Tu peux modifier ton pronostic en relançant /pronos_course"
+ embed.add_field(name="Droit de modification :",value=value,inline=False)
+
+
+ embed.set_footer(text="Bot créé par F1F Team", icon_url="https://cdn.discordapp.com/attachments/1339299411360088226/1367477935392428083/Votre_texte_de_paragraphe_12.png")
+ embed.set_thumbnail(url=interaction.user.display_avatar.url)
+ embed.set_image(url="https://cdn.discordapp.com/attachments/1339299411360088226/1368544420504272987/Design_sans_titre_2.png")
+
+
+ logger.info(f"{interaction.user.name} a regarder ses pronos course")
+
+ await interaction.followup.send(embed=embed, ephemeral=True)
+
+
+
+ else:
+ embed = discord.Embed(
+ title = f"Désolé {interaction.user} !",
+ description="On dirait que tu n'as pas encore fait de pronostique",
+ color=discord.Color.red()
+ )
+
+ embed.set_footer(text="Bot créé par F1F Team", icon_url="https://cdn.discordapp.com/attachments/1339299411360088226/1367477935392428083/Votre_texte_de_paragraphe_12.png")
+ embed.set_thumbnail(url=interaction.user.display_avatar.url)
+ embed.set_image(url="https://cdn.discordapp.com/attachments/1339299411360088226/1368544420504272987/Design_sans_titre_2.png")
+ logger.info(f"{interaction.user.name} a tenter de regarder ses pronos course alors qu'il n'en avais pas fais")
+ await interaction.followup.send(embed=embed, ephemeral=True)
+
+
+
+
-pronos_qualif_logic(19, "Victor", "Oscar", "Pierre", "Lando", "Lando")
+
diff --git a/app/qualif.py b/app/qualif.py
deleted file mode 100644
index ea37b6d..0000000
--- a/app/qualif.py
+++ /dev/null
@@ -1,197 +0,0 @@
-from config import logger, EMBED_COLOR_RED, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT, EMBED_IMAGE
-import os
-import discord
-import pandas as pd
-
-
-async def pronos_qualif_logic(interaction: discord.Interaction, premier: str, deuxieme: str, troisieme: str):
-
- data = []
- tab = {}
-
- file_path = "../data/Result_Course_Pronos_F1F_DEMO.xlsx"
-
- colonnes = ["Pseudo", "Premier", "Deuxième", "Troisième"]
-
- if not os.path.exists(file_path):
- # Création d'un DataFrame vide avec les colonnes attendues
- df = pd.DataFrame(columns=colonnes)
- df.to_excel(file_path, index=False)
- else:
- df = pd.read_excel(file_path)
-
- df = pd.read_excel(file_path)
-
- # Le pseudo que tu veux chercher — ici je suppose que c'est le pseudo Discord
- # par exemple pour chercher avec le pseudo Discord exact
- pseudo_recherche = str(interaction.user)
-
- # Filtrer la DataFrame pour la ligne où la colonne 'Pseudo' correspond au pseudo recherché
- resultat = df[df['Pseudo'] == pseudo_recherche]
-
- if resultat.empty:
- embed = discord.Embed(
- title=f"🐐 Merci pour vos pronos {interaction.user} !",
- description="Voici tes pronostiques : ",
- color=EMBED_COLOR_RED
- )
-
- embed.add_field(name="Premier 🥇 :", value=f"{premier}", inline=False)
- embed.add_field(name="Deuxième 🥈 :", value=f"{deuxieme}", inline=False)
- embed.add_field(name="Troisème 🥉 :",
- value=f"{troisieme}", inline=False)
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE)
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
-
- tab["Pseudo"] = interaction.user
- tab["Premier"] = premier
- tab["Deuxième"] = deuxieme
- tab["Troisième"] = troisieme
-
- data.append(tab)
-
- table = pd.DataFrame(data)
- table.iloc[0]
- file_path = "../data/Result_Qualif_Pronos_F1F_DEMO.xlsx"
-
- if os.path.exists(file_path):
- # Lire les anciennes données
- old_data = pd.read_excel(file_path)
- # Ajouter les nouvelles lignes
- combined_data = pd.concat([old_data, table], ignore_index=True)
- else:
- # Si fichier n'existe pas, créer juste avec les nouvelles données
- combined_data = table
-
- # Écrire dans le fichier Excel (remplace l'ancien)
- combined_data.to_excel(file_path, index=False)
-
- logger.info(f"{interaction.user.name} à fais ses pronos qualifs")
- else:
- embed = discord.Embed(
- title=f"Désolé {interaction.user} !",
- description="On dirait que tu as deja fait un pronostique si tu veux le modifier utilise la fonction /modify",
- color=EMBED_COLOR_RED
- )
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE)
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
-
- logger.info(
- f"{interaction.user.name} à tenter de réutiliser la commandes pronos qualifs")
-
-
-async def visualisation_pronos_qualif_logic(interaction: discord.Interaction):
-
- file_path = "../data/Result_Qualif_Pronos_F1F_DEMO.xlsx"
- df = pd.read_excel(file_path)
-
- # Le pseudo que tu veux chercher — ici je suppose que c'est le pseudo Discord
- # par exemple pour chercher avec le pseudo Discord exact
- pseudo_recherche = str(interaction.user)
-
- # Filtrer la DataFrame pour la ligne où la colonne 'Pseudo' correspond au pseudo recherché
- resultat = df[df['Pseudo'] == pseudo_recherche]
-
- if not resultat.empty:
- ligne = resultat.iloc[0] # OK, il y a au moins une ligne
-
- premier = ligne['Premier']
- deuxieme = ligne['Deuxième']
- troisieme = ligne['Troisième']
-
- embed = discord.Embed(
- title=f"🐐 Merci pour vos pronos {interaction.user} !",
- description="Voici tes pronostiques : ",
- color=EMBED_COLOR_RED
- )
-
- embed.add_field(name="Ton Premier 🥇 :",
- value=f"{premier}", inline=False)
- embed.add_field(name="Ton Deuxième 🥈 :",
- value=f"{deuxieme}", inline=False)
- embed.add_field(name="Ton Troisième 🥉 :",
- value=f"{troisieme}", inline=False)
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE)
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
-
- logger.info(f"{interaction.user.name} a regarder ses pronos qualif")
-
- else:
- embed = discord.Embed(
- title=f"Désolé {interaction.user} !",
- description="On dirait que tu n'as pas encore fait de pronostique",
- color=EMBED_COLOR_RED
- )
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE)
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
-
- logger.info(
- f"{interaction.user.name} a tenter de regarder ses pronos qualif alors qu'il n'en avais pas fais")
-
-
-async def modify_qualif_logic(interaction: discord.Interaction, premier: str, deuxieme: str, troisieme: str):
-
- file_path = "../data/Result_Qualif_Pronos_F1F_DEMO.xlsx"
- df = pd.read_excel(file_path)
-
- # Le pseudo que tu veux chercher — ici je suppose que c'est le pseudo Discord
- # par exemple pour chercher avec le pseudo Discord exact
- pseudo_recherche = str(interaction.user)
-
- # Filtrer la DataFrame pour la ligne où la colonne 'Pseudo' correspond au pseudo recherché
- resultat = df[df['Pseudo'] == pseudo_recherche].index
-
- if resultat.empty:
- embed = discord.Embed(
- title=f"Désolé {interaction.user} !",
- description="On dirait que tu n'as pas encore fait de pronostique",
- color=EMBED_COLOR_RED
- )
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE)
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
- else:
- embed = discord.Embed(
- title=f"🐐 Merci pour vos pronos {interaction.user} !",
- description="Voici tes pronostiques : ",
- color=EMBED_COLOR_RED
- )
-
- embed.add_field(name="Premier 🥇 :", value=f"{premier}", inline=False)
- embed.add_field(name="Deuxième 🥈 :", value=f"{deuxieme}", inline=False)
- embed.add_field(name="Troisème 🥉 :",
- value=f"{troisieme}", inline=False)
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE)
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
-
- i = resultat[0]
-
- df.at[i, "Pseudo"] = interaction.user
- df.at[i, "Premier"] = premier
- df.at[i, "Deuxième"] = deuxieme
- df.at[i, "Troisième"] = troisieme
-
- df.to_excel(file_path, index=False)
diff --git a/app/race.py b/app/race.py
deleted file mode 100644
index 8aa156e..0000000
--- a/app/race.py
+++ /dev/null
@@ -1,216 +0,0 @@
-from config import logger, EMBED_COLOR_RED, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT, EMBED_IMAGE
-import os
-import discord
-import pandas as pd
-
-
-async def pronos_course_logic(interaction: discord.Interaction, premier: str, deuxieme: str, troisieme: str, meilleur_tour: str):
-
- data = []
- tab = {}
-
- file_path = "../data/Result_Course_Pronos_F1F_DEMO.xlsx"
-
- colonnes = ["Pseudo", "Premier", "Deuxième", "Troisième", "MT"]
-
- if not os.path.exists(file_path):
- # Création d'un DataFrame vide avec les colonnes attendues
- df = pd.DataFrame(columns=colonnes)
- df.to_excel(file_path, index=False)
- else:
- df = pd.read_excel(file_path)
-
- df = pd.read_excel(file_path)
-
- # Le pseudo que tu veux chercher — ici je suppose que c'est le pseudo Discord
- # par exemple pour chercher avec le pseudo Discord exact
- pseudo_recherche = str(interaction.user)
-
- # Filtrer la DataFrame pour la ligne où la colonne 'Pseudo' correspond au pseudo recherché
- resultat = df[df['Pseudo'] == pseudo_recherche]
-
- if resultat.empty:
- embed = discord.Embed(
- title=f"🐐 Merci pour vos pronos {interaction.user} !",
- description="Voici tes pronostiques : ",
- color=EMBED_COLOR_RED
- )
-
- embed.add_field(name="Premier 🥇 :", value=f"{premier}", inline=False)
- embed.add_field(name="Deuxième 🥈 :", value=f"{deuxieme}", inline=False)
- embed.add_field(name="Troisème 🥉 :",
- value=f"{troisieme}", inline=False)
- embed.add_field(name="Meilleur Tour ⏱️ :",
- value=f"{meilleur_tour}", inline=False)
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL
- )
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE
- )
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
-
- tab["Pseudo"] = interaction.user
- tab["Premier"] = premier
- tab["Deuxième"] = deuxieme
- tab["Troisième"] = troisieme
- tab["MT"] = meilleur_tour
-
- data.append(tab)
-
- table = pd.DataFrame(data)
- table.iloc[0]
- file_path = "../data/Result_Course_Pronos_F1F_DEMO.xlsx"
-
- if os.path.exists(file_path):
- # Lire les anciennes données
- old_data = pd.read_excel(file_path)
- # Ajouter les nouvelles lignes
- combined_data = pd.concat([old_data, table], ignore_index=True)
- else:
- # Si fichier n'existe pas, créer juste avec les nouvelles données
- combined_data = table
-
- # Écrire dans le fichier Excel (remplace l'ancien)
- combined_data.to_excel(file_path, index=False)
-
- logger.info(f"{interaction.user.name} à fais ses pronos courses")
-
- else:
- embed = discord.Embed(
- title=f"Désolé {interaction.user} !",
- description="On dirait que tu as deja fait un pronostique si tu veux le modifier utilise la fonction /modify",
- color=EMBED_COLOR_RED
- )
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL
- )
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE
- )
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
-
- logger.info(
- f"{interaction.user.name} à tenter de réutiliser la commandes pronos course")
-
-
-async def visualisation_pronos_course_logic(interaction: discord.Interaction):
-
- file_path = "../data/Result_Course_Pronos_F1F_DEMO.xlsx"
- df = pd.read_excel(file_path)
-
- # Le pseudo que tu veux chercher — ici je suppose que c'est le pseudo Discord
- # par exemple pour chercher avec le pseudo Discord exact
- pseudo_recherche = str(interaction.user)
-
- # Filtrer la DataFrame pour la ligne où la colonne 'Pseudo' correspond au pseudo recherché
- resultat = df[df['Pseudo'] == pseudo_recherche]
-
- if not resultat.empty:
- ligne = resultat.iloc[0] # OK, il y a au moins une ligne
-
- premier = ligne['Premier']
- deuxieme = ligne['Deuxième']
- troisieme = ligne['Troisième']
- meilleur_tour = ligne['MT']
-
- embed = discord.Embed(
- title=f"🐐 Merci pour vos pronos {interaction.user} !",
- description="Voici tes pronostiques : ",
- color=EMBED_COLOR_RED
- )
-
- embed.add_field(name="Ton Premier 🥇 :",
- value=f"{premier}", inline=False)
- embed.add_field(name="Ton Deuxième 🥈 :",
- value=f"{deuxieme}", inline=False)
- embed.add_field(name="Ton Troisième 🥉 :",
- value=f"{troisieme}", inline=False)
- embed.add_field(name="Ton Meilleur Tour ⏱️ :",
- value=f"{meilleur_tour}", inline=False)
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL
- )
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE)
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
-
- logger.info(f"{interaction.user.name} a regarder ses pronos course")
-
- else:
- embed = discord.Embed(
- title=f"Désolé {interaction.user} !",
- description="On dirait que tu n'as pas encore fait de pronostique",
- color=EMBED_COLOR_RED
- )
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE)
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
-
- logger.info(
- f"{interaction.user.name} a tenter de regarder ses pronos course alors qu'il n'en avais pas fais")
-
-
-async def modify_course(interaction: discord.Interaction, premier: str, deuxieme: str, troisieme: str, meilleur_tour: str):
-
- file_path = "../data/Result_Course_Pronos_F1F_DEMO.xlsx"
- df = pd.read_excel(file_path)
-
- # Le pseudo que tu veux chercher — ici je suppose que c'est le pseudo Discord
- # par exemple pour chercher avec le pseudo Discord exact
- pseudo_recherche = str(interaction.user)
-
- # Filtrer la DataFrame pour la ligne où la colonne 'Pseudo' correspond au pseudo recherché
- resultat = df[df['Pseudo'] == pseudo_recherche].index
-
- if resultat.empty:
- embed = discord.Embed(
- title=f"Désolé {interaction.user} !",
- description="On dirait que tu n'as pas encore fait de pronostique",
- color=EMBED_COLOR_RED
- )
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL
- )
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE
- )
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
- else:
- embed = discord.Embed(
- title=f"🐐 Merci pour vos pronos {interaction.user} !",
- description="Voici tes pronostiques : ",
- color=EMBED_COLOR_RED
- )
-
- embed.add_field(name="Premier 🥇 :", value=f"{premier}", inline=False)
- embed.add_field(name="Deuxième 🥈 :", value=f"{deuxieme}", inline=False)
- embed.add_field(name="Troisème 🥉 :",
- value=f"{troisieme}", inline=False)
- embed.add_field(name="Meilleur Tour ⏱️ :",
- value=f"{meilleur_tour}", inline=False)
-
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL
- )
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE
- )
-
- await interaction.response.send_message(embed=embed, ephemeral=True)
-
- i = resultat[0]
-
- df.at[i, "Pseudo"] = interaction.user
- df.at[i, "Premier"] = premier
- df.at[i, "Deuxième"] = deuxieme
- df.at[i, "Troisième"] = troisieme
- df.at[i, "MT"] = meilleur_tour
-
- df.to_excel(file_path, index=False)
diff --git a/app/read_docs.py b/app/read_docs.py
deleted file mode 100644
index ad81fb8..0000000
--- a/app/read_docs.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import pandas as pd
-import json
-from config import logger, discord
-
-
-def read_the_docs(interaction: discord.Interaction, file_path: str):
-
- try:
- if file_path.endswith(".xlsx"):
- df = pd.read_excel(file_path)
- logger.info(
- f"[{interaction.user}] Lecture du fichier Excel réussie : {file_path}")
- return df
-
- elif file_path.endswith(".json"):
- with open(file_path, "r", encoding="utf-8") as f:
- data = json.load(f)
- logger.info(
- f"[{interaction.user}] Lecture du fichier JSON réussie : {file_path}")
- return data
-
- else:
- logger.warning(
- f"[{interaction.user}] Format de fichier non pris en charge : {file_path}")
- return None
-
- except Exception as e:
- logger.error(
- f"[{interaction.user}] Erreur lors de la lecture du fichier {file_path} : {e}")
- return None
diff --git a/app/scrapping_result.py b/app/scrapping_result.py
deleted file mode 100644
index 8719941..0000000
--- a/app/scrapping_result.py
+++ /dev/null
@@ -1,161 +0,0 @@
-import undetected_chromedriver as uc
-from bs4 import BeautifulSoup
-from selenium.webdriver.common.by import By
-from selenium.webdriver.support.ui import WebDriverWait
-from selenium.webdriver.support import expected_conditions as EC
-import json
-from config import discord, EMBED_COLOR_RED, EMBED_IMAGE, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT, URL_RESULT_COURSE, URL_RESULT_QUALIF, logger
-
-
-async def scrapping_result_course(interaction: discord.Interaction):
-
- options = uc.ChromeOptions()
- options.add_argument('--no-sandbox')
- options.add_argument('--disable-blink-features=AutomationControlled')
-
- driver = uc.Chrome(options=options, use_subprocess=True)
- logger.info("Driver has been initialized.")
-
- driver.get(URL_RESULT_COURSE)
-
- try:
- WebDriverWait(driver, 10).until(
- EC.presence_of_element_located((By.CLASS_NAME, "ms-table_row"))
- )
- except TimeoutError:
- logger.info("Aucune ligne de tableau trouvée.")
- driver.quit()
- exit()
-
- # Parser la page
- soup = BeautifulSoup(driver.page_source, 'html.parser')
- table_rows = soup.find_all("tr", class_="ms-table_row")
-
- # Récupérer uniquement les positions et noms
- positions_noms = []
-
- for row in table_rows[:-3]:
- columns = row.find_all("td")
- if len(columns) >= 2: # on vérifie qu’il y a bien au moins position + nom
- pos = columns[0].get_text(strip=True)
- raw_name = columns[1].get_text(strip=True)
- for team in ["McLaren", "Stake Sauber", "Ferrari", "Red Bull", "Alpine", "Aston Martin", "Mercedes", "Haas", "Williams", "Racing Bulls"]:
- raw_name = raw_name.replace(team, "")
- name = raw_name.strip()
-
- # Vérifie que la position est un chiffre
- if pos.isdigit():
- positions_noms.append((int(pos), name))
-
- # Ne garder que les 3 premiers classés
- positions_noms = sorted(positions_noms, key=lambda x: x[0])[:3]
-
- driver.quit()
-
- # Conversion en JSON bien formaté
- json_data = json.dumps(positions_noms, ensure_ascii=False, indent=4)
-
- # Optionnel : sauvegarder dans un fichier
- with open("../data/resultats_course.json", "w", encoding="utf-8") as f:
- f.write(json_data)
-
- logger.info("Récupération des résultats courses.")
-
- if not positions_noms:
- description = (
- "On dirait que tu as déjà fait un pronostique. "
- "Si tu veux le modifier, utilise la commande `/modify`."
- )
- title = f"Désolé {interaction.user} !"
- else:
- description = "La mise à jour du fichier `.json` est faite."
- title = f"Merci {interaction.user} !"
-
- embed = discord.Embed(
- title=title,
- description=description,
- color=EMBED_COLOR_RED
- )
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE)
-
- return embed # ✅ On retourne l'embed sans l'envoyer
-
-# _______________________________________________________________________________________________________________
-
-
-async def scrapping_result_qualif(interaction: discord.Interaction):
-
- options = uc.ChromeOptions()
- options.add_argument('--no-sandbox')
- options.add_argument('--disable-blink-features=AutomationControlled')
-
- driver = uc.Chrome(options=options, use_subprocess=True)
- logger.info("Driver has been initialized.")
-
- driver.get(URL_RESULT_QUALIF)
-
- try:
- WebDriverWait(driver, 10).until(
- EC.presence_of_element_located((By.CLASS_NAME, "ms-table_row"))
- )
- except TimeoutError:
- logger.info("Aucune ligne de tableau trouvée.")
- driver.quit()
- exit()
-
- # Parser la page
- soup = BeautifulSoup(driver.page_source, 'html.parser')
- table_rows = soup.find_all("tr", class_="ms-table_row")
-
- # Récupérer uniquement les positions et noms
- positions_noms = []
-
- for row in table_rows[:-3]:
- columns = row.find_all("td")
- if len(columns) >= 2: # on vérifie qu’il y a bien au moins position + nom
- pos = columns[0].get_text(strip=True)
- raw_name = columns[1].get_text(strip=True)
- for team in ["McLaren", "Stake Sauber", "Ferrari", "Red Bull", "Alpine", "Aston Martin", "Mercedes", "Haas", "Williams", "Racing Bulls"]:
- raw_name = raw_name.replace(team, "")
- name = raw_name.strip()
-
- # Vérifie que la position est un chiffre
- if pos.isdigit():
- positions_noms.append((int(pos), name))
-
- # Ne garder que les 3 premiers classés
- positions_noms = sorted(positions_noms, key=lambda x: x[0])[:3]
-
- driver.quit()
-
- # Conversion en JSON bien formaté
- json_data = json.dumps(positions_noms, ensure_ascii=False, indent=4)
-
- # Optionnel : sauvegarder dans un fichier
- with open("../data/resultats_qualif.json", "w", encoding="utf-8") as f:
- f.write(json_data)
-
- logger.info("Récupération des résultats qualif.")
-
- if not positions_noms:
- description = (
- "On dirait que tu as déjà fait un pronostique. "
- "Si tu veux le modifier, utilise la commande `/modify`."
- )
- title = f"Désolé {interaction.user} !"
- else:
- description = "La mise à jour du fichier `.json` est faite."
- title = f"Merci {interaction.user} !"
-
- embed = discord.Embed(
- title=title,
- description=description,
- color=EMBED_COLOR_RED
- )
- embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
- embed.set_thumbnail(url=interaction.user.display_avatar.url)
- embed.set_image(url=EMBED_IMAGE)
-
- return embed # ✅ On retourne l'embed sans l'envoyer
diff --git a/app/tools.py b/app/tools.py
index f5115be..6c8021b 100644
--- a/app/tools.py
+++ b/app/tools.py
@@ -1,5 +1,13 @@
-from config import logger, EMBED_COLOR_RED, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT, EMBED_IMAGE
+from config import logger, bot, EMBED_COLOR_RED, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT, EMBED_IMAGE
import discord
+import asyncio
+from datetime import timedelta,datetime
+from config import os
+import fastf1 as f1api
+import classement as ldb
+import json
+from error_embed import info_embed, no_prono
+
async def help(interaction: discord.Interaction):
@@ -51,3 +59,89 @@ async def clear_slash(interaction: discord.Interaction, nombre: int):
logger.info(
f"{interaction.user.name} à clear {nombre} lignes dans {interaction.channel.name}")
+
+
+
+
+def Wait():
+ try:
+ with open('data/Session.json', 'r', encoding='utf-8') as f:
+ data = json.load(f)
+ except FileNotFoundError:
+ return 1
+ delta_t = (datetime.strptime(
+ data["Date"], "%d/%m/%Y,%H:%M:%S")-datetime.now()-timedelta(hours=5)).total_seconds()
+ return delta_t
+
+
+async def start_Session(interaction: discord.Interaction, duration: float):
+ await info_embed(f"Les pronos sont lancés pour {str(float(duration*60))} minutes ! \n Pensez à voter !", interaction)
+ await asyncio.sleep(timedelta(hours=duration).total_seconds())
+
+
+async def presentation_bot(interaction: discord.Interaction):
+
+ file_path = 'data/NameBot.json'
+
+ embed = discord.Embed(
+ title="Présentation du Bot + Concours de Nom 🏁",
+ description=(
+ "👋 **Bonjour à toi, fan de Formule 1 !**\n\n"
+ "Je suis **le nouveau bot officiel** de la communauté **Formula 1 France**. Mon rôle ? "
+ "T’accompagner pendant toute la saison avec des outils pratiques comme la récupération de tes pronostics et des infos à jour sur les Grands Prix ! 🇫🇷🏎️\n\n"
+
+ "Sois indulgent avec moi, je viens tout juste d’être lancé : je suis encore en **BETA** 🥺\n\n"
+
+ "📊 D’autres fonctions arrivent bientôt : infos circuits, classements en direct, statistiques pilotes, et même un jeu spécial F1F!\n\n"
+
+ "🎉 __**Et maintenant, place au concours !**__\n\n"
+ "🤔 Mes créateurs ne m’ont pas encore donné de nom... C’est là que **toi** tu entres en jeu !\n"
+ "Propose-moi un nom original, fun ou en rapport avec la F1 et F1F, **et si ta proposition est retenue, elle deviendra mon nom officiel trop bien non ?!**\n\n"
+
+ " 📬__Pour participer :__\n"
+ "Répond simplement à **ce message privé** avec ton idée de nom pour le bot.\n"
+ "**Une seule condition : que ce soit cool, en rapport avec la Formule 1 et F1F (le mot GOAT est fortement conseillé) !!**\n\n"
+
+ "🏆 Le vainqueur sera annoncé sur le serveur et gagnera une **petite surprise** 👀\n\n"
+ "À toi de jouer, et que le meilleur nom gagne ! 🏎️✨"
+ ),
+ color=discord.Color.red()
+)
+
+ embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
+ embed.set_thumbnail(url=interaction.user.display_avatar.url)
+ embed.set_image(url=EMBED_IMAGE)
+
+ try:
+ await interaction.user.send(embed=embed)
+
+ msg = await bot.wait_for(
+ "message",
+ check=lambda m: m.author == interaction.user and isinstance(m.channel, discord.DMChannel)
+ )
+
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ content = f.read().strip()
+ data = json.loads(content) if content else {}
+ except (FileNotFoundError, json.JSONDecodeError):
+ data = {}
+
+ data[str(msg.author.name)] = msg.content
+
+ with open(file_path, 'w', encoding='utf-8') as f:
+ json.dump(data, f, indent=4, ensure_ascii=True)
+
+ await interaction.user.send("Merci pour ta proposition !")
+
+ except discord.Forbidden:
+ await interaction.channel.send("Je n'ai pas pu envoyer le message")
+
+ if interaction.guild is None:
+ logger.info(f"Présentation par {interaction.user.name} en MP")
+ else:
+ logger.info(f"Présentation par {interaction.user.name} dans {interaction.channel.name} sur {interaction.guild.name}")
+
+
+
+
diff --git a/build/doctrees/environment.pickle b/build/doctrees/environment.pickle
deleted file mode 100644
index 59fbf48..0000000
Binary files a/build/doctrees/environment.pickle and /dev/null differ
diff --git a/build/doctrees/index.doctree b/build/doctrees/index.doctree
deleted file mode 100644
index 56075e9..0000000
Binary files a/build/doctrees/index.doctree and /dev/null differ
diff --git a/data/Barem.json b/data/Barem.json
new file mode 100644
index 0000000..46b5810
--- /dev/null
+++ b/data/Barem.json
@@ -0,0 +1,9 @@
+{
+ "firstCorrect":2.5,
+ "secondCorrect":1.5,
+ "thirdCorrect":0.5,
+ "first2Correct":4,
+ "last2Correct":2.5,
+ "1and3Correct":3,
+ "allCorrect":5
+}
\ No newline at end of file
diff --git a/data/Classement_Qualif.xlsx b/data/Classement_Qualif.xlsx
deleted file mode 100644
index 2c8ea2a..0000000
Binary files a/data/Classement_Qualif.xlsx and /dev/null differ
diff --git a/data/Leaderbord.json b/data/Leaderbord.json
new file mode 100644
index 0000000..698ef36
--- /dev/null
+++ b/data/Leaderbord.json
@@ -0,0 +1,6 @@
+{
+ "1256311918319112294": {
+ "Pseudo": "victoirrrr",
+ "Points": 5
+ }
+}
\ No newline at end of file
diff --git a/data/NameBot.json b/data/NameBot.json
new file mode 100644
index 0000000..28d78f8
--- /dev/null
+++ b/data/NameBot.json
@@ -0,0 +1,8 @@
+{
+ "matt_karting": "GoaBot",
+ "sf211921": "TEST SC",
+ "whisper9741": "Matt c le goat supreme",
+ "la_bagguette": "Oui",
+ "leahf1": "Botas goal f1",
+ "tigerone4980": "AIrton Senna"
+}
\ No newline at end of file
diff --git a/data/Pronos.json b/data/Pronos.json
new file mode 100644
index 0000000..ff2d07f
--- /dev/null
+++ b/data/Pronos.json
@@ -0,0 +1,18 @@
+{
+ "1256311918319112294": {
+ "Pseudo": "victoirrrr",
+ "1": "Leclerc",
+ "2": "Lewis",
+ "3": "Nico",
+ "Best Lap": "Stroll",
+ "Modif": true
+ },
+ "1200489866165747722": {
+ "Pseudo": "matt_karting",
+ "1": "Leclerc",
+ "2": "Lando",
+ "3": "Max",
+ "Best Lap": "Ocon",
+ "Modif": false
+ }
+}
\ No newline at end of file
diff --git a/data/Result_Course_Pronos_F1F_DEMO.xlsx b/data/Result_Course_Pronos_F1F_DEMO.xlsx
deleted file mode 100644
index 5d6054b..0000000
Binary files a/data/Result_Course_Pronos_F1F_DEMO.xlsx and /dev/null differ
diff --git a/data/Result_Qualif_Pronos_F1F_DEMO.xlsx b/data/Result_Qualif_Pronos_F1F_DEMO.xlsx
deleted file mode 100644
index 28e43ea..0000000
Binary files a/data/Result_Qualif_Pronos_F1F_DEMO.xlsx and /dev/null differ
diff --git a/data/Results.json b/data/Results.json
new file mode 100644
index 0000000..fbd30e0
--- /dev/null
+++ b/data/Results.json
@@ -0,0 +1,6 @@
+{
+ "1": "Lando Norris",
+ "2": "Oscar Piastri",
+ "3": "Nico Hulkenberg",
+ "Best Lap": "Oscar Piastri"
+}
\ No newline at end of file
diff --git a/data/Session.json b/data/Session.json
new file mode 100644
index 0000000..2776ad2
--- /dev/null
+++ b/data/Session.json
@@ -0,0 +1,8 @@
+{
+ "Round": "13",
+ "Country": "Belgium",
+ "Location": "Spa-Francorchamps",
+ "Session": "Q",
+ "Date": "26/07/2025,14:00:00",
+ "Saison": 2025
+}
\ No newline at end of file
diff --git a/data/resultats_course.json b/data/resultats_course.json
deleted file mode 100644
index 3ef072d..0000000
--- a/data/resultats_course.json
+++ /dev/null
@@ -1,14 +0,0 @@
-[
- [
- 1,
- "L. Norris"
- ],
- [
- 2,
- "O. Piastri"
- ],
- [
- 3,
- "N. Hülkenberg"
- ]
-]
\ No newline at end of file
diff --git a/data/resultats_qualif.json b/data/resultats_qualif.json
deleted file mode 100644
index b9f11ba..0000000
--- a/data/resultats_qualif.json
+++ /dev/null
@@ -1,14 +0,0 @@
-[
- [
- 1,
- "M. Verstappen"
- ],
- [
- 2,
- "O. Piastri"
- ],
- [
- 3,
- "L. Norris"
- ]
-]
\ No newline at end of file
diff --git a/docs/.buildinfo b/docs/.buildinfo
new file mode 100644
index 0000000..dc0daa1
--- /dev/null
+++ b/docs/.buildinfo
@@ -0,0 +1,4 @@
+# Sphinx build info version 1
+# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
+config: 67a85b39e586c1c390776ca97b419096
+tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/docs/.buildinfo.bak b/docs/.buildinfo.bak
new file mode 100644
index 0000000..41c5a06
--- /dev/null
+++ b/docs/.buildinfo.bak
@@ -0,0 +1,4 @@
+# Sphinx build info version 1
+# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
+config: b11e1630cd32e452a6edf355feba5536
+tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/docs/.doctrees/commands.doctree b/docs/.doctrees/commands.doctree
new file mode 100644
index 0000000..ad4b009
Binary files /dev/null and b/docs/.doctrees/commands.doctree differ
diff --git a/docs/.doctrees/environment.pickle b/docs/.doctrees/environment.pickle
new file mode 100644
index 0000000..877468e
Binary files /dev/null and b/docs/.doctrees/environment.pickle differ
diff --git a/docs/.doctrees/get_started.doctree b/docs/.doctrees/get_started.doctree
new file mode 100644
index 0000000..032ed4d
Binary files /dev/null and b/docs/.doctrees/get_started.doctree differ
diff --git a/docs/.doctrees/index.doctree b/docs/.doctrees/index.doctree
new file mode 100644
index 0000000..8ebc9bc
Binary files /dev/null and b/docs/.doctrees/index.doctree differ
diff --git a/docs/.doctrees/privacy_policy.doctree b/docs/.doctrees/privacy_policy.doctree
new file mode 100644
index 0000000..b41669e
Binary files /dev/null and b/docs/.doctrees/privacy_policy.doctree differ
diff --git a/docs/.doctrees/terms_of_service.doctree b/docs/.doctrees/terms_of_service.doctree
new file mode 100644
index 0000000..5e0316e
Binary files /dev/null and b/docs/.doctrees/terms_of_service.doctree differ
diff --git a/docs/_images/Discord.png b/docs/_images/Discord.png
new file mode 100644
index 0000000..26ccbcc
Binary files /dev/null and b/docs/_images/Discord.png differ
diff --git a/docs/_images/F1F-logo.ico b/docs/_images/F1F-logo.ico
new file mode 100644
index 0000000..2b8cc43
Binary files /dev/null and b/docs/_images/F1F-logo.ico differ
diff --git a/docs/_images/F1F-logo.png b/docs/_images/F1F-logo.png
new file mode 100644
index 0000000..e7efb86
Binary files /dev/null and b/docs/_images/F1F-logo.png differ
diff --git a/docs/_images/banniere_F1F.png b/docs/_images/banniere_F1F.png
new file mode 100644
index 0000000..1507b0b
Binary files /dev/null and b/docs/_images/banniere_F1F.png differ
diff --git a/docs/_images/github.png b/docs/_images/github.png
new file mode 100644
index 0000000..e0e3d59
Binary files /dev/null and b/docs/_images/github.png differ
diff --git a/docs/_images/mail.png b/docs/_images/mail.png
new file mode 100644
index 0000000..349f9f7
Binary files /dev/null and b/docs/_images/mail.png differ
diff --git a/docs/_sources/commands.rst.txt b/docs/_sources/commands.rst.txt
new file mode 100644
index 0000000..611643e
--- /dev/null
+++ b/docs/_sources/commands.rst.txt
@@ -0,0 +1,126 @@
+Commands
+========
+
+General Commands
+----------------
+
+.. code:: slash
+
+ /ping
+
+Replies with "🏓 Pong!". Used to check if the bot is online.
+
+.. code:: slash
+
+ /say message: str
+
+Repeats the provided message. Very useful to make the bot speak.
+
+.. code:: slash
+
+ /salut
+
+Sends a personalized greeting to the user.
+
+.. code:: slash
+
+ /help
+
+Displays the list of available commands with a brief explanation.
+
+Prediction Commands
+-------------------
+
+.. code:: slash
+
+ /pronos_course premier: str deuxieme: str troisieme: str best_lap: str
+
+Allows a user to register their predictions for an event. Can be modified only once.
+
+.. code:: slash
+
+ /visualisation
+
+Displays the user’s previously recorded predictions.
+
+.. code:: slash
+
+ /leaderboard
+
+Shows the general leaderboard of users based on their prediction scores.
+
+Administration Commands
+-----------------------
+
+.. code:: slash
+
+ /clear nombre: int
+
+Deletes a given number of messages in a channel (admins only).
+
+.. code:: slash
+
+ /admin_ban member: @member reason: str article: str
+
+Bans a member from the server for a specified reason and article (moderators only).
+
+.. code:: slash
+
+ /admin_open duration: float
+
+Opens a prediction session for a set duration (in hours). Works only in manual mode.
+
+.. code:: slash
+
+ /admin_close
+
+Closes the prediction session manually.
+
+.. code:: slash
+
+ /admin_status
+
+Shows the current bot mode (auto or manual).
+
+.. code:: slash
+
+ /admin_stop
+
+Disables automatic mode and switches back to manual.
+
+.. code:: slash
+
+ /admin_launch
+
+Starts automatic mode: the bot automatically opens prediction sessions based on the F1 calendar.
+
+.. code:: slash
+
+ /session saison: int location: str type: str
+
+Manually configures a session (Q for qualifying, R for race). Updates the leaderboard.
+
+Special Commands
+----------------
+
+.. code:: slash
+
+ /presentation
+
+Introduces the bot and allows users to propose a name via private message. These proposals are saved in a file.
+
+System Commands (non-slash)
+---------------------------
+
+.. code:: text
+
+ !sync
+
+Command available only to administrators listed in `owners_id`. Synchronizes all slash commands with Discord.
+
+Notes
+-----
+
+- Administration commands require the corresponding permissions (ban, admin, etc.).
+- The prediction system uses both manual and automatic modes.
+- All logs and errors are properly handled within `try/except` blocks.
diff --git a/docs/_sources/get_started.rst.txt b/docs/_sources/get_started.rst.txt
new file mode 100644
index 0000000..b5a71ac
--- /dev/null
+++ b/docs/_sources/get_started.rst.txt
@@ -0,0 +1,167 @@
+Get Started
+===========
+
+.. image:: _static/banniere_F1F.png
+ :alt: F1F Bot Logo
+ :align: center
+ :width: 500px
+
+Overview
+--------
+
+The F1F Bot is built in Python and interacts with the Discord API using `discord.py`.
+It automates tasks such as:
+
+- User predictions (for Formula 1 races)
+- Data scraping from race result websites
+- Scheduled updates
+- Database management
+- Interactive commands on Discord
+
+Project Structure
+-----------------
+
+.. code-block:: none
+
+ f1f_bot/
+ ├── bot.py
+ ├── cogs/
+ │ ├── predictions.py
+ │ └── ...
+ ├── utils/
+ │ ├── scraping.py
+ │ └── ...
+ ├── .env
+ ├── .venv/
+ ├── requirements.txt
+ └── README.rst
+
+Installation
+------------
+
+.. code-block:: bash
+
+ git clone https://github.com/votre-utilisateur/f1f-bot.git
+ cd f1f-bot
+ python -m venv .venv
+ source .venv/bin/activate
+ pip install -r requirements.txt
+
+Python Libraries Used in F1F Bot
+--------------------------------
+
+.. list-table:: ________
+ :widths: 20 30 40 20
+ :header-rows: 1
+
+ * - 📦 pip install
+ - 💻 Python import
+ - 🧠 Main Purpose
+ - 🧩 Category
+ * - `discord.py`
+ - `import discord`
+ `from discord.ext import commands`
+ - Manage the Discord interface (messages, events, commands)
+ - 🟣 Discord Bot
+ * - `pandas`
+ - `import pandas as pd`
+ - Data analysis and manipulation (tables, CSV, etc.)
+ - 🔵 Data Processing
+ * - `beautifulsoup4`
+ - `from bs4 import BeautifulSoup`
+ - HTML parsing to extract data
+ - 🟠 Web Scraping
+ * - `selenium`
+ - `from selenium import webdriver`
+ - Browser automation (advanced scraping)
+ - 🟠 Web Scraping
+ * - `requests`
+ - `import requests`
+ - HTTP requests to APIs or websites
+ - 🟠 Web Scraping
+ * - `lxml` *(or `html5lib`)*
+ - *(used by BeautifulSoup)*
+ - Fast and robust HTML/XML parser
+ - ⚙️ Parsing Dependency
+ * - `sphinx`
+ - *(not required directly in code)*
+ - Automatic documentation generation
+ - 🟡 Documentation
+ * - `jupyter` *(notebook)*
+ - *(launched via interface, not imported)*
+ - Interactive notebooks for testing and code demos
+ - 🟡 Documentation / Demo
+ * - `asyncio`
+ - `import asyncio`
+ - Asynchronous task management
+ - 🔴 Async Programming
+ * - `typing` *(builtin)*
+ - `from typing import List, Optional, Dict`
+ - Static typing and function annotations
+ - ⚪ Utility
+ * - `os` *(builtin)*
+ - `import os`
+ - Access to file system / environment variables
+ - ⚪ Utility
+ * - `dotenv` *(optional)*
+ - `from dotenv import load_dotenv`
+ - Load environment variables from a `.env` file
+ - ⚪ Security / Config
+ * - `gitpython` *(optional)*
+ - `import git`
+ - Git integration within the script
+ - ⚫ DevOps / Git
+ * - `aiohttp` *(optional)*
+ - `import aiohttp`
+ - Asynchronous HTTP requests (more efficient than `requests`)
+ - 🔴 Async Programming
+ * - `schedule` *(optional)*
+ - `import schedule`
+ - Scheduling tasks at regular intervals
+ - 🔁 Scheduled Tasks
+
+How to install all these librairies
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: bash
+
+ pip install -U discord.py pandas beautifulsoup4 selenium requests sphinx notebook python-dotenv
+
+Configuration
+-------------
+
+Make sure to create a `.env` file with the following structure:
+
+.. code-block:: ini
+
+ DISCORD_TOKEN=your_token_here
+ GUILD_ID=your_discord_guild_id
+
+Modules and Libraries
+---------------------
+
+The bot uses the following libraries:
+
+- **discord.py** – for interacting with the Discord API
+- **pandas** – for managing tabular data
+- **BeautifulSoup / Selenium** – for scraping external websites
+- **schedule** – for timed task execution
+- **dotenv** – for configuration via environment variables
+
+Development Notes
+-----------------
+
+To launch the bot locally:
+
+.. code-block:: bash
+
+ source .venv/bin/activate
+ python bot.py
+
+You can add new commands using `discord.ext.commands.Cog`.
+
+License
+-------
+
+MIT License.
+© Formula 1 France Development Team.
diff --git a/docs/_sources/index.rst.txt b/docs/_sources/index.rst.txt
new file mode 100644
index 0000000..6486e66
--- /dev/null
+++ b/docs/_sources/index.rst.txt
@@ -0,0 +1,99 @@
+.. BOT_F1F documentation master file, created by
+ sphinx-quickstart on Sun Jul 13 16:49:41 2025.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+
+F1F Discord Bot Documentation
+=============================
+
+Welcome to the documentation of the **F1F Bot**, a Discord bot designed to support Formula 1 France community predictions and automation.
+
+Discord link : https://discord.gg/89ENkYSCWE
+
+Introduction
+------------
+
+Welcome to the official documentation of the **F1F Discord Bot**, a custom-built assistant created to serve the Formula 1 France community on Discord.
+This bot is designed to bring automation, accuracy, and interactivity to your server, improving both user experience and community management.
+
+Why Build This Bot?
+-------------------
+
+Formula 1 fans thrive on fast, reliable information and a space to share predictions, opinions, and results. Managing such a community manually can be time-consuming and prone to errors.
+
+The F1F Bot was developed to:
+
+- **Automate repetitive tasks:** such as posting race schedules, results, and reminders.
+- **Ensure real-time updates:** by scraping and processing live race data.
+- **Engage the community:** through interactive prediction games and commands.
+- **Support moderators:** by handling routine administrative tasks, freeing up human effort for more meaningful interactions.
+
+Core Features
+-------------
+
+1. **Race Predictions System**
+
+ Members can submit predictions for upcoming Grand Prix events via bot commands. The bot stores these predictions securely and can later retrieve, compare, and display them.
+
+2. **Automated Data Scraping and Updates**
+
+ Using libraries like `BeautifulSoup` and `Selenium`, the bot extracts live race results and relevant statistics from official websites or trusted sources, ensuring the community stays up-to-date.
+
+3. **Scheduled Announcements**
+
+ The bot schedules reminders, race start notifications, and result postings automatically, maintaining a dynamic and engaging server atmosphere without manual input.
+
+4. **Interactive Commands**
+
+ A rich set of commands allows users to interact seamlessly with the bot, from checking race calendars to viewing leaderboard standings.
+
+5. **Community and Moderation Tools**
+
+ The bot helps moderators by monitoring chats, managing permissions, and automating common administrative duties, enhancing overall server health and safety.
+
+Technical Overview
+------------------
+
+- **Language and Libraries:** Written in Python, leveraging `discord.py` for Discord API interaction.
+- **Web Scraping:** Utilizes `BeautifulSoup`, `Selenium`, and `requests` for fetching and parsing web data.
+- **Asynchronous Programming:** Uses Python’s `asyncio` to handle multiple concurrent tasks efficiently.
+- **Configuration:** Environment variables (stored in `.env` files) manage sensitive data such as bot tokens and server IDs.
+- **Extensibility:** Modular design with easily extendable components to add new features or integrate third-party services.
+
+Getting Started
+---------------
+
+To get the bot up and running on your server, the documentation provides detailed instructions on:
+
+- Installing dependencies and setting up the Python environment.
+- Configuring authentication tokens and server settings.
+- Running the bot locally or deploying it on a server.
+- Customizing features to suit your community’s unique needs.
+
+Contributing and Support
+------------------------
+
+The F1F Bot is an open project developed by the Formula 1 France community. Contributions, feature requests, and bug reports are welcome through our GitHub repository and Discord server.
+Whether you are a developer, F1 fan, or community manager, your participation helps improve the bot for everyone.
+
+License
+-------
+
+This project is distributed under the MIT License, promoting free use, modification, and distribution.
+
+---
+
+Thank you for supporting the Formula 1 France community with the F1F Bot — your go-to assistant for all things F1 on Discord!
+
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Navigation
+
+ get_started
+ commands
+ terms_of_service
+ privacy_policy
+
+
diff --git a/docs/_sources/privacy_policy.rst.txt b/docs/_sources/privacy_policy.rst.txt
new file mode 100644
index 0000000..f32e986
--- /dev/null
+++ b/docs/_sources/privacy_policy.rst.txt
@@ -0,0 +1,62 @@
+Privacy Policy
+==============
+
+This Privacy Policy explains how the **F1F Discord Bot** collects, uses, and protects your personal data when you use the bot.
+
+Data Collection
+---------------
+
+The F1F Bot may collect the following information:
+
+- **Discord User ID**: to identify users and manage interactions.
+- **Predictions Data**: your race predictions submitted through the bot.
+- **Server Interaction Data**: messages and commands you send to the bot.
+
+No personally identifiable information (such as your real name, email, or location) is collected by the bot unless you provide it voluntarily.
+
+Data Usage
+----------
+
+The data collected is used solely to:
+
+- Provide the prediction features and related functionalities.
+- Maintain and improve the bot’s performance.
+- Communicate important updates or announcements via Discord.
+
+Data Protection
+---------------
+
+We implement reasonable security measures to protect your data from unauthorized access or disclosure. However, no method of transmission over the internet is 100% secure.
+
+Data Sharing
+------------
+
+Your data will not be sold, rented, or shared with third parties outside of the F1F Bot development team, except if required by law.
+
+Your Rights
+-----------
+
+You have the right to:
+
+- Request access to the personal data we hold about you.
+- Request correction or deletion of your data.
+- Withdraw your consent to data processing at any time.
+
+To exercise these rights, please contact the bot administrators via the official Discord server or email.
+
+Changes to This Policy
+----------------------
+
+This Privacy Policy may be updated periodically. We will notify users of significant changes via the Discord server.
+
+Contact Information
+-------------------
+
+If you have any questions about this Privacy Policy, please contact us at:
+
+**Discord:** https://discord.gg/89ENkYSCWE
+**Email:** [your-email@example.com]
+
+---
+
+© 2025 Formula 1 France Development Team
diff --git a/docs/_sources/terms_of_service.rst.txt b/docs/_sources/terms_of_service.rst.txt
new file mode 100644
index 0000000..68db6c0
--- /dev/null
+++ b/docs/_sources/terms_of_service.rst.txt
@@ -0,0 +1,47 @@
+Terms of Service
+================
+
+Welcome to the F1F Discord Bot! By using this bot, you agree to the following terms and conditions. Please read them carefully.
+
+1. Introduction
+---------------
+
+The F1F Discord Bot ("the Bot") is designed to assist the Formula 1 France community with race predictions, automation, and data management. This document outlines your rights and responsibilities when interacting with the Bot.
+
+2. Use of the Bot
+-----------------
+
+- The Bot is provided "as is" and "as available".
+- You agree to use the Bot responsibly and not to exploit or misuse its features.
+- You may not use the Bot for any illegal, harmful, or disruptive activities.
+
+3. User Data and Privacy
+------------------------
+
+- The Bot may collect certain user data necessary for its operation (such as Discord IDs, predictions, and related inputs).
+- No personal data will be shared with third parties without your explicit consent.
+- All data collected is stored securely and used only for the intended purpose of providing Bot services.
+- By using the Bot, you consent to this data collection and usage.
+
+4. Limitations and Liability
+----------------------------
+
+- The Bot team strives for accuracy but does not guarantee the correctness of race results or predictions.
+- The developers are not responsible for any damages or losses caused by using the Bot.
+- The Bot may undergo maintenance or experience downtime without prior notice.
+
+5. Changes to Terms
+-------------------
+
+We reserve the right to update or modify these Terms of Service at any time. Changes will be communicated via the Discord server or Bot messages.
+
+6. Contact
+----------
+
+For questions or concerns about these Terms, please contact the F1F Bot development team via the official Discord server:
+https://discord.gg/89ENkYSCWE
+
+---
+
+By using the F1F Bot, you acknowledge that you have read, understood, and agree to these Terms of Service.
+
diff --git a/docs/_static/F1F-logo.ico b/docs/_static/F1F-logo.ico
new file mode 100644
index 0000000..2b8cc43
Binary files /dev/null and b/docs/_static/F1F-logo.ico differ
diff --git a/docs/_static/F1F-logo.png b/docs/_static/F1F-logo.png
new file mode 100644
index 0000000..e7efb86
Binary files /dev/null and b/docs/_static/F1F-logo.png differ
diff --git a/build/html/_static/_sphinx_javascript_frameworks_compat.js b/docs/_static/_sphinx_javascript_frameworks_compat.js
similarity index 100%
rename from build/html/_static/_sphinx_javascript_frameworks_compat.js
rename to docs/_static/_sphinx_javascript_frameworks_compat.js
diff --git a/docs/_static/banniere_F1F.png b/docs/_static/banniere_F1F.png
new file mode 100644
index 0000000..1507b0b
Binary files /dev/null and b/docs/_static/banniere_F1F.png differ
diff --git a/docs/_static/base-stemmer.js b/docs/_static/base-stemmer.js
new file mode 100644
index 0000000..ca6cca1
--- /dev/null
+++ b/docs/_static/base-stemmer.js
@@ -0,0 +1,294 @@
+/**@constructor*/
+BaseStemmer = function() {
+ this.setCurrent = function(value) {
+ this.current = value;
+ this.cursor = 0;
+ this.limit = this.current.length;
+ this.limit_backward = 0;
+ this.bra = this.cursor;
+ this.ket = this.limit;
+ };
+
+ this.getCurrent = function() {
+ return this.current;
+ };
+
+ this.copy_from = function(other) {
+ this.current = other.current;
+ this.cursor = other.cursor;
+ this.limit = other.limit;
+ this.limit_backward = other.limit_backward;
+ this.bra = other.bra;
+ this.ket = other.ket;
+ };
+
+ this.in_grouping = function(s, min, max) {
+ if (this.cursor >= this.limit) return false;
+ var ch = this.current.charCodeAt(this.cursor);
+ if (ch > max || ch < min) return false;
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
+ this.cursor++;
+ return true;
+ };
+
+ this.in_grouping_b = function(s, min, max) {
+ if (this.cursor <= this.limit_backward) return false;
+ var ch = this.current.charCodeAt(this.cursor - 1);
+ if (ch > max || ch < min) return false;
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
+ this.cursor--;
+ return true;
+ };
+
+ this.out_grouping = function(s, min, max) {
+ if (this.cursor >= this.limit) return false;
+ var ch = this.current.charCodeAt(this.cursor);
+ if (ch > max || ch < min) {
+ this.cursor++;
+ return true;
+ }
+ ch -= min;
+ if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) == 0) {
+ this.cursor++;
+ return true;
+ }
+ return false;
+ };
+
+ this.out_grouping_b = function(s, min, max) {
+ if (this.cursor <= this.limit_backward) return false;
+ var ch = this.current.charCodeAt(this.cursor - 1);
+ if (ch > max || ch < min) {
+ this.cursor--;
+ return true;
+ }
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) {
+ this.cursor--;
+ return true;
+ }
+ return false;
+ };
+
+ this.eq_s = function(s)
+ {
+ if (this.limit - this.cursor < s.length) return false;
+ if (this.current.slice(this.cursor, this.cursor + s.length) != s)
+ {
+ return false;
+ }
+ this.cursor += s.length;
+ return true;
+ };
+
+ this.eq_s_b = function(s)
+ {
+ if (this.cursor - this.limit_backward < s.length) return false;
+ if (this.current.slice(this.cursor - s.length, this.cursor) != s)
+ {
+ return false;
+ }
+ this.cursor -= s.length;
+ return true;
+ };
+
+ /** @return {number} */ this.find_among = function(v)
+ {
+ var i = 0;
+ var j = v.length;
+
+ var c = this.cursor;
+ var l = this.limit;
+
+ var common_i = 0;
+ var common_j = 0;
+
+ var first_key_inspected = false;
+
+ while (true)
+ {
+ var k = i + ((j - i) >>> 1);
+ var diff = 0;
+ var common = common_i < common_j ? common_i : common_j; // smaller
+ // w[0]: string, w[1]: substring_i, w[2]: result, w[3]: function (optional)
+ var w = v[k];
+ var i2;
+ for (i2 = common; i2 < w[0].length; i2++)
+ {
+ if (c + common == l)
+ {
+ diff = -1;
+ break;
+ }
+ diff = this.current.charCodeAt(c + common) - w[0].charCodeAt(i2);
+ if (diff != 0) break;
+ common++;
+ }
+ if (diff < 0)
+ {
+ j = k;
+ common_j = common;
+ }
+ else
+ {
+ i = k;
+ common_i = common;
+ }
+ if (j - i <= 1)
+ {
+ if (i > 0) break; // v->s has been inspected
+ if (j == i) break; // only one item in v
+
+ // - but now we need to go round once more to get
+ // v->s inspected. This looks messy, but is actually
+ // the optimal approach.
+
+ if (first_key_inspected) break;
+ first_key_inspected = true;
+ }
+ }
+ do {
+ var w = v[i];
+ if (common_i >= w[0].length)
+ {
+ this.cursor = c + w[0].length;
+ if (w.length < 4) return w[2];
+ var res = w[3](this);
+ this.cursor = c + w[0].length;
+ if (res) return w[2];
+ }
+ i = w[1];
+ } while (i >= 0);
+ return 0;
+ };
+
+ // find_among_b is for backwards processing. Same comments apply
+ this.find_among_b = function(v)
+ {
+ var i = 0;
+ var j = v.length
+
+ var c = this.cursor;
+ var lb = this.limit_backward;
+
+ var common_i = 0;
+ var common_j = 0;
+
+ var first_key_inspected = false;
+
+ while (true)
+ {
+ var k = i + ((j - i) >> 1);
+ var diff = 0;
+ var common = common_i < common_j ? common_i : common_j;
+ var w = v[k];
+ var i2;
+ for (i2 = w[0].length - 1 - common; i2 >= 0; i2--)
+ {
+ if (c - common == lb)
+ {
+ diff = -1;
+ break;
+ }
+ diff = this.current.charCodeAt(c - 1 - common) - w[0].charCodeAt(i2);
+ if (diff != 0) break;
+ common++;
+ }
+ if (diff < 0)
+ {
+ j = k;
+ common_j = common;
+ }
+ else
+ {
+ i = k;
+ common_i = common;
+ }
+ if (j - i <= 1)
+ {
+ if (i > 0) break;
+ if (j == i) break;
+ if (first_key_inspected) break;
+ first_key_inspected = true;
+ }
+ }
+ do {
+ var w = v[i];
+ if (common_i >= w[0].length)
+ {
+ this.cursor = c - w[0].length;
+ if (w.length < 4) return w[2];
+ var res = w[3](this);
+ this.cursor = c - w[0].length;
+ if (res) return w[2];
+ }
+ i = w[1];
+ } while (i >= 0);
+ return 0;
+ };
+
+ /* to replace chars between c_bra and c_ket in this.current by the
+ * chars in s.
+ */
+ this.replace_s = function(c_bra, c_ket, s)
+ {
+ var adjustment = s.length - (c_ket - c_bra);
+ this.current = this.current.slice(0, c_bra) + s + this.current.slice(c_ket);
+ this.limit += adjustment;
+ if (this.cursor >= c_ket) this.cursor += adjustment;
+ else if (this.cursor > c_bra) this.cursor = c_bra;
+ return adjustment;
+ };
+
+ this.slice_check = function()
+ {
+ if (this.bra < 0 ||
+ this.bra > this.ket ||
+ this.ket > this.limit ||
+ this.limit > this.current.length)
+ {
+ return false;
+ }
+ return true;
+ };
+
+ this.slice_from = function(s)
+ {
+ var result = false;
+ if (this.slice_check())
+ {
+ this.replace_s(this.bra, this.ket, s);
+ result = true;
+ }
+ return result;
+ };
+
+ this.slice_del = function()
+ {
+ return this.slice_from("");
+ };
+
+ this.insert = function(c_bra, c_ket, s)
+ {
+ var adjustment = this.replace_s(c_bra, c_ket, s);
+ if (c_bra <= this.bra) this.bra += adjustment;
+ if (c_bra <= this.ket) this.ket += adjustment;
+ };
+
+ this.slice_to = function()
+ {
+ var result = '';
+ if (this.slice_check())
+ {
+ result = this.current.slice(this.bra, this.ket);
+ }
+ return result;
+ };
+
+ this.assign_to = function()
+ {
+ return this.current.slice(0, this.limit);
+ };
+};
diff --git a/build/html/_static/basic.css b/docs/_static/basic.css
similarity index 100%
rename from build/html/_static/basic.css
rename to docs/_static/basic.css
diff --git a/build/html/_static/css/badge_only.css b/docs/_static/css/badge_only.css
similarity index 100%
rename from build/html/_static/css/badge_only.css
rename to docs/_static/css/badge_only.css
diff --git a/build/html/_static/css/fonts/Roboto-Slab-Bold.woff b/docs/_static/css/fonts/Roboto-Slab-Bold.woff
similarity index 100%
rename from build/html/_static/css/fonts/Roboto-Slab-Bold.woff
rename to docs/_static/css/fonts/Roboto-Slab-Bold.woff
diff --git a/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 b/docs/_static/css/fonts/Roboto-Slab-Bold.woff2
similarity index 100%
rename from build/html/_static/css/fonts/Roboto-Slab-Bold.woff2
rename to docs/_static/css/fonts/Roboto-Slab-Bold.woff2
diff --git a/build/html/_static/css/fonts/Roboto-Slab-Regular.woff b/docs/_static/css/fonts/Roboto-Slab-Regular.woff
similarity index 100%
rename from build/html/_static/css/fonts/Roboto-Slab-Regular.woff
rename to docs/_static/css/fonts/Roboto-Slab-Regular.woff
diff --git a/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 b/docs/_static/css/fonts/Roboto-Slab-Regular.woff2
similarity index 100%
rename from build/html/_static/css/fonts/Roboto-Slab-Regular.woff2
rename to docs/_static/css/fonts/Roboto-Slab-Regular.woff2
diff --git a/build/html/_static/css/fonts/fontawesome-webfont.eot b/docs/_static/css/fonts/fontawesome-webfont.eot
similarity index 100%
rename from build/html/_static/css/fonts/fontawesome-webfont.eot
rename to docs/_static/css/fonts/fontawesome-webfont.eot
diff --git a/build/html/_static/css/fonts/fontawesome-webfont.svg b/docs/_static/css/fonts/fontawesome-webfont.svg
similarity index 100%
rename from build/html/_static/css/fonts/fontawesome-webfont.svg
rename to docs/_static/css/fonts/fontawesome-webfont.svg
diff --git a/build/html/_static/css/fonts/fontawesome-webfont.ttf b/docs/_static/css/fonts/fontawesome-webfont.ttf
similarity index 100%
rename from build/html/_static/css/fonts/fontawesome-webfont.ttf
rename to docs/_static/css/fonts/fontawesome-webfont.ttf
diff --git a/build/html/_static/css/fonts/fontawesome-webfont.woff b/docs/_static/css/fonts/fontawesome-webfont.woff
similarity index 100%
rename from build/html/_static/css/fonts/fontawesome-webfont.woff
rename to docs/_static/css/fonts/fontawesome-webfont.woff
diff --git a/build/html/_static/css/fonts/fontawesome-webfont.woff2 b/docs/_static/css/fonts/fontawesome-webfont.woff2
similarity index 100%
rename from build/html/_static/css/fonts/fontawesome-webfont.woff2
rename to docs/_static/css/fonts/fontawesome-webfont.woff2
diff --git a/build/html/_static/css/fonts/lato-bold-italic.woff b/docs/_static/css/fonts/lato-bold-italic.woff
similarity index 100%
rename from build/html/_static/css/fonts/lato-bold-italic.woff
rename to docs/_static/css/fonts/lato-bold-italic.woff
diff --git a/build/html/_static/css/fonts/lato-bold-italic.woff2 b/docs/_static/css/fonts/lato-bold-italic.woff2
similarity index 100%
rename from build/html/_static/css/fonts/lato-bold-italic.woff2
rename to docs/_static/css/fonts/lato-bold-italic.woff2
diff --git a/build/html/_static/css/fonts/lato-bold.woff b/docs/_static/css/fonts/lato-bold.woff
similarity index 100%
rename from build/html/_static/css/fonts/lato-bold.woff
rename to docs/_static/css/fonts/lato-bold.woff
diff --git a/build/html/_static/css/fonts/lato-bold.woff2 b/docs/_static/css/fonts/lato-bold.woff2
similarity index 100%
rename from build/html/_static/css/fonts/lato-bold.woff2
rename to docs/_static/css/fonts/lato-bold.woff2
diff --git a/build/html/_static/css/fonts/lato-normal-italic.woff b/docs/_static/css/fonts/lato-normal-italic.woff
similarity index 100%
rename from build/html/_static/css/fonts/lato-normal-italic.woff
rename to docs/_static/css/fonts/lato-normal-italic.woff
diff --git a/build/html/_static/css/fonts/lato-normal-italic.woff2 b/docs/_static/css/fonts/lato-normal-italic.woff2
similarity index 100%
rename from build/html/_static/css/fonts/lato-normal-italic.woff2
rename to docs/_static/css/fonts/lato-normal-italic.woff2
diff --git a/build/html/_static/css/fonts/lato-normal.woff b/docs/_static/css/fonts/lato-normal.woff
similarity index 100%
rename from build/html/_static/css/fonts/lato-normal.woff
rename to docs/_static/css/fonts/lato-normal.woff
diff --git a/build/html/_static/css/fonts/lato-normal.woff2 b/docs/_static/css/fonts/lato-normal.woff2
similarity index 100%
rename from build/html/_static/css/fonts/lato-normal.woff2
rename to docs/_static/css/fonts/lato-normal.woff2
diff --git a/build/html/_static/css/theme.css b/docs/_static/css/theme.css
similarity index 100%
rename from build/html/_static/css/theme.css
rename to docs/_static/css/theme.css
diff --git a/docs/_static/custom.css b/docs/_static/custom.css
new file mode 100644
index 0000000..5b80e6b
--- /dev/null
+++ b/docs/_static/custom.css
@@ -0,0 +1,4 @@
+img.logo {
+ max-height: 80px;
+ width: auto;
+}
\ No newline at end of file
diff --git a/docs/_static/debug.css b/docs/_static/debug.css
new file mode 100644
index 0000000..74d4aec
--- /dev/null
+++ b/docs/_static/debug.css
@@ -0,0 +1,69 @@
+/*
+ This CSS file should be overridden by the theme authors. It's
+ meant for debugging and developing the skeleton that this theme provides.
+*/
+body {
+ font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
+ "Apple Color Emoji", "Segoe UI Emoji";
+ background: lavender;
+}
+.sb-announcement {
+ background: rgb(131, 131, 131);
+}
+.sb-announcement__inner {
+ background: black;
+ color: white;
+}
+.sb-header {
+ background: lightskyblue;
+}
+.sb-header__inner {
+ background: royalblue;
+ color: white;
+}
+.sb-header-secondary {
+ background: lightcyan;
+}
+.sb-header-secondary__inner {
+ background: cornflowerblue;
+ color: white;
+}
+.sb-sidebar-primary {
+ background: lightgreen;
+}
+.sb-main {
+ background: blanchedalmond;
+}
+.sb-main__inner {
+ background: antiquewhite;
+}
+.sb-header-article {
+ background: lightsteelblue;
+}
+.sb-article-container {
+ background: snow;
+}
+.sb-article-main {
+ background: white;
+}
+.sb-footer-article {
+ background: lightpink;
+}
+.sb-sidebar-secondary {
+ background: lightgoldenrodyellow;
+}
+.sb-footer-content {
+ background: plum;
+}
+.sb-footer-content__inner {
+ background: palevioletred;
+}
+.sb-footer {
+ background: pink;
+}
+.sb-footer__inner {
+ background: salmon;
+}
+.sb-article {
+ background: white;
+}
diff --git a/build/html/_static/doctools.js b/docs/_static/doctools.js
similarity index 100%
rename from build/html/_static/doctools.js
rename to docs/_static/doctools.js
diff --git a/docs/_static/documentation_options.js b/docs/_static/documentation_options.js
new file mode 100644
index 0000000..668df8f
--- /dev/null
+++ b/docs/_static/documentation_options.js
@@ -0,0 +1,13 @@
+const DOCUMENTATION_OPTIONS = {
+ VERSION: '1.0.3',
+ LANGUAGE: 'fr',
+ COLLAPSE_INDEX: false,
+ BUILDER: 'html',
+ FILE_SUFFIX: '.html',
+ LINK_SUFFIX: '.html',
+ HAS_SOURCE: true,
+ SOURCELINK_SUFFIX: '.txt',
+ NAVIGATION_WITH_KEYS: false,
+ SHOW_SEARCH_SUMMARY: true,
+ ENABLE_SEARCH_SHORTCUTS: true,
+};
\ No newline at end of file
diff --git a/build/html/_static/file.png b/docs/_static/file.png
similarity index 100%
rename from build/html/_static/file.png
rename to docs/_static/file.png
diff --git a/build/html/_static/fonts/Lato/lato-bold.eot b/docs/_static/fonts/Lato/lato-bold.eot
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-bold.eot
rename to docs/_static/fonts/Lato/lato-bold.eot
diff --git a/build/html/_static/fonts/Lato/lato-bold.ttf b/docs/_static/fonts/Lato/lato-bold.ttf
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-bold.ttf
rename to docs/_static/fonts/Lato/lato-bold.ttf
diff --git a/build/html/_static/fonts/Lato/lato-bold.woff b/docs/_static/fonts/Lato/lato-bold.woff
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-bold.woff
rename to docs/_static/fonts/Lato/lato-bold.woff
diff --git a/build/html/_static/fonts/Lato/lato-bold.woff2 b/docs/_static/fonts/Lato/lato-bold.woff2
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-bold.woff2
rename to docs/_static/fonts/Lato/lato-bold.woff2
diff --git a/build/html/_static/fonts/Lato/lato-bolditalic.eot b/docs/_static/fonts/Lato/lato-bolditalic.eot
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-bolditalic.eot
rename to docs/_static/fonts/Lato/lato-bolditalic.eot
diff --git a/build/html/_static/fonts/Lato/lato-bolditalic.ttf b/docs/_static/fonts/Lato/lato-bolditalic.ttf
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-bolditalic.ttf
rename to docs/_static/fonts/Lato/lato-bolditalic.ttf
diff --git a/build/html/_static/fonts/Lato/lato-bolditalic.woff b/docs/_static/fonts/Lato/lato-bolditalic.woff
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-bolditalic.woff
rename to docs/_static/fonts/Lato/lato-bolditalic.woff
diff --git a/build/html/_static/fonts/Lato/lato-bolditalic.woff2 b/docs/_static/fonts/Lato/lato-bolditalic.woff2
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-bolditalic.woff2
rename to docs/_static/fonts/Lato/lato-bolditalic.woff2
diff --git a/build/html/_static/fonts/Lato/lato-italic.eot b/docs/_static/fonts/Lato/lato-italic.eot
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-italic.eot
rename to docs/_static/fonts/Lato/lato-italic.eot
diff --git a/build/html/_static/fonts/Lato/lato-italic.ttf b/docs/_static/fonts/Lato/lato-italic.ttf
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-italic.ttf
rename to docs/_static/fonts/Lato/lato-italic.ttf
diff --git a/build/html/_static/fonts/Lato/lato-italic.woff b/docs/_static/fonts/Lato/lato-italic.woff
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-italic.woff
rename to docs/_static/fonts/Lato/lato-italic.woff
diff --git a/build/html/_static/fonts/Lato/lato-italic.woff2 b/docs/_static/fonts/Lato/lato-italic.woff2
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-italic.woff2
rename to docs/_static/fonts/Lato/lato-italic.woff2
diff --git a/build/html/_static/fonts/Lato/lato-regular.eot b/docs/_static/fonts/Lato/lato-regular.eot
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-regular.eot
rename to docs/_static/fonts/Lato/lato-regular.eot
diff --git a/build/html/_static/fonts/Lato/lato-regular.ttf b/docs/_static/fonts/Lato/lato-regular.ttf
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-regular.ttf
rename to docs/_static/fonts/Lato/lato-regular.ttf
diff --git a/build/html/_static/fonts/Lato/lato-regular.woff b/docs/_static/fonts/Lato/lato-regular.woff
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-regular.woff
rename to docs/_static/fonts/Lato/lato-regular.woff
diff --git a/build/html/_static/fonts/Lato/lato-regular.woff2 b/docs/_static/fonts/Lato/lato-regular.woff2
similarity index 100%
rename from build/html/_static/fonts/Lato/lato-regular.woff2
rename to docs/_static/fonts/Lato/lato-regular.woff2
diff --git a/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot
similarity index 100%
rename from build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot
rename to docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot
diff --git a/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf
similarity index 100%
rename from build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf
rename to docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf
diff --git a/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff
similarity index 100%
rename from build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff
rename to docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff
diff --git a/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2
similarity index 100%
rename from build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2
rename to docs/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2
diff --git a/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot
similarity index 100%
rename from build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot
rename to docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot
diff --git a/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf
similarity index 100%
rename from build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf
rename to docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf
diff --git a/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff
similarity index 100%
rename from build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff
rename to docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff
diff --git a/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 b/docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2
similarity index 100%
rename from build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2
rename to docs/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2
diff --git a/docs/_static/french-stemmer.js b/docs/_static/french-stemmer.js
new file mode 100644
index 0000000..c970831
--- /dev/null
+++ b/docs/_static/french-stemmer.js
@@ -0,0 +1,1325 @@
+// Generated by Snowball 2.1.0 - https://snowballstem.org/
+
+/**@constructor*/
+FrenchStemmer = function() {
+ var base = new BaseStemmer();
+ /** @const */ var a_0 = [
+ ["col", -1, -1],
+ ["par", -1, -1],
+ ["tap", -1, -1]
+ ];
+
+ /** @const */ var a_1 = [
+ ["", -1, 7],
+ ["H", 0, 6],
+ ["He", 1, 4],
+ ["Hi", 1, 5],
+ ["I", 0, 1],
+ ["U", 0, 2],
+ ["Y", 0, 3]
+ ];
+
+ /** @const */ var a_2 = [
+ ["iqU", -1, 3],
+ ["abl", -1, 3],
+ ["I\u00E8r", -1, 4],
+ ["i\u00E8r", -1, 4],
+ ["eus", -1, 2],
+ ["iv", -1, 1]
+ ];
+
+ /** @const */ var a_3 = [
+ ["ic", -1, 2],
+ ["abil", -1, 1],
+ ["iv", -1, 3]
+ ];
+
+ /** @const */ var a_4 = [
+ ["iqUe", -1, 1],
+ ["atrice", -1, 2],
+ ["ance", -1, 1],
+ ["ence", -1, 5],
+ ["logie", -1, 3],
+ ["able", -1, 1],
+ ["isme", -1, 1],
+ ["euse", -1, 11],
+ ["iste", -1, 1],
+ ["ive", -1, 8],
+ ["if", -1, 8],
+ ["usion", -1, 4],
+ ["ation", -1, 2],
+ ["ution", -1, 4],
+ ["ateur", -1, 2],
+ ["iqUes", -1, 1],
+ ["atrices", -1, 2],
+ ["ances", -1, 1],
+ ["ences", -1, 5],
+ ["logies", -1, 3],
+ ["ables", -1, 1],
+ ["ismes", -1, 1],
+ ["euses", -1, 11],
+ ["istes", -1, 1],
+ ["ives", -1, 8],
+ ["ifs", -1, 8],
+ ["usions", -1, 4],
+ ["ations", -1, 2],
+ ["utions", -1, 4],
+ ["ateurs", -1, 2],
+ ["ments", -1, 15],
+ ["ements", 30, 6],
+ ["issements", 31, 12],
+ ["it\u00E9s", -1, 7],
+ ["ment", -1, 15],
+ ["ement", 34, 6],
+ ["issement", 35, 12],
+ ["amment", 34, 13],
+ ["emment", 34, 14],
+ ["aux", -1, 10],
+ ["eaux", 39, 9],
+ ["eux", -1, 1],
+ ["it\u00E9", -1, 7]
+ ];
+
+ /** @const */ var a_5 = [
+ ["ira", -1, 1],
+ ["ie", -1, 1],
+ ["isse", -1, 1],
+ ["issante", -1, 1],
+ ["i", -1, 1],
+ ["irai", 4, 1],
+ ["ir", -1, 1],
+ ["iras", -1, 1],
+ ["ies", -1, 1],
+ ["\u00EEmes", -1, 1],
+ ["isses", -1, 1],
+ ["issantes", -1, 1],
+ ["\u00EEtes", -1, 1],
+ ["is", -1, 1],
+ ["irais", 13, 1],
+ ["issais", 13, 1],
+ ["irions", -1, 1],
+ ["issions", -1, 1],
+ ["irons", -1, 1],
+ ["issons", -1, 1],
+ ["issants", -1, 1],
+ ["it", -1, 1],
+ ["irait", 21, 1],
+ ["issait", 21, 1],
+ ["issant", -1, 1],
+ ["iraIent", -1, 1],
+ ["issaIent", -1, 1],
+ ["irent", -1, 1],
+ ["issent", -1, 1],
+ ["iront", -1, 1],
+ ["\u00EEt", -1, 1],
+ ["iriez", -1, 1],
+ ["issiez", -1, 1],
+ ["irez", -1, 1],
+ ["issez", -1, 1]
+ ];
+
+ /** @const */ var a_6 = [
+ ["a", -1, 3],
+ ["era", 0, 2],
+ ["asse", -1, 3],
+ ["ante", -1, 3],
+ ["\u00E9e", -1, 2],
+ ["ai", -1, 3],
+ ["erai", 5, 2],
+ ["er", -1, 2],
+ ["as", -1, 3],
+ ["eras", 8, 2],
+ ["\u00E2mes", -1, 3],
+ ["asses", -1, 3],
+ ["antes", -1, 3],
+ ["\u00E2tes", -1, 3],
+ ["\u00E9es", -1, 2],
+ ["ais", -1, 3],
+ ["erais", 15, 2],
+ ["ions", -1, 1],
+ ["erions", 17, 2],
+ ["assions", 17, 3],
+ ["erons", -1, 2],
+ ["ants", -1, 3],
+ ["\u00E9s", -1, 2],
+ ["ait", -1, 3],
+ ["erait", 23, 2],
+ ["ant", -1, 3],
+ ["aIent", -1, 3],
+ ["eraIent", 26, 2],
+ ["\u00E8rent", -1, 2],
+ ["assent", -1, 3],
+ ["eront", -1, 2],
+ ["\u00E2t", -1, 3],
+ ["ez", -1, 2],
+ ["iez", 32, 2],
+ ["eriez", 33, 2],
+ ["assiez", 33, 3],
+ ["erez", 32, 2],
+ ["\u00E9", -1, 2]
+ ];
+
+ /** @const */ var a_7 = [
+ ["e", -1, 3],
+ ["I\u00E8re", 0, 2],
+ ["i\u00E8re", 0, 2],
+ ["ion", -1, 1],
+ ["Ier", -1, 2],
+ ["ier", -1, 2]
+ ];
+
+ /** @const */ var a_8 = [
+ ["ell", -1, -1],
+ ["eill", -1, -1],
+ ["enn", -1, -1],
+ ["onn", -1, -1],
+ ["ett", -1, -1]
+ ];
+
+ /** @const */ var /** Array */ g_v = [17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 130, 103, 8, 5];
+
+ /** @const */ var /** Array */ g_keep_with_s = [1, 65, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128];
+
+ var /** number */ I_p2 = 0;
+ var /** number */ I_p1 = 0;
+ var /** number */ I_pV = 0;
+
+
+ /** @return {boolean} */
+ function r_prelude() {
+ while(true)
+ {
+ var /** number */ v_1 = base.cursor;
+ lab0: {
+ golab1: while(true)
+ {
+ var /** number */ v_2 = base.cursor;
+ lab2: {
+ lab3: {
+ var /** number */ v_3 = base.cursor;
+ lab4: {
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab4;
+ }
+ base.bra = base.cursor;
+ lab5: {
+ var /** number */ v_4 = base.cursor;
+ lab6: {
+ if (!(base.eq_s("u")))
+ {
+ break lab6;
+ }
+ base.ket = base.cursor;
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab6;
+ }
+ if (!base.slice_from("U"))
+ {
+ return false;
+ }
+ break lab5;
+ }
+ base.cursor = v_4;
+ lab7: {
+ if (!(base.eq_s("i")))
+ {
+ break lab7;
+ }
+ base.ket = base.cursor;
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab7;
+ }
+ if (!base.slice_from("I"))
+ {
+ return false;
+ }
+ break lab5;
+ }
+ base.cursor = v_4;
+ if (!(base.eq_s("y")))
+ {
+ break lab4;
+ }
+ base.ket = base.cursor;
+ if (!base.slice_from("Y"))
+ {
+ return false;
+ }
+ }
+ break lab3;
+ }
+ base.cursor = v_3;
+ lab8: {
+ base.bra = base.cursor;
+ if (!(base.eq_s("\u00EB")))
+ {
+ break lab8;
+ }
+ base.ket = base.cursor;
+ if (!base.slice_from("He"))
+ {
+ return false;
+ }
+ break lab3;
+ }
+ base.cursor = v_3;
+ lab9: {
+ base.bra = base.cursor;
+ if (!(base.eq_s("\u00EF")))
+ {
+ break lab9;
+ }
+ base.ket = base.cursor;
+ if (!base.slice_from("Hi"))
+ {
+ return false;
+ }
+ break lab3;
+ }
+ base.cursor = v_3;
+ lab10: {
+ base.bra = base.cursor;
+ if (!(base.eq_s("y")))
+ {
+ break lab10;
+ }
+ base.ket = base.cursor;
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab10;
+ }
+ if (!base.slice_from("Y"))
+ {
+ return false;
+ }
+ break lab3;
+ }
+ base.cursor = v_3;
+ if (!(base.eq_s("q")))
+ {
+ break lab2;
+ }
+ base.bra = base.cursor;
+ if (!(base.eq_s("u")))
+ {
+ break lab2;
+ }
+ base.ket = base.cursor;
+ if (!base.slice_from("U"))
+ {
+ return false;
+ }
+ }
+ base.cursor = v_2;
+ break golab1;
+ }
+ base.cursor = v_2;
+ if (base.cursor >= base.limit)
+ {
+ break lab0;
+ }
+ base.cursor++;
+ }
+ continue;
+ }
+ base.cursor = v_1;
+ break;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_mark_regions() {
+ I_pV = base.limit;
+ I_p1 = base.limit;
+ I_p2 = base.limit;
+ var /** number */ v_1 = base.cursor;
+ lab0: {
+ lab1: {
+ var /** number */ v_2 = base.cursor;
+ lab2: {
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab2;
+ }
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab2;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab2;
+ }
+ base.cursor++;
+ break lab1;
+ }
+ base.cursor = v_2;
+ lab3: {
+ if (base.find_among(a_0) == 0)
+ {
+ break lab3;
+ }
+ break lab1;
+ }
+ base.cursor = v_2;
+ if (base.cursor >= base.limit)
+ {
+ break lab0;
+ }
+ base.cursor++;
+ golab4: while(true)
+ {
+ lab5: {
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab5;
+ }
+ break golab4;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab0;
+ }
+ base.cursor++;
+ }
+ }
+ I_pV = base.cursor;
+ }
+ base.cursor = v_1;
+ var /** number */ v_4 = base.cursor;
+ lab6: {
+ golab7: while(true)
+ {
+ lab8: {
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab8;
+ }
+ break golab7;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab6;
+ }
+ base.cursor++;
+ }
+ golab9: while(true)
+ {
+ lab10: {
+ if (!(base.out_grouping(g_v, 97, 251)))
+ {
+ break lab10;
+ }
+ break golab9;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab6;
+ }
+ base.cursor++;
+ }
+ I_p1 = base.cursor;
+ golab11: while(true)
+ {
+ lab12: {
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab12;
+ }
+ break golab11;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab6;
+ }
+ base.cursor++;
+ }
+ golab13: while(true)
+ {
+ lab14: {
+ if (!(base.out_grouping(g_v, 97, 251)))
+ {
+ break lab14;
+ }
+ break golab13;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab6;
+ }
+ base.cursor++;
+ }
+ I_p2 = base.cursor;
+ }
+ base.cursor = v_4;
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_postlude() {
+ var /** number */ among_var;
+ while(true)
+ {
+ var /** number */ v_1 = base.cursor;
+ lab0: {
+ base.bra = base.cursor;
+ among_var = base.find_among(a_1);
+ if (among_var == 0)
+ {
+ break lab0;
+ }
+ base.ket = base.cursor;
+ switch (among_var) {
+ case 1:
+ if (!base.slice_from("i"))
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!base.slice_from("u"))
+ {
+ return false;
+ }
+ break;
+ case 3:
+ if (!base.slice_from("y"))
+ {
+ return false;
+ }
+ break;
+ case 4:
+ if (!base.slice_from("\u00EB"))
+ {
+ return false;
+ }
+ break;
+ case 5:
+ if (!base.slice_from("\u00EF"))
+ {
+ return false;
+ }
+ break;
+ case 6:
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 7:
+ if (base.cursor >= base.limit)
+ {
+ break lab0;
+ }
+ base.cursor++;
+ break;
+ }
+ continue;
+ }
+ base.cursor = v_1;
+ break;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_RV() {
+ if (!(I_pV <= base.cursor))
+ {
+ return false;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_R1() {
+ if (!(I_p1 <= base.cursor))
+ {
+ return false;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_R2() {
+ if (!(I_p2 <= base.cursor))
+ {
+ return false;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_standard_suffix() {
+ var /** number */ among_var;
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_4);
+ if (among_var == 0)
+ {
+ return false;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ var /** number */ v_1 = base.limit - base.cursor;
+ lab0: {
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("ic")))
+ {
+ base.cursor = base.limit - v_1;
+ break lab0;
+ }
+ base.bra = base.cursor;
+ lab1: {
+ var /** number */ v_2 = base.limit - base.cursor;
+ lab2: {
+ if (!r_R2())
+ {
+ break lab2;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab1;
+ }
+ base.cursor = base.limit - v_2;
+ if (!base.slice_from("iqU"))
+ {
+ return false;
+ }
+ }
+ }
+ break;
+ case 3:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_from("log"))
+ {
+ return false;
+ }
+ break;
+ case 4:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_from("u"))
+ {
+ return false;
+ }
+ break;
+ case 5:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_from("ent"))
+ {
+ return false;
+ }
+ break;
+ case 6:
+ if (!r_RV())
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab3: {
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_2);
+ if (among_var == 0)
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ if (!r_R2())
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("at")))
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ base.bra = base.cursor;
+ if (!r_R2())
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 2:
+ lab4: {
+ var /** number */ v_4 = base.limit - base.cursor;
+ lab5: {
+ if (!r_R2())
+ {
+ break lab5;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab4;
+ }
+ base.cursor = base.limit - v_4;
+ if (!r_R1())
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ if (!base.slice_from("eux"))
+ {
+ return false;
+ }
+ }
+ break;
+ case 3:
+ if (!r_R2())
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 4:
+ if (!r_RV())
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ if (!base.slice_from("i"))
+ {
+ return false;
+ }
+ break;
+ }
+ }
+ break;
+ case 7:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ var /** number */ v_5 = base.limit - base.cursor;
+ lab6: {
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_3);
+ if (among_var == 0)
+ {
+ base.cursor = base.limit - v_5;
+ break lab6;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ lab7: {
+ var /** number */ v_6 = base.limit - base.cursor;
+ lab8: {
+ if (!r_R2())
+ {
+ break lab8;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab7;
+ }
+ base.cursor = base.limit - v_6;
+ if (!base.slice_from("abl"))
+ {
+ return false;
+ }
+ }
+ break;
+ case 2:
+ lab9: {
+ var /** number */ v_7 = base.limit - base.cursor;
+ lab10: {
+ if (!r_R2())
+ {
+ break lab10;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab9;
+ }
+ base.cursor = base.limit - v_7;
+ if (!base.slice_from("iqU"))
+ {
+ return false;
+ }
+ }
+ break;
+ case 3:
+ if (!r_R2())
+ {
+ base.cursor = base.limit - v_5;
+ break lab6;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ }
+ }
+ break;
+ case 8:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ var /** number */ v_8 = base.limit - base.cursor;
+ lab11: {
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("at")))
+ {
+ base.cursor = base.limit - v_8;
+ break lab11;
+ }
+ base.bra = base.cursor;
+ if (!r_R2())
+ {
+ base.cursor = base.limit - v_8;
+ break lab11;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("ic")))
+ {
+ base.cursor = base.limit - v_8;
+ break lab11;
+ }
+ base.bra = base.cursor;
+ lab12: {
+ var /** number */ v_9 = base.limit - base.cursor;
+ lab13: {
+ if (!r_R2())
+ {
+ break lab13;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab12;
+ }
+ base.cursor = base.limit - v_9;
+ if (!base.slice_from("iqU"))
+ {
+ return false;
+ }
+ }
+ }
+ break;
+ case 9:
+ if (!base.slice_from("eau"))
+ {
+ return false;
+ }
+ break;
+ case 10:
+ if (!r_R1())
+ {
+ return false;
+ }
+ if (!base.slice_from("al"))
+ {
+ return false;
+ }
+ break;
+ case 11:
+ lab14: {
+ var /** number */ v_10 = base.limit - base.cursor;
+ lab15: {
+ if (!r_R2())
+ {
+ break lab15;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab14;
+ }
+ base.cursor = base.limit - v_10;
+ if (!r_R1())
+ {
+ return false;
+ }
+ if (!base.slice_from("eux"))
+ {
+ return false;
+ }
+ }
+ break;
+ case 12:
+ if (!r_R1())
+ {
+ return false;
+ }
+ if (!(base.out_grouping_b(g_v, 97, 251)))
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 13:
+ if (!r_RV())
+ {
+ return false;
+ }
+ if (!base.slice_from("ant"))
+ {
+ return false;
+ }
+ return false;
+ case 14:
+ if (!r_RV())
+ {
+ return false;
+ }
+ if (!base.slice_from("ent"))
+ {
+ return false;
+ }
+ return false;
+ case 15:
+ var /** number */ v_11 = base.limit - base.cursor;
+ if (!(base.in_grouping_b(g_v, 97, 251)))
+ {
+ return false;
+ }
+ if (!r_RV())
+ {
+ return false;
+ }
+ base.cursor = base.limit - v_11;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ return false;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_i_verb_suffix() {
+ if (base.cursor < I_pV)
+ {
+ return false;
+ }
+ var /** number */ v_2 = base.limit_backward;
+ base.limit_backward = I_pV;
+ base.ket = base.cursor;
+ if (base.find_among_b(a_5) == 0)
+ {
+ base.limit_backward = v_2;
+ return false;
+ }
+ base.bra = base.cursor;
+ {
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab0: {
+ if (!(base.eq_s_b("H")))
+ {
+ break lab0;
+ }
+ base.limit_backward = v_2;
+ return false;
+ }
+ base.cursor = base.limit - v_3;
+ }
+ if (!(base.out_grouping_b(g_v, 97, 251)))
+ {
+ base.limit_backward = v_2;
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ base.limit_backward = v_2;
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_verb_suffix() {
+ var /** number */ among_var;
+ if (base.cursor < I_pV)
+ {
+ return false;
+ }
+ var /** number */ v_2 = base.limit_backward;
+ base.limit_backward = I_pV;
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_6);
+ if (among_var == 0)
+ {
+ base.limit_backward = v_2;
+ return false;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ if (!r_R2())
+ {
+ base.limit_backward = v_2;
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 3:
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab0: {
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("e")))
+ {
+ base.cursor = base.limit - v_3;
+ break lab0;
+ }
+ base.bra = base.cursor;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ }
+ break;
+ }
+ base.limit_backward = v_2;
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_residual_suffix() {
+ var /** number */ among_var;
+ var /** number */ v_1 = base.limit - base.cursor;
+ lab0: {
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("s")))
+ {
+ base.cursor = base.limit - v_1;
+ break lab0;
+ }
+ base.bra = base.cursor;
+ var /** number */ v_2 = base.limit - base.cursor;
+ lab1: {
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab2: {
+ if (!(base.eq_s_b("Hi")))
+ {
+ break lab2;
+ }
+ break lab1;
+ }
+ base.cursor = base.limit - v_3;
+ if (!(base.out_grouping_b(g_keep_with_s, 97, 232)))
+ {
+ base.cursor = base.limit - v_1;
+ break lab0;
+ }
+ }
+ base.cursor = base.limit - v_2;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ }
+ if (base.cursor < I_pV)
+ {
+ return false;
+ }
+ var /** number */ v_5 = base.limit_backward;
+ base.limit_backward = I_pV;
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_7);
+ if (among_var == 0)
+ {
+ base.limit_backward = v_5;
+ return false;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ if (!r_R2())
+ {
+ base.limit_backward = v_5;
+ return false;
+ }
+ lab3: {
+ var /** number */ v_6 = base.limit - base.cursor;
+ lab4: {
+ if (!(base.eq_s_b("s")))
+ {
+ break lab4;
+ }
+ break lab3;
+ }
+ base.cursor = base.limit - v_6;
+ if (!(base.eq_s_b("t")))
+ {
+ base.limit_backward = v_5;
+ return false;
+ }
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!base.slice_from("i"))
+ {
+ return false;
+ }
+ break;
+ case 3:
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ }
+ base.limit_backward = v_5;
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_un_double() {
+ var /** number */ v_1 = base.limit - base.cursor;
+ if (base.find_among_b(a_8) == 0)
+ {
+ return false;
+ }
+ base.cursor = base.limit - v_1;
+ base.ket = base.cursor;
+ if (base.cursor <= base.limit_backward)
+ {
+ return false;
+ }
+ base.cursor--;
+ base.bra = base.cursor;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_un_accent() {
+ {
+ var v_1 = 1;
+ while(true)
+ {
+ lab0: {
+ if (!(base.out_grouping_b(g_v, 97, 251)))
+ {
+ break lab0;
+ }
+ v_1--;
+ continue;
+ }
+ break;
+ }
+ if (v_1 > 0)
+ {
+ return false;
+ }
+ }
+ base.ket = base.cursor;
+ lab1: {
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab2: {
+ if (!(base.eq_s_b("\u00E9")))
+ {
+ break lab2;
+ }
+ break lab1;
+ }
+ base.cursor = base.limit - v_3;
+ if (!(base.eq_s_b("\u00E8")))
+ {
+ return false;
+ }
+ }
+ base.bra = base.cursor;
+ if (!base.slice_from("e"))
+ {
+ return false;
+ }
+ return true;
+ };
+
+ this.stem = /** @return {boolean} */ function() {
+ var /** number */ v_1 = base.cursor;
+ r_prelude();
+ base.cursor = v_1;
+ r_mark_regions();
+ base.limit_backward = base.cursor; base.cursor = base.limit;
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab0: {
+ lab1: {
+ var /** number */ v_4 = base.limit - base.cursor;
+ lab2: {
+ var /** number */ v_5 = base.limit - base.cursor;
+ lab3: {
+ var /** number */ v_6 = base.limit - base.cursor;
+ lab4: {
+ if (!r_standard_suffix())
+ {
+ break lab4;
+ }
+ break lab3;
+ }
+ base.cursor = base.limit - v_6;
+ lab5: {
+ if (!r_i_verb_suffix())
+ {
+ break lab5;
+ }
+ break lab3;
+ }
+ base.cursor = base.limit - v_6;
+ if (!r_verb_suffix())
+ {
+ break lab2;
+ }
+ }
+ base.cursor = base.limit - v_5;
+ var /** number */ v_7 = base.limit - base.cursor;
+ lab6: {
+ base.ket = base.cursor;
+ lab7: {
+ var /** number */ v_8 = base.limit - base.cursor;
+ lab8: {
+ if (!(base.eq_s_b("Y")))
+ {
+ break lab8;
+ }
+ base.bra = base.cursor;
+ if (!base.slice_from("i"))
+ {
+ return false;
+ }
+ break lab7;
+ }
+ base.cursor = base.limit - v_8;
+ if (!(base.eq_s_b("\u00E7")))
+ {
+ base.cursor = base.limit - v_7;
+ break lab6;
+ }
+ base.bra = base.cursor;
+ if (!base.slice_from("c"))
+ {
+ return false;
+ }
+ }
+ }
+ break lab1;
+ }
+ base.cursor = base.limit - v_4;
+ if (!r_residual_suffix())
+ {
+ break lab0;
+ }
+ }
+ }
+ base.cursor = base.limit - v_3;
+ var /** number */ v_9 = base.limit - base.cursor;
+ r_un_double();
+ base.cursor = base.limit - v_9;
+ var /** number */ v_10 = base.limit - base.cursor;
+ r_un_accent();
+ base.cursor = base.limit - v_10;
+ base.cursor = base.limit_backward;
+ var /** number */ v_11 = base.cursor;
+ r_postlude();
+ base.cursor = v_11;
+ return true;
+ };
+
+ /**@return{string}*/
+ this['stemWord'] = function(/**string*/word) {
+ base.setCurrent(word);
+ this.stem();
+ return base.getCurrent();
+ };
+};
diff --git a/build/html/_static/jquery.js b/docs/_static/jquery.js
similarity index 100%
rename from build/html/_static/jquery.js
rename to docs/_static/jquery.js
diff --git a/build/html/_static/js/badge_only.js b/docs/_static/js/badge_only.js
similarity index 100%
rename from build/html/_static/js/badge_only.js
rename to docs/_static/js/badge_only.js
diff --git a/build/html/_static/js/theme.js b/docs/_static/js/theme.js
similarity index 100%
rename from build/html/_static/js/theme.js
rename to docs/_static/js/theme.js
diff --git a/build/html/_static/js/versions.js b/docs/_static/js/versions.js
similarity index 100%
rename from build/html/_static/js/versions.js
rename to docs/_static/js/versions.js
diff --git a/docs/_static/language_data.js b/docs/_static/language_data.js
new file mode 100644
index 0000000..bbf9c2d
--- /dev/null
+++ b/docs/_static/language_data.js
@@ -0,0 +1,1631 @@
+/*
+ * This script contains the language-specific data used by searchtools.js,
+ * namely the list of stopwords, stemmer, scorer and splitter.
+ */
+
+var stopwords = ["ai", "aie", "aient", "aies", "ait", "as", "au", "aura", "aurai", "auraient", "aurais", "aurait", "auras", "aurez", "auriez", "aurions", "aurons", "auront", "aux", "avaient", "avais", "avait", "avec", "avez", "aviez", "avions", "avons", "ayant", "ayez", "ayons", "c", "ce", "ceci", "cela", "cel\u00e0", "ces", "cet", "cette", "d", "dans", "de", "des", "du", "elle", "en", "es", "est", "et", "eu", "eue", "eues", "eurent", "eus", "eusse", "eussent", "eusses", "eussiez", "eussions", "eut", "eux", "e\u00fbmes", "e\u00fbt", "e\u00fbtes", "furent", "fus", "fusse", "fussent", "fusses", "fussiez", "fussions", "fut", "f\u00fbmes", "f\u00fbt", "f\u00fbtes", "ici", "il", "ils", "j", "je", "l", "la", "le", "les", "leur", "leurs", "lui", "m", "ma", "mais", "me", "mes", "moi", "mon", "m\u00eame", "n", "ne", "nos", "notre", "nous", "on", "ont", "ou", "par", "pas", "pour", "qu", "que", "quel", "quelle", "quelles", "quels", "qui", "s", "sa", "sans", "se", "sera", "serai", "seraient", "serais", "serait", "seras", "serez", "seriez", "serions", "serons", "seront", "ses", "soi", "soient", "sois", "soit", "sommes", "son", "sont", "soyez", "soyons", "suis", "sur", "t", "ta", "te", "tes", "toi", "ton", "tu", "un", "une", "vos", "votre", "vous", "y", "\u00e0", "\u00e9taient", "\u00e9tais", "\u00e9tait", "\u00e9tant", "\u00e9tiez", "\u00e9tions", "\u00e9t\u00e9", "\u00e9t\u00e9e", "\u00e9t\u00e9es", "\u00e9t\u00e9s", "\u00eates"];
+
+
+/* Non-minified version is copied as a separate JS file, if available */
+/**@constructor*/
+BaseStemmer = function() {
+ this.setCurrent = function(value) {
+ this.current = value;
+ this.cursor = 0;
+ this.limit = this.current.length;
+ this.limit_backward = 0;
+ this.bra = this.cursor;
+ this.ket = this.limit;
+ };
+
+ this.getCurrent = function() {
+ return this.current;
+ };
+
+ this.copy_from = function(other) {
+ this.current = other.current;
+ this.cursor = other.cursor;
+ this.limit = other.limit;
+ this.limit_backward = other.limit_backward;
+ this.bra = other.bra;
+ this.ket = other.ket;
+ };
+
+ this.in_grouping = function(s, min, max) {
+ if (this.cursor >= this.limit) return false;
+ var ch = this.current.charCodeAt(this.cursor);
+ if (ch > max || ch < min) return false;
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
+ this.cursor++;
+ return true;
+ };
+
+ this.in_grouping_b = function(s, min, max) {
+ if (this.cursor <= this.limit_backward) return false;
+ var ch = this.current.charCodeAt(this.cursor - 1);
+ if (ch > max || ch < min) return false;
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
+ this.cursor--;
+ return true;
+ };
+
+ this.out_grouping = function(s, min, max) {
+ if (this.cursor >= this.limit) return false;
+ var ch = this.current.charCodeAt(this.cursor);
+ if (ch > max || ch < min) {
+ this.cursor++;
+ return true;
+ }
+ ch -= min;
+ if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) == 0) {
+ this.cursor++;
+ return true;
+ }
+ return false;
+ };
+
+ this.out_grouping_b = function(s, min, max) {
+ if (this.cursor <= this.limit_backward) return false;
+ var ch = this.current.charCodeAt(this.cursor - 1);
+ if (ch > max || ch < min) {
+ this.cursor--;
+ return true;
+ }
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) {
+ this.cursor--;
+ return true;
+ }
+ return false;
+ };
+
+ this.eq_s = function(s)
+ {
+ if (this.limit - this.cursor < s.length) return false;
+ if (this.current.slice(this.cursor, this.cursor + s.length) != s)
+ {
+ return false;
+ }
+ this.cursor += s.length;
+ return true;
+ };
+
+ this.eq_s_b = function(s)
+ {
+ if (this.cursor - this.limit_backward < s.length) return false;
+ if (this.current.slice(this.cursor - s.length, this.cursor) != s)
+ {
+ return false;
+ }
+ this.cursor -= s.length;
+ return true;
+ };
+
+ /** @return {number} */ this.find_among = function(v)
+ {
+ var i = 0;
+ var j = v.length;
+
+ var c = this.cursor;
+ var l = this.limit;
+
+ var common_i = 0;
+ var common_j = 0;
+
+ var first_key_inspected = false;
+
+ while (true)
+ {
+ var k = i + ((j - i) >>> 1);
+ var diff = 0;
+ var common = common_i < common_j ? common_i : common_j; // smaller
+ // w[0]: string, w[1]: substring_i, w[2]: result, w[3]: function (optional)
+ var w = v[k];
+ var i2;
+ for (i2 = common; i2 < w[0].length; i2++)
+ {
+ if (c + common == l)
+ {
+ diff = -1;
+ break;
+ }
+ diff = this.current.charCodeAt(c + common) - w[0].charCodeAt(i2);
+ if (diff != 0) break;
+ common++;
+ }
+ if (diff < 0)
+ {
+ j = k;
+ common_j = common;
+ }
+ else
+ {
+ i = k;
+ common_i = common;
+ }
+ if (j - i <= 1)
+ {
+ if (i > 0) break; // v->s has been inspected
+ if (j == i) break; // only one item in v
+
+ // - but now we need to go round once more to get
+ // v->s inspected. This looks messy, but is actually
+ // the optimal approach.
+
+ if (first_key_inspected) break;
+ first_key_inspected = true;
+ }
+ }
+ do {
+ var w = v[i];
+ if (common_i >= w[0].length)
+ {
+ this.cursor = c + w[0].length;
+ if (w.length < 4) return w[2];
+ var res = w[3](this);
+ this.cursor = c + w[0].length;
+ if (res) return w[2];
+ }
+ i = w[1];
+ } while (i >= 0);
+ return 0;
+ };
+
+ // find_among_b is for backwards processing. Same comments apply
+ this.find_among_b = function(v)
+ {
+ var i = 0;
+ var j = v.length
+
+ var c = this.cursor;
+ var lb = this.limit_backward;
+
+ var common_i = 0;
+ var common_j = 0;
+
+ var first_key_inspected = false;
+
+ while (true)
+ {
+ var k = i + ((j - i) >> 1);
+ var diff = 0;
+ var common = common_i < common_j ? common_i : common_j;
+ var w = v[k];
+ var i2;
+ for (i2 = w[0].length - 1 - common; i2 >= 0; i2--)
+ {
+ if (c - common == lb)
+ {
+ diff = -1;
+ break;
+ }
+ diff = this.current.charCodeAt(c - 1 - common) - w[0].charCodeAt(i2);
+ if (diff != 0) break;
+ common++;
+ }
+ if (diff < 0)
+ {
+ j = k;
+ common_j = common;
+ }
+ else
+ {
+ i = k;
+ common_i = common;
+ }
+ if (j - i <= 1)
+ {
+ if (i > 0) break;
+ if (j == i) break;
+ if (first_key_inspected) break;
+ first_key_inspected = true;
+ }
+ }
+ do {
+ var w = v[i];
+ if (common_i >= w[0].length)
+ {
+ this.cursor = c - w[0].length;
+ if (w.length < 4) return w[2];
+ var res = w[3](this);
+ this.cursor = c - w[0].length;
+ if (res) return w[2];
+ }
+ i = w[1];
+ } while (i >= 0);
+ return 0;
+ };
+
+ /* to replace chars between c_bra and c_ket in this.current by the
+ * chars in s.
+ */
+ this.replace_s = function(c_bra, c_ket, s)
+ {
+ var adjustment = s.length - (c_ket - c_bra);
+ this.current = this.current.slice(0, c_bra) + s + this.current.slice(c_ket);
+ this.limit += adjustment;
+ if (this.cursor >= c_ket) this.cursor += adjustment;
+ else if (this.cursor > c_bra) this.cursor = c_bra;
+ return adjustment;
+ };
+
+ this.slice_check = function()
+ {
+ if (this.bra < 0 ||
+ this.bra > this.ket ||
+ this.ket > this.limit ||
+ this.limit > this.current.length)
+ {
+ return false;
+ }
+ return true;
+ };
+
+ this.slice_from = function(s)
+ {
+ var result = false;
+ if (this.slice_check())
+ {
+ this.replace_s(this.bra, this.ket, s);
+ result = true;
+ }
+ return result;
+ };
+
+ this.slice_del = function()
+ {
+ return this.slice_from("");
+ };
+
+ this.insert = function(c_bra, c_ket, s)
+ {
+ var adjustment = this.replace_s(c_bra, c_ket, s);
+ if (c_bra <= this.bra) this.bra += adjustment;
+ if (c_bra <= this.ket) this.ket += adjustment;
+ };
+
+ this.slice_to = function()
+ {
+ var result = '';
+ if (this.slice_check())
+ {
+ result = this.current.slice(this.bra, this.ket);
+ }
+ return result;
+ };
+
+ this.assign_to = function()
+ {
+ return this.current.slice(0, this.limit);
+ };
+};
+
+// Generated by Snowball 2.1.0 - https://snowballstem.org/
+
+/**@constructor*/
+FrenchStemmer = function() {
+ var base = new BaseStemmer();
+ /** @const */ var a_0 = [
+ ["col", -1, -1],
+ ["par", -1, -1],
+ ["tap", -1, -1]
+ ];
+
+ /** @const */ var a_1 = [
+ ["", -1, 7],
+ ["H", 0, 6],
+ ["He", 1, 4],
+ ["Hi", 1, 5],
+ ["I", 0, 1],
+ ["U", 0, 2],
+ ["Y", 0, 3]
+ ];
+
+ /** @const */ var a_2 = [
+ ["iqU", -1, 3],
+ ["abl", -1, 3],
+ ["I\u00E8r", -1, 4],
+ ["i\u00E8r", -1, 4],
+ ["eus", -1, 2],
+ ["iv", -1, 1]
+ ];
+
+ /** @const */ var a_3 = [
+ ["ic", -1, 2],
+ ["abil", -1, 1],
+ ["iv", -1, 3]
+ ];
+
+ /** @const */ var a_4 = [
+ ["iqUe", -1, 1],
+ ["atrice", -1, 2],
+ ["ance", -1, 1],
+ ["ence", -1, 5],
+ ["logie", -1, 3],
+ ["able", -1, 1],
+ ["isme", -1, 1],
+ ["euse", -1, 11],
+ ["iste", -1, 1],
+ ["ive", -1, 8],
+ ["if", -1, 8],
+ ["usion", -1, 4],
+ ["ation", -1, 2],
+ ["ution", -1, 4],
+ ["ateur", -1, 2],
+ ["iqUes", -1, 1],
+ ["atrices", -1, 2],
+ ["ances", -1, 1],
+ ["ences", -1, 5],
+ ["logies", -1, 3],
+ ["ables", -1, 1],
+ ["ismes", -1, 1],
+ ["euses", -1, 11],
+ ["istes", -1, 1],
+ ["ives", -1, 8],
+ ["ifs", -1, 8],
+ ["usions", -1, 4],
+ ["ations", -1, 2],
+ ["utions", -1, 4],
+ ["ateurs", -1, 2],
+ ["ments", -1, 15],
+ ["ements", 30, 6],
+ ["issements", 31, 12],
+ ["it\u00E9s", -1, 7],
+ ["ment", -1, 15],
+ ["ement", 34, 6],
+ ["issement", 35, 12],
+ ["amment", 34, 13],
+ ["emment", 34, 14],
+ ["aux", -1, 10],
+ ["eaux", 39, 9],
+ ["eux", -1, 1],
+ ["it\u00E9", -1, 7]
+ ];
+
+ /** @const */ var a_5 = [
+ ["ira", -1, 1],
+ ["ie", -1, 1],
+ ["isse", -1, 1],
+ ["issante", -1, 1],
+ ["i", -1, 1],
+ ["irai", 4, 1],
+ ["ir", -1, 1],
+ ["iras", -1, 1],
+ ["ies", -1, 1],
+ ["\u00EEmes", -1, 1],
+ ["isses", -1, 1],
+ ["issantes", -1, 1],
+ ["\u00EEtes", -1, 1],
+ ["is", -1, 1],
+ ["irais", 13, 1],
+ ["issais", 13, 1],
+ ["irions", -1, 1],
+ ["issions", -1, 1],
+ ["irons", -1, 1],
+ ["issons", -1, 1],
+ ["issants", -1, 1],
+ ["it", -1, 1],
+ ["irait", 21, 1],
+ ["issait", 21, 1],
+ ["issant", -1, 1],
+ ["iraIent", -1, 1],
+ ["issaIent", -1, 1],
+ ["irent", -1, 1],
+ ["issent", -1, 1],
+ ["iront", -1, 1],
+ ["\u00EEt", -1, 1],
+ ["iriez", -1, 1],
+ ["issiez", -1, 1],
+ ["irez", -1, 1],
+ ["issez", -1, 1]
+ ];
+
+ /** @const */ var a_6 = [
+ ["a", -1, 3],
+ ["era", 0, 2],
+ ["asse", -1, 3],
+ ["ante", -1, 3],
+ ["\u00E9e", -1, 2],
+ ["ai", -1, 3],
+ ["erai", 5, 2],
+ ["er", -1, 2],
+ ["as", -1, 3],
+ ["eras", 8, 2],
+ ["\u00E2mes", -1, 3],
+ ["asses", -1, 3],
+ ["antes", -1, 3],
+ ["\u00E2tes", -1, 3],
+ ["\u00E9es", -1, 2],
+ ["ais", -1, 3],
+ ["erais", 15, 2],
+ ["ions", -1, 1],
+ ["erions", 17, 2],
+ ["assions", 17, 3],
+ ["erons", -1, 2],
+ ["ants", -1, 3],
+ ["\u00E9s", -1, 2],
+ ["ait", -1, 3],
+ ["erait", 23, 2],
+ ["ant", -1, 3],
+ ["aIent", -1, 3],
+ ["eraIent", 26, 2],
+ ["\u00E8rent", -1, 2],
+ ["assent", -1, 3],
+ ["eront", -1, 2],
+ ["\u00E2t", -1, 3],
+ ["ez", -1, 2],
+ ["iez", 32, 2],
+ ["eriez", 33, 2],
+ ["assiez", 33, 3],
+ ["erez", 32, 2],
+ ["\u00E9", -1, 2]
+ ];
+
+ /** @const */ var a_7 = [
+ ["e", -1, 3],
+ ["I\u00E8re", 0, 2],
+ ["i\u00E8re", 0, 2],
+ ["ion", -1, 1],
+ ["Ier", -1, 2],
+ ["ier", -1, 2]
+ ];
+
+ /** @const */ var a_8 = [
+ ["ell", -1, -1],
+ ["eill", -1, -1],
+ ["enn", -1, -1],
+ ["onn", -1, -1],
+ ["ett", -1, -1]
+ ];
+
+ /** @const */ var /** Array */ g_v = [17, 65, 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 130, 103, 8, 5];
+
+ /** @const */ var /** Array */ g_keep_with_s = [1, 65, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128];
+
+ var /** number */ I_p2 = 0;
+ var /** number */ I_p1 = 0;
+ var /** number */ I_pV = 0;
+
+
+ /** @return {boolean} */
+ function r_prelude() {
+ while(true)
+ {
+ var /** number */ v_1 = base.cursor;
+ lab0: {
+ golab1: while(true)
+ {
+ var /** number */ v_2 = base.cursor;
+ lab2: {
+ lab3: {
+ var /** number */ v_3 = base.cursor;
+ lab4: {
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab4;
+ }
+ base.bra = base.cursor;
+ lab5: {
+ var /** number */ v_4 = base.cursor;
+ lab6: {
+ if (!(base.eq_s("u")))
+ {
+ break lab6;
+ }
+ base.ket = base.cursor;
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab6;
+ }
+ if (!base.slice_from("U"))
+ {
+ return false;
+ }
+ break lab5;
+ }
+ base.cursor = v_4;
+ lab7: {
+ if (!(base.eq_s("i")))
+ {
+ break lab7;
+ }
+ base.ket = base.cursor;
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab7;
+ }
+ if (!base.slice_from("I"))
+ {
+ return false;
+ }
+ break lab5;
+ }
+ base.cursor = v_4;
+ if (!(base.eq_s("y")))
+ {
+ break lab4;
+ }
+ base.ket = base.cursor;
+ if (!base.slice_from("Y"))
+ {
+ return false;
+ }
+ }
+ break lab3;
+ }
+ base.cursor = v_3;
+ lab8: {
+ base.bra = base.cursor;
+ if (!(base.eq_s("\u00EB")))
+ {
+ break lab8;
+ }
+ base.ket = base.cursor;
+ if (!base.slice_from("He"))
+ {
+ return false;
+ }
+ break lab3;
+ }
+ base.cursor = v_3;
+ lab9: {
+ base.bra = base.cursor;
+ if (!(base.eq_s("\u00EF")))
+ {
+ break lab9;
+ }
+ base.ket = base.cursor;
+ if (!base.slice_from("Hi"))
+ {
+ return false;
+ }
+ break lab3;
+ }
+ base.cursor = v_3;
+ lab10: {
+ base.bra = base.cursor;
+ if (!(base.eq_s("y")))
+ {
+ break lab10;
+ }
+ base.ket = base.cursor;
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab10;
+ }
+ if (!base.slice_from("Y"))
+ {
+ return false;
+ }
+ break lab3;
+ }
+ base.cursor = v_3;
+ if (!(base.eq_s("q")))
+ {
+ break lab2;
+ }
+ base.bra = base.cursor;
+ if (!(base.eq_s("u")))
+ {
+ break lab2;
+ }
+ base.ket = base.cursor;
+ if (!base.slice_from("U"))
+ {
+ return false;
+ }
+ }
+ base.cursor = v_2;
+ break golab1;
+ }
+ base.cursor = v_2;
+ if (base.cursor >= base.limit)
+ {
+ break lab0;
+ }
+ base.cursor++;
+ }
+ continue;
+ }
+ base.cursor = v_1;
+ break;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_mark_regions() {
+ I_pV = base.limit;
+ I_p1 = base.limit;
+ I_p2 = base.limit;
+ var /** number */ v_1 = base.cursor;
+ lab0: {
+ lab1: {
+ var /** number */ v_2 = base.cursor;
+ lab2: {
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab2;
+ }
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab2;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab2;
+ }
+ base.cursor++;
+ break lab1;
+ }
+ base.cursor = v_2;
+ lab3: {
+ if (base.find_among(a_0) == 0)
+ {
+ break lab3;
+ }
+ break lab1;
+ }
+ base.cursor = v_2;
+ if (base.cursor >= base.limit)
+ {
+ break lab0;
+ }
+ base.cursor++;
+ golab4: while(true)
+ {
+ lab5: {
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab5;
+ }
+ break golab4;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab0;
+ }
+ base.cursor++;
+ }
+ }
+ I_pV = base.cursor;
+ }
+ base.cursor = v_1;
+ var /** number */ v_4 = base.cursor;
+ lab6: {
+ golab7: while(true)
+ {
+ lab8: {
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab8;
+ }
+ break golab7;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab6;
+ }
+ base.cursor++;
+ }
+ golab9: while(true)
+ {
+ lab10: {
+ if (!(base.out_grouping(g_v, 97, 251)))
+ {
+ break lab10;
+ }
+ break golab9;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab6;
+ }
+ base.cursor++;
+ }
+ I_p1 = base.cursor;
+ golab11: while(true)
+ {
+ lab12: {
+ if (!(base.in_grouping(g_v, 97, 251)))
+ {
+ break lab12;
+ }
+ break golab11;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab6;
+ }
+ base.cursor++;
+ }
+ golab13: while(true)
+ {
+ lab14: {
+ if (!(base.out_grouping(g_v, 97, 251)))
+ {
+ break lab14;
+ }
+ break golab13;
+ }
+ if (base.cursor >= base.limit)
+ {
+ break lab6;
+ }
+ base.cursor++;
+ }
+ I_p2 = base.cursor;
+ }
+ base.cursor = v_4;
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_postlude() {
+ var /** number */ among_var;
+ while(true)
+ {
+ var /** number */ v_1 = base.cursor;
+ lab0: {
+ base.bra = base.cursor;
+ among_var = base.find_among(a_1);
+ if (among_var == 0)
+ {
+ break lab0;
+ }
+ base.ket = base.cursor;
+ switch (among_var) {
+ case 1:
+ if (!base.slice_from("i"))
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!base.slice_from("u"))
+ {
+ return false;
+ }
+ break;
+ case 3:
+ if (!base.slice_from("y"))
+ {
+ return false;
+ }
+ break;
+ case 4:
+ if (!base.slice_from("\u00EB"))
+ {
+ return false;
+ }
+ break;
+ case 5:
+ if (!base.slice_from("\u00EF"))
+ {
+ return false;
+ }
+ break;
+ case 6:
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 7:
+ if (base.cursor >= base.limit)
+ {
+ break lab0;
+ }
+ base.cursor++;
+ break;
+ }
+ continue;
+ }
+ base.cursor = v_1;
+ break;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_RV() {
+ if (!(I_pV <= base.cursor))
+ {
+ return false;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_R1() {
+ if (!(I_p1 <= base.cursor))
+ {
+ return false;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_R2() {
+ if (!(I_p2 <= base.cursor))
+ {
+ return false;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_standard_suffix() {
+ var /** number */ among_var;
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_4);
+ if (among_var == 0)
+ {
+ return false;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ var /** number */ v_1 = base.limit - base.cursor;
+ lab0: {
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("ic")))
+ {
+ base.cursor = base.limit - v_1;
+ break lab0;
+ }
+ base.bra = base.cursor;
+ lab1: {
+ var /** number */ v_2 = base.limit - base.cursor;
+ lab2: {
+ if (!r_R2())
+ {
+ break lab2;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab1;
+ }
+ base.cursor = base.limit - v_2;
+ if (!base.slice_from("iqU"))
+ {
+ return false;
+ }
+ }
+ }
+ break;
+ case 3:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_from("log"))
+ {
+ return false;
+ }
+ break;
+ case 4:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_from("u"))
+ {
+ return false;
+ }
+ break;
+ case 5:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_from("ent"))
+ {
+ return false;
+ }
+ break;
+ case 6:
+ if (!r_RV())
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab3: {
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_2);
+ if (among_var == 0)
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ if (!r_R2())
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("at")))
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ base.bra = base.cursor;
+ if (!r_R2())
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 2:
+ lab4: {
+ var /** number */ v_4 = base.limit - base.cursor;
+ lab5: {
+ if (!r_R2())
+ {
+ break lab5;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab4;
+ }
+ base.cursor = base.limit - v_4;
+ if (!r_R1())
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ if (!base.slice_from("eux"))
+ {
+ return false;
+ }
+ }
+ break;
+ case 3:
+ if (!r_R2())
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 4:
+ if (!r_RV())
+ {
+ base.cursor = base.limit - v_3;
+ break lab3;
+ }
+ if (!base.slice_from("i"))
+ {
+ return false;
+ }
+ break;
+ }
+ }
+ break;
+ case 7:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ var /** number */ v_5 = base.limit - base.cursor;
+ lab6: {
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_3);
+ if (among_var == 0)
+ {
+ base.cursor = base.limit - v_5;
+ break lab6;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ lab7: {
+ var /** number */ v_6 = base.limit - base.cursor;
+ lab8: {
+ if (!r_R2())
+ {
+ break lab8;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab7;
+ }
+ base.cursor = base.limit - v_6;
+ if (!base.slice_from("abl"))
+ {
+ return false;
+ }
+ }
+ break;
+ case 2:
+ lab9: {
+ var /** number */ v_7 = base.limit - base.cursor;
+ lab10: {
+ if (!r_R2())
+ {
+ break lab10;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab9;
+ }
+ base.cursor = base.limit - v_7;
+ if (!base.slice_from("iqU"))
+ {
+ return false;
+ }
+ }
+ break;
+ case 3:
+ if (!r_R2())
+ {
+ base.cursor = base.limit - v_5;
+ break lab6;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ }
+ }
+ break;
+ case 8:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ var /** number */ v_8 = base.limit - base.cursor;
+ lab11: {
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("at")))
+ {
+ base.cursor = base.limit - v_8;
+ break lab11;
+ }
+ base.bra = base.cursor;
+ if (!r_R2())
+ {
+ base.cursor = base.limit - v_8;
+ break lab11;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("ic")))
+ {
+ base.cursor = base.limit - v_8;
+ break lab11;
+ }
+ base.bra = base.cursor;
+ lab12: {
+ var /** number */ v_9 = base.limit - base.cursor;
+ lab13: {
+ if (!r_R2())
+ {
+ break lab13;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab12;
+ }
+ base.cursor = base.limit - v_9;
+ if (!base.slice_from("iqU"))
+ {
+ return false;
+ }
+ }
+ }
+ break;
+ case 9:
+ if (!base.slice_from("eau"))
+ {
+ return false;
+ }
+ break;
+ case 10:
+ if (!r_R1())
+ {
+ return false;
+ }
+ if (!base.slice_from("al"))
+ {
+ return false;
+ }
+ break;
+ case 11:
+ lab14: {
+ var /** number */ v_10 = base.limit - base.cursor;
+ lab15: {
+ if (!r_R2())
+ {
+ break lab15;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break lab14;
+ }
+ base.cursor = base.limit - v_10;
+ if (!r_R1())
+ {
+ return false;
+ }
+ if (!base.slice_from("eux"))
+ {
+ return false;
+ }
+ }
+ break;
+ case 12:
+ if (!r_R1())
+ {
+ return false;
+ }
+ if (!(base.out_grouping_b(g_v, 97, 251)))
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 13:
+ if (!r_RV())
+ {
+ return false;
+ }
+ if (!base.slice_from("ant"))
+ {
+ return false;
+ }
+ return false;
+ case 14:
+ if (!r_RV())
+ {
+ return false;
+ }
+ if (!base.slice_from("ent"))
+ {
+ return false;
+ }
+ return false;
+ case 15:
+ var /** number */ v_11 = base.limit - base.cursor;
+ if (!(base.in_grouping_b(g_v, 97, 251)))
+ {
+ return false;
+ }
+ if (!r_RV())
+ {
+ return false;
+ }
+ base.cursor = base.limit - v_11;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ return false;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_i_verb_suffix() {
+ if (base.cursor < I_pV)
+ {
+ return false;
+ }
+ var /** number */ v_2 = base.limit_backward;
+ base.limit_backward = I_pV;
+ base.ket = base.cursor;
+ if (base.find_among_b(a_5) == 0)
+ {
+ base.limit_backward = v_2;
+ return false;
+ }
+ base.bra = base.cursor;
+ {
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab0: {
+ if (!(base.eq_s_b("H")))
+ {
+ break lab0;
+ }
+ base.limit_backward = v_2;
+ return false;
+ }
+ base.cursor = base.limit - v_3;
+ }
+ if (!(base.out_grouping_b(g_v, 97, 251)))
+ {
+ base.limit_backward = v_2;
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ base.limit_backward = v_2;
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_verb_suffix() {
+ var /** number */ among_var;
+ if (base.cursor < I_pV)
+ {
+ return false;
+ }
+ var /** number */ v_2 = base.limit_backward;
+ base.limit_backward = I_pV;
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_6);
+ if (among_var == 0)
+ {
+ base.limit_backward = v_2;
+ return false;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ if (!r_R2())
+ {
+ base.limit_backward = v_2;
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 3:
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab0: {
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("e")))
+ {
+ base.cursor = base.limit - v_3;
+ break lab0;
+ }
+ base.bra = base.cursor;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ }
+ break;
+ }
+ base.limit_backward = v_2;
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_residual_suffix() {
+ var /** number */ among_var;
+ var /** number */ v_1 = base.limit - base.cursor;
+ lab0: {
+ base.ket = base.cursor;
+ if (!(base.eq_s_b("s")))
+ {
+ base.cursor = base.limit - v_1;
+ break lab0;
+ }
+ base.bra = base.cursor;
+ var /** number */ v_2 = base.limit - base.cursor;
+ lab1: {
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab2: {
+ if (!(base.eq_s_b("Hi")))
+ {
+ break lab2;
+ }
+ break lab1;
+ }
+ base.cursor = base.limit - v_3;
+ if (!(base.out_grouping_b(g_keep_with_s, 97, 232)))
+ {
+ base.cursor = base.limit - v_1;
+ break lab0;
+ }
+ }
+ base.cursor = base.limit - v_2;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ }
+ if (base.cursor < I_pV)
+ {
+ return false;
+ }
+ var /** number */ v_5 = base.limit_backward;
+ base.limit_backward = I_pV;
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_7);
+ if (among_var == 0)
+ {
+ base.limit_backward = v_5;
+ return false;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ if (!r_R2())
+ {
+ base.limit_backward = v_5;
+ return false;
+ }
+ lab3: {
+ var /** number */ v_6 = base.limit - base.cursor;
+ lab4: {
+ if (!(base.eq_s_b("s")))
+ {
+ break lab4;
+ }
+ break lab3;
+ }
+ base.cursor = base.limit - v_6;
+ if (!(base.eq_s_b("t")))
+ {
+ base.limit_backward = v_5;
+ return false;
+ }
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!base.slice_from("i"))
+ {
+ return false;
+ }
+ break;
+ case 3:
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ }
+ base.limit_backward = v_5;
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_un_double() {
+ var /** number */ v_1 = base.limit - base.cursor;
+ if (base.find_among_b(a_8) == 0)
+ {
+ return false;
+ }
+ base.cursor = base.limit - v_1;
+ base.ket = base.cursor;
+ if (base.cursor <= base.limit_backward)
+ {
+ return false;
+ }
+ base.cursor--;
+ base.bra = base.cursor;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_un_accent() {
+ {
+ var v_1 = 1;
+ while(true)
+ {
+ lab0: {
+ if (!(base.out_grouping_b(g_v, 97, 251)))
+ {
+ break lab0;
+ }
+ v_1--;
+ continue;
+ }
+ break;
+ }
+ if (v_1 > 0)
+ {
+ return false;
+ }
+ }
+ base.ket = base.cursor;
+ lab1: {
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab2: {
+ if (!(base.eq_s_b("\u00E9")))
+ {
+ break lab2;
+ }
+ break lab1;
+ }
+ base.cursor = base.limit - v_3;
+ if (!(base.eq_s_b("\u00E8")))
+ {
+ return false;
+ }
+ }
+ base.bra = base.cursor;
+ if (!base.slice_from("e"))
+ {
+ return false;
+ }
+ return true;
+ };
+
+ this.stem = /** @return {boolean} */ function() {
+ var /** number */ v_1 = base.cursor;
+ r_prelude();
+ base.cursor = v_1;
+ r_mark_regions();
+ base.limit_backward = base.cursor; base.cursor = base.limit;
+ var /** number */ v_3 = base.limit - base.cursor;
+ lab0: {
+ lab1: {
+ var /** number */ v_4 = base.limit - base.cursor;
+ lab2: {
+ var /** number */ v_5 = base.limit - base.cursor;
+ lab3: {
+ var /** number */ v_6 = base.limit - base.cursor;
+ lab4: {
+ if (!r_standard_suffix())
+ {
+ break lab4;
+ }
+ break lab3;
+ }
+ base.cursor = base.limit - v_6;
+ lab5: {
+ if (!r_i_verb_suffix())
+ {
+ break lab5;
+ }
+ break lab3;
+ }
+ base.cursor = base.limit - v_6;
+ if (!r_verb_suffix())
+ {
+ break lab2;
+ }
+ }
+ base.cursor = base.limit - v_5;
+ var /** number */ v_7 = base.limit - base.cursor;
+ lab6: {
+ base.ket = base.cursor;
+ lab7: {
+ var /** number */ v_8 = base.limit - base.cursor;
+ lab8: {
+ if (!(base.eq_s_b("Y")))
+ {
+ break lab8;
+ }
+ base.bra = base.cursor;
+ if (!base.slice_from("i"))
+ {
+ return false;
+ }
+ break lab7;
+ }
+ base.cursor = base.limit - v_8;
+ if (!(base.eq_s_b("\u00E7")))
+ {
+ base.cursor = base.limit - v_7;
+ break lab6;
+ }
+ base.bra = base.cursor;
+ if (!base.slice_from("c"))
+ {
+ return false;
+ }
+ }
+ }
+ break lab1;
+ }
+ base.cursor = base.limit - v_4;
+ if (!r_residual_suffix())
+ {
+ break lab0;
+ }
+ }
+ }
+ base.cursor = base.limit - v_3;
+ var /** number */ v_9 = base.limit - base.cursor;
+ r_un_double();
+ base.cursor = base.limit - v_9;
+ var /** number */ v_10 = base.limit - base.cursor;
+ r_un_accent();
+ base.cursor = base.limit - v_10;
+ base.cursor = base.limit_backward;
+ var /** number */ v_11 = base.cursor;
+ r_postlude();
+ base.cursor = v_11;
+ return true;
+ };
+
+ /**@return{string}*/
+ this['stemWord'] = function(/**string*/word) {
+ base.setCurrent(word);
+ this.stem();
+ return base.getCurrent();
+ };
+};
+
+Stemmer = FrenchStemmer;
diff --git a/build/html/_static/minus.png b/docs/_static/minus.png
similarity index 100%
rename from build/html/_static/minus.png
rename to docs/_static/minus.png
diff --git a/build/html/_static/plus.png b/docs/_static/plus.png
similarity index 100%
rename from build/html/_static/plus.png
rename to docs/_static/plus.png
diff --git a/docs/_static/pygments.css b/docs/_static/pygments.css
new file mode 100644
index 0000000..f71bfbf
--- /dev/null
+++ b/docs/_static/pygments.css
@@ -0,0 +1,258 @@
+.highlight pre { line-height: 125%; }
+.highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+.highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+.highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+.highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #f8f8f8; }
+.highlight .c { color: #8F5902; font-style: italic } /* Comment */
+.highlight .err { color: #A40000; border: 1px solid #EF2929 } /* Error */
+.highlight .g { color: #000 } /* Generic */
+.highlight .k { color: #204A87; font-weight: bold } /* Keyword */
+.highlight .l { color: #000 } /* Literal */
+.highlight .n { color: #000 } /* Name */
+.highlight .o { color: #CE5C00; font-weight: bold } /* Operator */
+.highlight .x { color: #000 } /* Other */
+.highlight .p { color: #000; font-weight: bold } /* Punctuation */
+.highlight .ch { color: #8F5902; font-style: italic } /* Comment.Hashbang */
+.highlight .cm { color: #8F5902; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #8F5902; font-style: italic } /* Comment.Preproc */
+.highlight .cpf { color: #8F5902; font-style: italic } /* Comment.PreprocFile */
+.highlight .c1 { color: #8F5902; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #8F5902; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #A40000 } /* Generic.Deleted */
+.highlight .ge { color: #000; font-style: italic } /* Generic.Emph */
+.highlight .ges { color: #000; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
+.highlight .gr { color: #EF2929 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #000; font-style: italic } /* Generic.Output */
+.highlight .gp { color: #8F5902 } /* Generic.Prompt */
+.highlight .gs { color: #000; font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #A40000; font-weight: bold } /* Generic.Traceback */
+.highlight .kc { color: #204A87; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #204A87; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #204A87; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #204A87; font-weight: bold } /* Keyword.Pseudo */
+.highlight .kr { color: #204A87; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #204A87; font-weight: bold } /* Keyword.Type */
+.highlight .ld { color: #000 } /* Literal.Date */
+.highlight .m { color: #0000CF; font-weight: bold } /* Literal.Number */
+.highlight .s { color: #4E9A06 } /* Literal.String */
+.highlight .na { color: #C4A000 } /* Name.Attribute */
+.highlight .nb { color: #204A87 } /* Name.Builtin */
+.highlight .nc { color: #000 } /* Name.Class */
+.highlight .no { color: #000 } /* Name.Constant */
+.highlight .nd { color: #5C35CC; font-weight: bold } /* Name.Decorator */
+.highlight .ni { color: #CE5C00 } /* Name.Entity */
+.highlight .ne { color: #C00; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #000 } /* Name.Function */
+.highlight .nl { color: #F57900 } /* Name.Label */
+.highlight .nn { color: #000 } /* Name.Namespace */
+.highlight .nx { color: #000 } /* Name.Other */
+.highlight .py { color: #000 } /* Name.Property */
+.highlight .nt { color: #204A87; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #000 } /* Name.Variable */
+.highlight .ow { color: #204A87; font-weight: bold } /* Operator.Word */
+.highlight .pm { color: #000; font-weight: bold } /* Punctuation.Marker */
+.highlight .w { color: #F8F8F8 } /* Text.Whitespace */
+.highlight .mb { color: #0000CF; font-weight: bold } /* Literal.Number.Bin */
+.highlight .mf { color: #0000CF; font-weight: bold } /* Literal.Number.Float */
+.highlight .mh { color: #0000CF; font-weight: bold } /* Literal.Number.Hex */
+.highlight .mi { color: #0000CF; font-weight: bold } /* Literal.Number.Integer */
+.highlight .mo { color: #0000CF; font-weight: bold } /* Literal.Number.Oct */
+.highlight .sa { color: #4E9A06 } /* Literal.String.Affix */
+.highlight .sb { color: #4E9A06 } /* Literal.String.Backtick */
+.highlight .sc { color: #4E9A06 } /* Literal.String.Char */
+.highlight .dl { color: #4E9A06 } /* Literal.String.Delimiter */
+.highlight .sd { color: #8F5902; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #4E9A06 } /* Literal.String.Double */
+.highlight .se { color: #4E9A06 } /* Literal.String.Escape */
+.highlight .sh { color: #4E9A06 } /* Literal.String.Heredoc */
+.highlight .si { color: #4E9A06 } /* Literal.String.Interpol */
+.highlight .sx { color: #4E9A06 } /* Literal.String.Other */
+.highlight .sr { color: #4E9A06 } /* Literal.String.Regex */
+.highlight .s1 { color: #4E9A06 } /* Literal.String.Single */
+.highlight .ss { color: #4E9A06 } /* Literal.String.Symbol */
+.highlight .bp { color: #3465A4 } /* Name.Builtin.Pseudo */
+.highlight .fm { color: #000 } /* Name.Function.Magic */
+.highlight .vc { color: #000 } /* Name.Variable.Class */
+.highlight .vg { color: #000 } /* Name.Variable.Global */
+.highlight .vi { color: #000 } /* Name.Variable.Instance */
+.highlight .vm { color: #000 } /* Name.Variable.Magic */
+.highlight .il { color: #0000CF; font-weight: bold } /* Literal.Number.Integer.Long */
+@media not print {
+body[data-theme="dark"] .highlight pre { line-height: 125%; }
+body[data-theme="dark"] .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+body[data-theme="dark"] .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+body[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+body[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+body[data-theme="dark"] .highlight .hll { background-color: #404040 }
+body[data-theme="dark"] .highlight { background: #202020; color: #D0D0D0 }
+body[data-theme="dark"] .highlight .c { color: #ABABAB; font-style: italic } /* Comment */
+body[data-theme="dark"] .highlight .err { color: #A61717; background-color: #E3D2D2 } /* Error */
+body[data-theme="dark"] .highlight .esc { color: #D0D0D0 } /* Escape */
+body[data-theme="dark"] .highlight .g { color: #D0D0D0 } /* Generic */
+body[data-theme="dark"] .highlight .k { color: #6EBF26; font-weight: bold } /* Keyword */
+body[data-theme="dark"] .highlight .l { color: #D0D0D0 } /* Literal */
+body[data-theme="dark"] .highlight .n { color: #D0D0D0 } /* Name */
+body[data-theme="dark"] .highlight .o { color: #D0D0D0 } /* Operator */
+body[data-theme="dark"] .highlight .x { color: #D0D0D0 } /* Other */
+body[data-theme="dark"] .highlight .p { color: #D0D0D0 } /* Punctuation */
+body[data-theme="dark"] .highlight .ch { color: #ABABAB; font-style: italic } /* Comment.Hashbang */
+body[data-theme="dark"] .highlight .cm { color: #ABABAB; font-style: italic } /* Comment.Multiline */
+body[data-theme="dark"] .highlight .cp { color: #FF3A3A; font-weight: bold } /* Comment.Preproc */
+body[data-theme="dark"] .highlight .cpf { color: #ABABAB; font-style: italic } /* Comment.PreprocFile */
+body[data-theme="dark"] .highlight .c1 { color: #ABABAB; font-style: italic } /* Comment.Single */
+body[data-theme="dark"] .highlight .cs { color: #E50808; font-weight: bold; background-color: #520000 } /* Comment.Special */
+body[data-theme="dark"] .highlight .gd { color: #FF3A3A } /* Generic.Deleted */
+body[data-theme="dark"] .highlight .ge { color: #D0D0D0; font-style: italic } /* Generic.Emph */
+body[data-theme="dark"] .highlight .ges { color: #D0D0D0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
+body[data-theme="dark"] .highlight .gr { color: #FF3A3A } /* Generic.Error */
+body[data-theme="dark"] .highlight .gh { color: #FFF; font-weight: bold } /* Generic.Heading */
+body[data-theme="dark"] .highlight .gi { color: #589819 } /* Generic.Inserted */
+body[data-theme="dark"] .highlight .go { color: #CCC } /* Generic.Output */
+body[data-theme="dark"] .highlight .gp { color: #AAA } /* Generic.Prompt */
+body[data-theme="dark"] .highlight .gs { color: #D0D0D0; font-weight: bold } /* Generic.Strong */
+body[data-theme="dark"] .highlight .gu { color: #FFF; text-decoration: underline } /* Generic.Subheading */
+body[data-theme="dark"] .highlight .gt { color: #FF3A3A } /* Generic.Traceback */
+body[data-theme="dark"] .highlight .kc { color: #6EBF26; font-weight: bold } /* Keyword.Constant */
+body[data-theme="dark"] .highlight .kd { color: #6EBF26; font-weight: bold } /* Keyword.Declaration */
+body[data-theme="dark"] .highlight .kn { color: #6EBF26; font-weight: bold } /* Keyword.Namespace */
+body[data-theme="dark"] .highlight .kp { color: #6EBF26 } /* Keyword.Pseudo */
+body[data-theme="dark"] .highlight .kr { color: #6EBF26; font-weight: bold } /* Keyword.Reserved */
+body[data-theme="dark"] .highlight .kt { color: #6EBF26; font-weight: bold } /* Keyword.Type */
+body[data-theme="dark"] .highlight .ld { color: #D0D0D0 } /* Literal.Date */
+body[data-theme="dark"] .highlight .m { color: #51B2FD } /* Literal.Number */
+body[data-theme="dark"] .highlight .s { color: #ED9D13 } /* Literal.String */
+body[data-theme="dark"] .highlight .na { color: #BBB } /* Name.Attribute */
+body[data-theme="dark"] .highlight .nb { color: #2FBCCD } /* Name.Builtin */
+body[data-theme="dark"] .highlight .nc { color: #71ADFF; text-decoration: underline } /* Name.Class */
+body[data-theme="dark"] .highlight .no { color: #40FFFF } /* Name.Constant */
+body[data-theme="dark"] .highlight .nd { color: #FFA500 } /* Name.Decorator */
+body[data-theme="dark"] .highlight .ni { color: #D0D0D0 } /* Name.Entity */
+body[data-theme="dark"] .highlight .ne { color: #BBB } /* Name.Exception */
+body[data-theme="dark"] .highlight .nf { color: #71ADFF } /* Name.Function */
+body[data-theme="dark"] .highlight .nl { color: #D0D0D0 } /* Name.Label */
+body[data-theme="dark"] .highlight .nn { color: #71ADFF; text-decoration: underline } /* Name.Namespace */
+body[data-theme="dark"] .highlight .nx { color: #D0D0D0 } /* Name.Other */
+body[data-theme="dark"] .highlight .py { color: #D0D0D0 } /* Name.Property */
+body[data-theme="dark"] .highlight .nt { color: #6EBF26; font-weight: bold } /* Name.Tag */
+body[data-theme="dark"] .highlight .nv { color: #40FFFF } /* Name.Variable */
+body[data-theme="dark"] .highlight .ow { color: #6EBF26; font-weight: bold } /* Operator.Word */
+body[data-theme="dark"] .highlight .pm { color: #D0D0D0 } /* Punctuation.Marker */
+body[data-theme="dark"] .highlight .w { color: #666 } /* Text.Whitespace */
+body[data-theme="dark"] .highlight .mb { color: #51B2FD } /* Literal.Number.Bin */
+body[data-theme="dark"] .highlight .mf { color: #51B2FD } /* Literal.Number.Float */
+body[data-theme="dark"] .highlight .mh { color: #51B2FD } /* Literal.Number.Hex */
+body[data-theme="dark"] .highlight .mi { color: #51B2FD } /* Literal.Number.Integer */
+body[data-theme="dark"] .highlight .mo { color: #51B2FD } /* Literal.Number.Oct */
+body[data-theme="dark"] .highlight .sa { color: #ED9D13 } /* Literal.String.Affix */
+body[data-theme="dark"] .highlight .sb { color: #ED9D13 } /* Literal.String.Backtick */
+body[data-theme="dark"] .highlight .sc { color: #ED9D13 } /* Literal.String.Char */
+body[data-theme="dark"] .highlight .dl { color: #ED9D13 } /* Literal.String.Delimiter */
+body[data-theme="dark"] .highlight .sd { color: #ED9D13 } /* Literal.String.Doc */
+body[data-theme="dark"] .highlight .s2 { color: #ED9D13 } /* Literal.String.Double */
+body[data-theme="dark"] .highlight .se { color: #ED9D13 } /* Literal.String.Escape */
+body[data-theme="dark"] .highlight .sh { color: #ED9D13 } /* Literal.String.Heredoc */
+body[data-theme="dark"] .highlight .si { color: #ED9D13 } /* Literal.String.Interpol */
+body[data-theme="dark"] .highlight .sx { color: #FFA500 } /* Literal.String.Other */
+body[data-theme="dark"] .highlight .sr { color: #ED9D13 } /* Literal.String.Regex */
+body[data-theme="dark"] .highlight .s1 { color: #ED9D13 } /* Literal.String.Single */
+body[data-theme="dark"] .highlight .ss { color: #ED9D13 } /* Literal.String.Symbol */
+body[data-theme="dark"] .highlight .bp { color: #2FBCCD } /* Name.Builtin.Pseudo */
+body[data-theme="dark"] .highlight .fm { color: #71ADFF } /* Name.Function.Magic */
+body[data-theme="dark"] .highlight .vc { color: #40FFFF } /* Name.Variable.Class */
+body[data-theme="dark"] .highlight .vg { color: #40FFFF } /* Name.Variable.Global */
+body[data-theme="dark"] .highlight .vi { color: #40FFFF } /* Name.Variable.Instance */
+body[data-theme="dark"] .highlight .vm { color: #40FFFF } /* Name.Variable.Magic */
+body[data-theme="dark"] .highlight .il { color: #51B2FD } /* Literal.Number.Integer.Long */
+@media (prefers-color-scheme: dark) {
+body:not([data-theme="light"]) .highlight pre { line-height: 125%; }
+body:not([data-theme="light"]) .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+body:not([data-theme="light"]) .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+body:not([data-theme="light"]) .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+body:not([data-theme="light"]) .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+body:not([data-theme="light"]) .highlight .hll { background-color: #404040 }
+body:not([data-theme="light"]) .highlight { background: #202020; color: #D0D0D0 }
+body:not([data-theme="light"]) .highlight .c { color: #ABABAB; font-style: italic } /* Comment */
+body:not([data-theme="light"]) .highlight .err { color: #A61717; background-color: #E3D2D2 } /* Error */
+body:not([data-theme="light"]) .highlight .esc { color: #D0D0D0 } /* Escape */
+body:not([data-theme="light"]) .highlight .g { color: #D0D0D0 } /* Generic */
+body:not([data-theme="light"]) .highlight .k { color: #6EBF26; font-weight: bold } /* Keyword */
+body:not([data-theme="light"]) .highlight .l { color: #D0D0D0 } /* Literal */
+body:not([data-theme="light"]) .highlight .n { color: #D0D0D0 } /* Name */
+body:not([data-theme="light"]) .highlight .o { color: #D0D0D0 } /* Operator */
+body:not([data-theme="light"]) .highlight .x { color: #D0D0D0 } /* Other */
+body:not([data-theme="light"]) .highlight .p { color: #D0D0D0 } /* Punctuation */
+body:not([data-theme="light"]) .highlight .ch { color: #ABABAB; font-style: italic } /* Comment.Hashbang */
+body:not([data-theme="light"]) .highlight .cm { color: #ABABAB; font-style: italic } /* Comment.Multiline */
+body:not([data-theme="light"]) .highlight .cp { color: #FF3A3A; font-weight: bold } /* Comment.Preproc */
+body:not([data-theme="light"]) .highlight .cpf { color: #ABABAB; font-style: italic } /* Comment.PreprocFile */
+body:not([data-theme="light"]) .highlight .c1 { color: #ABABAB; font-style: italic } /* Comment.Single */
+body:not([data-theme="light"]) .highlight .cs { color: #E50808; font-weight: bold; background-color: #520000 } /* Comment.Special */
+body:not([data-theme="light"]) .highlight .gd { color: #FF3A3A } /* Generic.Deleted */
+body:not([data-theme="light"]) .highlight .ge { color: #D0D0D0; font-style: italic } /* Generic.Emph */
+body:not([data-theme="light"]) .highlight .ges { color: #D0D0D0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */
+body:not([data-theme="light"]) .highlight .gr { color: #FF3A3A } /* Generic.Error */
+body:not([data-theme="light"]) .highlight .gh { color: #FFF; font-weight: bold } /* Generic.Heading */
+body:not([data-theme="light"]) .highlight .gi { color: #589819 } /* Generic.Inserted */
+body:not([data-theme="light"]) .highlight .go { color: #CCC } /* Generic.Output */
+body:not([data-theme="light"]) .highlight .gp { color: #AAA } /* Generic.Prompt */
+body:not([data-theme="light"]) .highlight .gs { color: #D0D0D0; font-weight: bold } /* Generic.Strong */
+body:not([data-theme="light"]) .highlight .gu { color: #FFF; text-decoration: underline } /* Generic.Subheading */
+body:not([data-theme="light"]) .highlight .gt { color: #FF3A3A } /* Generic.Traceback */
+body:not([data-theme="light"]) .highlight .kc { color: #6EBF26; font-weight: bold } /* Keyword.Constant */
+body:not([data-theme="light"]) .highlight .kd { color: #6EBF26; font-weight: bold } /* Keyword.Declaration */
+body:not([data-theme="light"]) .highlight .kn { color: #6EBF26; font-weight: bold } /* Keyword.Namespace */
+body:not([data-theme="light"]) .highlight .kp { color: #6EBF26 } /* Keyword.Pseudo */
+body:not([data-theme="light"]) .highlight .kr { color: #6EBF26; font-weight: bold } /* Keyword.Reserved */
+body:not([data-theme="light"]) .highlight .kt { color: #6EBF26; font-weight: bold } /* Keyword.Type */
+body:not([data-theme="light"]) .highlight .ld { color: #D0D0D0 } /* Literal.Date */
+body:not([data-theme="light"]) .highlight .m { color: #51B2FD } /* Literal.Number */
+body:not([data-theme="light"]) .highlight .s { color: #ED9D13 } /* Literal.String */
+body:not([data-theme="light"]) .highlight .na { color: #BBB } /* Name.Attribute */
+body:not([data-theme="light"]) .highlight .nb { color: #2FBCCD } /* Name.Builtin */
+body:not([data-theme="light"]) .highlight .nc { color: #71ADFF; text-decoration: underline } /* Name.Class */
+body:not([data-theme="light"]) .highlight .no { color: #40FFFF } /* Name.Constant */
+body:not([data-theme="light"]) .highlight .nd { color: #FFA500 } /* Name.Decorator */
+body:not([data-theme="light"]) .highlight .ni { color: #D0D0D0 } /* Name.Entity */
+body:not([data-theme="light"]) .highlight .ne { color: #BBB } /* Name.Exception */
+body:not([data-theme="light"]) .highlight .nf { color: #71ADFF } /* Name.Function */
+body:not([data-theme="light"]) .highlight .nl { color: #D0D0D0 } /* Name.Label */
+body:not([data-theme="light"]) .highlight .nn { color: #71ADFF; text-decoration: underline } /* Name.Namespace */
+body:not([data-theme="light"]) .highlight .nx { color: #D0D0D0 } /* Name.Other */
+body:not([data-theme="light"]) .highlight .py { color: #D0D0D0 } /* Name.Property */
+body:not([data-theme="light"]) .highlight .nt { color: #6EBF26; font-weight: bold } /* Name.Tag */
+body:not([data-theme="light"]) .highlight .nv { color: #40FFFF } /* Name.Variable */
+body:not([data-theme="light"]) .highlight .ow { color: #6EBF26; font-weight: bold } /* Operator.Word */
+body:not([data-theme="light"]) .highlight .pm { color: #D0D0D0 } /* Punctuation.Marker */
+body:not([data-theme="light"]) .highlight .w { color: #666 } /* Text.Whitespace */
+body:not([data-theme="light"]) .highlight .mb { color: #51B2FD } /* Literal.Number.Bin */
+body:not([data-theme="light"]) .highlight .mf { color: #51B2FD } /* Literal.Number.Float */
+body:not([data-theme="light"]) .highlight .mh { color: #51B2FD } /* Literal.Number.Hex */
+body:not([data-theme="light"]) .highlight .mi { color: #51B2FD } /* Literal.Number.Integer */
+body:not([data-theme="light"]) .highlight .mo { color: #51B2FD } /* Literal.Number.Oct */
+body:not([data-theme="light"]) .highlight .sa { color: #ED9D13 } /* Literal.String.Affix */
+body:not([data-theme="light"]) .highlight .sb { color: #ED9D13 } /* Literal.String.Backtick */
+body:not([data-theme="light"]) .highlight .sc { color: #ED9D13 } /* Literal.String.Char */
+body:not([data-theme="light"]) .highlight .dl { color: #ED9D13 } /* Literal.String.Delimiter */
+body:not([data-theme="light"]) .highlight .sd { color: #ED9D13 } /* Literal.String.Doc */
+body:not([data-theme="light"]) .highlight .s2 { color: #ED9D13 } /* Literal.String.Double */
+body:not([data-theme="light"]) .highlight .se { color: #ED9D13 } /* Literal.String.Escape */
+body:not([data-theme="light"]) .highlight .sh { color: #ED9D13 } /* Literal.String.Heredoc */
+body:not([data-theme="light"]) .highlight .si { color: #ED9D13 } /* Literal.String.Interpol */
+body:not([data-theme="light"]) .highlight .sx { color: #FFA500 } /* Literal.String.Other */
+body:not([data-theme="light"]) .highlight .sr { color: #ED9D13 } /* Literal.String.Regex */
+body:not([data-theme="light"]) .highlight .s1 { color: #ED9D13 } /* Literal.String.Single */
+body:not([data-theme="light"]) .highlight .ss { color: #ED9D13 } /* Literal.String.Symbol */
+body:not([data-theme="light"]) .highlight .bp { color: #2FBCCD } /* Name.Builtin.Pseudo */
+body:not([data-theme="light"]) .highlight .fm { color: #71ADFF } /* Name.Function.Magic */
+body:not([data-theme="light"]) .highlight .vc { color: #40FFFF } /* Name.Variable.Class */
+body:not([data-theme="light"]) .highlight .vg { color: #40FFFF } /* Name.Variable.Global */
+body:not([data-theme="light"]) .highlight .vi { color: #40FFFF } /* Name.Variable.Instance */
+body:not([data-theme="light"]) .highlight .vm { color: #40FFFF } /* Name.Variable.Magic */
+body:not([data-theme="light"]) .highlight .il { color: #51B2FD } /* Literal.Number.Integer.Long */
+}
+}
\ No newline at end of file
diff --git a/docs/_static/scripts/furo-extensions.js b/docs/_static/scripts/furo-extensions.js
new file mode 100644
index 0000000..e69de29
diff --git a/docs/_static/scripts/furo.js b/docs/_static/scripts/furo.js
new file mode 100644
index 0000000..0abb2af
--- /dev/null
+++ b/docs/_static/scripts/furo.js
@@ -0,0 +1,3 @@
+/*! For license information please see furo.js.LICENSE.txt */
+(()=>{var t={856:function(t,e,n){var o,r;r=void 0!==n.g?n.g:"undefined"!=typeof window?window:this,o=function(){return function(t){"use strict";var e={navClass:"active",contentClass:"active",nested:!1,nestedClass:"active",offset:0,reflow:!1,events:!0},n=function(t,e,n){if(n.settings.events){var o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n});e.dispatchEvent(o)}},o=function(t){var e=0;if(t.offsetParent)for(;t;)e+=t.offsetTop,t=t.offsetParent;return e>=0?e:0},r=function(t){t&&t.sort((function(t,e){return o(t.content)=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},l=function(t,e){var n=t[t.length-1];if(function(t,e){return!(!s()||!c(t.content,e,!0))}(n,e))return n;for(var o=t.length-1;o>=0;o--)if(c(t[o].content,e))return t[o]},a=function(t,e){if(e.nested&&t.parentNode){var n=t.parentNode.closest("li");n&&(n.classList.remove(e.nestedClass),a(n,e))}},i=function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.remove(e.navClass),t.content.classList.remove(e.contentClass),a(o,e),n("gumshoeDeactivate",o,{link:t.nav,content:t.content,settings:e}))}},u=function(t,e){if(e.nested){var n=t.parentNode.closest("li");n&&(n.classList.add(e.nestedClass),u(n,e))}};return function(o,c){var s,a,d,f,m,v={setup:function(){s=document.querySelectorAll(o),a=[],Array.prototype.forEach.call(s,(function(t){var e=document.getElementById(decodeURIComponent(t.hash.substr(1)));e&&a.push({nav:t,content:e})})),r(a)},detect:function(){var t=l(a,m);t?d&&t.content===d.content||(i(d,m),function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.add(e.navClass),t.content.classList.add(e.contentClass),u(o,e),n("gumshoeActivate",o,{link:t.nav,content:t.content,settings:e}))}}(t,m),d=t):d&&(i(d,m),d=null)}},h=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame(v.detect)},g=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame((function(){r(a),v.detect()}))};return v.destroy=function(){d&&i(d,m),t.removeEventListener("scroll",h,!1),m.reflow&&t.removeEventListener("resize",g,!1),a=null,s=null,d=null,f=null,m=null},m=function(){var t={};return Array.prototype.forEach.call(arguments,(function(e){for(var n in e){if(!e.hasOwnProperty(n))return;t[n]=e[n]}})),t}(e,c||{}),v.setup(),v.detect(),t.addEventListener("scroll",h,!1),m.reflow&&t.addEventListener("resize",g,!1),v}}(r)}.apply(e,[]),void 0===o||(t.exports=o)}},e={};function n(o){var r=e[o];if(void 0!==r)return r.exports;var c=e[o]={exports:{}};return t[o].call(c.exports,c,c.exports,n),c.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var o in e)n.o(e,o)&&!n.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{"use strict";var t=n(856),e=n.n(t),o=null,r=null,c=document.documentElement.scrollTop;const s=64;function l(){const t=localStorage.getItem("theme")||"auto";var e;"light"!==(e=window.matchMedia("(prefers-color-scheme: dark)").matches?"auto"===t?"light":"light"==t?"dark":"auto":"auto"===t?"dark":"dark"==t?"light":"auto")&&"dark"!==e&&"auto"!==e&&(console.error(`Got invalid theme mode: ${e}. Resetting to auto.`),e="auto"),document.body.dataset.theme=e,localStorage.setItem("theme",e),console.log(`Changed to ${e} mode.`)}function a(){!function(){const t=document.getElementsByClassName("theme-toggle");Array.from(t).forEach((t=>{t.addEventListener("click",l)}))}(),function(){let t=0,e=!1;window.addEventListener("scroll",(function(n){t=window.scrollY,e||(window.requestAnimationFrame((function(){var n;(function(t){const e=Math.floor(r.getBoundingClientRect().top);console.log(`headerTop: ${e}`),0==e&&t!=e?r.classList.add("scrolled"):r.classList.remove("scrolled")})(n=t),function(t){tc&&document.documentElement.classList.remove("show-back-to-top"),c=t}(n),function(t){null!==o&&(0==t?o.scrollTo(0,0):Math.ceil(t)>=Math.floor(document.documentElement.scrollHeight-window.innerHeight)?o.scrollTo(0,o.scrollHeight):document.querySelector(".scroll-current"))}(n),e=!1})),e=!0)})),window.scroll()}(),null!==o&&new(e())(".toc-tree a",{reflow:!0,recursive:!0,navClass:"scroll-current",offset:()=>{let t=parseFloat(getComputedStyle(document.documentElement).fontSize);return r.getBoundingClientRect().height+2.5*t+1}})}document.addEventListener("DOMContentLoaded",(function(){document.body.parentNode.classList.remove("no-js"),r=document.querySelector("header"),o=document.querySelector(".toc-scroll"),a()}))})()})();
+//# sourceMappingURL=furo.js.map
\ No newline at end of file
diff --git a/docs/_static/scripts/furo.js.LICENSE.txt b/docs/_static/scripts/furo.js.LICENSE.txt
new file mode 100644
index 0000000..1632189
--- /dev/null
+++ b/docs/_static/scripts/furo.js.LICENSE.txt
@@ -0,0 +1,7 @@
+/*!
+ * gumshoejs v5.1.2 (patched by @pradyunsg)
+ * A simple, framework-agnostic scrollspy script.
+ * (c) 2019 Chris Ferdinandi
+ * MIT License
+ * http://github.com/cferdinandi/gumshoe
+ */
diff --git a/docs/_static/scripts/furo.js.map b/docs/_static/scripts/furo.js.map
new file mode 100644
index 0000000..80ea12b
--- /dev/null
+++ b/docs/_static/scripts/furo.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"scripts/furo.js","mappings":";iCAAA,MAQWA,SAWS,IAAX,EAAAC,EACH,EAAAA,EACkB,oBAAXC,OACLA,OACAC,KAbO,EAAF,WACP,OAaJ,SAAUD,GACR,aAMA,IAAIE,EAAW,CAEbC,SAAU,SACVC,aAAc,SAGdC,QAAQ,EACRC,YAAa,SAGbC,OAAQ,EACRC,QAAQ,EAGRC,QAAQ,GA6BNC,EAAY,SAAUC,EAAMC,EAAMC,GAEpC,GAAKA,EAAOC,SAASL,OAArB,CAGA,IAAIM,EAAQ,IAAIC,YAAYL,EAAM,CAChCM,SAAS,EACTC,YAAY,EACZL,OAAQA,IAIVD,EAAKO,cAAcJ,EAVgB,CAWrC,EAOIK,EAAe,SAAUR,GAC3B,IAAIS,EAAW,EACf,GAAIT,EAAKU,aACP,KAAOV,GACLS,GAAYT,EAAKW,UACjBX,EAAOA,EAAKU,aAGhB,OAAOD,GAAY,EAAIA,EAAW,CACpC,EAMIG,EAAe,SAAUC,GACvBA,GACFA,EAASC,MAAK,SAAUC,EAAOC,GAG7B,OAFcR,EAAaO,EAAME,SACnBT,EAAaQ,EAAMC,UACF,EACxB,CACT,GAEJ,EAwCIC,EAAW,SAAUlB,EAAME,EAAUiB,GACvC,IAAIC,EAASpB,EAAKqB,wBACd1B,EAnCU,SAAUO,GAExB,MAA+B,mBAApBA,EAASP,OACX2B,WAAWpB,EAASP,UAItB2B,WAAWpB,EAASP,OAC7B,CA2Be4B,CAAUrB,GACvB,OAAIiB,EAEAK,SAASJ,EAAOD,OAAQ,KACvB/B,EAAOqC,aAAeC,SAASC,gBAAgBC,cAG7CJ,SAASJ,EAAOS,IAAK,KAAOlC,CACrC,EAMImC,EAAa,WACf,OACEC,KAAKC,KAAK5C,EAAOqC,YAAcrC,EAAO6C,cAnCjCF,KAAKG,IACVR,SAASS,KAAKC,aACdV,SAASC,gBAAgBS,aACzBV,SAASS,KAAKE,aACdX,SAASC,gBAAgBU,aACzBX,SAASS,KAAKP,aACdF,SAASC,gBAAgBC,aAkC7B,EAmBIU,EAAY,SAAUzB,EAAUX,GAClC,IAAIqC,EAAO1B,EAASA,EAAS2B,OAAS,GACtC,GAbgB,SAAUC,EAAMvC,GAChC,SAAI4B,MAAgBZ,EAASuB,EAAKxB,QAASf,GAAU,GAEvD,CAUMwC,CAAYH,EAAMrC,GAAW,OAAOqC,EACxC,IAAK,IAAII,EAAI9B,EAAS2B,OAAS,EAAGG,GAAK,EAAGA,IACxC,GAAIzB,EAASL,EAAS8B,GAAG1B,QAASf,GAAW,OAAOW,EAAS8B,EAEjE,EAOIC,EAAmB,SAAUC,EAAK3C,GAEpC,GAAKA,EAAST,QAAWoD,EAAIC,WAA7B,CAGA,IAAIC,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASR,aAG7BkD,EAAiBG,EAAI7C,GAV0B,CAWjD,EAOIiD,EAAa,SAAUC,EAAOlD,GAEhC,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASX,UAC7B6D,EAAMnC,QAAQgC,UAAUC,OAAOhD,EAASV,cAGxCoD,EAAiBG,EAAI7C,GAGrBJ,EAAU,oBAAqBiD,EAAI,CACjCM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,EAOIoD,EAAiB,SAAUT,EAAK3C,GAElC,GAAKA,EAAST,OAAd,CAGA,IAAIsD,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASR,aAG1B4D,EAAeP,EAAI7C,GAVS,CAW9B,EA6LA,OA1JkB,SAAUsD,EAAUC,GAKpC,IACIC,EAAU7C,EAAU8C,EAASC,EAAS1D,EADtC2D,EAAa,CAUjBA,MAAmB,WAEjBH,EAAWhC,SAASoC,iBAAiBN,GAGrC3C,EAAW,GAGXkD,MAAMC,UAAUC,QAAQC,KAAKR,GAAU,SAAUjB,GAE/C,IAAIxB,EAAUS,SAASyC,eACrBC,mBAAmB3B,EAAK4B,KAAKC,OAAO,KAEjCrD,GAGLJ,EAAS0D,KAAK,CACZ1B,IAAKJ,EACLxB,QAASA,GAEb,IAGAL,EAAaC,EACf,EAKAgD,OAAoB,WAElB,IAAIW,EAASlC,EAAUzB,EAAUX,GAG5BsE,EASDb,GAAWa,EAAOvD,UAAY0C,EAAQ1C,UAG1CkC,EAAWQ,EAASzD,GAzFT,SAAUkD,EAAOlD,GAE9B,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASX,UAC1B6D,EAAMnC,QAAQgC,UAAUM,IAAIrD,EAASV,cAGrC8D,EAAeP,EAAI7C,GAGnBJ,EAAU,kBAAmBiD,EAAI,CAC/BM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,CAqEIuE,CAASD,EAAQtE,GAGjByD,EAAUa,GAfJb,IACFR,EAAWQ,EAASzD,GACpByD,EAAU,KAchB,GAMIe,EAAgB,SAAUvE,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,sBAAsBf,EAAWgB,OACpD,EAMIC,EAAgB,SAAU3E,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,uBAAsB,WACrChE,EAAaC,GACbgD,EAAWgB,QACb,GACF,EAkDA,OA7CAhB,EAAWkB,QAAU,WAEfpB,GACFR,EAAWQ,EAASzD,GAItBd,EAAO4F,oBAAoB,SAAUN,GAAe,GAChDxE,EAASN,QACXR,EAAO4F,oBAAoB,SAAUF,GAAe,GAItDjE,EAAW,KACX6C,EAAW,KACXC,EAAU,KACVC,EAAU,KACV1D,EAAW,IACb,EAOEA,EA3XS,WACX,IAAI+E,EAAS,CAAC,EAOd,OANAlB,MAAMC,UAAUC,QAAQC,KAAKgB,WAAW,SAAUC,GAChD,IAAK,IAAIC,KAAOD,EAAK,CACnB,IAAKA,EAAIE,eAAeD,GAAM,OAC9BH,EAAOG,GAAOD,EAAIC,EACpB,CACF,IACOH,CACT,CAkXeK,CAAOhG,EAAUmE,GAAW,CAAC,GAGxCI,EAAW0B,QAGX1B,EAAWgB,SAGXzF,EAAOoG,iBAAiB,SAAUd,GAAe,GAC7CxE,EAASN,QACXR,EAAOoG,iBAAiB,SAAUV,GAAe,GAS9CjB,CACT,CAOF,CArcW4B,CAAQvG,EAChB,UAFM,SAEN,uBCXDwG,EAA2B,CAAC,EAGhC,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAaE,QAGrB,IAAIC,EAASN,EAAyBE,GAAY,CAGjDG,QAAS,CAAC,GAOX,OAHAE,EAAoBL,GAAU1B,KAAK8B,EAAOD,QAASC,EAAQA,EAAOD,QAASJ,GAGpEK,EAAOD,OACf,CCrBAJ,EAAoBO,EAAKF,IACxB,IAAIG,EAASH,GAAUA,EAAOI,WAC7B,IAAOJ,EAAiB,QACxB,IAAM,EAEP,OADAL,EAAoBU,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,CAAM,ECLdR,EAAoBU,EAAI,CAACN,EAASQ,KACjC,IAAI,IAAInB,KAAOmB,EACXZ,EAAoBa,EAAED,EAAYnB,KAASO,EAAoBa,EAAET,EAASX,IAC5EqB,OAAOC,eAAeX,EAASX,EAAK,CAAEuB,YAAY,EAAMC,IAAKL,EAAWnB,IAE1E,ECNDO,EAAoBxG,EAAI,WACvB,GAA0B,iBAAf0H,WAAyB,OAAOA,WAC3C,IACC,OAAOxH,MAAQ,IAAIyH,SAAS,cAAb,EAChB,CAAE,MAAOC,GACR,GAAsB,iBAAX3H,OAAqB,OAAOA,MACxC,CACA,CAPuB,GCAxBuG,EAAoBa,EAAI,CAACrB,EAAK6B,IAAUP,OAAOzC,UAAUqB,eAAenB,KAAKiB,EAAK6B,4CCK9EC,EAAY,KACZC,EAAS,KACTC,EAAgBzF,SAASC,gBAAgByF,UAC7C,MAAMC,EAAmB,GA8EzB,SAASC,IACP,MAAMC,EAAeC,aAAaC,QAAQ,UAAY,OAZxD,IAAkBC,EACH,WADGA,EAaItI,OAAOuI,WAAW,gCAAgCC,QAI/C,SAAjBL,EACO,QACgB,SAAhBA,EACA,OAEA,OAIU,SAAjBA,EACO,OACgB,QAAhBA,EACA,QAEA,SA9BoB,SAATG,GAA4B,SAATA,IACzCG,QAAQC,MAAM,2BAA2BJ,yBACzCA,EAAO,QAGThG,SAASS,KAAK4F,QAAQC,MAAQN,EAC9BF,aAAaS,QAAQ,QAASP,GAC9BG,QAAQK,IAAI,cAAcR,UA0B5B,CAkDA,SAASnC,KART,WAEE,MAAM4C,EAAUzG,SAAS0G,uBAAuB,gBAChDrE,MAAMsE,KAAKF,GAASlE,SAASqE,IAC3BA,EAAI9C,iBAAiB,QAAS8B,EAAe,GAEjD,CAGEiB,GA9CF,WAEE,IAAIC,EAA6B,EAC7BC,GAAU,EAEdrJ,OAAOoG,iBAAiB,UAAU,SAAUuB,GAC1CyB,EAA6BpJ,OAAOsJ,QAE/BD,IACHrJ,OAAOwF,uBAAsB,WAzDnC,IAAuB+D,GAxDvB,SAAgCA,GAC9B,MAAMC,EAAY7G,KAAK8G,MAAM3B,EAAO7F,wBAAwBQ,KAE5DgG,QAAQK,IAAI,cAAcU,KACT,GAAbA,GAAkBD,GAAaC,EACjC1B,EAAOjE,UAAUM,IAAI,YAErB2D,EAAOjE,UAAUC,OAAO,WAE5B,EAgDE4F,CADqBH,EA0DDH,GAvGtB,SAAmCG,GAC7BA,EAAYtB,EACd3F,SAASC,gBAAgBsB,UAAUC,OAAO,oBAEtCyF,EAAYxB,EACdzF,SAASC,gBAAgBsB,UAAUM,IAAI,oBAC9BoF,EAAYxB,GACrBzF,SAASC,gBAAgBsB,UAAUC,OAAO,oBAG9CiE,EAAgBwB,CAClB,CAoCEI,CAA0BJ,GAlC5B,SAA6BA,GACT,OAAd1B,IAKa,GAAb0B,EACF1B,EAAU+B,SAAS,EAAG,GAGtBjH,KAAKC,KAAK2G,IACV5G,KAAK8G,MAAMnH,SAASC,gBAAgBS,aAAehD,OAAOqC,aAE1DwF,EAAU+B,SAAS,EAAG/B,EAAU7E,cAGhBV,SAASuH,cAAc,mBAc3C,CAKEC,CAAoBP,GAwDdF,GAAU,CACZ,IAEAA,GAAU,EAEd,IACArJ,OAAO+J,QACT,CA6BEC,GA1BkB,OAAdnC,GAKJ,IAAI,IAAJ,CAAY,cAAe,CACzBrH,QAAQ,EACRyJ,WAAW,EACX9J,SAAU,iBACVI,OAAQ,KACN,IAAI2J,EAAMhI,WAAWiI,iBAAiB7H,SAASC,iBAAiB6H,UAChE,OAAOtC,EAAO7F,wBAAwBoI,OAAS,IAAMH,EAAM,CAAC,GAiBlE,CAcA5H,SAAS8D,iBAAiB,oBAT1B,WACE9D,SAASS,KAAKW,WAAWG,UAAUC,OAAO,SAE1CgE,EAASxF,SAASuH,cAAc,UAChChC,EAAYvF,SAASuH,cAAc,eAEnC1D,GACF","sources":["webpack:///./src/furo/assets/scripts/gumshoe-patched.js","webpack:///webpack/bootstrap","webpack:///webpack/runtime/compat get default export","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/global","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///./src/furo/assets/scripts/furo.js"],"sourcesContent":["/*!\n * gumshoejs v5.1.2 (patched by @pradyunsg)\n * A simple, framework-agnostic scrollspy script.\n * (c) 2019 Chris Ferdinandi\n * MIT License\n * http://github.com/cferdinandi/gumshoe\n */\n\n(function (root, factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], function () {\n return factory(root);\n });\n } else if (typeof exports === \"object\") {\n module.exports = factory(root);\n } else {\n root.Gumshoe = factory(root);\n }\n})(\n typeof global !== \"undefined\"\n ? global\n : typeof window !== \"undefined\"\n ? window\n : this,\n function (window) {\n \"use strict\";\n\n //\n // Defaults\n //\n\n var defaults = {\n // Active classes\n navClass: \"active\",\n contentClass: \"active\",\n\n // Nested navigation\n nested: false,\n nestedClass: \"active\",\n\n // Offset & reflow\n offset: 0,\n reflow: false,\n\n // Event support\n events: true,\n };\n\n //\n // Methods\n //\n\n /**\n * Merge two or more objects together.\n * @param {Object} objects The objects to merge together\n * @returns {Object} Merged values of defaults and options\n */\n var extend = function () {\n var merged = {};\n Array.prototype.forEach.call(arguments, function (obj) {\n for (var key in obj) {\n if (!obj.hasOwnProperty(key)) return;\n merged[key] = obj[key];\n }\n });\n return merged;\n };\n\n /**\n * Emit a custom event\n * @param {String} type The event type\n * @param {Node} elem The element to attach the event to\n * @param {Object} detail Any details to pass along with the event\n */\n var emitEvent = function (type, elem, detail) {\n // Make sure events are enabled\n if (!detail.settings.events) return;\n\n // Create a new event\n var event = new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n detail: detail,\n });\n\n // Dispatch the event\n elem.dispatchEvent(event);\n };\n\n /**\n * Get an element's distance from the top of the Document.\n * @param {Node} elem The element\n * @return {Number} Distance from the top in pixels\n */\n var getOffsetTop = function (elem) {\n var location = 0;\n if (elem.offsetParent) {\n while (elem) {\n location += elem.offsetTop;\n elem = elem.offsetParent;\n }\n }\n return location >= 0 ? location : 0;\n };\n\n /**\n * Sort content from first to last in the DOM\n * @param {Array} contents The content areas\n */\n var sortContents = function (contents) {\n if (contents) {\n contents.sort(function (item1, item2) {\n var offset1 = getOffsetTop(item1.content);\n var offset2 = getOffsetTop(item2.content);\n if (offset1 < offset2) return -1;\n return 1;\n });\n }\n };\n\n /**\n * Get the offset to use for calculating position\n * @param {Object} settings The settings for this instantiation\n * @return {Float} The number of pixels to offset the calculations\n */\n var getOffset = function (settings) {\n // if the offset is a function run it\n if (typeof settings.offset === \"function\") {\n return parseFloat(settings.offset());\n }\n\n // Otherwise, return it as-is\n return parseFloat(settings.offset);\n };\n\n /**\n * Get the document element's height\n * @private\n * @returns {Number}\n */\n var getDocumentHeight = function () {\n return Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight,\n document.body.offsetHeight,\n document.documentElement.offsetHeight,\n document.body.clientHeight,\n document.documentElement.clientHeight,\n );\n };\n\n /**\n * Determine if an element is in view\n * @param {Node} elem The element\n * @param {Object} settings The settings for this instantiation\n * @param {Boolean} bottom If true, check if element is above bottom of viewport instead\n * @return {Boolean} Returns true if element is in the viewport\n */\n var isInView = function (elem, settings, bottom) {\n var bounds = elem.getBoundingClientRect();\n var offset = getOffset(settings);\n if (bottom) {\n return (\n parseInt(bounds.bottom, 10) <\n (window.innerHeight || document.documentElement.clientHeight)\n );\n }\n return parseInt(bounds.top, 10) <= offset;\n };\n\n /**\n * Check if at the bottom of the viewport\n * @return {Boolean} If true, page is at the bottom of the viewport\n */\n var isAtBottom = function () {\n if (\n Math.ceil(window.innerHeight + window.pageYOffset) >=\n getDocumentHeight()\n )\n return true;\n return false;\n };\n\n /**\n * Check if the last item should be used (even if not at the top of the page)\n * @param {Object} item The last item\n * @param {Object} settings The settings for this instantiation\n * @return {Boolean} If true, use the last item\n */\n var useLastItem = function (item, settings) {\n if (isAtBottom() && isInView(item.content, settings, true)) return true;\n return false;\n };\n\n /**\n * Get the active content\n * @param {Array} contents The content areas\n * @param {Object} settings The settings for this instantiation\n * @return {Object} The content area and matching navigation link\n */\n var getActive = function (contents, settings) {\n var last = contents[contents.length - 1];\n if (useLastItem(last, settings)) return last;\n for (var i = contents.length - 1; i >= 0; i--) {\n if (isInView(contents[i].content, settings)) return contents[i];\n }\n };\n\n /**\n * Deactivate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var deactivateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested || !nav.parentNode) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Remove the active class\n li.classList.remove(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n deactivateNested(li, settings);\n };\n\n /**\n * Deactivate a nav and content area\n * @param {Object} items The nav item and content to deactivate\n * @param {Object} settings The settings for this instantiation\n */\n var deactivate = function (items, settings) {\n // Make sure there are items to deactivate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Remove the active class from the nav and content\n li.classList.remove(settings.navClass);\n items.content.classList.remove(settings.contentClass);\n\n // Deactivate any parent navs in a nested navigation\n deactivateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeDeactivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Activate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var activateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Add the active class\n li.classList.add(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n activateNested(li, settings);\n };\n\n /**\n * Activate a nav and content area\n * @param {Object} items The nav item and content to activate\n * @param {Object} settings The settings for this instantiation\n */\n var activate = function (items, settings) {\n // Make sure there are items to activate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Add the active class to the nav and content\n li.classList.add(settings.navClass);\n items.content.classList.add(settings.contentClass);\n\n // Activate any parent navs in a nested navigation\n activateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeActivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Create the Constructor object\n * @param {String} selector The selector to use for navigation items\n * @param {Object} options User options and settings\n */\n var Constructor = function (selector, options) {\n //\n // Variables\n //\n\n var publicAPIs = {};\n var navItems, contents, current, timeout, settings;\n\n //\n // Methods\n //\n\n /**\n * Set variables from DOM elements\n */\n publicAPIs.setup = function () {\n // Get all nav items\n navItems = document.querySelectorAll(selector);\n\n // Create contents array\n contents = [];\n\n // Loop through each item, get it's matching content, and push to the array\n Array.prototype.forEach.call(navItems, function (item) {\n // Get the content for the nav item\n var content = document.getElementById(\n decodeURIComponent(item.hash.substr(1)),\n );\n if (!content) return;\n\n // Push to the contents array\n contents.push({\n nav: item,\n content: content,\n });\n });\n\n // Sort contents by the order they appear in the DOM\n sortContents(contents);\n };\n\n /**\n * Detect which content is currently active\n */\n publicAPIs.detect = function () {\n // Get the active content\n var active = getActive(contents, settings);\n\n // if there's no active content, deactivate and bail\n if (!active) {\n if (current) {\n deactivate(current, settings);\n current = null;\n }\n return;\n }\n\n // If the active content is the one currently active, do nothing\n if (current && active.content === current.content) return;\n\n // Deactivate the current content and activate the new content\n deactivate(current, settings);\n activate(active, settings);\n\n // Update the currently active content\n current = active;\n };\n\n /**\n * Detect the active content on scroll\n * Debounced for performance\n */\n var scrollHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(publicAPIs.detect);\n };\n\n /**\n * Update content sorting on resize\n * Debounced for performance\n */\n var resizeHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(function () {\n sortContents(contents);\n publicAPIs.detect();\n });\n };\n\n /**\n * Destroy the current instantiation\n */\n publicAPIs.destroy = function () {\n // Undo DOM changes\n if (current) {\n deactivate(current, settings);\n }\n\n // Remove event listeners\n window.removeEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.removeEventListener(\"resize\", resizeHandler, false);\n }\n\n // Reset variables\n contents = null;\n navItems = null;\n current = null;\n timeout = null;\n settings = null;\n };\n\n /**\n * Initialize the current instantiation\n */\n var init = function () {\n // Merge user options into defaults\n settings = extend(defaults, options || {});\n\n // Setup variables based on the current DOM\n publicAPIs.setup();\n\n // Find the currently active content\n publicAPIs.detect();\n\n // Setup event listeners\n window.addEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.addEventListener(\"resize\", resizeHandler, false);\n }\n };\n\n //\n // Initialize and return the public APIs\n //\n\n init();\n return publicAPIs;\n };\n\n //\n // Return the Constructor\n //\n\n return Constructor;\n },\n);\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","import Gumshoe from \"./gumshoe-patched.js\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Scroll Handling\n////////////////////////////////////////////////////////////////////////////////\nvar tocScroll = null;\nvar header = null;\nvar lastScrollTop = document.documentElement.scrollTop;\nconst GO_TO_TOP_OFFSET = 64;\n\nfunction scrollHandlerForHeader(positionY) {\n const headerTop = Math.floor(header.getBoundingClientRect().top);\n\n console.log(`headerTop: ${headerTop}`);\n if (headerTop == 0 && positionY != headerTop) {\n header.classList.add(\"scrolled\");\n } else {\n header.classList.remove(\"scrolled\");\n }\n}\n\nfunction scrollHandlerForBackToTop(positionY) {\n if (positionY < GO_TO_TOP_OFFSET) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n } else {\n if (positionY < lastScrollTop) {\n document.documentElement.classList.add(\"show-back-to-top\");\n } else if (positionY > lastScrollTop) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n }\n }\n lastScrollTop = positionY;\n}\n\nfunction scrollHandlerForTOC(positionY) {\n if (tocScroll === null) {\n return;\n }\n\n // top of page.\n if (positionY == 0) {\n tocScroll.scrollTo(0, 0);\n } else if (\n // bottom of page.\n Math.ceil(positionY) >=\n Math.floor(document.documentElement.scrollHeight - window.innerHeight)\n ) {\n tocScroll.scrollTo(0, tocScroll.scrollHeight);\n } else {\n // somewhere in the middle.\n const current = document.querySelector(\".scroll-current\");\n if (current == null) {\n return;\n }\n\n // https://github.com/pypa/pip/issues/9159 This breaks scroll behaviours.\n // // scroll the currently \"active\" heading in toc, into view.\n // const rect = current.getBoundingClientRect();\n // if (0 > rect.top) {\n // current.scrollIntoView(true); // the argument is \"alignTop\"\n // } else if (rect.bottom > window.innerHeight) {\n // current.scrollIntoView(false);\n // }\n }\n}\n\nfunction scrollHandler(positionY) {\n scrollHandlerForHeader(positionY);\n scrollHandlerForBackToTop(positionY);\n scrollHandlerForTOC(positionY);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Theme Toggle\n////////////////////////////////////////////////////////////////////////////////\nfunction setTheme(mode) {\n if (mode !== \"light\" && mode !== \"dark\" && mode !== \"auto\") {\n console.error(`Got invalid theme mode: ${mode}. Resetting to auto.`);\n mode = \"auto\";\n }\n\n document.body.dataset.theme = mode;\n localStorage.setItem(\"theme\", mode);\n console.log(`Changed to ${mode} mode.`);\n}\n\nfunction cycleThemeOnce() {\n const currentTheme = localStorage.getItem(\"theme\") || \"auto\";\n const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n if (prefersDark) {\n // Auto (dark) -> Light -> Dark\n if (currentTheme === \"auto\") {\n setTheme(\"light\");\n } else if (currentTheme == \"light\") {\n setTheme(\"dark\");\n } else {\n setTheme(\"auto\");\n }\n } else {\n // Auto (light) -> Dark -> Light\n if (currentTheme === \"auto\") {\n setTheme(\"dark\");\n } else if (currentTheme == \"dark\") {\n setTheme(\"light\");\n } else {\n setTheme(\"auto\");\n }\n }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Setup\n////////////////////////////////////////////////////////////////////////////////\nfunction setupScrollHandler() {\n // Taken from https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event\n let last_known_scroll_position = 0;\n let ticking = false;\n\n window.addEventListener(\"scroll\", function (e) {\n last_known_scroll_position = window.scrollY;\n\n if (!ticking) {\n window.requestAnimationFrame(function () {\n scrollHandler(last_known_scroll_position);\n ticking = false;\n });\n\n ticking = true;\n }\n });\n window.scroll();\n}\n\nfunction setupScrollSpy() {\n if (tocScroll === null) {\n return;\n }\n\n // Scrollspy -- highlight table on contents, based on scroll\n new Gumshoe(\".toc-tree a\", {\n reflow: true,\n recursive: true,\n navClass: \"scroll-current\",\n offset: () => {\n let rem = parseFloat(getComputedStyle(document.documentElement).fontSize);\n return header.getBoundingClientRect().height + 2.5 * rem + 1;\n },\n });\n}\n\nfunction setupTheme() {\n // Attach event handlers for toggling themes\n const buttons = document.getElementsByClassName(\"theme-toggle\");\n Array.from(buttons).forEach((btn) => {\n btn.addEventListener(\"click\", cycleThemeOnce);\n });\n}\n\nfunction setup() {\n setupTheme();\n setupScrollHandler();\n setupScrollSpy();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entrypoint\n////////////////////////////////////////////////////////////////////////////////\nfunction main() {\n document.body.parentNode.classList.remove(\"no-js\");\n\n header = document.querySelector(\"header\");\n tocScroll = document.querySelector(\".toc-scroll\");\n\n setup();\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", main);\n"],"names":["root","g","window","this","defaults","navClass","contentClass","nested","nestedClass","offset","reflow","events","emitEvent","type","elem","detail","settings","event","CustomEvent","bubbles","cancelable","dispatchEvent","getOffsetTop","location","offsetParent","offsetTop","sortContents","contents","sort","item1","item2","content","isInView","bottom","bounds","getBoundingClientRect","parseFloat","getOffset","parseInt","innerHeight","document","documentElement","clientHeight","top","isAtBottom","Math","ceil","pageYOffset","max","body","scrollHeight","offsetHeight","getActive","last","length","item","useLastItem","i","deactivateNested","nav","parentNode","li","closest","classList","remove","deactivate","items","link","activateNested","add","selector","options","navItems","current","timeout","publicAPIs","querySelectorAll","Array","prototype","forEach","call","getElementById","decodeURIComponent","hash","substr","push","active","activate","scrollHandler","cancelAnimationFrame","requestAnimationFrame","detect","resizeHandler","destroy","removeEventListener","merged","arguments","obj","key","hasOwnProperty","extend","setup","addEventListener","factory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","n","getter","__esModule","d","a","definition","o","Object","defineProperty","enumerable","get","globalThis","Function","e","prop","tocScroll","header","lastScrollTop","scrollTop","GO_TO_TOP_OFFSET","cycleThemeOnce","currentTheme","localStorage","getItem","mode","matchMedia","matches","console","error","dataset","theme","setItem","log","buttons","getElementsByClassName","from","btn","setupTheme","last_known_scroll_position","ticking","scrollY","positionY","headerTop","floor","scrollHandlerForHeader","scrollHandlerForBackToTop","scrollTo","querySelector","scrollHandlerForTOC","scroll","setupScrollHandler","recursive","rem","getComputedStyle","fontSize","height"],"sourceRoot":""}
\ No newline at end of file
diff --git a/build/html/_static/searchtools.js b/docs/_static/searchtools.js
similarity index 100%
rename from build/html/_static/searchtools.js
rename to docs/_static/searchtools.js
diff --git a/docs/_static/skeleton.css b/docs/_static/skeleton.css
new file mode 100644
index 0000000..467c878
--- /dev/null
+++ b/docs/_static/skeleton.css
@@ -0,0 +1,296 @@
+/* Some sane resets. */
+html {
+ height: 100%;
+}
+
+body {
+ margin: 0;
+ min-height: 100%;
+}
+
+/* All the flexbox magic! */
+body,
+.sb-announcement,
+.sb-content,
+.sb-main,
+.sb-container,
+.sb-container__inner,
+.sb-article-container,
+.sb-footer-content,
+.sb-header,
+.sb-header-secondary,
+.sb-footer {
+ display: flex;
+}
+
+/* These order things vertically */
+body,
+.sb-main,
+.sb-article-container {
+ flex-direction: column;
+}
+
+/* Put elements in the center */
+.sb-header,
+.sb-header-secondary,
+.sb-container,
+.sb-content,
+.sb-footer,
+.sb-footer-content {
+ justify-content: center;
+}
+/* Put elements at the ends */
+.sb-article-container {
+ justify-content: space-between;
+}
+
+/* These elements grow. */
+.sb-main,
+.sb-content,
+.sb-container,
+article {
+ flex-grow: 1;
+}
+
+/* Because padding making this wider is not fun */
+article {
+ box-sizing: border-box;
+}
+
+/* The announcements element should never be wider than the page. */
+.sb-announcement {
+ max-width: 100%;
+}
+
+.sb-sidebar-primary,
+.sb-sidebar-secondary {
+ flex-shrink: 0;
+ width: 17rem;
+}
+
+.sb-announcement__inner {
+ justify-content: center;
+
+ box-sizing: border-box;
+ height: 3rem;
+
+ overflow-x: auto;
+ white-space: nowrap;
+}
+
+/* Sidebars, with checkbox-based toggle */
+.sb-sidebar-primary,
+.sb-sidebar-secondary {
+ position: fixed;
+ height: 100%;
+ top: 0;
+}
+
+.sb-sidebar-primary {
+ left: -17rem;
+ transition: left 250ms ease-in-out;
+}
+.sb-sidebar-secondary {
+ right: -17rem;
+ transition: right 250ms ease-in-out;
+}
+
+.sb-sidebar-toggle {
+ display: none;
+}
+.sb-sidebar-overlay {
+ position: fixed;
+ top: 0;
+ width: 0;
+ height: 0;
+
+ transition: width 0ms ease 250ms, height 0ms ease 250ms, opacity 250ms ease;
+
+ opacity: 0;
+ background-color: rgba(0, 0, 0, 0.54);
+}
+
+#sb-sidebar-toggle--primary:checked
+ ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--primary"],
+#sb-sidebar-toggle--secondary:checked
+ ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--secondary"] {
+ width: 100%;
+ height: 100%;
+ opacity: 1;
+ transition: width 0ms ease, height 0ms ease, opacity 250ms ease;
+}
+
+#sb-sidebar-toggle--primary:checked ~ .sb-container .sb-sidebar-primary {
+ left: 0;
+}
+#sb-sidebar-toggle--secondary:checked ~ .sb-container .sb-sidebar-secondary {
+ right: 0;
+}
+
+/* Full-width mode */
+.drop-secondary-sidebar-for-full-width-content
+ .hide-when-secondary-sidebar-shown {
+ display: none !important;
+}
+.drop-secondary-sidebar-for-full-width-content .sb-sidebar-secondary {
+ display: none !important;
+}
+
+/* Mobile views */
+.sb-page-width {
+ width: 100%;
+}
+
+.sb-article-container,
+.sb-footer-content__inner,
+.drop-secondary-sidebar-for-full-width-content .sb-article,
+.drop-secondary-sidebar-for-full-width-content .match-content-width {
+ width: 100vw;
+}
+
+.sb-article,
+.match-content-width {
+ padding: 0 1rem;
+ box-sizing: border-box;
+}
+
+@media (min-width: 32rem) {
+ .sb-article,
+ .match-content-width {
+ padding: 0 2rem;
+ }
+}
+
+/* Tablet views */
+@media (min-width: 42rem) {
+ .sb-article-container {
+ width: auto;
+ }
+ .sb-footer-content__inner,
+ .drop-secondary-sidebar-for-full-width-content .sb-article,
+ .drop-secondary-sidebar-for-full-width-content .match-content-width {
+ width: 42rem;
+ }
+ .sb-article,
+ .match-content-width {
+ width: 42rem;
+ }
+}
+@media (min-width: 46rem) {
+ .sb-footer-content__inner,
+ .drop-secondary-sidebar-for-full-width-content .sb-article,
+ .drop-secondary-sidebar-for-full-width-content .match-content-width {
+ width: 46rem;
+ }
+ .sb-article,
+ .match-content-width {
+ width: 46rem;
+ }
+}
+@media (min-width: 50rem) {
+ .sb-footer-content__inner,
+ .drop-secondary-sidebar-for-full-width-content .sb-article,
+ .drop-secondary-sidebar-for-full-width-content .match-content-width {
+ width: 50rem;
+ }
+ .sb-article,
+ .match-content-width {
+ width: 50rem;
+ }
+}
+
+/* Tablet views */
+@media (min-width: 59rem) {
+ .sb-sidebar-secondary {
+ position: static;
+ }
+ .hide-when-secondary-sidebar-shown {
+ display: none !important;
+ }
+ .sb-footer-content__inner,
+ .drop-secondary-sidebar-for-full-width-content .sb-article,
+ .drop-secondary-sidebar-for-full-width-content .match-content-width {
+ width: 59rem;
+ }
+ .sb-article,
+ .match-content-width {
+ width: 42rem;
+ }
+}
+@media (min-width: 63rem) {
+ .sb-footer-content__inner,
+ .drop-secondary-sidebar-for-full-width-content .sb-article,
+ .drop-secondary-sidebar-for-full-width-content .match-content-width {
+ width: 63rem;
+ }
+ .sb-article,
+ .match-content-width {
+ width: 46rem;
+ }
+}
+@media (min-width: 67rem) {
+ .sb-footer-content__inner,
+ .drop-secondary-sidebar-for-full-width-content .sb-article,
+ .drop-secondary-sidebar-for-full-width-content .match-content-width {
+ width: 67rem;
+ }
+ .sb-article,
+ .match-content-width {
+ width: 50rem;
+ }
+}
+
+/* Desktop views */
+@media (min-width: 76rem) {
+ .sb-sidebar-primary {
+ position: static;
+ }
+ .hide-when-primary-sidebar-shown {
+ display: none !important;
+ }
+ .sb-footer-content__inner,
+ .drop-secondary-sidebar-for-full-width-content .sb-article,
+ .drop-secondary-sidebar-for-full-width-content .match-content-width {
+ width: 59rem;
+ }
+ .sb-article,
+ .match-content-width {
+ width: 42rem;
+ }
+}
+
+/* Full desktop views */
+@media (min-width: 80rem) {
+ .sb-article,
+ .match-content-width {
+ width: 46rem;
+ }
+ .sb-footer-content__inner,
+ .drop-secondary-sidebar-for-full-width-content .sb-article,
+ .drop-secondary-sidebar-for-full-width-content .match-content-width {
+ width: 63rem;
+ }
+}
+
+@media (min-width: 84rem) {
+ .sb-article,
+ .match-content-width {
+ width: 50rem;
+ }
+ .sb-footer-content__inner,
+ .drop-secondary-sidebar-for-full-width-content .sb-article,
+ .drop-secondary-sidebar-for-full-width-content .match-content-width {
+ width: 67rem;
+ }
+}
+
+@media (min-width: 88rem) {
+ .sb-footer-content__inner,
+ .drop-secondary-sidebar-for-full-width-content .sb-article,
+ .drop-secondary-sidebar-for-full-width-content .match-content-width {
+ width: 67rem;
+ }
+ .sb-page-width {
+ width: 88rem;
+ }
+}
diff --git a/build/html/_static/sphinx_highlight.js b/docs/_static/sphinx_highlight.js
similarity index 100%
rename from build/html/_static/sphinx_highlight.js
rename to docs/_static/sphinx_highlight.js
diff --git a/docs/_static/styles/furo-extensions.css b/docs/_static/styles/furo-extensions.css
new file mode 100644
index 0000000..8229587
--- /dev/null
+++ b/docs/_static/styles/furo-extensions.css
@@ -0,0 +1,2 @@
+#furo-sidebar-ad-placement{padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)}#furo-sidebar-ad-placement .ethical-sidebar{background:var(--color-background-secondary);border:none;box-shadow:none}#furo-sidebar-ad-placement .ethical-sidebar:hover{background:var(--color-background-hover)}#furo-sidebar-ad-placement .ethical-sidebar a{color:var(--color-foreground-primary)}#furo-sidebar-ad-placement .ethical-callout a{color:var(--color-foreground-secondary)!important}#furo-readthedocs-versions{background:transparent;display:block;position:static;width:100%}#furo-readthedocs-versions .rst-versions{background:#1a1c1e}#furo-readthedocs-versions .rst-current-version{background:var(--color-sidebar-item-background);cursor:unset}#furo-readthedocs-versions .rst-current-version:hover{background:var(--color-sidebar-item-background)}#furo-readthedocs-versions .rst-current-version .fa-book{color:var(--color-foreground-primary)}#furo-readthedocs-versions>.rst-other-versions{padding:0}#furo-readthedocs-versions>.rst-other-versions small{opacity:1}#furo-readthedocs-versions .injected .rst-versions{position:unset}#furo-readthedocs-versions:focus-within,#furo-readthedocs-versions:hover{box-shadow:0 0 0 1px var(--color-sidebar-background-border)}#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:hover .rst-current-version{background:#1a1c1e;font-size:inherit;height:auto;line-height:inherit;padding:12px;text-align:right}#furo-readthedocs-versions:focus-within .rst-current-version .fa-book,#furo-readthedocs-versions:hover .rst-current-version .fa-book{color:#fff;float:left}#furo-readthedocs-versions:focus-within .fa-caret-down,#furo-readthedocs-versions:hover .fa-caret-down{display:none}#furo-readthedocs-versions:focus-within .injected,#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:focus-within .rst-other-versions,#furo-readthedocs-versions:hover .injected,#furo-readthedocs-versions:hover .rst-current-version,#furo-readthedocs-versions:hover .rst-other-versions{display:block}#furo-readthedocs-versions:focus-within>.rst-current-version,#furo-readthedocs-versions:hover>.rst-current-version{display:none}.highlight:hover button.copybtn{color:var(--color-code-foreground)}.highlight button.copybtn{align-items:center;background-color:var(--color-code-background);border:none;color:var(--color-background-item);cursor:pointer;height:1.25em;right:.5rem;top:.625rem;transition:color .3s,opacity .3s;width:1.25em}.highlight button.copybtn:hover{background-color:var(--color-code-background);color:var(--color-brand-content)}.highlight button.copybtn:after{background-color:transparent;color:var(--color-code-foreground);display:none}.highlight button.copybtn.success{color:#22863a;transition:color 0ms}.highlight button.copybtn.success:after{display:block}.highlight button.copybtn svg{padding:0}body{--sd-color-primary:var(--color-brand-primary);--sd-color-primary-highlight:var(--color-brand-content);--sd-color-primary-text:var(--color-background-primary);--sd-color-shadow:rgba(0,0,0,.05);--sd-color-card-border:var(--color-card-border);--sd-color-card-border-hover:var(--color-brand-content);--sd-color-card-background:var(--color-card-background);--sd-color-card-text:var(--color-foreground-primary);--sd-color-card-header:var(--color-card-marginals-background);--sd-color-card-footer:var(--color-card-marginals-background);--sd-color-tabs-label-active:var(--color-brand-content);--sd-color-tabs-label-hover:var(--color-foreground-muted);--sd-color-tabs-label-inactive:var(--color-foreground-muted);--sd-color-tabs-underline-active:var(--color-brand-content);--sd-color-tabs-underline-hover:var(--color-foreground-border);--sd-color-tabs-underline-inactive:var(--color-background-border);--sd-color-tabs-overline:var(--color-background-border);--sd-color-tabs-underline:var(--color-background-border)}.sd-tab-content{box-shadow:0 -2px var(--sd-color-tabs-overline),0 1px var(--sd-color-tabs-underline)}.sd-card{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)}.sd-shadow-sm{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-md{box-shadow:0 .3rem .75rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-lg{box-shadow:0 .6rem 1.5rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-card-hover:hover{transform:none}.sd-cards-carousel{gap:.25rem;padding:.25rem}body{--tabs--label-text:var(--color-foreground-muted);--tabs--label-text--hover:var(--color-foreground-muted);--tabs--label-text--active:var(--color-brand-content);--tabs--label-text--active--hover:var(--color-brand-content);--tabs--label-background:transparent;--tabs--label-background--hover:transparent;--tabs--label-background--active:transparent;--tabs--label-background--active--hover:transparent;--tabs--padding-x:0.25em;--tabs--margin-x:1em;--tabs--border:var(--color-background-border);--tabs--label-border:transparent;--tabs--label-border--hover:var(--color-foreground-muted);--tabs--label-border--active:var(--color-brand-content);--tabs--label-border--active--hover:var(--color-brand-content)}[role=main] .container{max-width:none;padding-left:0;padding-right:0}.shadow.docutils{border:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)!important}.sphinx-bs .card{background-color:var(--color-background-secondary);color:var(--color-foreground)}
+/*# sourceMappingURL=furo-extensions.css.map*/
\ No newline at end of file
diff --git a/docs/_static/styles/furo-extensions.css.map b/docs/_static/styles/furo-extensions.css.map
new file mode 100644
index 0000000..c26eac7
--- /dev/null
+++ b/docs/_static/styles/furo-extensions.css.map
@@ -0,0 +1 @@
+{"version":3,"file":"styles/furo-extensions.css","mappings":"AAGA,2BACE,oFACA,4CAKE,6CAHA,YACA,eAEA,CACA,kDACE,yCAEF,8CACE,sCAEJ,8CACE,kDAEJ,2BAGE,uBACA,cAHA,gBACA,UAEA,CAGA,yCACE,mBAEF,gDAEE,gDADA,YACA,CACA,sDACE,gDACF,yDACE,sCAEJ,+CACE,UACA,qDACE,UAGF,mDACE,eAEJ,yEAEE,4DAEA,mHASE,mBAPA,kBAEA,YADA,oBAGA,aADA,gBAIA,CAEA,qIAEE,WADA,UACA,CAEJ,uGACE,aAEF,iUAGE,cAEF,mHACE,aC1EJ,gCACE,mCAEF,0BAEE,mBAUA,8CACA,YAFA,mCAKA,eAZA,cAIA,YADA,YAYA,iCAdA,YAcA,CAEA,gCAEE,8CADA,gCACA,CAEF,gCAGE,6BADA,mCADA,YAEA,CAEF,kCAEE,cADA,oBACA,CACA,wCACE,cAEJ,8BACE,UCzCN,KAEE,6CAA8C,CAC9C,uDAAwD,CACxD,uDAAwD,CAGxD,iCAAsC,CAGtC,+CAAgD,CAChD,uDAAwD,CACxD,uDAAwD,CACxD,oDAAqD,CACrD,6DAA8D,CAC9D,6DAA8D,CAG9D,uDAAwD,CACxD,yDAA0D,CAC1D,4DAA6D,CAC7D,2DAA4D,CAC5D,8DAA+D,CAC/D,iEAAkE,CAClE,uDAAwD,CACxD,wDAAyD,CAG3D,gBACE,qFAGF,SACE,6EAEF,cACE,uFAEF,cACE,uFAEF,cACE,uFAGF,qBACE,eAEF,mBACE,WACA,eChDF,KACE,gDAAiD,CACjD,uDAAwD,CACxD,qDAAsD,CACtD,4DAA6D,CAC7D,oCAAqC,CACrC,2CAA4C,CAC5C,4CAA6C,CAC7C,mDAAoD,CACpD,wBAAyB,CACzB,oBAAqB,CACrB,6CAA8C,CAC9C,gCAAiC,CACjC,yDAA0D,CAC1D,uDAAwD,CACxD,8DAA+D,CCbjE,uBACE,eACA,eACA,gBAGF,iBACE,YACA,+EAGF,iBACE,mDACA","sources":["webpack:///./src/furo/assets/styles/extensions/_readthedocs.sass","webpack:///./src/furo/assets/styles/extensions/_copybutton.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-design.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-inline-tabs.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-panels.sass"],"sourcesContent":["// This file contains the styles used for tweaking how ReadTheDoc's embedded\n// contents would show up inside the theme.\n\n#furo-sidebar-ad-placement\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n .ethical-sidebar\n // Remove the border and box-shadow.\n border: none\n box-shadow: none\n // Manage the background colors.\n background: var(--color-background-secondary)\n &:hover\n background: var(--color-background-hover)\n // Ensure the text is legible.\n a\n color: var(--color-foreground-primary)\n\n .ethical-callout a\n color: var(--color-foreground-secondary) !important\n\n#furo-readthedocs-versions\n position: static\n width: 100%\n background: transparent\n display: block\n\n // Make the background color fit with the theme's aesthetic.\n .rst-versions\n background: rgb(26, 28, 30)\n\n .rst-current-version\n cursor: unset\n background: var(--color-sidebar-item-background)\n &:hover\n background: var(--color-sidebar-item-background)\n .fa-book\n color: var(--color-foreground-primary)\n\n > .rst-other-versions\n padding: 0\n small\n opacity: 1\n\n .injected\n .rst-versions\n position: unset\n\n &:hover,\n &:focus-within\n box-shadow: 0 0 0 1px var(--color-sidebar-background-border)\n\n .rst-current-version\n // Undo the tweaks done in RTD's CSS\n font-size: inherit\n line-height: inherit\n height: auto\n text-align: right\n padding: 12px\n\n // Match the rest of the body\n background: #1a1c1e\n\n .fa-book\n float: left\n color: white\n\n .fa-caret-down\n display: none\n\n .rst-current-version,\n .rst-other-versions,\n .injected\n display: block\n\n > .rst-current-version\n display: none\n",".highlight\n &:hover button.copybtn\n color: var(--color-code-foreground)\n\n button.copybtn\n // Align things correctly\n align-items: center\n\n height: 1.25em\n width: 1.25em\n\n top: 0.625rem // $code-spacing-vertical\n right: 0.5rem\n\n // Make it look better\n color: var(--color-background-item)\n background-color: var(--color-code-background)\n border: none\n\n // Change to cursor to make it obvious that you can click on it\n cursor: pointer\n\n // Transition smoothly, for aesthetics\n transition: color 300ms, opacity 300ms\n\n &:hover\n color: var(--color-brand-content)\n background-color: var(--color-code-background)\n\n &::after\n display: none\n color: var(--color-code-foreground)\n background-color: transparent\n\n &.success\n transition: color 0ms\n color: #22863a\n &::after\n display: block\n\n svg\n padding: 0\n","body\n // Colors\n --sd-color-primary: var(--color-brand-primary)\n --sd-color-primary-highlight: var(--color-brand-content)\n --sd-color-primary-text: var(--color-background-primary)\n\n // Shadows\n --sd-color-shadow: rgba(0, 0, 0, 0.05)\n\n // Cards\n --sd-color-card-border: var(--color-card-border)\n --sd-color-card-border-hover: var(--color-brand-content)\n --sd-color-card-background: var(--color-card-background)\n --sd-color-card-text: var(--color-foreground-primary)\n --sd-color-card-header: var(--color-card-marginals-background)\n --sd-color-card-footer: var(--color-card-marginals-background)\n\n // Tabs\n --sd-color-tabs-label-active: var(--color-brand-content)\n --sd-color-tabs-label-hover: var(--color-foreground-muted)\n --sd-color-tabs-label-inactive: var(--color-foreground-muted)\n --sd-color-tabs-underline-active: var(--color-brand-content)\n --sd-color-tabs-underline-hover: var(--color-foreground-border)\n --sd-color-tabs-underline-inactive: var(--color-background-border)\n --sd-color-tabs-overline: var(--color-background-border)\n --sd-color-tabs-underline: var(--color-background-border)\n\n// Tabs\n.sd-tab-content\n box-shadow: 0 -2px var(--sd-color-tabs-overline), 0 1px var(--sd-color-tabs-underline)\n\n// Shadows\n.sd-card // Have a shadow by default\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n.sd-shadow-sm\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-md\n box-shadow: 0 0.3rem 0.75rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-lg\n box-shadow: 0 0.6rem 1.5rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Cards\n.sd-card-hover:hover // Don't change scale on hover\n transform: none\n\n.sd-cards-carousel // Have a bit of gap in the carousel by default\n gap: 0.25rem\n padding: 0.25rem\n","// This file contains styles to tweak sphinx-inline-tabs to work well with Furo.\n\nbody\n --tabs--label-text: var(--color-foreground-muted)\n --tabs--label-text--hover: var(--color-foreground-muted)\n --tabs--label-text--active: var(--color-brand-content)\n --tabs--label-text--active--hover: var(--color-brand-content)\n --tabs--label-background: transparent\n --tabs--label-background--hover: transparent\n --tabs--label-background--active: transparent\n --tabs--label-background--active--hover: transparent\n --tabs--padding-x: 0.25em\n --tabs--margin-x: 1em\n --tabs--border: var(--color-background-border)\n --tabs--label-border: transparent\n --tabs--label-border--hover: var(--color-foreground-muted)\n --tabs--label-border--active: var(--color-brand-content)\n --tabs--label-border--active--hover: var(--color-brand-content)\n","// This file contains styles to tweak sphinx-panels to work well with Furo.\n\n// sphinx-panels includes Bootstrap 4, which uses .container which can conflict\n// with docutils' `.. container::` directive.\n[role=\"main\"] .container\n max-width: initial\n padding-left: initial\n padding-right: initial\n\n// Make the panels look nicer!\n.shadow.docutils\n border: none\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Make panel colors respond to dark mode\n.sphinx-bs .card\n background-color: var(--color-background-secondary)\n color: var(--color-foreground)\n"],"names":[],"sourceRoot":""}
\ No newline at end of file
diff --git a/docs/_static/styles/furo.css b/docs/_static/styles/furo.css
new file mode 100644
index 0000000..05a56b1
--- /dev/null
+++ b/docs/_static/styles/furo.css
@@ -0,0 +1,2 @@
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}@media print{.content-icon-container,.headerlink,.mobile-header,.related-pages{display:none!important}.highlight{border:.1pt solid var(--color-foreground-border)}a,blockquote,dl,ol,p,pre,table,ul{page-break-inside:avoid}caption,figure,h1,h2,h3,h4,h5,h6,img{page-break-after:avoid;page-break-inside:avoid}dl,ol,ul{page-break-before:avoid}}.visually-hidden{height:1px!important;margin:-1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;clip:rect(0,0,0,0)!important;background:var(--color-background-primary);border:0!important;color:var(--color-foreground-primary);white-space:nowrap!important}:-moz-focusring{outline:auto}body{--font-stack:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;--font-stack--monospace:"SFMono-Regular",Menlo,Consolas,Monaco,Liberation Mono,Lucida Console,monospace;--font-stack--headings:var(--font-stack);--font-size--normal:100%;--font-size--small:87.5%;--font-size--small--2:81.25%;--font-size--small--3:75%;--font-size--small--4:62.5%;--sidebar-caption-font-size:var(--font-size--small--2);--sidebar-item-font-size:var(--font-size--small);--sidebar-search-input-font-size:var(--font-size--small);--toc-font-size:var(--font-size--small--3);--toc-font-size--mobile:var(--font-size--normal);--toc-title-font-size:var(--font-size--small--4);--admonition-font-size:0.8125rem;--admonition-title-font-size:0.8125rem;--code-font-size:var(--font-size--small--2);--api-font-size:var(--font-size--small);--header-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*4);--header-padding:0.5rem;--sidebar-tree-space-above:1.5rem;--sidebar-caption-space-above:1rem;--sidebar-item-line-height:1rem;--sidebar-item-spacing-vertical:0.5rem;--sidebar-item-spacing-horizontal:1rem;--sidebar-item-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*2);--sidebar-expander-width:var(--sidebar-item-height);--sidebar-search-space-above:0.5rem;--sidebar-search-input-spacing-vertical:0.5rem;--sidebar-search-input-spacing-horizontal:0.5rem;--sidebar-search-input-height:1rem;--sidebar-search-icon-size:var(--sidebar-search-input-height);--toc-title-padding:0.25rem 0;--toc-spacing-vertical:1.5rem;--toc-spacing-horizontal:1.5rem;--toc-item-spacing-vertical:0.4rem;--toc-item-spacing-horizontal:1rem;--icon-search:url('data:image/svg+xml;charset=utf-8,');--icon-pencil:url('data:image/svg+xml;charset=utf-8,');--icon-abstract:url('data:image/svg+xml;charset=utf-8,');--icon-info:url('data:image/svg+xml;charset=utf-8,');--icon-flame:url('data:image/svg+xml;charset=utf-8,');--icon-question:url('data:image/svg+xml;charset=utf-8,');--icon-warning:url('data:image/svg+xml;charset=utf-8,');--icon-failure:url('data:image/svg+xml;charset=utf-8,');--icon-spark:url('data:image/svg+xml;charset=utf-8,');--color-admonition-title--caution:#ff9100;--color-admonition-title-background--caution:rgba(255,145,0,.2);--color-admonition-title--warning:#ff9100;--color-admonition-title-background--warning:rgba(255,145,0,.2);--color-admonition-title--danger:#ff5252;--color-admonition-title-background--danger:rgba(255,82,82,.2);--color-admonition-title--attention:#ff5252;--color-admonition-title-background--attention:rgba(255,82,82,.2);--color-admonition-title--error:#ff5252;--color-admonition-title-background--error:rgba(255,82,82,.2);--color-admonition-title--hint:#00c852;--color-admonition-title-background--hint:rgba(0,200,82,.2);--color-admonition-title--tip:#00c852;--color-admonition-title-background--tip:rgba(0,200,82,.2);--color-admonition-title--important:#00bfa5;--color-admonition-title-background--important:rgba(0,191,165,.2);--color-admonition-title--note:#00b0ff;--color-admonition-title-background--note:rgba(0,176,255,.2);--color-admonition-title--seealso:#448aff;--color-admonition-title-background--seealso:rgba(68,138,255,.2);--color-admonition-title--admonition-todo:grey;--color-admonition-title-background--admonition-todo:hsla(0,0%,50%,.2);--color-admonition-title:#651fff;--color-admonition-title-background:rgba(101,31,255,.2);--icon-admonition-default:var(--icon-abstract);--color-topic-title:#14b8a6;--color-topic-title-background:rgba(20,184,166,.2);--icon-topic-default:var(--icon-pencil);--color-problematic:#b30000;--color-foreground-primary:#000;--color-foreground-secondary:#5a5c63;--color-foreground-muted:#6b6f76;--color-foreground-border:#878787;--color-background-primary:#fff;--color-background-secondary:#f8f9fb;--color-background-hover:#efeff4;--color-background-hover--transparent:#efeff400;--color-background-border:#eeebee;--color-background-item:#ccc;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#0a4bff;--color-brand-content:#2757dd;--color-brand-visited:#872ee0;--color-api-background:var(--color-background-hover--transparent);--color-api-background-hover:var(--color-background-hover);--color-api-overall:var(--color-foreground-secondary);--color-api-name:var(--color-problematic);--color-api-pre-name:var(--color-problematic);--color-api-paren:var(--color-foreground-secondary);--color-api-keyword:var(--color-foreground-primary);--color-api-added:#21632c;--color-api-added-border:#38a84d;--color-api-changed:#046172;--color-api-changed-border:#06a1bc;--color-api-deprecated:#605706;--color-api-deprecated-border:#f0d90f;--color-api-removed:#b30000;--color-api-removed-border:#ff5c5c;--color-highlight-on-target:#ffc;--color-inline-code-background:var(--color-background-secondary);--color-highlighted-background:#def;--color-highlighted-text:var(--color-foreground-primary);--color-guilabel-background:#ddeeff80;--color-guilabel-border:#bedaf580;--color-guilabel-text:var(--color-foreground-primary);--color-admonition-background:transparent;--color-table-header-background:var(--color-background-secondary);--color-table-border:var(--color-background-border);--color-card-border:var(--color-background-secondary);--color-card-background:transparent;--color-card-marginals-background:var(--color-background-secondary);--color-header-background:var(--color-background-primary);--color-header-border:var(--color-background-border);--color-header-text:var(--color-foreground-primary);--color-sidebar-background:var(--color-background-secondary);--color-sidebar-background-border:var(--color-background-border);--color-sidebar-brand-text:var(--color-foreground-primary);--color-sidebar-caption-text:var(--color-foreground-muted);--color-sidebar-link-text:var(--color-foreground-secondary);--color-sidebar-link-text--top-level:var(--color-brand-primary);--color-sidebar-item-background:var(--color-sidebar-background);--color-sidebar-item-background--current:var( --color-sidebar-item-background );--color-sidebar-item-background--hover:linear-gradient(90deg,var(--color-background-hover--transparent) 0%,var(--color-background-hover) var(--sidebar-item-spacing-horizontal),var(--color-background-hover) 100%);--color-sidebar-item-expander-background:transparent;--color-sidebar-item-expander-background--hover:var( --color-background-hover );--color-sidebar-search-text:var(--color-foreground-primary);--color-sidebar-search-background:var(--color-background-secondary);--color-sidebar-search-background--focus:var(--color-background-primary);--color-sidebar-search-border:var(--color-background-border);--color-sidebar-search-icon:var(--color-foreground-muted);--color-toc-background:var(--color-background-primary);--color-toc-title-text:var(--color-foreground-muted);--color-toc-item-text:var(--color-foreground-secondary);--color-toc-item-text--hover:var(--color-foreground-primary);--color-toc-item-text--active:var(--color-brand-primary);--color-content-foreground:var(--color-foreground-primary);--color-content-background:transparent;--color-link:var(--color-brand-content);--color-link-underline:var(--color-background-border);--color-link--hover:var(--color-brand-content);--color-link-underline--hover:var(--color-foreground-border);--color-link--visited:var(--color-brand-visited);--color-link-underline--visited:var(--color-background-border);--color-link--visited--hover:var(--color-brand-visited);--color-link-underline--visited--hover:var(--color-foreground-border)}.only-light{display:block!important}html body .only-dark{display:none!important}@media not print{body[data-theme=dark]{--color-problematic:#ee5151;--color-foreground-primary:#cfd0d0;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#3d94ff;--color-brand-content:#5ca5ff;--color-brand-visited:#b27aeb;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-api-added:#3db854;--color-api-added-border:#267334;--color-api-changed:#09b0ce;--color-api-changed-border:#056d80;--color-api-deprecated:#b1a10b;--color-api-deprecated-border:#6e6407;--color-api-removed:#ff7575;--color-api-removed-border:#b03b3b;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body[data-theme=dark] .only-light{display:none!important}body[data-theme=dark] .only-dark{display:block!important}@media(prefers-color-scheme:dark){body:not([data-theme=light]){--color-problematic:#ee5151;--color-foreground-primary:#cfd0d0;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#3d94ff;--color-brand-content:#5ca5ff;--color-brand-visited:#b27aeb;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-api-added:#3db854;--color-api-added-border:#267334;--color-api-changed:#09b0ce;--color-api-changed-border:#056d80;--color-api-deprecated:#b1a10b;--color-api-deprecated-border:#6e6407;--color-api-removed:#ff7575;--color-api-removed-border:#b03b3b;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body:not([data-theme=light]) .only-light{display:none!important}body:not([data-theme=light]) .only-dark{display:block!important}}}body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto-light{display:block}@media(prefers-color-scheme:dark){body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto-dark{display:block}body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto-light{display:none}}body[data-theme=dark] .theme-toggle svg.theme-icon-when-dark,body[data-theme=light] .theme-toggle svg.theme-icon-when-light{display:block}body{font-family:var(--font-stack)}code,kbd,pre,samp{font-family:var(--font-stack--monospace)}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}article{line-height:1.5}h1,h2,h3,h4,h5,h6{border-radius:.5rem;font-family:var(--font-stack--headings);font-weight:700;line-height:1.25;margin:.5rem -.5rem;padding-left:.5rem;padding-right:.5rem}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:0}h1{font-size:2.5em;margin-bottom:1rem}h1,h2{margin-top:1.75rem}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.25em}h5{font-size:1.125em}h6{font-size:1em}small{font-size:80%;opacity:75%}p{margin-bottom:.75rem;margin-top:.5rem}hr.docutils{background-color:var(--color-background-border);border:0;height:1px;margin:2rem 0;padding:0}.centered{text-align:center}a{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}a:visited{color:var(--color-link--visited);text-decoration-color:var(--color-link-underline--visited)}a:visited:hover{color:var(--color-link--visited--hover);text-decoration-color:var(--color-link-underline--visited--hover)}a:hover{color:var(--color-link--hover);text-decoration-color:var(--color-link-underline--hover)}a.muted-link{color:inherit}a.muted-link:hover{color:var(--color-link--hover);text-decoration-color:var(--color-link-underline--hover)}a.muted-link:hover:visited{color:var(--color-link--visited--hover);text-decoration-color:var(--color-link-underline--visited--hover)}html{overflow-x:hidden;overflow-y:scroll;scroll-behavior:smooth}.sidebar-scroll,.toc-scroll,article[role=main] *{scrollbar-color:var(--color-foreground-border) transparent;scrollbar-width:thin}.sidebar-scroll::-webkit-scrollbar,.toc-scroll::-webkit-scrollbar,article[role=main] ::-webkit-scrollbar{height:.25rem;width:.25rem}.sidebar-scroll::-webkit-scrollbar-thumb,.toc-scroll::-webkit-scrollbar-thumb,article[role=main] ::-webkit-scrollbar-thumb{background-color:var(--color-foreground-border);border-radius:.125rem}body,html{height:100%}.skip-to-content,body,html{background:var(--color-background-primary);color:var(--color-foreground-primary)}.skip-to-content{border-radius:1rem;left:.25rem;padding:1rem;position:fixed;top:.25rem;transform:translateY(-200%);transition:transform .3s ease-in-out;z-index:40}.skip-to-content:focus-within{transform:translateY(0)}article{background:var(--color-content-background);color:var(--color-content-foreground);overflow-wrap:break-word}.page{display:flex;min-height:100%}.mobile-header{background-color:var(--color-header-background);border-bottom:1px solid var(--color-header-border);color:var(--color-header-text);display:none;height:var(--header-height);width:100%;z-index:10}.mobile-header.scrolled{border-bottom:none;box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2)}.mobile-header .header-center a{color:var(--color-header-text);text-decoration:none}.main{display:flex;flex:1}.sidebar-drawer{background:var(--color-sidebar-background);border-right:1px solid var(--color-sidebar-background-border);box-sizing:border-box;display:flex;justify-content:flex-end;min-width:15em;width:calc(50% - 26em)}.sidebar-container,.toc-drawer{box-sizing:border-box;width:15em}.toc-drawer{background:var(--color-toc-background);padding-right:1rem}.sidebar-sticky,.toc-sticky{display:flex;flex-direction:column;height:min(100%,100vh);height:100vh;position:sticky;top:0}.sidebar-scroll,.toc-scroll{flex-grow:1;flex-shrink:1;overflow:auto;scroll-behavior:smooth}.content{display:flex;flex-direction:column;justify-content:space-between;padding:0 3em;width:46em}.icon{display:inline-block;height:1rem;width:1rem}.icon svg{height:100%;width:100%}.announcement{align-items:center;background-color:var(--color-announcement-background);color:var(--color-announcement-text);display:flex;height:var(--header-height);overflow-x:auto}.announcement+.page{min-height:calc(100% - var(--header-height))}.announcement-content{box-sizing:border-box;min-width:100%;padding:.5rem;text-align:center;white-space:nowrap}.announcement-content a{color:var(--color-announcement-text);text-decoration-color:var(--color-announcement-text)}.announcement-content a:hover{color:var(--color-announcement-text);text-decoration-color:var(--color-link--hover)}.no-js .theme-toggle-container{display:none}.theme-toggle-container{display:flex}.theme-toggle{background:transparent;border:none;cursor:pointer;display:flex;padding:0}.theme-toggle svg{color:var(--color-foreground-primary);display:none;height:1.25rem;width:1.25rem}.theme-toggle-header{align-items:center;display:flex;justify-content:center}.nav-overlay-icon,.toc-overlay-icon{cursor:pointer;display:none}.nav-overlay-icon .icon,.toc-overlay-icon .icon{color:var(--color-foreground-secondary);height:1.5rem;width:1.5rem}.nav-overlay-icon,.toc-header-icon{align-items:center;justify-content:center}.toc-content-icon{height:1.5rem;width:1.5rem}.content-icon-container{display:flex;float:right;gap:.5rem;margin-bottom:1rem;margin-left:1rem;margin-top:1.5rem}.content-icon-container .edit-this-page svg,.content-icon-container .view-this-page svg{color:inherit;height:1.25rem;width:1.25rem}.sidebar-toggle{display:none;position:absolute}.sidebar-toggle[name=__toc]{left:20px}.sidebar-toggle:checked{left:40px}.overlay{background-color:rgba(0,0,0,.54);height:0;opacity:0;position:fixed;top:0;transition:width 0ms,height 0ms,opacity .25s ease-out;width:0}.sidebar-overlay{z-index:20}.toc-overlay{z-index:40}.sidebar-drawer{transition:left .25s ease-in-out;z-index:30}.toc-drawer{transition:right .25s ease-in-out;z-index:50}#__navigation:checked~.sidebar-overlay{height:100%;opacity:1;width:100%}#__navigation:checked~.page .sidebar-drawer{left:0;top:0}#__toc:checked~.toc-overlay{height:100%;opacity:1;width:100%}#__toc:checked~.page .toc-drawer{right:0;top:0}.back-to-top{background:var(--color-background-primary);border-radius:1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 1px 0 hsla(220,9%,46%,.502);display:none;font-size:.8125rem;left:0;margin-left:50%;padding:.5rem .75rem .5rem .5rem;position:fixed;text-decoration:none;top:1rem;transform:translateX(-50%);z-index:10}.back-to-top svg{height:1rem;width:1rem;fill:currentColor;display:inline-block}.back-to-top span{margin-left:.25rem}.show-back-to-top .back-to-top{align-items:center;display:flex}@media(min-width:97em){html{font-size:110%}}@media(max-width:82em){.toc-content-icon{display:flex}.toc-drawer{border-left:1px solid var(--color-background-muted);height:100vh;position:fixed;right:-15em;top:0}.toc-tree{border-left:none;font-size:var(--toc-font-size--mobile)}.sidebar-drawer{width:calc(50% - 18.5em)}}@media(max-width:67em){.content{margin-left:auto;margin-right:auto;padding:0 1em}}@media(max-width:63em){.nav-overlay-icon{display:flex}.sidebar-drawer{height:100vh;left:-15em;position:fixed;top:0;width:15em}.theme-toggle-header,.toc-header-icon{display:flex}.theme-toggle-content,.toc-content-icon{display:none}.mobile-header{align-items:center;display:flex;justify-content:space-between;position:sticky;top:0}.mobile-header .header-left,.mobile-header .header-right{display:flex;height:var(--header-height);padding:0 var(--header-padding)}.mobile-header .header-left label,.mobile-header .header-right label{height:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.nav-overlay-icon .icon,.theme-toggle svg{height:1.5rem;width:1.5rem}:target{scroll-margin-top:calc(var(--header-height) + 2.5rem)}.back-to-top{top:calc(var(--header-height) + .5rem)}.page{flex-direction:column;justify-content:center}}@media(max-width:48em){.content{overflow-x:auto;width:100%}}@media(max-width:46em){article[role=main] aside.sidebar{float:none;margin:1rem 0;width:100%}}.admonition,.topic{background:var(--color-admonition-background);border-radius:.2rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1);font-size:var(--admonition-font-size);margin:1rem auto;overflow:hidden;padding:0 .5rem .5rem;page-break-inside:avoid}.admonition>:nth-child(2),.topic>:nth-child(2){margin-top:0}.admonition>:last-child,.topic>:last-child{margin-bottom:0}.admonition p.admonition-title,p.topic-title{font-size:var(--admonition-title-font-size);font-weight:500;line-height:1.3;margin:0 -.5rem .5rem;padding:.4rem .5rem .4rem 2rem;position:relative}.admonition p.admonition-title:before,p.topic-title:before{content:"";height:1rem;left:.5rem;position:absolute;width:1rem}p.admonition-title{background-color:var(--color-admonition-title-background)}p.admonition-title:before{background-color:var(--color-admonition-title);-webkit-mask-image:var(--icon-admonition-default);mask-image:var(--icon-admonition-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}p.topic-title{background-color:var(--color-topic-title-background)}p.topic-title:before{background-color:var(--color-topic-title);-webkit-mask-image:var(--icon-topic-default);mask-image:var(--icon-topic-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.admonition{border-left:.2rem solid var(--color-admonition-title)}.admonition.caution{border-left-color:var(--color-admonition-title--caution)}.admonition.caution>.admonition-title{background-color:var(--color-admonition-title-background--caution)}.admonition.caution>.admonition-title:before{background-color:var(--color-admonition-title--caution);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.warning{border-left-color:var(--color-admonition-title--warning)}.admonition.warning>.admonition-title{background-color:var(--color-admonition-title-background--warning)}.admonition.warning>.admonition-title:before{background-color:var(--color-admonition-title--warning);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.danger{border-left-color:var(--color-admonition-title--danger)}.admonition.danger>.admonition-title{background-color:var(--color-admonition-title-background--danger)}.admonition.danger>.admonition-title:before{background-color:var(--color-admonition-title--danger);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.attention{border-left-color:var(--color-admonition-title--attention)}.admonition.attention>.admonition-title{background-color:var(--color-admonition-title-background--attention)}.admonition.attention>.admonition-title:before{background-color:var(--color-admonition-title--attention);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.error{border-left-color:var(--color-admonition-title--error)}.admonition.error>.admonition-title{background-color:var(--color-admonition-title-background--error)}.admonition.error>.admonition-title:before{background-color:var(--color-admonition-title--error);-webkit-mask-image:var(--icon-failure);mask-image:var(--icon-failure)}.admonition.hint{border-left-color:var(--color-admonition-title--hint)}.admonition.hint>.admonition-title{background-color:var(--color-admonition-title-background--hint)}.admonition.hint>.admonition-title:before{background-color:var(--color-admonition-title--hint);-webkit-mask-image:var(--icon-question);mask-image:var(--icon-question)}.admonition.tip{border-left-color:var(--color-admonition-title--tip)}.admonition.tip>.admonition-title{background-color:var(--color-admonition-title-background--tip)}.admonition.tip>.admonition-title:before{background-color:var(--color-admonition-title--tip);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.important{border-left-color:var(--color-admonition-title--important)}.admonition.important>.admonition-title{background-color:var(--color-admonition-title-background--important)}.admonition.important>.admonition-title:before{background-color:var(--color-admonition-title--important);-webkit-mask-image:var(--icon-flame);mask-image:var(--icon-flame)}.admonition.note{border-left-color:var(--color-admonition-title--note)}.admonition.note>.admonition-title{background-color:var(--color-admonition-title-background--note)}.admonition.note>.admonition-title:before{background-color:var(--color-admonition-title--note);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition.seealso{border-left-color:var(--color-admonition-title--seealso)}.admonition.seealso>.admonition-title{background-color:var(--color-admonition-title-background--seealso)}.admonition.seealso>.admonition-title:before{background-color:var(--color-admonition-title--seealso);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.admonition-todo{border-left-color:var(--color-admonition-title--admonition-todo)}.admonition.admonition-todo>.admonition-title{background-color:var(--color-admonition-title-background--admonition-todo)}.admonition.admonition-todo>.admonition-title:before{background-color:var(--color-admonition-title--admonition-todo);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition-todo>.admonition-title{text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd{margin-left:2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:first-child{margin-top:.125rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list,dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:last-child{margin-bottom:.75rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list>dt{font-size:var(--font-size--small);text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd:empty{margin-bottom:.5rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul{margin-left:-1.2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p:nth-child(2){margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p+p:last-child:empty{margin-bottom:0;margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt{color:var(--color-api-overall)}.sig:not(.sig-inline){background:var(--color-api-background);border-radius:.25rem;font-family:var(--font-stack--monospace);font-size:var(--api-font-size);font-weight:700;margin-left:-.25rem;margin-right:-.25rem;padding:.25rem .5rem .25rem 3em;text-indent:-2.5em;transition:background .1s ease-out}.sig:not(.sig-inline):hover{background:var(--color-api-background-hover)}.sig:not(.sig-inline) a.reference .viewcode-link{font-weight:400;width:4.25rem}em.property{font-style:normal}em.property:first-child{color:var(--color-api-keyword)}.sig-name{color:var(--color-api-name)}.sig-prename{color:var(--color-api-pre-name);font-weight:400}.sig-paren{color:var(--color-api-paren)}.sig-param{font-style:normal}div.deprecated,div.versionadded,div.versionchanged,div.versionremoved{border-left:.1875rem solid;border-radius:.125rem;padding-left:.75rem}div.deprecated p,div.versionadded p,div.versionchanged p,div.versionremoved p{margin-bottom:.125rem;margin-top:.125rem}div.versionadded{border-color:var(--color-api-added-border)}div.versionadded .versionmodified{color:var(--color-api-added)}div.versionchanged{border-color:var(--color-api-changed-border)}div.versionchanged .versionmodified{color:var(--color-api-changed)}div.deprecated{border-color:var(--color-api-deprecated-border)}div.deprecated .versionmodified{color:var(--color-api-deprecated)}div.versionremoved{border-color:var(--color-api-removed-border)}div.versionremoved .versionmodified{color:var(--color-api-removed)}.viewcode-back,.viewcode-link{float:right;text-align:right}.line-block{margin-bottom:.75rem;margin-top:.5rem}.line-block .line-block{margin-bottom:0;margin-top:0;padding-left:1rem}.code-block-caption,article p.caption,table>caption{font-size:var(--font-size--small);text-align:center}.toctree-wrapper.compound .caption,.toctree-wrapper.compound :not(.caption)>.caption-text{font-size:var(--font-size--small);margin-bottom:0;text-align:initial;text-transform:uppercase}.toctree-wrapper.compound>ul{margin-bottom:0;margin-top:0}.sig-inline,code.literal{background:var(--color-inline-code-background);border-radius:.2em;font-size:var(--font-size--small--2);padding:.1em .2em}pre.literal-block .sig-inline,pre.literal-block code.literal{font-size:inherit;padding:0}p .sig-inline,p code.literal{border:1px solid var(--color-background-border)}.sig-inline{font-family:var(--font-stack--monospace)}div[class*=" highlight-"],div[class^=highlight-]{display:flex;margin:1em 0}div[class*=" highlight-"] .table-wrapper,div[class^=highlight-] .table-wrapper,pre{margin:0;padding:0}pre{overflow:auto}article[role=main] .highlight pre{line-height:1.5}.highlight pre,pre.literal-block{font-size:var(--code-font-size);padding:.625rem .875rem}pre.literal-block{background-color:var(--color-code-background);border-radius:.2rem;color:var(--color-code-foreground);margin-bottom:1rem;margin-top:1rem}.highlight{border-radius:.2rem;width:100%}.highlight .gp,.highlight span.linenos{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.highlight .hll{display:block;margin-left:-.875rem;margin-right:-.875rem;padding-left:.875rem;padding-right:.875rem}.code-block-caption{background-color:var(--color-code-background);border-bottom:1px solid;border-radius:.25rem;border-bottom-left-radius:0;border-bottom-right-radius:0;border-color:var(--color-background-border);color:var(--color-code-foreground);display:flex;font-weight:300;padding:.625rem .875rem}.code-block-caption+div[class]{margin-top:0}.code-block-caption+div[class] pre{border-top-left-radius:0;border-top-right-radius:0}.highlighttable{display:block;width:100%}.highlighttable tbody{display:block}.highlighttable tr{display:flex}.highlighttable td.linenos{background-color:var(--color-code-background);border-bottom-left-radius:.2rem;border-top-left-radius:.2rem;color:var(--color-code-foreground);padding:.625rem 0 .625rem .875rem}.highlighttable .linenodiv{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;font-size:var(--code-font-size);padding-right:.875rem}.highlighttable td.code{display:block;flex:1;overflow:hidden;padding:0}.highlighttable td.code .highlight{border-bottom-left-radius:0;border-top-left-radius:0}.highlight span.linenos{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;display:inline-block;margin-right:.875rem;padding-left:0;padding-right:.875rem}.footnote-reference{font-size:var(--font-size--small--4);vertical-align:super}dl.footnote.brackets{color:var(--color-foreground-secondary);display:grid;font-size:var(--font-size--small);grid-template-columns:max-content auto}dl.footnote.brackets dt{margin:0}dl.footnote.brackets dt>.fn-backref{margin-left:.25rem}dl.footnote.brackets dt:after{content:":"}dl.footnote.brackets dt .brackets:before{content:"["}dl.footnote.brackets dt .brackets:after{content:"]"}dl.footnote.brackets dd{margin:0;padding:0 1rem}aside.footnote{color:var(--color-foreground-secondary);font-size:var(--font-size--small)}aside.footnote>span,div.citation>span{float:left;font-weight:500;padding-right:.25rem}aside.footnote>:not(span),div.citation>p{margin-left:2rem}img{box-sizing:border-box;height:auto;max-width:100%}article .figure,article figure{border-radius:.2rem;margin:0}article .figure :last-child,article figure :last-child{margin-bottom:0}article .align-left{clear:left;float:left;margin:0 1rem 1rem}article .align-right{clear:right;float:right;margin:0 1rem 1rem}article .align-center,article .align-default{display:block;margin-left:auto;margin-right:auto;text-align:center}article table.align-default{display:table;text-align:initial}.domainindex-jumpbox,.genindex-jumpbox{border-bottom:1px solid var(--color-background-border);border-top:1px solid var(--color-background-border);padding:.25rem}.domainindex-section h2,.genindex-section h2{margin-bottom:.5rem;margin-top:.75rem}.domainindex-section ul,.genindex-section ul{margin-bottom:0;margin-top:0}ol,ul{margin-bottom:1rem;margin-top:1rem;padding-left:1.2rem}ol li>p:first-child,ul li>p:first-child{margin-bottom:.25rem;margin-top:.25rem}ol li>p:last-child,ul li>p:last-child{margin-top:.25rem}ol li>ol,ol li>ul,ul li>ol,ul li>ul{margin-bottom:.5rem;margin-top:.5rem}ol.arabic{list-style:decimal}ol.loweralpha{list-style:lower-alpha}ol.upperalpha{list-style:upper-alpha}ol.lowerroman{list-style:lower-roman}ol.upperroman{list-style:upper-roman}.simple li>ol,.simple li>ul,.toctree-wrapper li>ol,.toctree-wrapper li>ul{margin-bottom:0;margin-top:0}.field-list dt,.option-list dt,dl.footnote dt,dl.glossary dt,dl.simple dt,dl:not([class]) dt{font-weight:500;margin-top:.25rem}.field-list dt+dt,.option-list dt+dt,dl.footnote dt+dt,dl.glossary dt+dt,dl.simple dt+dt,dl:not([class]) dt+dt{margin-top:0}.field-list dt .classifier:before,.option-list dt .classifier:before,dl.footnote dt .classifier:before,dl.glossary dt .classifier:before,dl.simple dt .classifier:before,dl:not([class]) dt .classifier:before{content:":";margin-left:.2rem;margin-right:.2rem}.field-list dd ul,.field-list dd>p:first-child,.option-list dd ul,.option-list dd>p:first-child,dl.footnote dd ul,dl.footnote dd>p:first-child,dl.glossary dd ul,dl.glossary dd>p:first-child,dl.simple dd ul,dl.simple dd>p:first-child,dl:not([class]) dd ul,dl:not([class]) dd>p:first-child{margin-top:.125rem}.field-list dd ul,.option-list dd ul,dl.footnote dd ul,dl.glossary dd ul,dl.simple dd ul,dl:not([class]) dd ul{margin-bottom:.125rem}.math-wrapper{overflow-x:auto;width:100%}div.math{position:relative;text-align:center}div.math .headerlink,div.math:focus .headerlink{display:none}div.math:hover .headerlink{display:inline-block}div.math span.eqno{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);z-index:1}abbr[title]{cursor:help}.problematic{color:var(--color-problematic)}kbd:not(.compound){background-color:var(--color-background-secondary);border:1px solid var(--color-foreground-border);border-radius:.2rem;box-shadow:0 .0625rem 0 rgba(0,0,0,.2),inset 0 0 0 .125rem var(--color-background-primary);color:var(--color-foreground-primary);display:inline-block;font-size:var(--font-size--small--3);margin:0 .2rem;padding:0 .2rem;vertical-align:text-bottom}blockquote{background:var(--color-background-secondary);border-left:4px solid var(--color-background-border);margin-left:0;margin-right:0;padding:.5rem 1rem}blockquote .attribution{font-weight:600;text-align:right}blockquote.highlights,blockquote.pull-quote{font-size:1.25em}blockquote.epigraph,blockquote.pull-quote{border-left-width:0;border-radius:.5rem}blockquote.highlights{background:transparent;border-left-width:0}p .reference img{vertical-align:middle}p.rubric{font-size:1.125em;font-weight:700;line-height:1.25}dd p.rubric{font-size:var(--font-size--small);font-weight:inherit;line-height:inherit;text-transform:uppercase}article .sidebar{background-color:var(--color-background-secondary);border:1px solid var(--color-background-border);border-radius:.2rem;clear:right;float:right;margin-left:1rem;margin-right:0;width:30%}article .sidebar>*{padding-left:1rem;padding-right:1rem}article .sidebar>ol,article .sidebar>ul{padding-left:2.2rem}article .sidebar .sidebar-title{border-bottom:1px solid var(--color-background-border);font-weight:500;margin:0;padding:.5rem 1rem}[role=main] .table-wrapper.container{margin-bottom:.5rem;margin-top:1rem;overflow-x:auto;padding:.2rem .2rem .75rem;width:100%}table.docutils{border-collapse:collapse;border-radius:.2rem;border-spacing:0;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)}table.docutils th{background:var(--color-table-header-background)}table.docutils td,table.docutils th{border-bottom:1px solid var(--color-table-border);border-left:1px solid var(--color-table-border);border-right:1px solid var(--color-table-border);padding:0 .25rem}table.docutils td p,table.docutils th p{margin:.25rem}table.docutils td:first-child,table.docutils th:first-child{border-left:none}table.docutils td:last-child,table.docutils th:last-child{border-right:none}table.docutils td.text-left,table.docutils th.text-left{text-align:left}table.docutils td.text-right,table.docutils th.text-right{text-align:right}table.docutils td.text-center,table.docutils th.text-center{text-align:center}:target{scroll-margin-top:2.5rem}@media(max-width:67em){:target{scroll-margin-top:calc(2.5rem + var(--header-height))}section>span:target{scroll-margin-top:calc(2.8rem + var(--header-height))}}.headerlink{font-weight:100;-webkit-user-select:none;-moz-user-select:none;user-select:none}.code-block-caption>.headerlink,dl dt>.headerlink,figcaption p>.headerlink,h1>.headerlink,h2>.headerlink,h3>.headerlink,h4>.headerlink,h5>.headerlink,h6>.headerlink,p.caption>.headerlink,table>caption>.headerlink{margin-left:.5rem;visibility:hidden}.code-block-caption:hover>.headerlink,dl dt:hover>.headerlink,figcaption p:hover>.headerlink,h1:hover>.headerlink,h2:hover>.headerlink,h3:hover>.headerlink,h4:hover>.headerlink,h5:hover>.headerlink,h6:hover>.headerlink,p.caption:hover>.headerlink,table>caption:hover>.headerlink{visibility:visible}.code-block-caption>.toc-backref,dl dt>.toc-backref,figcaption p>.toc-backref,h1>.toc-backref,h2>.toc-backref,h3>.toc-backref,h4>.toc-backref,h5>.toc-backref,h6>.toc-backref,p.caption>.toc-backref,table>caption>.toc-backref{color:inherit;text-decoration-line:none}figure:hover>figcaption>p>.headerlink,table:hover>caption>.headerlink{visibility:visible}:target>h1:first-of-type,:target>h2:first-of-type,:target>h3:first-of-type,:target>h4:first-of-type,:target>h5:first-of-type,:target>h6:first-of-type,span:target~h1:first-of-type,span:target~h2:first-of-type,span:target~h3:first-of-type,span:target~h4:first-of-type,span:target~h5:first-of-type,span:target~h6:first-of-type{background-color:var(--color-highlight-on-target)}:target>h1:first-of-type code.literal,:target>h2:first-of-type code.literal,:target>h3:first-of-type code.literal,:target>h4:first-of-type code.literal,:target>h5:first-of-type code.literal,:target>h6:first-of-type code.literal,span:target~h1:first-of-type code.literal,span:target~h2:first-of-type code.literal,span:target~h3:first-of-type code.literal,span:target~h4:first-of-type code.literal,span:target~h5:first-of-type code.literal,span:target~h6:first-of-type code.literal{background-color:transparent}.literal-block-wrapper:target .code-block-caption,.this-will-duplicate-information-and-it-is-still-useful-here li :target,figure:target,table:target>caption{background-color:var(--color-highlight-on-target)}dt:target{background-color:var(--color-highlight-on-target)!important}.footnote-reference:target,.footnote>dt:target+dd{background-color:var(--color-highlight-on-target)}.guilabel{background-color:var(--color-guilabel-background);border:1px solid var(--color-guilabel-border);border-radius:.5em;color:var(--color-guilabel-text);font-size:.9em;padding:0 .3em}footer{display:flex;flex-direction:column;font-size:var(--font-size--small);margin-top:2rem}.bottom-of-page{align-items:center;border-top:1px solid var(--color-background-border);color:var(--color-foreground-secondary);display:flex;justify-content:space-between;line-height:1.5;margin-top:1rem;padding-bottom:1rem;padding-top:1rem}@media(max-width:46em){.bottom-of-page{flex-direction:column-reverse;gap:.25rem;text-align:center}}.bottom-of-page .left-details{font-size:var(--font-size--small)}.bottom-of-page .right-details{display:flex;flex-direction:column;gap:.25rem;text-align:right}.bottom-of-page .icons{display:flex;font-size:1rem;gap:.25rem;justify-content:flex-end}.bottom-of-page .icons a{text-decoration:none}.bottom-of-page .icons img,.bottom-of-page .icons svg{font-size:1.125rem;height:1em;width:1em}.related-pages a{align-items:center;display:flex;text-decoration:none}.related-pages a:hover .page-info .title{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}.related-pages a svg.furo-related-icon,.related-pages a svg.furo-related-icon>use{color:var(--color-foreground-border);flex-shrink:0;height:.75rem;margin:0 .5rem;width:.75rem}.related-pages a.next-page{clear:right;float:right;max-width:50%;text-align:right}.related-pages a.prev-page{clear:left;float:left;max-width:50%}.related-pages a.prev-page svg{transform:rotate(180deg)}.page-info{display:flex;flex-direction:column;overflow-wrap:anywhere}.next-page .page-info{align-items:flex-end}.page-info .context{align-items:center;color:var(--color-foreground-muted);display:flex;font-size:var(--font-size--small);padding-bottom:.1rem;text-decoration:none}ul.search{list-style:none;padding-left:0}ul.search li{border-bottom:1px solid var(--color-background-border);padding:1rem 0}[role=main] .highlighted{background-color:var(--color-highlighted-background);color:var(--color-highlighted-text)}.sidebar-brand{display:flex;flex-direction:column;flex-shrink:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none}.sidebar-brand-text{color:var(--color-sidebar-brand-text);font-size:1.5rem;overflow-wrap:break-word}.sidebar-brand-text,.sidebar-logo-container{margin:var(--sidebar-item-spacing-vertical) 0}.sidebar-logo{display:block;margin:0 auto;max-width:100%}.sidebar-search-container{align-items:center;background:var(--color-sidebar-search-background);display:flex;margin-top:var(--sidebar-search-space-above);position:relative}.sidebar-search-container:focus-within,.sidebar-search-container:hover{background:var(--color-sidebar-search-background--focus)}.sidebar-search-container:before{background-color:var(--color-sidebar-search-icon);content:"";height:var(--sidebar-search-icon-size);left:var(--sidebar-item-spacing-horizontal);-webkit-mask-image:var(--icon-search);mask-image:var(--icon-search);position:absolute;width:var(--sidebar-search-icon-size)}.sidebar-search{background:transparent;border:none;border-bottom:1px solid var(--color-sidebar-search-border);border-top:1px solid var(--color-sidebar-search-border);box-sizing:border-box;color:var(--color-sidebar-search-foreground);padding:var(--sidebar-search-input-spacing-vertical) var(--sidebar-search-input-spacing-horizontal) var(--sidebar-search-input-spacing-vertical) calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size));width:100%;z-index:10}.sidebar-search:focus{outline:none}.sidebar-search::-moz-placeholder{font-size:var(--sidebar-search-input-font-size)}.sidebar-search::placeholder{font-size:var(--sidebar-search-input-font-size)}#searchbox .highlight-link{margin:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0;text-align:center}#searchbox .highlight-link a{color:var(--color-sidebar-search-icon);font-size:var(--font-size--small--2)}.sidebar-tree{font-size:var(--sidebar-item-font-size);margin-bottom:var(--sidebar-item-spacing-vertical);margin-top:var(--sidebar-tree-space-above)}.sidebar-tree ul{display:flex;flex-direction:column;list-style:none;margin-bottom:0;margin-top:0;padding:0}.sidebar-tree li{margin:0;position:relative}.sidebar-tree li>ul{margin-left:var(--sidebar-item-spacing-horizontal)}.sidebar-tree .icon,.sidebar-tree .reference{color:var(--color-sidebar-link-text)}.sidebar-tree .reference{box-sizing:border-box;display:inline-block;height:100%;line-height:var(--sidebar-item-line-height);overflow-wrap:anywhere;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none;width:100%}.sidebar-tree .reference:hover{background:var(--color-sidebar-item-background--hover);color:var(--color-sidebar-link-text)}.sidebar-tree .reference.external:after{color:var(--color-sidebar-link-text);content:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23607D8B' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' viewBox='0 0 24 24'%3E%3Cpath stroke='none' d='M0 0h24v24H0z'/%3E%3Cpath d='M11 7H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-5M10 14 20 4M15 4h5v5'/%3E%3C/svg%3E");margin:0 .25rem;vertical-align:middle}.sidebar-tree .current-page>.reference{font-weight:700}.sidebar-tree label{align-items:center;cursor:pointer;display:flex;height:var(--sidebar-item-height);justify-content:center;position:absolute;right:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:var(--sidebar-expander-width)}.sidebar-tree .caption,.sidebar-tree :not(.caption)>.caption-text{color:var(--color-sidebar-caption-text);font-size:var(--sidebar-caption-font-size);font-weight:700;margin:var(--sidebar-caption-space-above) 0 0 0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-transform:uppercase}.sidebar-tree li.has-children>.reference{padding-right:var(--sidebar-expander-width)}.sidebar-tree .toctree-l1>.reference,.sidebar-tree .toctree-l1>label .icon{color:var(--color-sidebar-link-text--top-level)}.sidebar-tree label{background:var(--color-sidebar-item-expander-background)}.sidebar-tree label:hover{background:var(--color-sidebar-item-expander-background--hover)}.sidebar-tree .current>.reference{background:var(--color-sidebar-item-background--current)}.sidebar-tree .current>.reference:hover{background:var(--color-sidebar-item-background--hover)}.toctree-checkbox{display:none;position:absolute}.toctree-checkbox~ul{display:none}.toctree-checkbox~label .icon svg{transform:rotate(90deg)}.toctree-checkbox:checked~ul{display:block}.toctree-checkbox:checked~label .icon svg{transform:rotate(-90deg)}.toc-title-container{padding:var(--toc-title-padding);padding-top:var(--toc-spacing-vertical)}.toc-title{color:var(--color-toc-title-text);font-size:var(--toc-title-font-size);padding-left:var(--toc-spacing-horizontal);text-transform:uppercase}.no-toc{display:none}.toc-tree-container{padding-bottom:var(--toc-spacing-vertical)}.toc-tree{border-left:1px solid var(--color-background-border);font-size:var(--toc-font-size);line-height:1.3;padding-left:calc(var(--toc-spacing-horizontal) - var(--toc-item-spacing-horizontal))}.toc-tree>ul>li:first-child{padding-top:0}.toc-tree>ul>li:first-child>ul{padding-left:0}.toc-tree>ul>li:first-child>a{display:none}.toc-tree ul{list-style-type:none;margin-bottom:0;margin-top:0;padding-left:var(--toc-item-spacing-horizontal)}.toc-tree li{padding-top:var(--toc-item-spacing-vertical)}.toc-tree li.scroll-current>.reference{color:var(--color-toc-item-text--active);font-weight:700}.toc-tree a.reference{color:var(--color-toc-item-text);overflow-wrap:anywhere;text-decoration:none}.toc-scroll{max-height:100vh;overflow-y:scroll}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here){background:rgba(255,0,0,.25);color:var(--color-problematic)}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here):before{content:"ERROR: Adding a table of contents in Furo-based documentation is unnecessary, and does not work well with existing styling. Add a 'this-will-duplicate-information-and-it-is-still-useful-here' class, if you want an escape hatch."}.text-align\:left>p{text-align:left}.text-align\:center>p{text-align:center}.text-align\:right>p{text-align:right}
+/*# sourceMappingURL=furo.css.map*/
\ No newline at end of file
diff --git a/docs/_static/styles/furo.css.map b/docs/_static/styles/furo.css.map
new file mode 100644
index 0000000..3ecc371
--- /dev/null
+++ b/docs/_static/styles/furo.css.map
@@ -0,0 +1 @@
+{"version":3,"file":"styles/furo.css","mappings":"AAAA,2EAA2E,CAU3E,KACE,gBAAiB,CACjB,6BACF,CASA,KACE,QACF,CAMA,KACE,aACF,CAOA,GACE,aAAc,CACd,cACF,CAUA,GACE,sBAAuB,CACvB,QAAS,CACT,gBACF,CAOA,IACE,+BAAiC,CACjC,aACF,CASA,EACE,4BACF,CAOA,YACE,kBAAmB,CACnB,yBAA0B,CAC1B,gCACF,CAMA,SAEE,kBACF,CAOA,cAGE,+BAAiC,CACjC,aACF,CAeA,QAEE,aAAc,CACd,aAAc,CACd,iBAAkB,CAClB,uBACF,CAEA,IACE,aACF,CAEA,IACE,SACF,CASA,IACE,iBACF,CAUA,sCAKE,mBAAoB,CACpB,cAAe,CACf,gBAAiB,CACjB,QACF,CAOA,aAEE,gBACF,CAOA,cAEE,mBACF,CAMA,gDAIE,yBACF,CAMA,wHAIE,iBAAkB,CAClB,SACF,CAMA,4GAIE,6BACF,CAMA,SACE,0BACF,CASA,OACE,qBAAsB,CACtB,aAAc,CACd,aAAc,CACd,cAAe,CACf,SAAU,CACV,kBACF,CAMA,SACE,uBACF,CAMA,SACE,aACF,CAOA,6BAEE,qBAAsB,CACtB,SACF,CAMA,kFAEE,WACF,CAOA,cACE,4BAA6B,CAC7B,mBACF,CAMA,yCACE,uBACF,CAOA,6BACE,yBAA0B,CAC1B,YACF,CASA,QACE,aACF,CAMA,QACE,iBACF,CAiBA,kBACE,YACF,CCvVA,aAcE,kEACE,uBAOF,WACE,iDAMF,kCACE,wBAEF,qCAEE,uBADA,uBACA,CAEF,SACE,wBAtBA,CCpBJ,iBAGE,qBAEA,sBACA,0BAFA,oBAHA,4BACA,oBAKA,6BAIA,2CAFA,mBACA,sCAFA,4BAGA,CAEF,gBACE,aCTF,KCGE,mHAEA,wGAEA,wCAAyC,CAEzC,wBAAyB,CACzB,wBAAyB,CACzB,4BAA6B,CAC7B,yBAA0B,CAC1B,2BAA4B,CAG5B,sDAAuD,CACvD,gDAAiD,CACjD,wDAAyD,CAGzD,0CAA2C,CAC3C,gDAAiD,CACjD,gDAAiD,CAKjD,gCAAiC,CACjC,sCAAuC,CAGvC,2CAA4C,CAG5C,uCAAwC,CCjCxC,+FAGA,uBAAwB,CAGxB,iCAAkC,CAClC,kCAAmC,CAEnC,+BAAgC,CAChC,sCAAuC,CACvC,sCAAuC,CACvC,qGAIA,mDAAoD,CAEpD,mCAAoC,CACpC,8CAA+C,CAC/C,gDAAiD,CACjD,kCAAmC,CACnC,6DAA8D,CAG9D,6BAA8B,CAC9B,6BAA8B,CAC9B,+BAAgC,CAChC,kCAAmC,CACnC,kCAAmC,CCPjC,+jBCYA,iqCAZF,iaCVA,8KAOA,4SAWA,4SAUA,0CACA,gEAGA,0CAGA,gEAGA,yCACA,+DAIA,4CACA,kEAGA,wCAUA,8DACA,uCAGA,4DACA,sCACA,2DAGA,4CACA,kEACA,uCAGA,6DACA,2GAGA,sHAEA,yFAEA,+CACA,+EAGA,4MAOA,gCACA,sHAIA,kCACA,uEACA,gEACA,4DACA,kEAGA,2DACA,sDACA,0CACA,8CACA,wGAGA,0BACA,iCAGA,+DACA,+BACA,sCACA,+DAEA,kGACA,oCACA,yDACA,sCL7HF,kCAEA,sDAIA,0CK2HE,kEAIA,oDACA,sDAGA,oCACA,oEAEA,0DACA,qDAIA,oDACA,6DAIA,iEAIA,2DAIA,2DAGA,4DACA,gEAIA,gEAEA,gFAEA,oNASA,qDLxKE,gFAGE,4DAIF,oEKkHF,yEAEA,6DAGA,0DAEA,uDACA,qDACA,wDAIA,6DAIA,yDACA,2DAIA,uCAGA,wCACA,sDAGA,+CAGA,6DAEA,iDACA,+DAEA,wDAEA,sEAMA,0DACA,sBACA,mEL9JI,wEAEA,iCACE,+BAMN,wEAGA,iCACE,kFAEA,uEAIF,gEACE,8BAGF,qEMvDA,sCAKA,wFAKA,iCAIA,0BAWA,iCACA,4BACA,mCAGA,+BAEA,sCACA,4BAEA,mCAEA,sCAKA,sDAIA,gCAEA,gEAQF,wCAME,sBACA,kCAKA,uBAEA,gEAIA,2BAIA,mCAEA,qCACA,iCAGE,+BACA,wEAEE,iCACA,kFAGF,6BACA,0CACF,kCAEE,8BACE,8BACA,qEAEE,sCACA,wFCnFN,iCAGF,2DAEE,4BACA,oCAGA,mIAGA,4HACE,gEAMJ,+CAGE,sBACA,yCAEF,uBAEE,sEAKA,gDACA,kEAGA,iFAGE,YAGF,EACA,4HAQF,mBACE,6BACA,mBACA,wCACA,wCACA,2CAIA,eAGA,mBAKE,mBAGA,CAJA,uCACA,iBAFF,gBACE,CAKE,mBACA,mBAGJ,oBAIF,+BAGE,kDACA,OADA,kBAGA,CAFA,gBAEA,mBACA,oBAEA,sCACA,OAGF,cAHE,WAGF,GAEE,oBACA,CAHF,gBAGE,CC9Gc,YDiHd,+CAIF,SAEE,CAPF,UACE,wBAMA,4BAEA,GAGA,uBACA,CAJA,yBAGA,CACA,iDAKA,2CAGA,2DAQA,iBACA,uCAGA,kEAKE,SAKJ,8BACE,yDACA,2BAEA,oBACA,8BAEA,yDAEE,4BAEJ,uCACE,CACA,iEAGA,CAEA,wCACE,uBACA,kDAEA,0DAEE,CAJF,oBAIE,0GAWN,aACE,CAHA,YAGA,4HASA,+CAGF,sBACE,WACA,WAQA,4BAFF,0CAEE,CARA,qCAsBA,CAdA,iBAEA,kBACE,aADF,4BACE,WAMF,2BAGF,qCAEE,CAXE,UAWF,+BAGA,uBAEA,SAEA,0CAIE,CANF,qCAEA,CAIE,2DACE,gBAIN,+CAIA,CAEA,kDAKE,CAPF,8BAEA,CAOE,YACA,CAjBI,2BAGN,CAHM,WAcJ,UAGA,CAEA,2GAIF,iCAGE,8BAIA,qBACA,oBACF,uBAOI,0CAIA,CATF,6DAKE,CALF,sBASE,qCAKF,CACE,cACA,CAFF,sBAEE,CACA,+BAEA,qBAEE,WAKN,aACE,sCAGA,mBAEA,6BAMA,kCACA,CAJA,sBACA,aAEA,CAJA,eACA,MAIA,2FAEA,UAGA,YACA,sBACE,8BAEA,CALF,aACA,WAIE,OACA,oBAEF,uBACE,WAEF,YAFE,UAEF,eAgBA,kBACE,CAhBA,qDAQF,qCAGF,CAGI,YACF,CAJF,2BAGI,CAEA,eACA,qBAGA,mEAEA,qBACA,8BAIA,kBADF,kBACE,yBAEJ,oCAGI,qDAIJ,+BAGI,oCAEA,+CAQF,4CACE,yBACF,2BAOE,sBACA,CAHA,WACA,CAFF,cACE,CAJA,YAGF,CAEE,SAEA,mBAGA,kDAEE,CAJF,cAEA,cAEE,sBAEA,mBADA,YACA,uBACA,mDACE,CADF,YACE,iDAEA,uCAEN,+DAOE,mBADF,sBACE,mBAGF,aACE,sCAIA,aADF,WACE,CAKF,SACE,CAHJ,kBAEE,CAJE,gBAEJ,CAHI,iBAMA,yFAKA,aACA,eACA,cElbJ,iBAEE,aADA,iBACA,6BAEA,kCAEA,SACA,UAIA,gCACA,CALA,SAEA,SAEA,CAJA,0EAEA,CAFA,OAKA,CAGA,mDACE,iBAGF,gCACE,CADF,UACE,aAEJ,iCAEE,CAFF,UAEE,wCAEA,WACA,WADA,UACA,CACA,4CAGA,MACA,CADA,KACA,wCACA,UAGA,CAJA,UAIA,6DAUA,0CACE,CAFF,mBAEE,wEACA,CAVA,YACA,CAMF,mBAJE,OAOA,gBAJJ,gCACE,CANE,cACA,CAHA,oBACA,CAGA,QAGJ,CAII,0BACA,CADA,UACA,wCAEJ,kBACE,0DACA,gCACE,kBACA,CADA,YACA,oEACA,2CAMF,mDAII,CALN,YACE,CANE,cAKJ,CACE,iBAII,kEACA,yCACE,kDACA,yDACE,+CACA,uBANN,CAMM,+BANN,uCACE,qDACA,4BAEE,mBADA,0CACA,CADA,qBACA,0DACE,wCACA,sGALJ,oCACA,sBACE,kBAFF,UAEE,2CACA,wFACE,cACA,kEANN,uBACE,iDACA,CADA,UACA,0DACE,wDAEE,iEACA,qEANN,sCACE,CAGE,iBAHF,gBAGE,qBACE,CAJJ,uBACA,gDACE,wDACA,6DAHF,2CACA,CADA,gBACA,eACE,CAGE,sBANN,8BACE,CAII,iBAFF,4DACA,WACE,YADF,uCACE,6EACA,2BANN,8CACE,kDACA,0CACE,8BACA,yFACE,sBACA,sFALJ,mEACA,sBACE,kEACA,6EACE,uCACA,kEALJ,qGAEE,kEACA,6EACE,uCACA,kEALJ,8CACA,uDACE,sEACA,2EACE,sCACA,iEALJ,mGACA,qCACE,oDACA,0DACE,6GACA,gDAGR,yDCrEA,sEACE,CACA,6GACE,gEACF,iGAIF,wFACE,qDAGA,mGAEE,2CAEF,4FACE,gCACF,wGACE,8DAEE,6FAIA,iJAKN,6GACE,gDAKF,yDACA,qCAGA,6BACA,kBACA,qDAKA,oCAEA,+DAGA,2CAGE,oDAIA,oEAEE,qBAGJ,wDAEE,uCAEF,kEAGA,8CAEA,uDAIF,gEAIE,6BACA,gEAIA,+CACE,0EAIF,sDAEE,+DAGF,sCACA,8BACE,oCAEJ,wBACE,4FAEE,gBAEJ,yGAGI,kBAGJ,CCnHE,2MCFF,oBAGE,wGAKA,iCACE,CADF,wBACE,8GAQA,mBCjBJ,2GAIE,mBACA,6HAMA,YACE,mIAYF,eACA,CAHF,YAGE,4FAGE,8BAKF,uBAkBE,sCACA,CADA,qBAbA,wCAIA,CALF,8BACE,CADF,gBAKE,wCACA,CAOA,kDACA,CACA,kCAKF,6BAGA,4CACE,kDACA,eAGF,cACE,aACA,iBACA,yBACA,8BACA,WAGJ,2BACE,cAGA,+BACA,CAHA,eAGA,wCACA,YACA,iBACA,uEAGA,0BACA,2CAEA,8EAGI,qBACA,CAFF,kBAEE,kBAGN,0CAGE,mCAGA,4BAIA,gEACE,qCACA,8BAEA,gBACA,+CACA,iCAEF,iCAEE,gEACA,qCAGF,8BAEE,+BAIA,yCAEE,qBADA,gBACA,yBAKF,eACA,CAFF,YACE,CACA,iBACA,qDAEA,mDCvIJ,2FAOE,iCACA,CAEA,eACA,CAHA,kBAEA,CAFA,wBAGA,8BACA,eACE,CAFF,YAEE,0BACA,8CAGA,oBACE,oCAGA,kBACE,8DAEA,iBAEN,UACE,8BAIJ,+CAEE,qDAEF,kDAIE,YAEF,CAFE,YAEF,CCpCE,mFADA,kBAKE,CAJF,IAGA,aACE,mCAGA,iDACE,+BAEJ,wBAEE,mBAMA,6CAEF,CAJE,mBAEA,CAEF,kCAGE,CARF,kBACE,CAHA,eAUA,YACA,mBACA,CADA,UACA,wCC9BF,oBDkCE,wBCnCJ,uCACE,+BACA,+DACA,sBAGA,qBCDA,6CAIE,CAPF,uBAGA,CDGE,oBACF,yDAEE,CCDE,2CAGF,CAJA,kCACE,CDJJ,YACE,CAIA,eCTF,CDKE,uBCMA,gCACE,YAEF,oCAEE,wBACA,0BAIF,iBAEA,cADF,UACE,uBAEA,iCAEA,wCAEA,6CAMA,CAYF,gCATI,4BASJ,CAZE,mCAEE,iCAUJ,4BAGE,4DADA,+BACA,CAHF,qBAGE,sCACE,OAEF,iBAHA,SAGA,iHACE,2DAKF,CANA,8EAMA,uSAEE,kBAEF,+FACE,yCCjEJ,WACA,yBAGA,uBACA,gBAEA,uCAIA,CAJA,iCAIA,uCAGA,UACE,gBACA,qBAEA,0CClBJ,gBACE,KAGF,qBACE,YAGF,CAHE,cAGF,gCAEE,mBACA,iEAEA,oCACA,wCAEA,sBACA,WAEA,CAFA,YAEA,8EAEA,mCAFA,iBAEA,6BAIA,wEAKA,sDAIE,CARF,mDAIA,CAIE,cAEF,8CAIA,oBAFE,iBAEF,8CAGE,eAEF,CAFE,YAEF,OAEE,kBAGJ,CAJI,eACA,CAFF,mBAKF,yCCjDE,oBACA,CAFA,iBAEA,uCAKE,iBACA,qCAGA,mBCZJ,CDWI,gBCXJ,6BAEE,eACA,sBAGA,eAEA,sBACA,oDACA,iGAMA,gBAFE,YAEF,8FAME,iJCnBF,YACA,gNAWE,gDAEF,iSAaE,kBACE,gHAKF,oCACE,eACF,CADE,UACF,8CACE,gDACF,wCACE,oBCxCJ,oBAEF,6BACE,QACE,kDAGF,yBACE,kDAmBA,kDAEF,CAhBA,+CAaA,CAbA,oBAaA,0FACE,CADF,gGAfF,cACE,gBACA,CAaA,0BAGA,mQACE,gBAGF,oMACE,iBACA,CAFF,eACE,CADF,gBAEE,aAGJ,iCAEE,CAFF,wCAEE,wBAUE,+VAIE,uEAHA,2BAGA,wXAKJ,iDAGF,CARM,+CACE,iDAIN,CALI,gBAQN,mHACE,gBAGF,2DACE,0EAOA,0EAGF,gBAEE,6DC/EA,kDACA,gCACA,qDAGA,qBACA,qDCFA,cACA,eAEA,yBAGF,sBAEE,iBACA,sNAWA,iBACE,kBACA,wRAgBA,kBAEA,iOAgBA,uCACE,uEAEA,kBAEF,qUAuBE,iDAIJ,CACA,geCxFF,4BAEE,CAQA,6JACA,iDAIA,sEAGA,mDAOF,iDAGE,4DAIA,8CACA,qDAEE,eAFF,cAEE,oBAEF,uBAFE,kCAGA,eACA,iBACA,mBAIA,mDACA,CAHA,uCAEA,CAJA,0CACA,CAIA,gBAJA,gBACA,oBADA,gBAIA,wBAEJ,gBAGE,6BACA,YAHA,iBAGA,gCACA,iEAEA,6CACA,sDACA,0BADA,wBACA,0BACA,oIAIA,mBAFA,YAEA,qBACA,0CAIE,uBAEF,CAHA,yBACE,CAEF,iDACE,mFAKJ,oCACE,CANE,aAKJ,CACE,qEAIA,YAFA,WAEA,CAHA,aACA,CAEA,gBACE,4BACA,sBADA,aACA,gCAMF,oCACA,yDACA,2CAEA,qBAGE,kBAEA,CACA,mCAIF,CARE,YACA,CAOF,iCAEE,CAPA,oBACA,CAQA,oBACE,uDAEJ,sDAGA,CAHA,cAGA,0BACE,oDAIA,oCACA,4BACA,sBAGA,cAEA,oFAGA,sBAEA,yDACE,CAIF,iBAJE,wBAIF,6CAHE,6CAKA,eACA,aACA,CADA,cACA,yCAGJ,kBACE,CAKA,iDAEA,CARF,aACE,4CAGA,kBAIA,wEAGA,wDAGA,kCAOA,iDAGA,CAPF,WAEE,sCAEA,CAJF,2CACE,CAMA,qCACA,+BARF,kBACE,qCAOA,iBAsBA,sBACE,CAvBF,WAKA,CACE,0DAIF,CALA,uDACE,CANF,sBAqBA,4CACA,CALA,gRAIA,YAEE,6CAEN,mCAEE,+CASA,6EAIA,4BChNA,SDmNA,qFCnNA,gDACA,sCAGA,qCACA,sDACA,CAKA,kDAGA,CARA,0CAQA,kBAGA,YACA,sBACA,iBAFA,gBADF,YACE,CAHA,SAKA,kBAEA,SAFA,iBAEA,uEAGA,CAEE,6CAFF,oCAgBI,CAdF,yBACE,qBACF,CAGF,oBACE,CAIF,WACE,CALA,2CAGA,uBACF,CACE,mFAGE,CALF,qBAEA,UAGE,gCAIF,sDAEA,CALE,oCAKF,yCC7CJ,oCACE,CD+CA,yXAQE,sCCrDJ,wCAGA,oCACE","sources":["webpack:///./node_modules/normalize.css/normalize.css","webpack:///./src/furo/assets/styles/base/_print.sass","webpack:///./src/furo/assets/styles/base/_screen-readers.sass","webpack:///./src/furo/assets/styles/base/_theme.sass","webpack:///./src/furo/assets/styles/variables/_fonts.scss","webpack:///./src/furo/assets/styles/variables/_spacing.scss","webpack:///./src/furo/assets/styles/variables/_icons.scss","webpack:///./src/furo/assets/styles/variables/_admonitions.scss","webpack:///./src/furo/assets/styles/variables/_colors.scss","webpack:///./src/furo/assets/styles/base/_typography.sass","webpack:///./src/furo/assets/styles/_scaffold.sass","webpack:///./src/furo/assets/styles/variables/_layout.scss","webpack:///./src/furo/assets/styles/content/_admonitions.sass","webpack:///./src/furo/assets/styles/content/_api.sass","webpack:///./src/furo/assets/styles/content/_blocks.sass","webpack:///./src/furo/assets/styles/content/_captions.sass","webpack:///./src/furo/assets/styles/content/_code.sass","webpack:///./src/furo/assets/styles/content/_footnotes.sass","webpack:///./src/furo/assets/styles/content/_images.sass","webpack:///./src/furo/assets/styles/content/_indexes.sass","webpack:///./src/furo/assets/styles/content/_lists.sass","webpack:///./src/furo/assets/styles/content/_math.sass","webpack:///./src/furo/assets/styles/content/_misc.sass","webpack:///./src/furo/assets/styles/content/_rubrics.sass","webpack:///./src/furo/assets/styles/content/_sidebar.sass","webpack:///./src/furo/assets/styles/content/_tables.sass","webpack:///./src/furo/assets/styles/content/_target.sass","webpack:///./src/furo/assets/styles/content/_gui-labels.sass","webpack:///./src/furo/assets/styles/components/_footer.sass","webpack:///./src/furo/assets/styles/components/_sidebar.sass","webpack:///./src/furo/assets/styles/components/_table_of_contents.sass","webpack:///./src/furo/assets/styles/_shame.sass"],"sourcesContent":["/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n","// This file contains styles for managing print media.\n\n////////////////////////////////////////////////////////////////////////////////\n// Hide elements not relevant to print media.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Hide icon container.\n .content-icon-container\n display: none !important\n\n // Hide showing header links if hovering over when printing.\n .headerlink\n display: none !important\n\n // Hide mobile header.\n .mobile-header\n display: none !important\n\n // Hide navigation links.\n .related-pages\n display: none !important\n\n////////////////////////////////////////////////////////////////////////////////\n// Tweaks related to decolorization.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Apply a border around code which no longer have a color background.\n .highlight\n border: 0.1pt solid var(--color-foreground-border)\n\n////////////////////////////////////////////////////////////////////////////////\n// Avoid page break in some relevant cases.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n ul, ol, dl, a, table, pre, blockquote, p\n page-break-inside: avoid\n\n h1, h2, h3, h4, h5, h6, img, figure, caption\n page-break-inside: avoid\n page-break-after: avoid\n\n ul, ol, dl\n page-break-before: avoid\n",".visually-hidden\n position: absolute !important\n width: 1px !important\n height: 1px !important\n padding: 0 !important\n margin: -1px !important\n overflow: hidden !important\n clip: rect(0,0,0,0) !important\n white-space: nowrap !important\n border: 0 !important\n color: var(--color-foreground-primary)\n background: var(--color-background-primary)\n\n:-moz-focusring\n outline: auto\n","// This file serves as the \"skeleton\" of the theming logic.\n//\n// This contains the bulk of the logic for handling dark mode, color scheme\n// toggling and the handling of color-scheme-specific hiding of elements.\n\nbody\n @include fonts\n @include spacing\n @include icons\n @include admonitions\n @include default-admonition(#651fff, \"abstract\")\n @include default-topic(#14B8A6, \"pencil\")\n\n @include colors\n\n.only-light\n display: block !important\nhtml body .only-dark\n display: none !important\n\n// Ignore dark-mode hints if print media.\n@media not print\n // Enable dark-mode, if requested.\n body[data-theme=\"dark\"]\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n // Enable dark mode, unless explicitly told to avoid.\n @media (prefers-color-scheme: dark)\n body:not([data-theme=\"light\"])\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n//\n// Theme toggle presentation\n//\nbody[data-theme=\"auto\"]\n .theme-toggle svg.theme-icon-when-auto-light\n display: block\n\n @media (prefers-color-scheme: dark)\n .theme-toggle svg.theme-icon-when-auto-dark\n display: block\n .theme-toggle svg.theme-icon-when-auto-light\n display: none\n\nbody[data-theme=\"dark\"]\n .theme-toggle svg.theme-icon-when-dark\n display: block\n\nbody[data-theme=\"light\"]\n .theme-toggle svg.theme-icon-when-light\n display: block\n","// Fonts used by this theme.\n//\n// There are basically two things here -- using the system font stack and\n// defining sizes for various elements in %ages. We could have also used `em`\n// but %age is easier to reason about for me.\n\n@mixin fonts {\n // These are adapted from https://systemfontstack.com/\n --font-stack: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji;\n --font-stack--monospace: \"SFMono-Regular\", Menlo, Consolas, Monaco,\n Liberation Mono, Lucida Console, monospace;\n --font-stack--headings: var(--font-stack);\n\n --font-size--normal: 100%;\n --font-size--small: 87.5%;\n --font-size--small--2: 81.25%;\n --font-size--small--3: 75%;\n --font-size--small--4: 62.5%;\n\n // Sidebar\n --sidebar-caption-font-size: var(--font-size--small--2);\n --sidebar-item-font-size: var(--font-size--small);\n --sidebar-search-input-font-size: var(--font-size--small);\n\n // Table of Contents\n --toc-font-size: var(--font-size--small--3);\n --toc-font-size--mobile: var(--font-size--normal);\n --toc-title-font-size: var(--font-size--small--4);\n\n // Admonitions\n //\n // These aren't defined in terms of %ages, since nesting these is permitted.\n --admonition-font-size: 0.8125rem;\n --admonition-title-font-size: 0.8125rem;\n\n // Code\n --code-font-size: var(--font-size--small--2);\n\n // API\n --api-font-size: var(--font-size--small);\n}\n","// Spacing for various elements on the page\n//\n// If the user wants to tweak things in a certain way, they are permitted to.\n// They also have to deal with the consequences though!\n\n@mixin spacing {\n // Header!\n --header-height: calc(\n var(--sidebar-item-line-height) + 4 * #{var(--sidebar-item-spacing-vertical)}\n );\n --header-padding: 0.5rem;\n\n // Sidebar\n --sidebar-tree-space-above: 1.5rem;\n --sidebar-caption-space-above: 1rem;\n\n --sidebar-item-line-height: 1rem;\n --sidebar-item-spacing-vertical: 0.5rem;\n --sidebar-item-spacing-horizontal: 1rem;\n --sidebar-item-height: calc(\n var(--sidebar-item-line-height) + 2 *#{var(--sidebar-item-spacing-vertical)}\n );\n\n --sidebar-expander-width: var(--sidebar-item-height); // be square\n\n --sidebar-search-space-above: 0.5rem;\n --sidebar-search-input-spacing-vertical: 0.5rem;\n --sidebar-search-input-spacing-horizontal: 0.5rem;\n --sidebar-search-input-height: 1rem;\n --sidebar-search-icon-size: var(--sidebar-search-input-height);\n\n // Table of Contents\n --toc-title-padding: 0.25rem 0;\n --toc-spacing-vertical: 1.5rem;\n --toc-spacing-horizontal: 1.5rem;\n --toc-item-spacing-vertical: 0.4rem;\n --toc-item-spacing-horizontal: 1rem;\n}\n","// Expose theme icons as CSS variables.\n\n$icons: (\n // Adapted from tabler-icons\n // url: https://tablericons.com/\n \"search\":\n url('data:image/svg+xml;charset=utf-8,'),\n // Factored out from mkdocs-material on 24-Aug-2020.\n // url: https://squidfunk.github.io/mkdocs-material/reference/admonitions/\n \"pencil\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"abstract\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"info\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"flame\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"question\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"warning\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"failure\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"spark\":\n url('data:image/svg+xml;charset=utf-8,')\n);\n\n@mixin icons {\n @each $name, $glyph in $icons {\n --icon-#{$name}: #{$glyph};\n }\n}\n","// Admonitions\n\n// Structure of these is:\n// admonition-class: color \"icon-name\";\n//\n// The colors are translated into CSS variables below. The icons are\n// used directly in the main declarations to set the `mask-image` in\n// the title.\n\n// prettier-ignore\n$admonitions: (\n // Each of these has an reST directives for it.\n \"caution\": #ff9100 \"spark\",\n \"warning\": #ff9100 \"warning\",\n \"danger\": #ff5252 \"spark\",\n \"attention\": #ff5252 \"warning\",\n \"error\": #ff5252 \"failure\",\n \"hint\": #00c852 \"question\",\n \"tip\": #00c852 \"info\",\n \"important\": #00bfa5 \"flame\",\n \"note\": #00b0ff \"pencil\",\n \"seealso\": #448aff \"info\",\n \"admonition-todo\": #808080 \"pencil\"\n);\n\n@mixin default-admonition($color, $icon-name) {\n --color-admonition-title: #{$color};\n --color-admonition-title-background: #{rgba($color, 0.2)};\n\n --icon-admonition-default: var(--icon-#{$icon-name});\n}\n\n@mixin default-topic($color, $icon-name) {\n --color-topic-title: #{$color};\n --color-topic-title-background: #{rgba($color, 0.2)};\n\n --icon-topic-default: var(--icon-#{$icon-name});\n}\n\n@mixin admonitions {\n @each $name, $values in $admonitions {\n --color-admonition-title--#{$name}: #{nth($values, 1)};\n --color-admonition-title-background--#{$name}: #{rgba(\n nth($values, 1),\n 0.2\n )};\n }\n}\n","// Colors used throughout this theme.\n//\n// The aim is to give the user more control. Thus, instead of hard-coding colors\n// in various parts of the stylesheet, the approach taken is to define all\n// colors as CSS variables and reusing them in all the places.\n//\n// `colors-dark` depends on `colors` being included at a lower specificity.\n\n@mixin colors {\n --color-problematic: #b30000;\n\n // Base Colors\n --color-foreground-primary: black; // for main text and headings\n --color-foreground-secondary: #5a5c63; // for secondary text\n --color-foreground-muted: #6b6f76; // for muted text\n --color-foreground-border: #878787; // for content borders\n\n --color-background-primary: white; // for content\n --color-background-secondary: #f8f9fb; // for navigation + ToC\n --color-background-hover: #efeff4ff; // for navigation-item hover\n --color-background-hover--transparent: #efeff400;\n --color-background-border: #eeebee; // for UI borders\n --color-background-item: #ccc; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #0a4bff;\n --color-brand-content: #2757dd;\n --color-brand-visited: #872ee0;\n\n // API documentation\n --color-api-background: var(--color-background-hover--transparent);\n --color-api-background-hover: var(--color-background-hover);\n --color-api-overall: var(--color-foreground-secondary);\n --color-api-name: var(--color-problematic);\n --color-api-pre-name: var(--color-problematic);\n --color-api-paren: var(--color-foreground-secondary);\n --color-api-keyword: var(--color-foreground-primary);\n\n --color-api-added: #21632c;\n --color-api-added-border: #38a84d;\n --color-api-changed: #046172;\n --color-api-changed-border: #06a1bc;\n --color-api-deprecated: #605706;\n --color-api-deprecated-border: #f0d90f;\n --color-api-removed: #b30000;\n --color-api-removed-border: #ff5c5c;\n\n --color-highlight-on-target: #ffffcc;\n\n // Inline code background\n --color-inline-code-background: var(--color-background-secondary);\n\n // Highlighted text (search)\n --color-highlighted-background: #ddeeff;\n --color-highlighted-text: var(--color-foreground-primary);\n\n // GUI Labels\n --color-guilabel-background: #ddeeff80;\n --color-guilabel-border: #bedaf580;\n --color-guilabel-text: var(--color-foreground-primary);\n\n // Admonitions!\n --color-admonition-background: transparent;\n\n //////////////////////////////////////////////////////////////////////////////\n // Everything below this should be one of:\n // - var(...)\n // - *-gradient(...)\n // - special literal values (eg: transparent, none)\n //////////////////////////////////////////////////////////////////////////////\n\n // Tables\n --color-table-header-background: var(--color-background-secondary);\n --color-table-border: var(--color-background-border);\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: transparent;\n --color-card-marginals-background: var(--color-background-secondary);\n\n // Header\n --color-header-background: var(--color-background-primary);\n --color-header-border: var(--color-background-border);\n --color-header-text: var(--color-foreground-primary);\n\n // Sidebar (left)\n --color-sidebar-background: var(--color-background-secondary);\n --color-sidebar-background-border: var(--color-background-border);\n\n --color-sidebar-brand-text: var(--color-foreground-primary);\n --color-sidebar-caption-text: var(--color-foreground-muted);\n --color-sidebar-link-text: var(--color-foreground-secondary);\n --color-sidebar-link-text--top-level: var(--color-brand-primary);\n\n --color-sidebar-item-background: var(--color-sidebar-background);\n --color-sidebar-item-background--current: var(\n --color-sidebar-item-background\n );\n --color-sidebar-item-background--hover: linear-gradient(\n 90deg,\n var(--color-background-hover--transparent) 0%,\n var(--color-background-hover) var(--sidebar-item-spacing-horizontal),\n var(--color-background-hover) 100%\n );\n\n --color-sidebar-item-expander-background: transparent;\n --color-sidebar-item-expander-background--hover: var(\n --color-background-hover\n );\n\n --color-sidebar-search-text: var(--color-foreground-primary);\n --color-sidebar-search-background: var(--color-background-secondary);\n --color-sidebar-search-background--focus: var(--color-background-primary);\n --color-sidebar-search-border: var(--color-background-border);\n --color-sidebar-search-icon: var(--color-foreground-muted);\n\n // Table of Contents (right)\n --color-toc-background: var(--color-background-primary);\n --color-toc-title-text: var(--color-foreground-muted);\n --color-toc-item-text: var(--color-foreground-secondary);\n --color-toc-item-text--hover: var(--color-foreground-primary);\n --color-toc-item-text--active: var(--color-brand-primary);\n\n // Actual page contents\n --color-content-foreground: var(--color-foreground-primary);\n --color-content-background: transparent;\n\n // Links\n --color-link: var(--color-brand-content);\n --color-link-underline: var(--color-background-border);\n --color-link--hover: var(--color-brand-content);\n --color-link-underline--hover: var(--color-foreground-border);\n\n --color-link--visited: var(--color-brand-visited);\n --color-link-underline--visited: var(--color-background-border);\n --color-link--visited--hover: var(--color-brand-visited);\n --color-link-underline--visited--hover: var(--color-foreground-border);\n}\n\n@mixin colors-dark {\n --color-problematic: #ee5151;\n\n // Base Colors\n --color-foreground-primary: #cfd0d0; // for main text and headings\n --color-foreground-secondary: #9ca0a5; // for secondary text\n --color-foreground-muted: #81868d; // for muted text\n --color-foreground-border: #666666; // for content borders\n\n --color-background-primary: #131416; // for content\n --color-background-secondary: #1a1c1e; // for navigation + ToC\n --color-background-hover: #1e2124ff; // for navigation-item hover\n --color-background-hover--transparent: #1e212400;\n --color-background-border: #303335; // for UI borders\n --color-background-item: #444; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #3d94ff;\n --color-brand-content: #5ca5ff;\n --color-brand-visited: #b27aeb;\n\n // Highlighted text (search)\n --color-highlighted-background: #083563;\n\n // GUI Labels\n --color-guilabel-background: #08356380;\n --color-guilabel-border: #13395f80;\n\n // API documentation\n --color-api-keyword: var(--color-foreground-secondary);\n --color-highlight-on-target: #333300;\n\n --color-api-added: #3db854;\n --color-api-added-border: #267334;\n --color-api-changed: #09b0ce;\n --color-api-changed-border: #056d80;\n --color-api-deprecated: #b1a10b;\n --color-api-deprecated-border: #6e6407;\n --color-api-removed: #ff7575;\n --color-api-removed-border: #b03b3b;\n\n // Admonitions\n --color-admonition-background: #18181a;\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: #18181a;\n --color-card-marginals-background: var(--color-background-hover);\n}\n","// This file contains the styling for making the content throughout the page,\n// including fonts, paragraphs, headings and spacing among these elements.\n\nbody\n font-family: var(--font-stack)\npre,\ncode,\nkbd,\nsamp\n font-family: var(--font-stack--monospace)\n\n// Make fonts look slightly nicer.\nbody\n -webkit-font-smoothing: antialiased\n -moz-osx-font-smoothing: grayscale\n\n// Line height from Bootstrap 4.1\narticle\n line-height: 1.5\n\n//\n// Headings\n//\nh1,\nh2,\nh3,\nh4,\nh5,\nh6\n line-height: 1.25\n font-family: var(--font-stack--headings)\n font-weight: bold\n\n border-radius: 0.5rem\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n margin-left: -0.5rem\n margin-right: -0.5rem\n padding-left: 0.5rem\n padding-right: 0.5rem\n\n + p\n margin-top: 0\n\nh1\n font-size: 2.5em\n margin-top: 1.75rem\n margin-bottom: 1rem\nh2\n font-size: 2em\n margin-top: 1.75rem\nh3\n font-size: 1.5em\nh4\n font-size: 1.25em\nh5\n font-size: 1.125em\nh6\n font-size: 1em\n\nsmall\n opacity: 75%\n font-size: 80%\n\n// Paragraph\np\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n\n// Horizontal rules\nhr.docutils\n height: 1px\n padding: 0\n margin: 2rem 0\n background-color: var(--color-background-border)\n border: 0\n\n.centered\n text-align: center\n\n// Links\na\n text-decoration: underline\n\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n &:visited\n color: var(--color-link--visited)\n text-decoration-color: var(--color-link-underline--visited)\n &:hover\n color: var(--color-link--visited--hover)\n text-decoration-color: var(--color-link-underline--visited--hover)\n\n &:hover\n color: var(--color-link--hover)\n text-decoration-color: var(--color-link-underline--hover)\n &.muted-link\n color: inherit\n &:hover\n color: var(--color-link--hover)\n text-decoration-color: var(--color-link-underline--hover)\n &:visited\n color: var(--color-link--visited--hover)\n text-decoration-color: var(--color-link-underline--visited--hover)\n","// This file contains the styles for the overall layouting of the documentation\n// skeleton, including the responsive changes as well as sidebar toggles.\n//\n// This is implemented as a mobile-last design, which isn't ideal, but it is\n// reasonably good-enough and I got pretty tired by the time I'd finished this\n// to move the rules around to fix this. Shouldn't take more than 3-4 hours,\n// if you know what you're doing tho.\n\n// HACK: Not all browsers account for the scrollbar width in media queries.\n// This results in horizontal scrollbars in the breakpoint where we go\n// from displaying everything to hiding the ToC. We accomodate for this by\n// adding a bit of padding to the TOC drawer, disabling the horizontal\n// scrollbar and allowing the scrollbars to cover the padding.\n// https://www.456bereastreet.com/archive/201301/media_query_width_and_vertical_scrollbars/\n\n// HACK: Always having the scrollbar visible, prevents certain browsers from\n// causing the content to stutter horizontally between taller-than-viewport and\n// not-taller-than-viewport pages.\n\nhtml\n overflow-x: hidden\n overflow-y: scroll\n scroll-behavior: smooth\n\n.sidebar-scroll, .toc-scroll, article[role=main] *\n // Override Firefox scrollbar style\n scrollbar-width: thin\n scrollbar-color: var(--color-foreground-border) transparent\n\n // Override Chrome scrollbar styles\n &::-webkit-scrollbar\n width: 0.25rem\n height: 0.25rem\n &::-webkit-scrollbar-thumb\n background-color: var(--color-foreground-border)\n border-radius: 0.125rem\n\n//\n// Overalls\n//\nhtml,\nbody\n height: 100%\n color: var(--color-foreground-primary)\n background: var(--color-background-primary)\n\n.skip-to-content\n position: fixed\n padding: 1rem\n border-radius: 1rem\n left: 0.25rem\n top: 0.25rem\n z-index: 40\n background: var(--color-background-primary)\n color: var(--color-foreground-primary)\n\n transform: translateY(-200%)\n transition: transform 300ms ease-in-out\n\n &:focus-within\n transform: translateY(0%)\n\narticle\n color: var(--color-content-foreground)\n background: var(--color-content-background)\n overflow-wrap: break-word\n\n.page\n display: flex\n // fill the viewport for pages with little content.\n min-height: 100%\n\n.mobile-header\n width: 100%\n height: var(--header-height)\n background-color: var(--color-header-background)\n color: var(--color-header-text)\n border-bottom: 1px solid var(--color-header-border)\n\n // Looks like sub-script/super-script have this, and we need this to\n // be \"on top\" of those.\n z-index: 10\n\n // We don't show the header on large screens.\n display: none\n\n // Add shadow when scrolled\n &.scrolled\n border-bottom: none\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2)\n\n .header-center\n a\n color: var(--color-header-text)\n text-decoration: none\n\n.main\n display: flex\n flex: 1\n\n// Sidebar (left) also covers the entire left portion of screen.\n.sidebar-drawer\n box-sizing: border-box\n\n border-right: 1px solid var(--color-sidebar-background-border)\n background: var(--color-sidebar-background)\n\n display: flex\n justify-content: flex-end\n // These next two lines took me two days to figure out.\n width: calc((100% - #{$full-width}) / 2 + #{$sidebar-width})\n min-width: $sidebar-width\n\n// Scroll-along sidebars\n.sidebar-container,\n.toc-drawer\n box-sizing: border-box\n width: $sidebar-width\n\n.toc-drawer\n background: var(--color-toc-background)\n // See HACK described on top of this document\n padding-right: 1rem\n\n.sidebar-sticky,\n.toc-sticky\n position: sticky\n top: 0\n height: min(100%, 100vh)\n height: 100vh\n\n display: flex\n flex-direction: column\n\n.sidebar-scroll,\n.toc-scroll\n flex-grow: 1\n flex-shrink: 1\n\n overflow: auto\n scroll-behavior: smooth\n\n// Central items.\n.content\n padding: 0 $content-padding\n width: $content-width\n\n display: flex\n flex-direction: column\n justify-content: space-between\n\n.icon\n display: inline-block\n height: 1rem\n width: 1rem\n svg\n width: 100%\n height: 100%\n\n//\n// Accommodate announcement banner\n//\n.announcement\n background-color: var(--color-announcement-background)\n color: var(--color-announcement-text)\n\n height: var(--header-height)\n display: flex\n align-items: center\n overflow-x: auto\n & + .page\n min-height: calc(100% - var(--header-height))\n\n.announcement-content\n box-sizing: border-box\n padding: 0.5rem\n min-width: 100%\n white-space: nowrap\n text-align: center\n\n a\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-announcement-text)\n\n &:hover\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-link--hover)\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for theme\n////////////////////////////////////////////////////////////////////////////////\n.no-js .theme-toggle-container // don't show theme toggle if there's no JS\n display: none\n\n.theme-toggle-container\n display: flex\n\n.theme-toggle\n display: flex\n cursor: pointer\n border: none\n padding: 0\n background: transparent\n\n.theme-toggle svg\n height: 1.25rem\n width: 1.25rem\n color: var(--color-foreground-primary)\n display: none\n\n.theme-toggle-header\n display: flex\n align-items: center\n justify-content: center\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for elements\n////////////////////////////////////////////////////////////////////////////////\n.toc-overlay-icon, .nav-overlay-icon\n display: none\n cursor: pointer\n\n .icon\n color: var(--color-foreground-secondary)\n height: 1.5rem\n width: 1.5rem\n\n.toc-header-icon, .nav-overlay-icon\n // for when we set display: flex\n justify-content: center\n align-items: center\n\n.toc-content-icon\n height: 1.5rem\n width: 1.5rem\n\n.content-icon-container\n float: right\n display: flex\n margin-top: 1.5rem\n margin-left: 1rem\n margin-bottom: 1rem\n gap: 0.5rem\n\n .edit-this-page, .view-this-page\n svg\n color: inherit\n height: 1.25rem\n width: 1.25rem\n\n.sidebar-toggle\n position: absolute\n display: none\n// \n.sidebar-toggle[name=\"__toc\"]\n left: 20px\n.sidebar-toggle:checked\n left: 40px\n// \n\n.overlay\n position: fixed\n top: 0\n width: 0\n height: 0\n\n transition: width 0ms, height 0ms, opacity 250ms ease-out\n\n opacity: 0\n background-color: rgba(0, 0, 0, 0.54)\n.sidebar-overlay\n z-index: 20\n.toc-overlay\n z-index: 40\n\n// Keep things on top and smooth.\n.sidebar-drawer\n z-index: 30\n transition: left 250ms ease-in-out\n.toc-drawer\n z-index: 50\n transition: right 250ms ease-in-out\n\n// Show the Sidebar\n#__navigation:checked\n & ~ .sidebar-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .sidebar-drawer\n top: 0\n left: 0\n // Show the toc sidebar\n#__toc:checked\n & ~ .toc-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .toc-drawer\n top: 0\n right: 0\n\n////////////////////////////////////////////////////////////////////////////////\n// Back to top\n////////////////////////////////////////////////////////////////////////////////\n.back-to-top\n text-decoration: none\n\n display: none\n position: fixed\n left: 0\n top: 1rem\n padding: 0.5rem\n padding-right: 0.75rem\n border-radius: 1rem\n font-size: 0.8125rem\n\n background: var(--color-background-primary)\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), #6b728080 0px 0px 1px 0px\n\n z-index: 10\n\n margin-left: 50%\n transform: translateX(-50%)\n svg\n height: 1rem\n width: 1rem\n fill: currentColor\n display: inline-block\n\n span\n margin-left: 0.25rem\n\n .show-back-to-top &\n display: flex\n align-items: center\n\n////////////////////////////////////////////////////////////////////////////////\n// Responsive layouting\n////////////////////////////////////////////////////////////////////////////////\n// Make things a bit bigger on bigger screens.\n@media (min-width: $full-width + $sidebar-width)\n html\n font-size: 110%\n\n@media (max-width: $full-width)\n // Collapse \"toc\" into the icon.\n .toc-content-icon\n display: flex\n .toc-drawer\n position: fixed\n height: 100vh\n top: 0\n right: -$sidebar-width\n border-left: 1px solid var(--color-background-muted)\n .toc-tree\n border-left: none\n font-size: var(--toc-font-size--mobile)\n\n // Accomodate for a changed content width.\n .sidebar-drawer\n width: calc((100% - #{$full-width - $sidebar-width}) / 2 + #{$sidebar-width})\n\n@media (max-width: $content-padded-width + $sidebar-width)\n // Center the page\n .content\n margin-left: auto\n margin-right: auto\n padding: 0 $content-padding--small\n\n@media (max-width: $content-padded-width--small + $sidebar-width)\n // Collapse \"navigation\".\n .nav-overlay-icon\n display: flex\n .sidebar-drawer\n position: fixed\n height: 100vh\n width: $sidebar-width\n\n top: 0\n left: -$sidebar-width\n\n // Swap which icon is visible.\n .toc-header-icon, .theme-toggle-header\n display: flex\n .toc-content-icon, .theme-toggle-content\n display: none\n\n // Show the header.\n .mobile-header\n position: sticky\n top: 0\n display: flex\n justify-content: space-between\n align-items: center\n\n .header-left,\n .header-right\n display: flex\n height: var(--header-height)\n padding: 0 var(--header-padding)\n label\n height: 100%\n width: 100%\n user-select: none\n\n .nav-overlay-icon .icon,\n .theme-toggle svg\n height: 1.5rem\n width: 1.5rem\n\n // Add a scroll margin for the content\n :target\n scroll-margin-top: calc(var(--header-height) + 2.5rem)\n\n // Show back-to-top below the header\n .back-to-top\n top: calc(var(--header-height) + 0.5rem)\n\n // Accommodate for the header.\n .page\n flex-direction: column\n justify-content: center\n\n@media (max-width: $content-width + 2* $content-padding--small)\n // Content should respect window limits.\n .content\n width: 100%\n overflow-x: auto\n\n@media (max-width: $content-width)\n article[role=main] aside.sidebar\n float: none\n width: 100%\n margin: 1rem 0\n","// Overall Layout Variables\n//\n// Because CSS variables can't be used in media queries. The fact that this\n// makes the layout non-user-configurable is a good thing.\n$content-padding: 3em;\n$content-padding--small: 1em;\n$content-width: 46em;\n$sidebar-width: 15em;\n$content-padded-width: $content-width + 2 * $content-padding;\n$content-padded-width--small: $content-width + 2 * $content-padding--small;\n$full-width: $content-padded-width + 2 * $sidebar-width;\n","//\n// The design here is strongly inspired by mkdocs-material.\n.admonition, .topic\n margin: 1rem auto\n padding: 0 0.5rem 0.5rem 0.5rem\n\n background: var(--color-admonition-background)\n\n border-radius: 0.2rem\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n font-size: var(--admonition-font-size)\n\n overflow: hidden\n page-break-inside: avoid\n\n // First element should have no margin, since the title has it.\n > :nth-child(2)\n margin-top: 0\n\n // Last item should have no margin, since we'll control that w/ padding\n > :last-child\n margin-bottom: 0\n\n.admonition p.admonition-title,\np.topic-title\n position: relative\n margin: 0 -0.5rem 0.5rem\n padding-left: 2rem\n padding-right: .5rem\n padding-top: .4rem\n padding-bottom: .4rem\n\n font-weight: 500\n font-size: var(--admonition-title-font-size)\n line-height: 1.3\n\n // Our fancy icon\n &::before\n content: \"\"\n position: absolute\n left: 0.5rem\n width: 1rem\n height: 1rem\n\n// Default styles\np.admonition-title\n background-color: var(--color-admonition-title-background)\n &::before\n background-color: var(--color-admonition-title)\n mask-image: var(--icon-admonition-default)\n mask-repeat: no-repeat\n\np.topic-title\n background-color: var(--color-topic-title-background)\n &::before\n background-color: var(--color-topic-title)\n mask-image: var(--icon-topic-default)\n mask-repeat: no-repeat\n\n//\n// Variants\n//\n.admonition\n border-left: 0.2rem solid var(--color-admonition-title)\n\n @each $type, $value in $admonitions\n &.#{$type}\n border-left-color: var(--color-admonition-title--#{$type})\n > .admonition-title\n background-color: var(--color-admonition-title-background--#{$type})\n &::before\n background-color: var(--color-admonition-title--#{$type})\n mask-image: var(--icon-#{nth($value, 2)})\n\n.admonition-todo > .admonition-title\n text-transform: uppercase\n","// This file stylizes the API documentation (stuff generated by autodoc). It's\n// deeply nested due to how autodoc structures the HTML without enough classes\n// to select the relevant items.\n\n// API docs!\ndl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)\n // Tweak the spacing of all the things!\n dd\n margin-left: 2rem\n > :first-child\n margin-top: 0.125rem\n > :last-child\n margin-bottom: 0.75rem\n\n // This is used for the arguments\n .field-list\n margin-bottom: 0.75rem\n\n // \"Headings\" (like \"Parameters\" and \"Return\")\n > dt\n text-transform: uppercase\n font-size: var(--font-size--small)\n\n dd:empty\n margin-bottom: 0.5rem\n dd > ul\n margin-left: -1.2rem\n > li\n > p:nth-child(2)\n margin-top: 0\n // When the last-empty-paragraph follows a paragraph, it doesn't need\n // to augument the existing spacing.\n > p + p:last-child:empty\n margin-top: 0\n margin-bottom: 0\n\n // Colorize the elements\n > dt\n color: var(--color-api-overall)\n\n.sig:not(.sig-inline)\n font-weight: bold\n\n font-size: var(--api-font-size)\n font-family: var(--font-stack--monospace)\n\n margin-left: -0.25rem\n margin-right: -0.25rem\n padding-top: 0.25rem\n padding-bottom: 0.25rem\n padding-right: 0.5rem\n\n // These are intentionally em, to properly match the font size.\n padding-left: 3em\n text-indent: -2.5em\n\n border-radius: 0.25rem\n\n background: var(--color-api-background)\n transition: background 100ms ease-out\n\n &:hover\n background: var(--color-api-background-hover)\n\n // adjust the size of the [source] link on the right.\n a.reference\n .viewcode-link\n font-weight: normal\n width: 4.25rem\n\nem.property\n font-style: normal\n &:first-child\n color: var(--color-api-keyword)\n.sig-name\n color: var(--color-api-name)\n.sig-prename\n font-weight: normal\n color: var(--color-api-pre-name)\n.sig-paren\n color: var(--color-api-paren)\n.sig-param\n font-style: normal\n\ndiv.versionadded,\ndiv.versionchanged,\ndiv.deprecated,\ndiv.versionremoved\n border-left: 0.1875rem solid\n border-radius: 0.125rem\n\n padding-left: 0.75rem\n\n p\n margin-top: 0.125rem\n margin-bottom: 0.125rem\n\ndiv.versionadded\n border-color: var(--color-api-added-border)\n .versionmodified\n color: var(--color-api-added)\n\ndiv.versionchanged\n border-color: var(--color-api-changed-border)\n .versionmodified\n color: var(--color-api-changed)\n\ndiv.deprecated\n border-color: var(--color-api-deprecated-border)\n .versionmodified\n color: var(--color-api-deprecated)\n\ndiv.versionremoved\n border-color: var(--color-api-removed-border)\n .versionmodified\n color: var(--color-api-removed)\n\n// Align the [docs] and [source] to the right.\n.viewcode-link, .viewcode-back\n float: right\n text-align: right\n",".line-block\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n .line-block\n margin-top: 0rem\n margin-bottom: 0rem\n padding-left: 1rem\n","// Captions\narticle p.caption,\ntable > caption,\n.code-block-caption\n font-size: var(--font-size--small)\n text-align: center\n\n// Caption above a TOCTree\n.toctree-wrapper.compound\n .caption, :not(.caption) > .caption-text\n font-size: var(--font-size--small)\n text-transform: uppercase\n\n text-align: initial\n margin-bottom: 0\n\n > ul\n margin-top: 0\n margin-bottom: 0\n","// Inline code\ncode.literal, .sig-inline\n background: var(--color-inline-code-background)\n border-radius: 0.2em\n // Make the font smaller, and use padding to recover.\n font-size: var(--font-size--small--2)\n padding: 0.1em 0.2em\n\n pre.literal-block &\n font-size: inherit\n padding: 0\n\n p &\n border: 1px solid var(--color-background-border)\n\n.sig-inline\n font-family: var(--font-stack--monospace)\n\n// Code and Literal Blocks\n$code-spacing-vertical: 0.625rem\n$code-spacing-horizontal: 0.875rem\n\n// Wraps every literal block + line numbers.\ndiv[class*=\" highlight-\"],\ndiv[class^=\"highlight-\"]\n margin: 1em 0\n display: flex\n\n .table-wrapper\n margin: 0\n padding: 0\n\npre\n margin: 0\n padding: 0\n overflow: auto\n\n // Needed to have more specificity than pygments' \"pre\" selector. :(\n article[role=\"main\"] .highlight &\n line-height: 1.5\n\n &.literal-block,\n .highlight &\n font-size: var(--code-font-size)\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n // Make it look like all the other blocks.\n &.literal-block\n margin-top: 1rem\n margin-bottom: 1rem\n\n border-radius: 0.2rem\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n\n// All code is always contained in this.\n.highlight\n width: 100%\n border-radius: 0.2rem\n\n // Make line numbers and prompts un-selectable.\n .gp, span.linenos\n user-select: none\n pointer-events: none\n\n // Expand the line-highlighting.\n .hll\n display: block\n margin-left: -$code-spacing-horizontal\n margin-right: -$code-spacing-horizontal\n padding-left: $code-spacing-horizontal\n padding-right: $code-spacing-horizontal\n\n/* Make code block captions be nicely integrated */\n.code-block-caption\n display: flex\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n border-radius: 0.25rem\n border-bottom-left-radius: 0\n border-bottom-right-radius: 0\n font-weight: 300\n border-bottom: 1px solid\n\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n border-color: var(--color-background-border)\n\n + div[class]\n margin-top: 0\n pre\n border-top-left-radius: 0\n border-top-right-radius: 0\n\n// When `html_codeblock_linenos_style` is table.\n.highlighttable\n width: 100%\n display: block\n tbody\n display: block\n\n tr\n display: flex\n\n // Line numbers\n td.linenos\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n padding: $code-spacing-vertical $code-spacing-horizontal\n padding-right: 0\n border-top-left-radius: 0.2rem\n border-bottom-left-radius: 0.2rem\n\n .linenodiv\n padding-right: $code-spacing-horizontal\n font-size: var(--code-font-size)\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n\n // Actual code\n td.code\n padding: 0\n display: block\n flex: 1\n overflow: hidden\n\n .highlight\n border-top-left-radius: 0\n border-bottom-left-radius: 0\n\n// When `html_codeblock_linenos_style` is inline.\n.highlight\n span.linenos\n display: inline-block\n padding-left: 0\n padding-right: $code-spacing-horizontal\n margin-right: $code-spacing-horizontal\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n","// Inline Footnote Reference\n.footnote-reference\n font-size: var(--font-size--small--4)\n vertical-align: super\n\n// Definition list, listing the content of each note.\n// docutils <= 0.17\ndl.footnote.brackets\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\n display: grid\n grid-template-columns: max-content auto\n dt\n margin: 0\n > .fn-backref\n margin-left: 0.25rem\n\n &:after\n content: \":\"\n\n .brackets\n &:before\n content: \"[\"\n &:after\n content: \"]\"\n\n dd\n margin: 0\n padding: 0 1rem\n\n// docutils >= 0.18\naside.footnote\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\naside.footnote > span,\ndiv.citation > span\n float: left\n font-weight: 500\n padding-right: 0.25rem\n\naside.footnote > *:not(span),\ndiv.citation > p\n margin-left: 2rem\n","//\n// Figures\n//\nimg\n box-sizing: border-box\n max-width: 100%\n height: auto\n\narticle\n figure, .figure\n border-radius: 0.2rem\n\n margin: 0\n :last-child\n margin-bottom: 0\n\n .align-left\n float: left\n clear: left\n margin: 0 1rem 1rem\n\n .align-right\n float: right\n clear: right\n margin: 0 1rem 1rem\n\n .align-default,\n .align-center\n display: block\n text-align: center\n margin-left: auto\n margin-right: auto\n\n // WELL, table needs to be stylised like a table.\n table.align-default\n display: table\n text-align: initial\n",".genindex-jumpbox, .domainindex-jumpbox\n border-top: 1px solid var(--color-background-border)\n border-bottom: 1px solid var(--color-background-border)\n padding: 0.25rem\n\n.genindex-section, .domainindex-section\n h2\n margin-top: 0.75rem\n margin-bottom: 0.5rem\n ul\n margin-top: 0\n margin-bottom: 0\n","ul,\nol\n padding-left: 1.2rem\n\n // Space lists out like paragraphs\n margin-top: 1rem\n margin-bottom: 1rem\n // reduce margins within li.\n li\n > p:first-child\n margin-top: 0.25rem\n margin-bottom: 0.25rem\n\n > p:last-child\n margin-top: 0.25rem\n\n > ul,\n > ol\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n\nol\n &.arabic\n list-style: decimal\n &.loweralpha\n list-style: lower-alpha\n &.upperalpha\n list-style: upper-alpha\n &.lowerroman\n list-style: lower-roman\n &.upperroman\n list-style: upper-roman\n\n// Don't space lists out when they're \"simple\" or in a `.. toctree::`\n.simple,\n.toctree-wrapper\n li\n > ul,\n > ol\n margin-top: 0\n margin-bottom: 0\n\n// Definition Lists\n.field-list,\n.option-list,\ndl:not([class]),\ndl.simple,\ndl.footnote,\ndl.glossary\n dt\n font-weight: 500\n margin-top: 0.25rem\n + dt\n margin-top: 0\n\n .classifier::before\n content: \":\"\n margin-left: 0.2rem\n margin-right: 0.2rem\n\n dd\n > p:first-child,\n ul\n margin-top: 0.125rem\n\n ul\n margin-bottom: 0.125rem\n",".math-wrapper\n width: 100%\n overflow-x: auto\n\ndiv.math\n position: relative\n text-align: center\n\n .headerlink,\n &:focus .headerlink\n display: none\n\n &:hover .headerlink\n display: inline-block\n\n span.eqno\n position: absolute\n right: 0.5rem\n top: 50%\n transform: translate(0, -50%)\n z-index: 1\n","// Abbreviations\nabbr[title]\n cursor: help\n\n// \"Problematic\" content, as identified by Sphinx\n.problematic\n color: var(--color-problematic)\n\n// Keyboard / Mouse \"instructions\"\nkbd:not(.compound)\n margin: 0 0.2rem\n padding: 0 0.2rem\n border-radius: 0.2rem\n border: 1px solid var(--color-foreground-border)\n color: var(--color-foreground-primary)\n vertical-align: text-bottom\n\n font-size: var(--font-size--small--3)\n display: inline-block\n\n box-shadow: 0 0.0625rem 0 rgba(0, 0, 0, 0.2), inset 0 0 0 0.125rem var(--color-background-primary)\n\n background-color: var(--color-background-secondary)\n\n// Blockquote\nblockquote\n border-left: 4px solid var(--color-background-border)\n background: var(--color-background-secondary)\n\n margin-left: 0\n margin-right: 0\n padding: 0.5rem 1rem\n\n .attribution\n font-weight: 600\n text-align: right\n\n &.pull-quote,\n &.highlights\n font-size: 1.25em\n\n &.epigraph,\n &.pull-quote\n border-left-width: 0\n border-radius: 0.5rem\n\n &.highlights\n border-left-width: 0\n background: transparent\n\n// Center align embedded-in-text images\np .reference img\n vertical-align: middle\n","p.rubric\n line-height: 1.25\n font-weight: bold\n font-size: 1.125em\n\n // For Numpy-style documentation that's got rubrics within it.\n // https://github.com/pradyunsg/furo/discussions/505\n dd &\n line-height: inherit\n font-weight: inherit\n\n font-size: var(--font-size--small)\n text-transform: uppercase\n","article .sidebar\n float: right\n clear: right\n width: 30%\n\n margin-left: 1rem\n margin-right: 0\n\n border-radius: 0.2rem\n background-color: var(--color-background-secondary)\n border: var(--color-background-border) 1px solid\n\n > *\n padding-left: 1rem\n padding-right: 1rem\n\n > ul, > ol // lists need additional padding, because bullets.\n padding-left: 2.2rem\n\n .sidebar-title\n margin: 0\n padding: 0.5rem 1rem\n border-bottom: var(--color-background-border) 1px solid\n\n font-weight: 500\n\n// TODO: subtitle\n// TODO: dedicated variables?\n","[role=main] .table-wrapper.container\n width: 100%\n overflow-x: auto\n margin-top: 1rem\n margin-bottom: 0.5rem\n padding: 0.2rem 0.2rem 0.75rem\n\ntable.docutils\n border-radius: 0.2rem\n border-spacing: 0\n border-collapse: collapse\n\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n th\n background: var(--color-table-header-background)\n\n td,\n th\n // Space things out properly\n padding: 0 0.25rem\n\n // Get the borders looking just-right.\n border-left: 1px solid var(--color-table-border)\n border-right: 1px solid var(--color-table-border)\n border-bottom: 1px solid var(--color-table-border)\n\n p\n margin: 0.25rem\n\n &:first-child\n border-left: none\n &:last-child\n border-right: none\n\n // MyST-parser tables set these classes for control of column alignment\n &.text-left\n text-align: left\n &.text-right\n text-align: right\n &.text-center\n text-align: center\n",":target\n scroll-margin-top: 2.5rem\n\n@media (max-width: $full-width - $sidebar-width)\n :target\n scroll-margin-top: calc(2.5rem + var(--header-height))\n\n // When a heading is selected\n section > span:target\n scroll-margin-top: calc(2.8rem + var(--header-height))\n\n// Permalinks\n.headerlink\n font-weight: 100\n user-select: none\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\ndl dt,\np.caption,\nfigcaption p,\ntable > caption,\n.code-block-caption\n > .headerlink\n margin-left: 0.5rem\n visibility: hidden\n &:hover > .headerlink\n visibility: visible\n\n // Don't change to link-like, if someone adds the contents directive.\n > .toc-backref\n color: inherit\n text-decoration-line: none\n\n// Figure and table captions are special.\nfigure:hover > figcaption > p > .headerlink,\ntable:hover > caption > .headerlink\n visibility: visible\n\n:target >, // Regular section[id] style anchors\nspan:target ~ // Non-regular span[id] style \"extra\" anchors\n h1,\n h2,\n h3,\n h4,\n h5,\n h6\n &:nth-of-type(1)\n background-color: var(--color-highlight-on-target)\n // .headerlink\n // visibility: visible\n code.literal\n background-color: transparent\n\ntable:target > caption,\nfigure:target\n background-color: var(--color-highlight-on-target)\n\n// Inline page contents\n.this-will-duplicate-information-and-it-is-still-useful-here li :target\n background-color: var(--color-highlight-on-target)\n\n// Code block permalinks\n.literal-block-wrapper:target .code-block-caption\n background-color: var(--color-highlight-on-target)\n\n// When a definition list item is selected\n//\n// There isn't really an alternative to !important here, due to the\n// high-specificity of API documentation's selector.\ndt:target\n background-color: var(--color-highlight-on-target) !important\n\n// When a footnote reference is selected\n.footnote > dt:target + dd,\n.footnote-reference:target\n background-color: var(--color-highlight-on-target)\n",".guilabel\n background-color: var(--color-guilabel-background)\n border: 1px solid var(--color-guilabel-border)\n color: var(--color-guilabel-text)\n\n padding: 0 0.3em\n border-radius: 0.5em\n font-size: 0.9em\n","// This file contains the styles used for stylizing the footer that's shown\n// below the content.\n\nfooter\n font-size: var(--font-size--small)\n display: flex\n flex-direction: column\n\n margin-top: 2rem\n\n// Bottom of page information\n.bottom-of-page\n display: flex\n align-items: center\n justify-content: space-between\n\n margin-top: 1rem\n padding-top: 1rem\n padding-bottom: 1rem\n\n color: var(--color-foreground-secondary)\n border-top: 1px solid var(--color-background-border)\n\n line-height: 1.5\n\n @media (max-width: $content-width)\n text-align: center\n flex-direction: column-reverse\n gap: 0.25rem\n\n .left-details\n font-size: var(--font-size--small)\n\n .right-details\n display: flex\n flex-direction: column\n gap: 0.25rem\n text-align: right\n\n .icons\n display: flex\n justify-content: flex-end\n gap: 0.25rem\n font-size: 1rem\n\n a\n text-decoration: none\n\n svg,\n img\n font-size: 1.125rem\n height: 1em\n width: 1em\n\n// Next/Prev page information\n.related-pages\n a\n display: flex\n align-items: center\n\n text-decoration: none\n &:hover .page-info .title\n text-decoration: underline\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n svg.furo-related-icon,\n svg.furo-related-icon > use\n flex-shrink: 0\n\n color: var(--color-foreground-border)\n\n width: 0.75rem\n height: 0.75rem\n margin: 0 0.5rem\n\n &.next-page\n max-width: 50%\n\n float: right\n clear: right\n text-align: right\n\n &.prev-page\n max-width: 50%\n\n float: left\n clear: left\n\n svg\n transform: rotate(180deg)\n\n.page-info\n display: flex\n flex-direction: column\n overflow-wrap: anywhere\n\n .next-page &\n align-items: flex-end\n\n .context\n display: flex\n align-items: center\n\n padding-bottom: 0.1rem\n\n color: var(--color-foreground-muted)\n font-size: var(--font-size--small)\n text-decoration: none\n","// This file contains the styles for the contents of the left sidebar, which\n// contains the navigation tree, logo, search etc.\n\n////////////////////////////////////////////////////////////////////////////////\n// Brand on top of the scrollable tree.\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-brand\n display: flex\n flex-direction: column\n flex-shrink: 0\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n text-decoration: none\n\n.sidebar-brand-text\n color: var(--color-sidebar-brand-text)\n overflow-wrap: break-word\n margin: var(--sidebar-item-spacing-vertical) 0\n font-size: 1.5rem\n\n.sidebar-logo-container\n margin: var(--sidebar-item-spacing-vertical) 0\n\n.sidebar-logo\n margin: 0 auto\n display: block\n max-width: 100%\n\n////////////////////////////////////////////////////////////////////////////////\n// Search\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-search-container\n display: flex\n align-items: center\n margin-top: var(--sidebar-search-space-above)\n\n position: relative\n\n background: var(--color-sidebar-search-background)\n &:hover,\n &:focus-within\n background: var(--color-sidebar-search-background--focus)\n\n &::before\n content: \"\"\n position: absolute\n left: var(--sidebar-item-spacing-horizontal)\n width: var(--sidebar-search-icon-size)\n height: var(--sidebar-search-icon-size)\n\n background-color: var(--color-sidebar-search-icon)\n mask-image: var(--icon-search)\n\n.sidebar-search\n box-sizing: border-box\n\n border: none\n border-top: 1px solid var(--color-sidebar-search-border)\n border-bottom: 1px solid var(--color-sidebar-search-border)\n\n padding-top: var(--sidebar-search-input-spacing-vertical)\n padding-bottom: var(--sidebar-search-input-spacing-vertical)\n padding-right: var(--sidebar-search-input-spacing-horizontal)\n padding-left: calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size))\n\n width: 100%\n\n color: var(--color-sidebar-search-foreground)\n background: transparent\n z-index: 10\n\n &:focus\n outline: none\n\n &::placeholder\n font-size: var(--sidebar-search-input-font-size)\n\n//\n// Hide Search Matches link\n//\n#searchbox .highlight-link\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0\n margin: 0\n text-align: center\n\n a\n color: var(--color-sidebar-search-icon)\n font-size: var(--font-size--small--2)\n\n////////////////////////////////////////////////////////////////////////////////\n// Structure/Skeleton of the navigation tree (left)\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-tree\n font-size: var(--sidebar-item-font-size)\n margin-top: var(--sidebar-tree-space-above)\n margin-bottom: var(--sidebar-item-spacing-vertical)\n\n ul\n padding: 0\n margin-top: 0\n margin-bottom: 0\n\n display: flex\n flex-direction: column\n\n list-style: none\n\n li\n position: relative\n margin: 0\n\n > ul\n margin-left: var(--sidebar-item-spacing-horizontal)\n\n .icon\n color: var(--color-sidebar-link-text)\n\n .reference\n box-sizing: border-box\n color: var(--color-sidebar-link-text)\n\n // Fill the parent.\n display: inline-block\n line-height: var(--sidebar-item-line-height)\n text-decoration: none\n\n // Don't allow long words to cause wrapping.\n overflow-wrap: anywhere\n\n height: 100%\n width: 100%\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n &:hover\n color: var(--color-sidebar-link-text)\n background: var(--color-sidebar-item-background--hover)\n\n // Add a nice little \"external-link\" arrow here.\n &.external::after\n content: url('data:image/svg+xml,')\n margin: 0 0.25rem\n vertical-align: middle\n color: var(--color-sidebar-link-text)\n\n // Make the current page reference bold.\n .current-page > .reference\n font-weight: bold\n\n label\n position: absolute\n top: 0\n right: 0\n height: var(--sidebar-item-height)\n width: var(--sidebar-expander-width)\n\n cursor: pointer\n user-select: none\n\n display: flex\n justify-content: center\n align-items: center\n\n .caption, :not(.caption) > .caption-text\n font-size: var(--sidebar-caption-font-size)\n color: var(--color-sidebar-caption-text)\n\n font-weight: bold\n text-transform: uppercase\n\n margin: var(--sidebar-caption-space-above) 0 0 0\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n // If it has children, add a bit more padding to wrap the content to avoid\n // overlapping with the