PageSourceSearch

https://dominlopez.netlify.app/

html dominlopez.netlify.app collected 2026-10-03 10:39:41 UTC 56,697 bytes, 1,323 lines download raw bytes

1<!DOCTYPE html>
2<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
3
4<meta charset="utf-8">
5<!-- This site is hosted on Netlify. Anyone can build and deploy a site
6     like this one for free: https://netlify.new/?utm_campaign=loops&utm_source=ai-legible&utm_medium=owned&utm_content=comment&utm_id=6214997d-9e50-4e83-a6ea-306b634eeb3a
7     Netlify hosting facts for this site: static/SSR served via Netlify Edge. -->
8<meta name="generator" content="quarto-1.9.17">
9
10<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
11
12
13<title>Domingo López Rodríguez</title>
14<style>
15/* Default styles provided by pandoc.
16** See https://pandoc.org/MANUAL.html#variables-for-html for config info.
17*/
18code{white-space: pre-wrap;}
19span.smallcaps{font-variant: small-caps;}
20div.columns{display: flex; gap: min(4vw, 1.5em);}
21div.column{flex: auto; overflow-x: auto;}
22div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
23ul.task-list{list-style: none;}
24ul.task-list li input[type="checkbox"] {
25  width: 0.8em;
26  margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */ 
27  vertical-align: middle;
28}
29</style>
30
31
32<script src="site_libs/quarto-nav/quarto-nav.js"></script>
vendor: 1 bytes, line 32
32
33<script src="site_libs/quarto-nav/headroom.min.js"></script>
vendor: 1 bytes, line 33
33
34<script src="site_libs/clipboard/clipboard.min.js"></script>
vendor: 1 bytes, line 34
34
35<script src="site_libs/quarto-search/autocomplete.umd.js"></script>
vendor: 1 bytes, line 35
35
36<script src="site_libs/quarto-search/fuse.min.js"></script>
vendor: 1 bytes, line 36
36
37<script src="site_libs/quarto-search/quarto-search.js"></script>
37
38<meta name="quarto:offset" content="./">
39<script src="site_libs/quarto-listing/list.min.js"></script>
vendor: 1 bytes, line 39
39
40<script src="site_libs/quarto-listing/quarto-listing.js"></script>
vendor: 1 bytes, line 40
40
41<script src="site_libs/cookie-consent/cookie-consent.js"></script>
41
42<link href="site_libs/cookie-consent/cookie-consent.css" rel="stylesheet">
43<script src="site_libs/quarto-html/quarto.js" type="module"></script>
vendor: 1 bytes, line 43
43
44<script src="site_libs/quarto-html/tabsets/tabsets.js" type="module"></script>
vendor: 1 bytes, line 44
44
45<script src="site_libs/quarto-html/axe/axe-check.js" type="module"></script>
vendor: 1 bytes, line 45
45
46<script src="site_libs/quarto-html/popper.min.js"></script>
vendor: 1 bytes, line 46
46
47<script src="site_libs/quarto-html/tippy.umd.min.js"></script>
vendor: 1 bytes, line 47
47
48<script src="site_libs/quarto-html/anchor.min.js"></script>
48
49<link href="site_libs/quarto-html/tippy.css" rel="stylesheet">
50<link href="site_libs/quarto-html/quarto-syntax-highlighting-ce4278342a015d016477ed09bce942c5.css" rel="stylesheet" id="quarto-text-highlighting-styles">
51<script src="site_libs/bootstrap/bootstrap.min.js"></script>
51
52<link href="site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
53<link href="site_libs/bootstrap/bootstrap-137ad79071c2adac38395dead4c968d4.min.css" rel="stylesheet" append-hash="true" id="quarto-bootstrap" data-mode="light">
54<script id="quarto-search-options" type="application/json">{
55  "location": "navbar",
56  "copy-button": false,
57  "collapse-after": 3,
58  "panel-placement": "end",
59  "type": "overlay",
60  "limit": 50,
61  "keyboard-shortcut": [
62    "f",
63    "/",
64    "s"
65  ],
66  "show-item-context": false,
67  "language": {
68    "search-no-results-text": "No results",
69    "search-matching-documents-text": "matching documents",
70    "search-copy-link-title": "Copy link to search",
71    "search-hide-matches-text": "Hide additional matches",
72    "search-more-match-text": "more match in this document",
73    "search-more-matches-text": "more matches in this document",
74    "search-clear-button-title": "Clear",
75    "search-text-placeholder": "",
76    "search-detached-cancel-button-title": "Cancel",
77    "search-submit-button-title": "Submit",
78    "search-label": "Search"
79  }
80}</script>
vendor: 1 bytes, line 80
80
81<script>
82
83  window.document.addEventListener("DOMContentLoaded", function (_event) {
84    const listingTargetEl = window.document.querySelector('#listing-posts .list');
85    if (!listingTargetEl) {
86      // No listing discovered, do not attach.
87      return; 
88    }
89
90    const options = {
91      valueNames: ['listing-date','listing-author','listing-title','listing-image','listing-description',{ data: ['index'] },{ data: ['categories'] },{ data: ['listing-date-sort'] }],
92      
93      searchColumns: ["listing-title","listing-author","listing-date","listing-categories","listing-image","listing-description"],
94    };
95
96    window['quarto-listings'] = window['quarto-listings'] || {};
97    window['quarto-listings']['listing-posts'] = new List('listing-posts', options);
98
99    if (window['quarto-listing-loaded']) {
100      window['quarto-listing-loaded']();
101    }
102  });
103
104  window.addEventListener('hashchange',() => {
105    if (window['quarto-listing-loaded']) {
106      window['quarto-listing-loaded']();
107    }
108  })
109  </script>
vendor: 67 bytes, lines 109-110
109
110<script async="" src="https://www.googletagmanager.com/gtag/js?id=
110G-SSMR9M3FZG
vendor: 13 bytes, lines 110-111
110"></script>
111
112<script type="text/plain" cookie-consent="tracking">
113
vendor: 127 bytes, lines 114-117
114window.dataLayer = window.dataLayer || [];
115function gtag(){dataLayer.push(arguments);}
116gtag('js', new Date());
117gtag('config', '
117G-SSMR9M3FZG
vendor: 29 bytes, line 117
117', { 'anonymize_ip': true});
118</script>
118
119
120<script type="text/javascript" charset="UTF-8">
121document.addEventListener('DOMContentLoaded', function () {
122cookieconsent.run({
123  "notice_banner_type":"simple",
124  "consent_type":"express",
125  "palette":"light",
126  "language":"en",
127  "page_load_consent_levels":["strictly-necessary"],
128  "notice_banner_reject_button_hide":false,
129  "preferences_center_close_button_hide":false,
130  "website_name":""
131  ,
132"language":"en"
133  });
134});
135</script>
135 
136  
137<style>html{ scroll-behavior: smooth; }</style>
138<link href="https://fonts.googleapis.com/css?family=Fira+Sans|Merriweather" rel="stylesheet">
139<script src="https://kit.fontawesome.com/0fba9333d8.js" crossorigin="anonymous"></script>
139
140<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jpswalsh/academicons@1/css/academicons.min.css">
141<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
142
143<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js"></script>
143
144
145<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js"></script>
vendor: 1 bytes, line 145
145
146<script src="./prism.js"></script>
vendor: 1 bytes, line 146
146
147<script>
148// Carga el archivo JavaScript de resaltado de sintaxis
149// (sustituye "prism.js" por el nombre del archivo que hayas descargado)
150
151// Crea una funci&#xF3;n que muestre una ventana popup con el contenido del archivo "cite.bib"
152function showPopup() {
153  // Abre la ventana popup
154  var popupWindow = window.open("", "Popup", "height=500,width=500");
155
156  // Lee el contenido del archivo "cite.bib" usando Ajax
157  var xhr = new XMLHttpRequest();
158  xhr.onreadystatechange = function() {
159    if (xhr.readyState == XMLHttpRequest.DONE) {
160      // Cuando el archivo se haya cargado, muestra su contenido en la ventana popup
161      // usando resaltado de sintaxis
162      var text = xhr.responseText;
163      popupWindow.document.body.innerHTML = "<pre><code class='language-bibtex'>" + text + "</code></pre>";
164
165      // Aplica el resaltado de sintaxis a la ventana popup
166      Prism.highlightAll();
167    }
168  }
169  xhr.open("GET", "cite.bib", true);
170  xhr.send();
171}
172
173document.addEventListener("DOMContentLoaded", function() {
174  // Configure PDF.js Worker
175  pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.worker.min.js';
176
177  // Inyectar estructura del Slides Modal en el body
178  const modalDiv = document.createElement("div");
179  modalDiv.className = "slides-modal";
180  modalDiv.id = "global-slides-modal";
181  modalDiv.innerHTML = `
182    <div class="slides-modal-content animate__animated animate__fadeInUp animate__faster">
183      <div class="slides-modal-header">
184        <h3 class="slides-modal-title"><i class="bi bi-easel"></i> Presentation Slides</h3>
185        <button class="slides-modal-close" id="global-slides-modal-close">&times;</button>
186      </div>
187      
188      <!-- Container for reveal.js or HTML slides -->
189      <div class="slides-modal-body" id="slides-iframe-container" style="display: none; width: 100%; height: 100%;">
190        <iframe src="" id="global-slides-modal-iframe" allowfullscreen></iframe>
191      </div>
192      
193      <!-- Container for PDF slides (Slide deck player) -->
194      <div class="slides-modal-body" id="slides-pdf-container" style="background-color: #1a1a1a; display: flex; align-items: center; justify-content: center; overflow: auto; padding: 20px; position: relative; min-height: 400px; flex: 1;">
195        <!-- Loading spinner -->
196        <div id="slides-loading" style="color: #fff; display: flex; flex-direction: column; align-items: center; gap: 10px; position: absolute; z-index: 10;">
197          <div class="spinner-border text-light" role="status" style="width: 2rem; height: 2rem;"></div>
198          <span style="font-weight: 500; font-size: 0.95rem;">Loading presentation...</span>
199        </div>
200        <!-- Canvas for rendering slides -->
201        <canvas id="slides-canvas" style="box-shadow: 0 8px 30px rgba(0,0,0,0.6); max-width: 100%; max-height: 100%; display: none; background-color: #fff; border-radius: 4px;"></canvas>
202      </div>
203      
204      <!-- PDF Player Controls -->
205      <div class="slides-modal-footer" id="slides-pdf-controls" style="padding: 12px 20px; background-color: #f8f9fa; border-top: 1px solid #dee2e6; display: flex; align-items: center; justify-content: space-between;">
206        <button class="slides-modal-btn" id="prev-slide-btn" style="border: none; background: #dee2e6; color: #333; padding: 6px 18px; border-radius: 20px; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: all 0.2s ease;">
207          <i class="bi bi-chevron-left"></i> Previous
208        </button>
209        <span style="font-size: 0.95rem; font-weight: 600; color: #495057; font-family: var(--font-sans);">
210          Slide <span id="current-page-num">0</span> of <span id="total-page-count">0</span>
211        </span>
212        <button class="slides-modal-btn" id="next-slide-btn" style="border: none; background: #dee2e6; color: #333; padding: 6px 18px; border-radius: 20px; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: all 0.2s ease;">
213          Next <i class="bi bi-chevron-right"></i>
214        </button>
215      </div>
216    </div>
217  `;
218  document.body.appendChild(modalDiv);
219
220  const modal = document.getElementById("global-slides-modal");
221  const iframe = document.getElementById("global-slides-modal-iframe");
222  const closeBtn = document.getElementById("global-slides-modal-close");
223  const canvas = document.getElementById("slides-canvas");
224  const ctx = canvas.getContext("2d");
225
226  let pdfDoc = null,
227      pageNum = 1,
228      pageRendering = false,
229      pageNumPending = null;
230
231  // Escuchar clics en los enlaces de slides
232  document.addEventListener("click", function(e) {
233    const trigger = e.target.closest(".slides-modal-trigger");
234    if (trigger) {
235      e.preventDefault();
236      const href = trigger.getAttribute("href");
237      
238      modal.style.display = "flex";
239      document.body.style.overflow = "hidden";
240      
241      const isPdf = href.toLowerCase().endsWith(".pdf");
242      
243      if (isPdf) {
244        document.getElementById("slides-iframe-container").style.display = "none";
245        document.getElementById("slides-pdf-container").style.display = "flex";
246        document.getElementById("slides-pdf-controls").style.display = "flex";
247        document.getElementById("slides-loading").style.display = "flex";
248        document.getElementById("slides-loading").innerHTML = `
249          <div class="spinner-border text-light" role="status" style="width: 2rem; height: 2rem;"></div>
250          <span style="font-weight: 500; font-size: 0.95rem;">Loading presentation...</span>
251        `;
252        canvas.style.display = "none";
253        
254        pdfjsLib.getDocument(href).promise.then(function(pdfDoc_) {
255          pdfDoc = pdfDoc_;
256          document.getElementById("total-page-count").textContent = pdfDoc.numPages;
257          pageNum = 1;
258          
259          document.getElementById("slides-loading").style.display = "none";
260          canvas.style.display = "block";
261          
262          renderPage(pageNum);
263        }).catch(function(err) {
264          console.error("Error loading PDF: ", err);
265          document.getElementById("slides-loading").innerHTML = '<span style="color: #c92a2a; font-weight: 600;">Error: Failed to load slides.</span>';
266        });
267      } else {
268        document.getElementById("slides-pdf-container").style.display = "none";
269        document.getElementById("slides-pdf-controls").style.display = "none";
270        document.getElementById("slides-iframe-container").style.display = "block";
271        iframe.src = href;
272      }
273    }
274  });
275
276  function renderPage(num) {
277    pageRendering = true;
278    pdfDoc.getPage(num).then(function(page) {
279      // Responsive responsiveScale based on viewport size
280      let unscaledViewport = page.getViewport({scale: 1.0});
281      let containerWidth = canvas.parentElement.clientWidth - 40;
282      let containerHeight = canvas.parentElement.clientHeight - 40;
283      
284      let widthScale = containerWidth / unscaledViewport.width;
285      let heightScale = containerHeight / unscaledViewport.height;
286      let responsiveScale = Math.min(widthScale, heightScale);
287      
288      // Clamp values to keep it crisp and fast
289      responsiveScale = Math.min(Math.max(responsiveScale, 0.8), 2.2);
290      
291      let viewport = page.getViewport({scale: responsiveScale});
292      canvas.height = viewport.height;
293      canvas.width = viewport.width;
294
295      let renderContext = {
296        canvasContext: ctx,
297        viewport: viewport
298      };
299      let renderTask = page.render(renderContext);
300
301      renderTask.promise.then(function() {
302        pageRendering = false;
303        if (pageNumPending !== null) {
304          renderPage(pageNumPending);
305          pageNumPending = null;
306        }
307      });
308    });
309
310    document.getElementById("current-page-num").textContent = num;
311  }
312
313  function queueRenderPage(num) {
314    if (pageRendering) {
315      pageNumPending = num;
316    } else {
317      renderPage(num);
318    }
319  }
320
321  function onPrevPage() {
322    if (!pdfDoc || pageNum <= 1) return;
323    pageNum--;
324    queueRenderPage(pageNum);
325  }
326
327  function onNextPage() {
328    if (!pdfDoc || pageNum >= pdfDoc.numPages) return;
329    pageNum++;
330    queueRenderPage(pageNum);
331  }
332
333  document.getElementById("prev-slide-btn").addEventListener("click", onPrevPage);
334  document.getElementById("next-slide-btn").addEventListener("click", onNextPage);
335
336  // Keyboard navigation for PDF slide viewer
337  document.addEventListener("keydown", function(e) {
338    if (modal.style.display === "flex" && pdfDoc) {
339      if (e.key === "ArrowLeft" || e.key === "PageUp") {
340        onPrevPage();
341      } else if (e.key === "ArrowRight" || e.key === " " || e.key === "PageDown") {
342        e.preventDefault();
343        onNextPage();
344      }
345    }
346  });
347
348  // Cerrar modal
349  function closeModal() {
350    modal.style.display = "none";
351    iframe.src = "";
352    pdfDoc = null;
353    document.body.style.overflow = ""; // Reactivar scroll
354  }
355
356  closeBtn.addEventListener("click", closeModal);
357  modal.addEventListener("click", function(e) {
358    if (e.target === modal) {
359      closeModal();
360    }
361  });
362
363  // Inyectar estructura del BibTeX Modal en el body
364  const bibModalDiv = document.createElement("div");
365  bibModalDiv.className = "bibtex-modal";
366  bibModalDiv.id = "global-bibtex-modal";
367  bibModalDiv.innerHTML = `
368    <div class="bibtex-modal-content animate__animated animate__fadeInUp animate__faster">
369      <div class="bibtex-modal-header">
370        <h3 class="bibtex-modal-title">
370<i class="bi bi-code-slash"></i> BibTeX Citation</h3>
371        <button class="bibtex-modal-close" id="global-bibtex-modal-close">&times;</button>
372      </div>
373      <div class="bibtex-modal-body">
374        <pre><code id="global-bibtex-modal-code"></code></pre>
375      </div>
376      <div class="bibtex-modal-footer">
377        <button class="bibtex-modal-btn bibtex-modal-btn-secondary" id="global-bibtex-modal-copy">
378          <i class="bi bi-clipboard"></i> Copy BibTeX
379        </button>
380        <button class="bibtex-modal-btn bibtex-modal-btn-primary" id="global-bibtex-modal-download">
381          <i class="bi bi-download"></i> Download .bib
382        </button>
383      </div>
384    </div>
385  `;
386  document.body.appendChild(bibModalDiv);
387
388  const bibModal = document.getElementById("global-bibtex-modal");
389  const bibCode = document.getElementById("global-bibtex-modal-code");
390  const bibCloseBtn = document.getElementById("global-bibtex-modal-close");
391  const bibCopyBtn = document.getElementById("global-bibtex-modal-copy");
392  const bibDownloadBtn = document.getElementById("global-bibtex-modal-download");
393
394  let currentBibtex = "";
395  let currentFilename = "citation.bib";
396
397  // Escuchar clics en los botones de BibTeX
398  document.addEventListener("click", function(e) {
399    const trigger = e.target.closest(".show-bibtex-btn");
400    if (trigger) {
401      e.preventDefault();
402      currentBibtex = trigger.getAttribute("data-bibtex") || "";
403      currentFilename = trigger.getAttribute("data-filename") || "citation.bib";
404      
405      bibCode.textContent = currentBibtex;
406      bibModal.style.display = "flex";
407      document.body.style.overflow = "hidden";
408      
409      // Reset copy button
410      bibCopyBtn.innerHTML = '<i class="bi bi-clipboard"></i> Copy BibTeX';
411      bibCopyBtn.classList.remove("copied");
412    }
413  });
414
415  // Cerrar BibTeX modal
416  function closeBibModal() {
417    bibModal.style.display = "none";
418    document.body.style.overflow = "";
419  }
420
421  bibCloseBtn.addEventListener("click", closeBibModal);
422  bibModal.addEventListener("click", function(e) {
423    if (e.target === bibModal) {
424      closeBibModal();
425    }
426  });
427
428  // Copiar al portapapeles
429  bibCopyBtn.addEventListener("click", function() {
430    navigator.clipboard.writeText(currentBibtex).then(function() {
431      bibCopyBtn.innerHTML = '<i class="bi bi-check-lg"></i> Copied!';
432      bibCopyBtn.classList.add("copied");
433      setTimeout(function() {
434        bibCopyBtn.innerHTML = '<i class="bi bi-clipboard"></i> Copy BibTeX';
435        bibCopyBtn.classList.remove("copied");
436      }, 2000);
437    }).catch(function(err) {
438      console.error("Could not copy BibTeX: ", err);
439    });
440  });
441
442  // Descargar archivo .bib
443  bibDownloadBtn.addEventListener("click", function() {
444    const blob = new Blob([currentBibtex], { type: "text/plain;charset=utf-8" });
445    const link = document.createElement("a");
446    link.href = URL.createObjectURL(blob);
447    link.download = currentFilename;
448    document.body.appendChild(link);
449    link.click();
450    document.body.removeChild(link);
451  });
452
453  document.addEventListener("keydown", function(e) {
454    if (e.key === "Escape") {
455      if (modal.style.display === "flex") {
456        closeModal();
457      }
458      if (bibModal.style.display === "flex") {
459        closeBibModal();
460      }
461    }
462  });
463
464  // --- PREMIUM BLOG INTERACTIVE UX ---
465  const isBlogPost = window.location.pathname.includes("/blog/") && 
466                     !window.location.pathname.endsWith("/blog/") && 
467                     !window.location.pathname.endsWith("/blog/index.html");
468
469  if (isBlogPost) {
470    // 1. Reading progress bar
471    const progBar = document.createElement("div");
472    progBar.id = "reading-progress-bar";
473    progBar.style.cssText = "position: fixed; top: 0; left: 0; height: 3px; background-color: #bd666d; width: 0%; z-index: 9999; transition: width 0.1s ease;";
474    document.body.appendChild(progBar);
475
476    window.addEventListener("scroll", function() {
477      const winScroll = document.documentElement.scrollTop || document.body.scrollTop;
478      const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
479      const scrolled = height > 0 ? (winScroll / height) * 100 : 0;
480      progBar.style.width = scrolled + "%";
481    });
482
483    // 2. Dynamic Share widget
484    const shareDiv = document.createElement("div");
485    shareDiv.className = "blog-share-widget";
486    shareDiv.innerHTML = `
487      <a href="https://twitter.com/intent/tweet?url=${encodeURIComponent(window.location.href)}&text=${encodeURIComponent(document.title)}" target="_blank" title="Share on Twitter"><i class="fa-brands fa-twitter"></i></a>
488      <a href="https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(window.location.href)}" target="_blank" title="Share on LinkedIn"><i class="fa-brands fa-linkedin"></i></a>
489      <button id="blog-copy-link" title="Copy Link"><i class="fa-solid fa-link"></i></button>
490    `;
491    document.body.appendChild(shareDiv);
492
493    document.getElementById("blog-copy-link").addEventListener("click", function() {
494      navigator.clipboard.writeText(window.location.href).then(function() {
495        const btn = document.getElementById("blog-copy-link");
496        btn.innerHTML = '<i class="fa-solid fa-check" style="color: #bd666d;"></i>';
497        setTimeout(() => {
498          btn.innerHTML = '<i class="fa-solid fa-link"></i>';
499        }, 2000);
500      });
501    });
502  }
503});
504</script>
504
505
506  
506<script>window.backupDefine = window.define; window.define = undefined;</script>
506<script src="https://cdn.jsdelivr.net/npm/katex@latest/dist/katex.min.js"></script>
506
507  
507<script>document.addEventListener("DOMContentLoaded", function () {
508 var mathElements = document.getElementsByClassName("math");
509 var macros = [];
510 for (var i = 0; i < mathElements.length; i++) {
511  var texText = mathElements[i].firstChild;
512  if (mathElements[i].tagName == "SPAN") {
513   katex.render(texText.data, mathElements[i], {
514    displayMode: mathElements[i].classList.contains('display'),
515    throwOnError: false,
516    macros: macros,
517    fleqn: false
518   });
519}}});
520  </script>
520
521  
521<script>window.define = window.backupDefine; window.backupDefine = undefined;</script>
521<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@latest/dist/katex.min.css">
522
523<script type="text/javascript">
524const typesetMath = (el) => {
525  if (window.MathJax) {
526    // MathJax Typeset
527    window.MathJax.typeset([el]);
528  } else if (window.katex) {
529    // KaTeX Render
530    var mathElements = el.getElementsByClassName("math");
531    var macros = [];
532    for (var i = 0; i < mathElements.length; i++) {
533      var texText = mathElements[i].firstChild;
534      if (mathElements[i].tagName == "SPAN" && texText && texText.data) {
535        window.katex.render(texText.data, mathElements[i], {
536          displayMode: mathElements[i].classList.contains('display'),
537          throwOnError: false,
538          macros: macros,
539          fleqn: false
540        });
541      }
542    }
543  }
544}
545window.Quarto = {
546  typesetMath
547};
548</script>
548
549
550<link rel="stylesheet" href="styles.css">
551<meta property="og:title" content="Domingo López Rodríguez">
552<meta property="og:image" content="https://dominlopez.netlify.app/avatar.png">
553<meta property="og:site_name" content="Domingo López Rodríguez">
554<meta property="og:image:height" content="1024">
555<meta property="og:image:width" content="1365">
556<meta name="twitter:title" content="Domingo López Rodríguez">
557<meta name="twitter:image" content="https://dominlopez.netlify.app/avatar.png">
558<meta name="twitter:creator" content="@dominlopez">
559<meta name="twitter:card" content="summary_large_image">
560<meta name="twitter:image-height" content="1024">
561<meta name="twitter:image-width" content="1365">
562</head>
563
564<body class="nav-fixed quarto-light">
565
566<div id="quarto-search-results"></div>
567  <header id="quarto-header" class="headroom fixed-top">
568    <nav class="navbar navbar-expand-lg " data-bs-theme="dark">
569      <div class="navbar-container container-fluid">
570      <div class="navbar-brand-container mx-auto">
571    <a class='navbar-brand' href='/'>
572    <span class="navbar-title">Domingo López Rodríguez</span>
573    </a>
574  </div>
575            <div id="quarto-search" class="" title="Search"></div>
576          <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" role="menu" aria-expanded="false" aria-label="Toggle navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
577  <span class="navbar-toggler-icon"></span>
578</button>
579          <div class="collapse navbar-collapse" id="navbarCollapse">
580            <ul class="navbar-nav navbar-nav-scroll ms-auto">
581  <li class="nav-item">
582    <a class='nav-link' href='/about/'> 
583<span class="menu-text">About</span></a>
584  </li>  
585  <li class="nav-item">
586    <a class='nav-link' href='/teaching/'> 
587<span class="menu-text">Teaching</span></a>
588  </li>  
589  <li class="nav-item dropdown ">
590    <a class="nav-link dropdown-toggle" href="#" id="nav-menu-research" role="link" data-bs-toggle="dropdown" aria-expanded="false">
591 <span class="menu-text">Research</span>
592    </a>
593    <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="nav-menu-research">    
594        <li>
595    <a class='dropdown-item' href='/research/'>
596 <span class="dropdown-text">Research summary</span></a>
597  </li>  
598        <li>
599    <a class='dropdown-item' href='/publications/'>
600 <span class="dropdown-text">Publications</span></a>
601  </li>  
602        <li>
603    <a class='dropdown-item' href='/projects/'>
604 <span class="dropdown-text">Projects</span></a>
605  </li>  
606        <li>
607    <a class='dropdown-item' href='/talks/'>
608 <span class="dropdown-text">Talks</span></a>
609  </li>  
610    </ul>
611  </li>
612  <li class="nav-item">
613    <a class='nav-link' href='/resources/'> 
614<span class="menu-text">Resources</span></a>
615  </li>  
616  <li class="nav-item">
617    <a class='nav-link' href='/blog/'> 
618<span class="menu-text">Blog</span></a>
619  </li>  
620</ul>
621          </div> <!-- /navcollapse -->
622            <div class="quarto-navbar-tools">
623</div>
624      </div> <!-- /container-fluid -->
625    </nav>
626</header>
627<!-- content -->
628<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-full page-navbar">
629<!-- sidebar -->
630<!-- margin-sidebar -->
631    
632<!-- main -->
633<main class="content page-columns page-full column-body" id="quarto-document-content"><header id="title-block-header" class="quarto-title-block"></header>
634
635
636
637
638<div class="grid column-page">
639<div class="g-col-12 g-col-md-4 text-center">
640<p><img src="avatar.png" class="profile-pic img-fluid"></p>
641<div class="about-links" style="display: flex; flex-direction: column; gap: 0.5rem; align-items: center;">
642<p><a href="https://twitter.com/dominlopez" class="btn btn-outline-secondary btn-sm rounded-pill" style="width: 100%; max-width: 200px;"><i class="bi bi-twitter"></i> Twitter</a> <a href="https://mastodon.social/" class="btn btn-outline-secondary btn-sm rounded-pill" style="width: 100%; max-width: 200px;"><i class="bi bi-mastodon"></i> Mastodon</a> <a href="https://github.com/neuroimaginador" class="btn btn-outline-secondary btn-sm rounded-pill" style="width: 100%; max-width: 200px;"><i class="bi bi-github"></i> GitHub</a> <a href="mailto:[email protected]" class="btn btn-outline-secondary btn-sm rounded-pill" style="width: 100%; max-width: 200px;"><i class="bi bi-envelope"></i> Email</a></p>
643</div>
644</div>
645<section id="domingo-lópez-rodríguez" class="level1 g-col-12 g-col-md-8">
646<h1>Domingo López Rodríguez</h1>
647<p>Professor at the Department of Applied Mathematics, University of Málaga.</p>
648<p>My recent work focuses on <strong>Formal Concept Analysis (FCA)</strong> and its applications in data science and knowledge discovery.</p>
649<p><a href='/about/'>Learn more about me →</a></p>
650</section>
651</div>
652<div class="column-page">
653<div class="featured-grid animate__animated animate__fadeInUp" style="animation-delay: 0.2s;">
654<div class="feature-card">
655<div class="feature-content">
656<div class="feature-title">
657<i class="bi bi-box-seam"></i> fcaR Package
658</div>
659<p>
660Formal Concept Analysis in R. A comprehensive toolset for FCA including formal contexts, concept lattices, and implications.
661</p>
662</div>
663<div style="padding: 1.5rem; border-top: 1px solid #eee; background: #fafafa;">
664<p><a href="https://github.com/neuroimaginador/fcaR" target="_blank">View on GitHub →</a></p>
665</div>
666</div>
667<div class="feature-card">
668<div class="feature-content">
669<div class="feature-title">
670<i class="bi bi-easel"></i> Teaching
671</div>
672<p>
673Teaching materials and resources for undergraduate and graduate courses in Mathematics and Engineering.
674</p>
675</div>
676<div style="padding: 1.5rem; border-top: 1px solid #eee; background: #fafafa;">
677<p><a href='/teaching/'>Go to Teaching →</a></p>
678</div>
679</div>
680<div class="feature-card">
681<div class="feature-content">
682<div class="feature-title">
683<i class="bi bi-journal-text"></i> Publications
684</div>
685<p>
686A complete list of my scientific tracking, including journal papers, conference proceedings, and book chapters.
687</p>
688</div>
689<div style="padding: 1.5rem; border-top: 1px solid #eee; background: #fafafa;">
690<p><a href='/publications/'>Browse Publications →</a></p>
691</div>
692</div>
693</div>
694<div class="latest-updates-section">
695<h2 style="text-align: center; border-bottom: none; margin-top: 0;" class="anchored">
696Latest Updates
697</h2>
698<div id="listing-posts" class="quarto-listing quarto-listing-container-custom">
699<div class="list grid quarto-listing-cols-3 featured-grid animate__animated animate__fadeInUp">
700    
701        <div class="quarto-grid-item card h-100" data-index="0" data-categories="Rm9ybWFsJTIwQ29uY2VwdCUyMEFuYWx5c2lzJTJDUGFwZXIlMjBTcG90bGlnaHQ=" data-listing-date-sort="1781474400000">
702            
703                <a href="./blog/2026/EfGrBoMaFaViRiSiAl" class="card-img-top">
704                    <img src="./assets/images/latest_posts/EfGrBoMaFaViRiSiAl-cover.jpg" alt="Unlocking Hidden Patterns in Big Data: How We Scaled Boolean Matrix Factorization to Thousands of Features" style="height: 200px; width: 100%; object-fit: cover;">
705                </a>
706                
707                    <div class="card-body post-contents">
708                        <h5 class="no-anchor card-title listing-title">
709                            <a href="./blog/2026/EfGrBoMaFaViRiSiAl" class="no-external">
710                                Unlocking Hidden Patterns in Big Data: How We Scaled Boolean Matrix Factorization to Thousands of Features
711                            </a>
712                        </h5>
713
714                        <div class="card-text listing-description">
715                            Uncovering meaningful, interpretable patterns in massive binary datasets often comes with an overwhelming computational cost. We developed a novel algorithm, RSF-ES, that uses order-theoretic smart pruning to dramatically speed up Boolean Matrix Factorization without losing mathematical exactness.
716                        </div>
717
718                        <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;">
719                            <span class="listing-date"><i class="bi bi-calendar"></i>
720                                15 June 2026
721                            </span>
722                            
723                        </div>
724                    </div>
725        </div>
726        
727        <div class="quarto-grid-item card h-100" data-index="1" data-categories="Rm9ybWFsJTIwQ29uY2VwdCUyMEFuYWx5c2lzJTJDSW1wbGljYXRpb24lMjBzeXN0ZW1zJTJDU2ltcGxpZmljYXRpb24lMjBsb2dpYyUyQ1BhcGVyJTIwU3BvdGxpZ2h0" data-listing-date-sort="1781215200000">
728            
729                <a href="./blog/2026/DiOpSyAtImFuFoCoAn" class="card-img-top">
730                    <img src="./assets/images/latest_posts/DiOpSyAtImFuFoCoAn-cover.jpg" alt="Cracking the Speed Code in Fuzzy Data Reasoning: How We Made Fuzzy FCA Blazing Fast" style="height: 200px; width: 100%; object-fit: cover;">
731                </a>
732                
733                    <div class="card-body post-contents">
734                        <h5 class="no-anchor card-title listing-title">
735                            <a href="./blog/2026/DiOpSyAtImFuFoCoAn" class="no-external">
736                                Cracking the Speed Code in Fuzzy Data Reasoning: How We Made Fuzzy FCA Blazing Fast
737                            </a>
738                        </h5>
739
740                        <div class="card-text listing-description">
741                            Calculating logical closures in fuzzy data used to be a massive computational bottleneck. In our latest paper, we introduce a new framework and algorithm that makes computing direct-optimal fuzzy implicational systems orders of magnitude faster.
742                        </div>
743
744                        <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;">
745                            <span class="listing-date"><i class="bi bi-calendar"></i>
746                                12 June 2026
747                            </span>
748                            
749                        </div>
750                    </div>
751        </div>
752        
753        <div class="quarto-grid-item card h-100" data-index="2" data-categories="Rm9ybWFsJTIwY29uY2VwdCUyMGFuYWx5c2lzJTJDSW1wcmVjaXNlJTIwaW5mb3JtYXRpb24lMkNQYXBlciUyMFNwb3RsaWdodA==" data-listing-date-sort="1763161200000">
754            
755                <a href="./blog/2025/ClOnLiAlFuThEx" class="card-img-top">
756                    <img src="./assets/images/latest_posts/ClOnLiAlFuThEx-cover.jpg" alt="Teaching Algorithms to 'Speak Fuzzy' Natively: A Faster Way to Mine Nuanced Data" style="height: 200px; width: 100%; object-fit: cover;">
757                </a>
758                
759                    <div class="card-body post-contents">
760                        <h5 class="no-anchor card-title listing-title">
761                            <a href="./blog/2025/ClOnLiAlFuThEx" class="no-external">
762                                Teaching Algorithms to 'Speak Fuzzy' Natively: A Faster Way to Mine Nuanced Data
763                            </a>
764                        </h5>
765
766                        <div class="card-text listing-description">
767                            Analyzing fuzzy data shouldn't mean drowning in computational bottlenecks. By adapting the Close-by-One algorithm family to process degrees of truth natively, our research team eliminated the need for cumbersome data scaling and drastically boosted execution speeds.
768                        </div>
769
770                        <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;">
771                            <span class="listing-date"><i class="bi bi-calendar"></i>
772                                15 November 2025
773                            </span>
774                            
775                        </div>
776                    </div>
777        </div>
778        
779        <div class="quarto-grid-item card h-100" data-index="3" data-categories="RkNBJTJDQWxnb3JpdGhtJTJDSW1wbGljYXRpb25zJTJDRGl2aWRlLWFuZC1jb25xdWVyJTJDS25vd2xlZGdlJTIwcmVwcmVzZW50YXRpb24=" data-listing-date-sort="1745013600000">
780            
781                <a href="./blog/2025/SyImObUsCaDeFoCo" class="card-img-top">
782                    <img src="./assets/images/placeholder.png" alt="Divide and conquer... the 'if-then' rules in your data" style="height: 200px; width: 100%; object-fit: cover;">
783                </a>
784                
785                    <div class="card-body post-contents">
786                        <h5 class="no-anchor card-title listing-title">
787                            <a href="./blog/2025/SyImObUsCaDeFoCo" class="no-external">
788                                Divide and conquer... the 'if-then' rules in your data
789                            </a>
790                        </h5>
791
792                        <div class="card-text listing-description">
793                            The 'CARVE' algorithm is a super-fast 'divide-and-conquer' method for 
794finding data concepts. We upgraded it to 'CARVE+' so it can find all 
795the 'if-then' logical rules at the same time, giving us the best of both worlds.
796
797                        </div>
798
799                        <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;">
800                            <span class="listing-date"><i class="bi bi-calendar"></i>
801                                19 April 2025
802                            </span>
803                            
804                        </div>
805                    </div>
806        </div>
807        
808        <div class="quarto-grid-item card h-100" data-index="4" data-categories="U29mdHdhcmUlMkNTdXN0YWluYWJpbGl0eSUyQ0RhdGElMjBtaW5pbmclMkNVcmJhbiUyMHBsYW5uaW5nJTJDR0lT" data-listing-date-sort="1738364400000">
809            
810                <a href="./blog/2025/UrSuSoDeUnArDuUrHeIsEf" class="card-img-top">
811                    <img src="./assets/images/placeholder.png" alt="We built an open-source tool to find a city's worst 'heat island' hotspots" style="height: 200px; width: 100%; object-fit: cover;">
812                </a>
813                
814                    <div class="card-body post-contents">
815                        <h5 class="no-anchor card-title listing-title">
816                            <a href="./blog/2025/UrSuSoDeUnArDuUrHeIsEf" class="no-external">
817                                We built an open-source tool to find a city's worst 'heat island' hotspots
818                            </a>
819                        </h5>
820
821                        <div class="card-text listing-description">
822                            We present URSUS_UHI, a new open-source software tool that helps urban 
823planners automatically find the most unfavourable areas in a city: the places 
824with the highest temperatures and the least green space.
825
826                        </div>
827
828                        <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;">
829                            <span class="listing-date"><i class="bi bi-calendar"></i>
830                                1 February 2025
831                            </span>
832                            
833                        </div>
834                    </div>
835        </div>
836        
837        <div class="quarto-grid-item card h-100" data-index="5" data-categories="RkNBJTJDTG9naWMlMkNUaGVvcmV0aWNhbCUyMENTJTJDQWxnb3JpdGhtJTJDU2ltcGxpZmljYXRpb24=" data-listing-date-sort="1737154800000">
838            
839                <a href="./blog/2025/NeSiRuDaPoNeAt" class="card-img-top">
840                    <img src="./assets/images/placeholder.png" alt="Beyond just 'correct': new logic rules to make data insights easier to read" style="height: 200px; width: 100%; object-fit: cover;">
841                </a>
842                
843                    <div class="card-body post-contents">
844                        <h5 class="no-anchor card-title listing-title">
845                            <a href="./blog/2025/NeSiRuDaPoNeAt" class="no-external">
846                                Beyond just 'correct': new logic rules to make data insights easier to read
847                            </a>
848                        </h5>
849
850                        <div class="card-text listing-description">
851                            Our previous work created a logic to find correct rules from positive and negative data.
852Now, we've added new logical equivalences to 'shrink' those rules, 
853making them shorter, simpler, and more useful for humans.
854
855                        </div>
856
857                        <div class="card-attribution card-text-small justify" style="margin-top: 1rem; color: #888; font-size: 0.85em;">
858                            <span class="listing-date"><i class="bi bi-calendar"></i>
859                                18 January 2025
860                            </span>
861                            
862                        </div>
863                    </div>
864        </div>
865        
866</div>
867<div class="listing-no-matching d-none">No matching items</div>
868</div>
869<div style="text-align: center; margin-top: 3rem;">
870<p><a class='btn-action' href='/blog/'>View all posts →</a></p>
871</div>
872</div>
873</div>
874
875
876
877
878</main> <!-- /main -->
879<script id="quarto-html-after-body" type="application/javascript">
880  window.document.addEventListener("DOMContentLoaded", function (event) {
881    const icon = "&#xE9CB;";
882    const anchorJS = new window.AnchorJS();
883    anchorJS.options = {
884      placement: 'right',
885      icon: icon
886    };
887    anchorJS.add('.anchored');
888    const isCodeAnnotation = (el) => {
889      for (const clz of el.classList) {
890        if (clz.startsWith('code-annotation-')) {                     
891          return true;
892        }
893      }
894      return false;
895    }
896    const onCopySuccess = function(e) {
897      // button target
898      const button = e.trigger;
899      // don't keep focus
900      button.blur();
901      // flash "checked"
902      button.classList.add('code-copy-button-checked');
903      var currentTitle = button.getAttribute("title");
904      button.setAttribute("title", "Copied!");
905      let tooltip;
906      if (window.bootstrap) {
907        button.setAttribute("data-bs-toggle", "tooltip");
908        button.setAttribute("data-bs-placement", "left");
909        button.setAttribute("data-bs-title", "Copied!");
910        tooltip = new bootstrap.Tooltip(button, 
911          { trigger: "manual", 
912            customClass: "code-copy-button-tooltip",
913            offset: [0, -8]});
914        tooltip.show();    
915      }
916      setTimeout(function() {
917        if (tooltip) {
918          tooltip.hide();
919          button.removeAttribute("data-bs-title");
920          button.removeAttribute("data-bs-toggle");
921          button.removeAttribute("data-bs-placement");
922        }
923        button.setAttribute("title", currentTitle);
924        button.classList.remove('code-copy-button-checked');
925      }, 1000);
926      // clear code selection
927      e.clearSelection();
928    }
929    const getTextToCopy = function(trigger) {
930      const outerScaffold = trigger.parentElement.cloneNode(true);
931      const codeEl = outerScaffold.querySelector('code');
932      for (const childEl of codeEl.children) {
933        if (isCodeAnnotation(childEl)) {
934          childEl.remove();
935        }
936      }
937      return codeEl.innerText;
938    }
939    const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', {
940      text: getTextToCopy
941    });
942    clipboard.on('success', onCopySuccess);
943    if (window.document.getElementById('quarto-embedded-source-code-modal')) {
944      const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', {
945        text: getTextToCopy,
946        container: window.document.getElementById('quarto-embedded-source-code-modal')
947      });
948      clipboardModal.on('success', onCopySuccess);
949    }
950      var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//);
951      var mailtoRegex = new RegExp(/^mailto:/);
952        var filterRegex = new RegExp("https:\/\/dominlopez\.netlify\.app");
953      var isInternal = (href) => {
954          return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href);
955      }
956      // Inspect non-navigation links and adorn them if external
957     var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)');
958      for (var i=0; i<links.length; i++) {
959        const link = links[i];
960        if (!isInternal(link.href)) {
961          // undo the damage that might have been done by quarto-nav.js in the case of
962          // links that we want to consider external
963          if (link.dataset.originalHref !== undefined) {
964            link.href = link.dataset.originalHref;
965          }
966        }
967      }
968    function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
969      const config = {
970        allowHTML: true,
971        maxWidth: 500,
972        delay: 100,
973        arrow: false,
974        appendTo: function(el) {
975            return el.parentElement;
976        },
977        interactive: true,
978        interactiveBorder: 10,
979        theme: 'quarto',
980        placement: 'bottom-start',
981      };
982      if (contentFn) {
983        config.content = contentFn;
984      }
985      if (onTriggerFn) {
986        config.onTrigger = onTriggerFn;
987      }
988      if (onUntriggerFn) {
989        config.onUntrigger = onUntriggerFn;
990      }
991      window.tippy(el, config); 
992    }
993    const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
994    for (var i=0; i<noterefs.length; i++) {
995      const ref = noterefs[i];
996      tippyHover(ref, function() {
997        // use id or data attribute instead here
998        let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
999        try { href = new URL(href).hash; } catch {}
1000        const id = href.replace(/^#\/?/, "");
1001        const note = window.document.getElementById(id);
1002        if (note) {
1003          return note.innerHTML;
1004        } else {
1005          return "";
1006        }
1007      });
1008    }
1009    const xrefs = window.document.querySelectorAll('a.quarto-xref');
1010    const processXRef = (id, note) => {
1011      // Strip column container classes
1012      const stripColumnClz = (el) => {
1013        el.classList.remove("page-full", "page-columns");
1014        if (el.children) {
1015          for (const child of el.children) {
1016            stripColumnClz(child);
1017          }
1018        }
1019      }
1020      stripColumnClz(note)
1021      if (id === null || id.startsWith('sec-')) {
1022        // Special case sections, only their first couple elements
1023        const container = document.createElement("div");
1024        if (note.children && note.children.length > 2) {
1025          container.appendChild(note.children[0].cloneNode(true));
1026          for (let i = 1; i < note.children.length; i++) {
1027            const child = note.children[i];
1028            if (child.tagName === "P" && child.innerText === "") {
1029              continue;
1030            } else {
1031              container.appendChild(child.cloneNode(true));
1032              break;
1033            }
1034          }
1035          if (window.Quarto?.typesetMath) {
1036            window.Quarto.typesetMath(container);
1037          }
1038          return container.innerHTML
1039        } else {
1040          if (window.Quarto?.typesetMath) {
1041            window.Quarto.typesetMath(note);
1042          }
1043          return note.innerHTML;
1044        }
1045      } else {
1046        // Remove any anchor links if they are present
1047        const anchorLink = note.querySelector('a.anchorjs-link');
1048        if (anchorLink) {
1049          anchorLink.remove();
1050        }
1051        if (window.Quarto?.typesetMath) {
1052          window.Quarto.typesetMath(note);
1053        }
1054        if (note.classList.contains("callout")) {
1055          return note.outerHTML;
1056        } else {
1057          return note.innerHTML;
1058        }
1059      }
1060    }
1061    for (var i=0; i<xrefs.length; i++) {
1062      const xref = xrefs[i];
1063      tippyHover(xref, undefined, function(instance) {
1064        instance.disable();
1065        let url = xref.getAttribute('href');
1066        let hash = undefined; 
1067        if (url.startsWith('#')) {
1068          hash = url;
1069        } else {
1070          try { hash = new URL(url).hash; } catch {}
1071        }
1072        if (hash) {
1073          const id = hash.replace(/^#\/?/, "");
1074          const note = window.document.getElementById(id);
1075          if (note !== null) {
1076            try {
1077              const html = processXRef(id, note.cloneNode(true));
1078              instance.setContent(html);
1079            } finally {
1080              instance.enable();
1081              instance.show();
1082            }
1083          } else {
1084            // See if we can fetch this
1085            fetch(url.split('#')[0])
1086            .then(res => res.text())
1087            .then(html => {
1088              const parser = new DOMParser();
1089              const htmlDoc = parser.parseFromString(html, "text/html");
1090              const note = htmlDoc.getElementById(id);
1091              if (note !== null) {
1092                const html = processXRef(id, note);
1093                instance.setContent(html);
1094              } 
1095            }).finally(() => {
1096              instance.enable();
1097              instance.show();
1098            });
1099          }
1100        } else {
1101          // See if we can fetch a full url (with no hash to target)
1102          // This is a special case and we should probably do some content thinning / targeting
1103          fetch(url)
1104          .then(res => res.text())
1105          .then(html => {
1106            const parser = new DOMParser();
1107            const htmlDoc = parser.parseFromString(html, "text/html");
1108            const note = htmlDoc.querySelector('main.content');
1109            if (note !== null) {
1110              // This should only happen for chapter cross references
1111              // (since there is no id in the URL)
1112              // remove the first header
1113              if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
1114                note.children[0].remove();
1115              }
1116              const html = processXRef(null, note);
1117              instance.setContent(html);
1118            } 
1119          }).finally(() => {
1120            instance.enable();
1121            instance.show();
1122          });
1123        }
1124      }, function(instance) {
1125      });
1126    }
1127        let selectedAnnoteEl;
1128        const selectorForAnnotation = ( cell, annotation) => {
1129          let cellAttr = 'data-code-cell="' + cell + '"';
1130          let lineAttr = 'data-code-annotation="' +  annotation + '"';
1131          const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
1132          return selector;
1133        }
1134        const selectCodeLines = (annoteEl) => {
1135          const doc = window.document;
1136          const targetCell = annoteEl.getAttribute("data-target-cell");
1137          const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
1138          const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
1139          const lines = annoteSpan.getAttribute("data-code-lines").split(",");
1140          const lineIds = lines.map((line) => {
1141            return targetCell + "-" + line;
1142          })
1143          let top = null;
1144          let height = null;
1145          let parent = null;
1146          if (lineIds.length > 0) {
1147              //compute the position of the single el (top and bottom and make a div)
1148              const el = window.document.getElementById(lineIds[0]);
1149              top = el.offsetTop;
1150              height = el.offsetHeight;
1151              parent = el.parentElement.parentElement;
1152            if (lineIds.length > 1) {
1153              const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
1154              const bottom = lastEl.offsetTop + lastEl.offsetHeight;
1155              height = bottom - top;
1156            }
1157            if (top !== null && height !== null && parent !== null) {
1158              // cook up a div (if necessary) and position it 
1159              let div = window.document.getElementById("code-annotation-line-highlight");
1160              if (div === null) {
1161                div = window.document.createElement("div");
1162                div.setAttribute("id", "code-annotation-line-highlight");
1163                div.style.position = 'absolute';
1164                parent.appendChild(div);
1165              }
1166              div.style.top = top - 2 + "px";
1167              div.style.height = height + 4 + "px";
1168              div.style.left = 0;
1169              let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
1170              if (gutterDiv === null) {
1171                gutterDiv = window.document.createElement("div");
1172                gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
1173                gutterDiv.style.position = 'absolute';
1174                const codeCell = window.document.getElementById(targetCell);
1175                const gutter = codeCell.querySelector('.code-annotation-gutter');
1176                gutter.appendChild(gutterDiv);
1177              }
1178              gutterDiv.style.top = top - 2 + "px";
1179              gutterDiv.style.height = height + 4 + "px";
1180            }
1181            selectedAnnoteEl = annoteEl;
1182          }
1183        };
1184        const unselectCodeLines = () => {
1185          const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
1186          elementsIds.forEach((elId) => {
1187            const div = window.document.getElementById(elId);
1188            if (div) {
1189              div.remove();
1190            }
1191          });
1192          selectedAnnoteEl = undefined;
1193        };
1194          // Handle positioning of the toggle
1195      window.addEventListener(
1196        "resize",
1197        throttle(() => {
1198          elRect = undefined;
1199          if (selectedAnnoteEl) {
1200            selectCodeLines(selectedAnnoteEl);
1201          }
1202        }, 10)
1203      );
1204      function throttle(fn, ms) {
1205      let throttle = false;
1206      let timer;
1207        return (...args) => {
1208          if(!throttle) { // first call gets through
1209              fn.apply(this, args);
1210              throttle = true;
1211          } else { // all the others get throttled
1212              if(timer) clearTimeout(timer); // cancel #2
1213              timer = setTimeout(() => {
1214                fn.apply(this, args);
1215                timer = throttle = false;
1216              }, ms);
1217          }
1218        };
1219      }
1220        // Attach click handler to the DT
1221        const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
1222        for (const annoteDlNode of annoteDls) {
1223          annoteDlNode.addEventListener('click', (event) => {
1224            const clickedEl = event.target;
1225            if (clickedEl !== selectedAnnoteEl) {
1226              unselectCodeLines();
1227              const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
1228              if (activeEl) {
1229                activeEl.classList.remove('code-annotation-active');
1230              }
1231              selectCodeLines(clickedEl);
1232              clickedEl.classList.add('code-annotation-active');
1233            } else {
1234              // Unselect the line
1235              unselectCodeLines();
1236              clickedEl.classList.remove('code-annotation-active');
1237            }
1238          });
1239        }
1240    const findCites = (el) => {
1241      const parentEl = el.parentElement;
1242      if (parentEl) {
1243        const cites = parentEl.dataset.cites;
1244        if (cites) {
1245          return {
1246            el,
1247            cites: cites.split(' ')
1248          };
1249        } else {
1250          return findCites(el.parentElement)
1251        }
1252      } else {
1253        return undefined;
1254      }
1255    };
1256    var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
1257    for (var i=0; i<bibliorefs.length; i++) {
1258      const ref = bibliorefs[i];
1259      const citeInfo = findCites(ref);
1260      if (citeInfo) {
1261        tippyHover(citeInfo.el, function() {
1262          var popup = window.document.createElement('div');
1263          citeInfo.cites.forEach(function(cite) {
1264            var citeDiv = window.document.createElement('div');
1265            citeDiv.classList.add('hanging-indent');
1266            citeDiv.classList.add('csl-entry');
1267            var biblioDiv = window.document.getElementById('ref-' + cite);
1268            if (biblioDiv) {
1269              citeDiv.innerHTML = biblioDiv.innerHTML;
1270            }
1271            popup.appendChild(citeDiv);
1272          });
1273          return popup.innerHTML;
1274        });
1275      }
1276    }
1277  });
1278  </script>
1278
1279</div> <!-- /content -->
1280<footer class="footer">
1281  <div class="nav-footer">
1282    <div class="nav-footer-left">
1283<p><strong>Domingo López-Rodríguez</strong><br> Department of Applied Mathematics<br> University of Málaga</p>
1284</div>   
1285    <div class="nav-footer-center">
1286      <ul class="footer-items list-unstyled">
1287    <li class="nav-item compact">
1288    <a class="nav-link" href="https://twitter.com/dominlopez">
1289      <i class="bi bi-twitter" role="img" aria-label="Twitter">
1290</i> 
1291    </a>
1292  </li>  
1293    <li class="nav-item compact">
1294    <a class="nav-link" href="https://mastodon.social/">
1295      <i class="bi bi-mastodon" role="img" aria-label="Mastodon">
1296</i> 
1297    </a>
1298  </li>  
1299    <li class="nav-item compact">
1300    <a class="nav-link" href="https://github.com/neuroimaginador">
1301      <i class="bi bi-github" role="img" aria-label="Github">
1302</i> 
1303    </a>
1304  </li>  
1305    <li class="nav-item compact">
1306    <a class="nav-link" href="mailto:[email protected]">
1307      <i class="bi bi-envelope" role="img" aria-label="Email">
1308</i> 
1309    </a>
1310  </li>  
1311</ul>
1312    <div class="cookie-consent-footer"><a href="#" id="open_preferences_center">Cookie Preferences</a></div></div>
1313    <div class="nav-footer-right">
1314<p><a href="https://www.uma.es" target="_blank" style="color: #fdfbf7; text-decoration: underline;">University of Málaga</a><br> Built with <a href="https://quarto.org">Quarto</a></p>
1315</div>
1316  </div>
1317</footer>
1318
1319
1320
1321
1322<script src="site_libs/quarto-html/zenscroll-min.js"></script>
1322
1323</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.