├── .gitignore ├── Code ├── GMM_cache │ └── html │ │ ├── setup callout_fd3671aec44580c6e75249dd4b3ccc05.rdb │ │ ├── xaringan-themer_d018f87595da89f5d2c9003f803ec708.rdb │ │ ├── all_peng_de3967149d8fd25e128759c9b9d41c60.rdb │ │ ├── all_peng_de3967149d8fd25e128759c9b9d41c60.rdx │ │ ├── all_peng_de3967149d8fd25e128759c9b9d41c60.RData │ │ ├── setup extra_57a889f2790437a66776fd24a36c2c9a.rdb │ │ ├── setup extra_57a889f2790437a66776fd24a36c2c9a.rdx │ │ ├── __packages │ │ ├── setup callout_fd3671aec44580c6e75249dd4b3ccc05.rdx │ │ ├── setup extra_57a889f2790437a66776fd24a36c2c9a.RData │ │ ├── setup callout_fd3671aec44580c6e75249dd4b3ccc05.RData │ │ ├── unnamed-chunk-1_457dbefbf901b81ac8d82fccfc016a00.rdb │ │ ├── unnamed-chunk-1_457dbefbf901b81ac8d82fccfc016a00.rdx │ │ ├── xaringan-themer_d018f87595da89f5d2c9003f803ec708.rdx │ │ ├── unnamed-chunk-1_457dbefbf901b81ac8d82fccfc016a00.RData │ │ ├── xaringan-themer_d018f87595da89f5d2c9003f803ec708.RData │ │ ├── peng_flipper_len_density_02ebcdc95377add6cf0fbdd301a8f1d9.rdb │ │ ├── peng_flipper_len_density_02ebcdc95377add6cf0fbdd301a8f1d9.rdx │ │ └── peng_flipper_len_density_02ebcdc95377add6cf0fbdd301a8f1d9.RData ├── figs │ ├── EM_loss.png │ ├── NBT_EM.png │ ├── code-1.png │ ├── loglik.png │ ├── EM_init-1.png │ ├── Kmeans_loss.png │ ├── all_peng-1.png │ ├── culmen_depth.png │ ├── posterior_df.png │ ├── code_EM_GMM-1.png │ ├── lter_penguins.png │ ├── palmerpenguins.png │ ├── plot-label-out-1.png │ ├── unnamed-chunk-1-1.png │ ├── unnamed-chunk-1-2.png │ ├── README-flipper-hist-1.png │ ├── peng_flipper_len_density-1.png │ ├── peng_flipper_len_density-2.png │ └── peng_flipper_len_density-3.png ├── images │ ├── All_peng.png │ ├── MLT Logo black background.png │ └── Annotation 2020-09-09 072247.png ├── README.md ├── libs │ ├── editable │ │ ├── editable.css │ │ └── editable.js │ ├── header-attrs │ │ └── header-attrs.js │ ├── xaringanExtra-extra-styles │ │ └── xaringanExtra-extra-styles.css │ ├── tile-view │ │ ├── tile-view.css │ │ └── tile-view.js │ ├── panelset │ │ ├── panelset.css │ │ └── panelset.js │ ├── js-cookie │ │ └── js.cookie.js │ ├── xaringanExtra-webcam │ │ └── webcam.js │ └── himalaya │ │ └── himalaya.js.map ├── assets │ ├── editable │ │ ├── editable.css │ │ └── editable.js │ ├── header-attrs │ │ └── header-attrs.js │ ├── xaringanExtra-extra-styles │ │ └── xaringanExtra-extra-styles.css │ ├── tile-view │ │ ├── tile-view.css │ │ └── tile-view.js │ ├── shareon │ │ ├── shareon.min.js │ │ └── shareon.min.css │ ├── panelset │ │ ├── panelset.css │ │ └── panelset.js │ ├── js-cookie │ │ └── js.cookie.js │ ├── xaringanExtra-webcam │ │ └── webcam.js │ ├── xaringanExtra-shareagain │ │ ├── shareagain.css │ │ └── shareagain.js │ ├── clipboard │ │ └── clipboard.min.js │ └── himalaya │ │ └── himalaya.js.map ├── refs.bib ├── xaringan-extra.css ├── xaringan-themer.css └── index.rmd └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .Rhistory 2 | .RData 3 | .Rproj.user 4 | -------------------------------------------------------------------------------- /Code/GMM_cache/html/setup callout_fd3671aec44580c6e75249dd4b3ccc05.rdb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Code/GMM_cache/html/xaringan-themer_d018f87595da89f5d2c9003f803ec708.rdb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Code/figs/EM_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/EM_loss.png -------------------------------------------------------------------------------- /Code/figs/NBT_EM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/NBT_EM.png -------------------------------------------------------------------------------- /Code/figs/code-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/code-1.png -------------------------------------------------------------------------------- /Code/figs/loglik.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/loglik.png -------------------------------------------------------------------------------- /Code/figs/EM_init-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/EM_init-1.png -------------------------------------------------------------------------------- /Code/figs/Kmeans_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/Kmeans_loss.png -------------------------------------------------------------------------------- /Code/figs/all_peng-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/all_peng-1.png -------------------------------------------------------------------------------- /Code/figs/culmen_depth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/culmen_depth.png -------------------------------------------------------------------------------- /Code/figs/posterior_df.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/posterior_df.png -------------------------------------------------------------------------------- /Code/images/All_peng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/images/All_peng.png -------------------------------------------------------------------------------- /Code/figs/code_EM_GMM-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/code_EM_GMM-1.png -------------------------------------------------------------------------------- /Code/figs/lter_penguins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/lter_penguins.png -------------------------------------------------------------------------------- /Code/figs/palmerpenguins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/palmerpenguins.png -------------------------------------------------------------------------------- /Code/figs/plot-label-out-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/plot-label-out-1.png -------------------------------------------------------------------------------- /Code/figs/unnamed-chunk-1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/unnamed-chunk-1-1.png -------------------------------------------------------------------------------- /Code/figs/unnamed-chunk-1-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/unnamed-chunk-1-2.png -------------------------------------------------------------------------------- /Code/figs/README-flipper-hist-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/README-flipper-hist-1.png -------------------------------------------------------------------------------- /Code/figs/peng_flipper_len_density-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/peng_flipper_len_density-1.png -------------------------------------------------------------------------------- /Code/figs/peng_flipper_len_density-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/peng_flipper_len_density-2.png -------------------------------------------------------------------------------- /Code/figs/peng_flipper_len_density-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/figs/peng_flipper_len_density-3.png -------------------------------------------------------------------------------- /Code/images/MLT Logo black background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/images/MLT Logo black background.png -------------------------------------------------------------------------------- /Code/README.md: -------------------------------------------------------------------------------- 1 | # Math for Machine Learning – Code 2 | 3 | This repository is for code and notebooks for specific math concepts created by the community. 4 | -------------------------------------------------------------------------------- /Code/images/Annotation 2020-09-09 072247.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/images/Annotation 2020-09-09 072247.png -------------------------------------------------------------------------------- /Code/GMM_cache/html/all_peng_de3967149d8fd25e128759c9b9d41c60.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/all_peng_de3967149d8fd25e128759c9b9d41c60.rdb -------------------------------------------------------------------------------- /Code/GMM_cache/html/all_peng_de3967149d8fd25e128759c9b9d41c60.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/all_peng_de3967149d8fd25e128759c9b9d41c60.rdx -------------------------------------------------------------------------------- /Code/GMM_cache/html/all_peng_de3967149d8fd25e128759c9b9d41c60.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/all_peng_de3967149d8fd25e128759c9b9d41c60.RData -------------------------------------------------------------------------------- /Code/GMM_cache/html/setup extra_57a889f2790437a66776fd24a36c2c9a.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/setup extra_57a889f2790437a66776fd24a36c2c9a.rdb -------------------------------------------------------------------------------- /Code/GMM_cache/html/setup extra_57a889f2790437a66776fd24a36c2c9a.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/setup extra_57a889f2790437a66776fd24a36c2c9a.rdx -------------------------------------------------------------------------------- /Code/GMM_cache/html/__packages: -------------------------------------------------------------------------------- 1 | base 2 | xaringanthemer 3 | tidyverse 4 | ggplot2 5 | tibble 6 | tidyr 7 | readr 8 | purrr 9 | dplyr 10 | stringr 11 | forcats 12 | palmerpenguins 13 | mixtools 14 | -------------------------------------------------------------------------------- /Code/GMM_cache/html/setup callout_fd3671aec44580c6e75249dd4b3ccc05.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/setup callout_fd3671aec44580c6e75249dd4b3ccc05.rdx -------------------------------------------------------------------------------- /Code/GMM_cache/html/setup extra_57a889f2790437a66776fd24a36c2c9a.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/setup extra_57a889f2790437a66776fd24a36c2c9a.RData -------------------------------------------------------------------------------- /Code/GMM_cache/html/setup callout_fd3671aec44580c6e75249dd4b3ccc05.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/setup callout_fd3671aec44580c6e75249dd4b3ccc05.RData -------------------------------------------------------------------------------- /Code/GMM_cache/html/unnamed-chunk-1_457dbefbf901b81ac8d82fccfc016a00.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/unnamed-chunk-1_457dbefbf901b81ac8d82fccfc016a00.rdb -------------------------------------------------------------------------------- /Code/GMM_cache/html/unnamed-chunk-1_457dbefbf901b81ac8d82fccfc016a00.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/unnamed-chunk-1_457dbefbf901b81ac8d82fccfc016a00.rdx -------------------------------------------------------------------------------- /Code/GMM_cache/html/xaringan-themer_d018f87595da89f5d2c9003f803ec708.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/xaringan-themer_d018f87595da89f5d2c9003f803ec708.rdx -------------------------------------------------------------------------------- /Code/GMM_cache/html/unnamed-chunk-1_457dbefbf901b81ac8d82fccfc016a00.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/unnamed-chunk-1_457dbefbf901b81ac8d82fccfc016a00.RData -------------------------------------------------------------------------------- /Code/GMM_cache/html/xaringan-themer_d018f87595da89f5d2c9003f803ec708.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/xaringan-themer_d018f87595da89f5d2c9003f803ec708.RData -------------------------------------------------------------------------------- /Code/GMM_cache/html/peng_flipper_len_density_02ebcdc95377add6cf0fbdd301a8f1d9.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/peng_flipper_len_density_02ebcdc95377add6cf0fbdd301a8f1d9.rdb -------------------------------------------------------------------------------- /Code/GMM_cache/html/peng_flipper_len_density_02ebcdc95377add6cf0fbdd301a8f1d9.rdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/peng_flipper_len_density_02ebcdc95377add6cf0fbdd301a8f1d9.rdx -------------------------------------------------------------------------------- /Code/GMM_cache/html/peng_flipper_len_density_02ebcdc95377add6cf0fbdd301a8f1d9.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Machine-Learning-Tokyo/ML-Math/HEAD/Code/GMM_cache/html/peng_flipper_len_density_02ebcdc95377add6cf0fbdd301a8f1d9.RData -------------------------------------------------------------------------------- /Code/libs/editable/editable.css: -------------------------------------------------------------------------------- 1 | [contenteditable] { 2 | outline-offset: 5px; 3 | transition: outline-offset 0.25s linear; 4 | } 5 | 6 | [contenteditable]:hover { 7 | outline: 1px dashed #ccc; 8 | } 9 | 10 | [contenteditable]:focus { 11 | outline: 1px solid #bbb; 12 | outline-offset: 10px; 13 | transition: outline-offset 0.5s linear; 14 | } 15 | -------------------------------------------------------------------------------- /Code/assets/editable/editable.css: -------------------------------------------------------------------------------- 1 | [contenteditable] { 2 | outline-offset: 5px; 3 | transition: outline-offset 0.25s linear; 4 | } 5 | 6 | [contenteditable]:hover { 7 | outline: 1px dashed #ccc; 8 | } 9 | 10 | [contenteditable]:focus { 11 | outline: 1px solid #bbb; 12 | outline-offset: 10px; 13 | transition: outline-offset 0.5s linear; 14 | } 15 | -------------------------------------------------------------------------------- /Code/assets/header-attrs/header-attrs.js: -------------------------------------------------------------------------------- 1 | // Pandoc 2.9 adds attributes on both header and div. We remove the former (to 2 | // be compatible with the behavior of Pandoc < 2.8). 3 | document.addEventListener('DOMContentLoaded', function(e) { 4 | var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); 5 | var i, h, a; 6 | for (i = 0; i < hs.length; i++) { 7 | h = hs[i]; 8 | if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 9 | a = h.attributes; 10 | while (a.length > 0) h.removeAttribute(a[0].name); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /Code/libs/header-attrs/header-attrs.js: -------------------------------------------------------------------------------- 1 | // Pandoc 2.9 adds attributes on both header and div. We remove the former (to 2 | // be compatible with the behavior of Pandoc < 2.8). 3 | document.addEventListener('DOMContentLoaded', function(e) { 4 | var hs = document.querySelectorAll("div.section[class*='level'] > :first-child"); 5 | var i, h, a; 6 | for (i = 0; i < hs.length; i++) { 7 | h = hs[i]; 8 | if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6 9 | a = h.attributes; 10 | while (a.length > 0) h.removeAttribute(a[0].name); 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /Code/assets/xaringanExtra-extra-styles/xaringanExtra-extra-styles.css: -------------------------------------------------------------------------------- 1 | /* Line Hover Indicator */ 2 | .remark-code-line:hover { 3 | font-weight: bold; 4 | opacity: 1 !important; 5 | } 6 | 7 | .remark-code-line:hover:before { 8 | content: "\25B6"; 9 | color: #6d7e8a; 10 | position: absolute; 11 | transform: translateX(-1.2em); 12 | animation: hover 0.66s alternate 8 cubic-bezier(0.445, 0.05, 0.55, 0.95); 13 | } 14 | 15 | @keyframes hover { 16 | 0% { 17 | transform: translateX(-1.2em); 18 | } 19 | 100% { 20 | transform: translateX(-0.8em); 21 | } 22 | } 23 | /* Mute highlighted line */ 24 | .remark-code-has-line-highlighted .remark-code-line:not(.remark-code-line-highlighted) { 25 | opacity: 0.4; 26 | } 27 | 28 | /* Override highlight color */ 29 | .remark-code-line-highlighted { 30 | background-color: inherit; 31 | } 32 | -------------------------------------------------------------------------------- /Code/libs/xaringanExtra-extra-styles/xaringanExtra-extra-styles.css: -------------------------------------------------------------------------------- 1 | /* Line Hover Indicator */ 2 | .remark-code-line:hover { 3 | font-weight: bold; 4 | opacity: 1 !important; 5 | } 6 | 7 | .remark-code-line:hover:before { 8 | content: "\25B6"; 9 | color: #6d7e8a; 10 | position: absolute; 11 | transform: translateX(-1.2em); 12 | animation: hover 0.66s alternate 8 cubic-bezier(0.445, 0.05, 0.55, 0.95); 13 | } 14 | 15 | @keyframes hover { 16 | 0% { 17 | transform: translateX(-1.2em); 18 | } 19 | 100% { 20 | transform: translateX(-0.8em); 21 | } 22 | } 23 | /* Mute highlighted line */ 24 | .remark-code-has-line-highlighted .remark-code-line:not(.remark-code-line-highlighted) { 25 | opacity: 0.4; 26 | } 27 | 28 | /* Override highlight color */ 29 | .remark-code-line-highlighted { 30 | background-color: inherit; 31 | } 32 | -------------------------------------------------------------------------------- /Code/assets/tile-view/tile-view.css: -------------------------------------------------------------------------------- 1 | .remark__tile-view * { 2 | box-sizing: border-box; 3 | } 4 | 5 | .remark__tile-view { 6 | background: lightgray; 7 | position: relative; 8 | width: 100%; 9 | height: 100%; 10 | padding: 3em; 11 | font-size: 18px; 12 | box-sizing: border-box; 13 | overflow: scroll; 14 | } 15 | 16 | .remark__tile-view__header { 17 | text-align: center; 18 | } 19 | 20 | .remark__tile-view__tiles { 21 | display: grid; 22 | /* Set column width in JS */ 23 | /* grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); */ 24 | justify-items: center; 25 | } 26 | 27 | .remark__tile-view__tile { 28 | position: relative; 29 | margin: 0.5em; 30 | padding: 0.5em; 31 | } 32 | 33 | .remark__tile-view__slide-container { 34 | margin: 0 auto; 35 | } 36 | 37 | .remark__tile-view__tile--current { 38 | background: #ffd863; 39 | border: 5px solid #ffd863; 40 | margin: calc(0.5em - 5px); 41 | border-radius: 0; 42 | } 43 | 44 | .remark__tile-view__tile--seen { 45 | opacity: 0.5; 46 | } 47 | 48 | .remark__tile-view__tile:hover { 49 | /* background: #993d70; */ 50 | background: #44bc96; 51 | opacity: 1; 52 | } 53 | -------------------------------------------------------------------------------- /Code/libs/tile-view/tile-view.css: -------------------------------------------------------------------------------- 1 | .remark__tile-view * { 2 | box-sizing: border-box; 3 | } 4 | 5 | .remark__tile-view { 6 | background: lightgray; 7 | position: relative; 8 | width: 100%; 9 | height: 100%; 10 | padding: 3em; 11 | font-size: 18px; 12 | box-sizing: border-box; 13 | overflow: scroll; 14 | } 15 | 16 | .remark__tile-view__header { 17 | text-align: center; 18 | } 19 | 20 | .remark__tile-view__tiles { 21 | display: grid; 22 | /* Set column width in JS */ 23 | /* grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); */ 24 | justify-items: center; 25 | } 26 | 27 | .remark__tile-view__tile { 28 | position: relative; 29 | margin: 0.5em; 30 | padding: 0.5em; 31 | } 32 | 33 | .remark__tile-view__slide-container { 34 | margin: 0 auto; 35 | } 36 | 37 | .remark__tile-view__tile--current { 38 | background: #ffd863; 39 | border: 5px solid #ffd863; 40 | margin: calc(0.5em - 5px); 41 | border-radius: 0; 42 | } 43 | 44 | .remark__tile-view__tile--seen { 45 | opacity: 0.5; 46 | } 47 | 48 | .remark__tile-view__tile:hover { 49 | /* background: #993d70; */ 50 | background: #44bc96; 51 | opacity: 1; 52 | } 53 | -------------------------------------------------------------------------------- /Code/refs.bib: -------------------------------------------------------------------------------- 1 | @article{FongEMAlgo, 2 | author = "Fong Chun Chan", 3 | title = "Fitting a Mixture Model Using the Expectation-Maximization Algorithm in R", 4 | journal = "https://tinyheero.github.io/2016/01/03/gmm-em.html", 5 | year = "2016" 6 | } 7 | @ARTICLE {FongKMeans, 8 | author = "Fong Chun Chan", 9 | title = "Using Mixture Models for Clustering", 10 | journal = "https://tinyheero.github.io/2015/10/13/mixture-model.html", 11 | year = "2015" 12 | } 13 | @ARTICLE {GarrickSlidesExtra, 14 | author = "Garrick Aden Buie", 15 | title = "Your Slides are So Extra", 16 | journal = "https://www.garrickadenbuie.com/talk/extra-special-xaringan/", 17 | year = "2015" 18 | } 19 | @ARTICLE {AllisonPP, 20 | author = "Allison Horst", 21 | title = "Introduction to palmerpenguins", 22 | journal = "https://allisonhorst.github.io/palmerpenguins/articles/intro.html", 23 | year = "2020" 24 | } 25 | @ARTICLE {RolandKrasser, 26 | author = "Roland Krasser", 27 | title = "explore: Simplifies Exploratory Data Analysis", 28 | journal = "https://cran.r-project.org/web/packages/explore/index.html", 29 | year = "2020" 30 | } 31 | @ARTICLE {ChuongSerfim, 32 | author = "Chuong Do and Serafim Batzoglou", 33 | title = "What is the expectation maximization algorithm?", 34 | journal = "Nature Biotechnology 26, pages 897 to 899", 35 | year = "2008" 36 | } -------------------------------------------------------------------------------- /Code/assets/shareon/shareon.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * shareon v1.4.1 by Nikita Karamov 3 | * https://shareon.js.org 4 | */ 5 | 6 | var shareon=function(){"use strict";var t={facebook:t=>"https://www.facebook.com/sharer/sharer.php?u="+t.url,linkedin:t=>`https://www.linkedin.com/shareArticle?mini=true&url=${t.url}&title=${t.title}`,messenger:t=>`https://www.facebook.com/dialog/send?app_id=3619024578167617&link=${t.url}&redirect_uri=${t.url}`,odnoklassniki:t=>`https://connect.ok.ru/offer?url=${t.url}&title=${t.title}${t.media?"&imageUrl="+t.media:""}`,pinterest:t=>`https://pinterest.com/pin/create/button/?url=${t.url}&description=${t.title}${t.media?"&media="+t.media:""}`,pocket:t=>"https://getpocket.com/edit.php?url="+t.url,reddit:t=>`https://www.reddit.com/submit?title=${t.title}&url=${t.url}`,telegram:t=>`https://telegram.me/share/url?url=${t.url}${t.text?"&text="+t.text:""}`,twitter:t=>`https://twitter.com/intent/tweet?url=${t.url}&text=${t.title}${t.via?"&via="+t.via:""}`,viber:t=>`viber://forward?text=${t.title}%0D%0A${t.url}${t.text?"%0D%0A%0D%0A"+t.text:""}`,vkontakte:t=>`https://vk.com/share.php?url=${t.url}&title=${t.title}${t.media?"&image="+t.media:""}`,whatsapp:t=>`whatsapp://send?text=${t.title}%0D%0A${t.url}${t.text?"%0D%0A%0D%0A"+t.text:""}`};const e=()=>{const e=document.getElementsByClassName("shareon");for(let r=0;r{window.open(o,"_blank","noopener,noreferrer").opener=null});break}}}}}};return window.onload=()=>{e()},e}(); 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ML-Math 2 | 3 | This repository is part of our **MLT もくもく会 Math Reading Sessions** and the **MLT Mathematics for Machine Learning Discussions**. 4 | 5 | 6 | # Review sessions and presentations 7 | 8 | ### [YouTube Playlist](https://www.meetup.com/Machine-Learning-Tokyo/events/270761078/) 9 | 10 | 11 | - [Singular Value Decomposition](https://youtu.be/ONScfggC-M0) by [Jayson Cunanan, Ph.D.](https://www.linkedin.com/in/jayson-cunanan-phd/), AI Researcher/Engineer at AI inside 株式会社 12 | - [Intro to Principal Component Analysis and Probabilistic PCA](https://www.youtube.com/watch?v=yyO08F5bFuA&list=PLaPdEEY26UXygpV-Cxch8Xkpl7IbFKBvK&index=5&t=0s) by [Hiroshi Urata, Data Scientist](https://www.linkedin.com/in/hiroshi-u/), Data Scientist, IBM Japan 13 | - [Fourier transforms and a brief comparison with SVD](https://www.youtube.com/watch?v=8zRmr25vYBw&list=PLaPdEEY26UXygpV-Cxch8Xkpl7IbFKBvK&index=4&t=0s) by [Jayson Cunanan, Ph.D.](https://www.linkedin.com/in/jayson-cunanan-phd/), AI Researcher/Engineer at AI inside 株式会社 14 | - [ML Math Review Session: Singular Value Decomposition](https://www.youtube.com/watch?v=PrIv-7SBTw8&list=PLaPdEEY26UXygpV-Cxch8Xkpl7IbFKBvK&index=3&t=0s) by [Emil Vatai](https://twitter.com/vatai), Postdoctoral Researcher, RIKEN, Japan (Review Chapter 4: Matrix Decompositions) 15 | - [ML Math Review Session: Groups, residue classes](https://www.youtube.com/watch?v=nOxQ1vRt_p0&list=PLaPdEEY26UXygpV-Cxch8Xkpl7IbFKBvK&index=2&t=0s) by [Emil Vatai](https://twitter.com/vatai), Postdoctoral Researcher, RIKEN, Japan (Review Chapter 2: Linear Algebra) 16 | - [ML Math Review Session: Gaussian Mixture Models](https://www.youtube.com/watch?v=2Tw0peN814k&t=10s) by [Pavitra Chakravarty](https://twitter.com/genomixgmailcom), Data Analyst, Converging Health, Dallas, TX USA (Review Chapter 11: Density Estimation with Gaussian Mixture Models) 17 | 18 | 19 | # Reading sessions 20 | 21 | Our [もくもく会 ML Math Reading Sessions](https://machinelearningtokyo.com/2019/11/28/ml-math-reading-sessions/) were based on "Mathematics For Machine Learning" by Marc Peter Deisenroth, A Aldo Faisal, and Cheng Soon Ong, to be published by Cambridge University Press. https://mml-book.github.io/ 22 | 23 | Sessions were held bi-weekly in different time zones on Sundays (PST, EST, GMT, CET, IST) and Mondays (APAC). 24 | 25 | 26 | ## Part I: Mathematical Foundations 27 | 28 | - Introduction and Motivation 29 | - Linear Algebra 30 | - Analytic Geometry 31 | - Matrix Decompositions 32 | - Vector Calculus 33 | - Probability and Distribution 34 | - Continuous Optimization 35 | 36 | ## Part II: Central Machine Learning Problems 37 | 38 | - When Models Meet Data 39 | - Linear Regression 40 | - Dimensionality Reduction with Principal Component Analysis 41 | - Density Estimation with Gaussian Mixture Models 42 | - Classification with Support Vector Machines 43 | -------------------------------------------------------------------------------- /Code/assets/panelset/panelset.css: -------------------------------------------------------------------------------- 1 | /* prefixed by https://autoprefixer.github.io (PostCSS: v7.0.23, autoprefixer: v9.7.3) */ 2 | 3 | .panelset { 4 | width: 100%; 5 | position: relative; 6 | --panel-tabs-border-bottom: #ddd; 7 | --panel-tab-foreground: currentColor; 8 | --panel-tab-background: unset; 9 | --panel-tab-active-foreground: currentColor; 10 | --panel-tab-active-background: unset; 11 | --panel-tab-hover-foreground: currentColor; 12 | --panel-tab-hover-background: unset; 13 | --panel-tab-active-border-color: currentColor; 14 | --panel-tab-hover-border-color: currentColor; 15 | --panel-tab-inactive-opacity: 0.5; 16 | --panel-tab-font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; 17 | } 18 | 19 | .panelset * { 20 | box-sizing: border-box; 21 | } 22 | 23 | .panelset .panel-tabs { 24 | display: -webkit-box; 25 | display: flex; 26 | flex-wrap: wrap; 27 | -webkit-box-orient: horizontal; 28 | -webkit-box-direction: normal; 29 | flex-direction: row; 30 | -webkit-box-pack: start; 31 | justify-content: start; 32 | -webkit-box-align: center; 33 | align-items: center; 34 | overflow-y: visible; 35 | overflow-x: auto; 36 | -webkit-overflow-scrolling: touch; 37 | padding: 0 0 2px 0; 38 | box-shadow: inset 0 -2px 0px var(--panel-tabs-border-bottom); 39 | } 40 | 41 | .panelset .panel-tabs * { 42 | -webkit-transition: opacity 0.5s ease; 43 | transition: opacity 0.5s ease; 44 | } 45 | 46 | .panelset .panel-tabs .panel-tab { 47 | min-height: 50px; 48 | display: -webkit-box; 49 | display: flex; 50 | -webkit-box-pack: center; 51 | justify-content: center; 52 | -webkit-box-align: center; 53 | align-items: center; 54 | padding: 0.5em 1em; 55 | font-family: var(--panel-tab-font-family); 56 | opacity: var(--panel-tab-inactive-opacity); 57 | border-top: 2px solid transparent; 58 | border-bottom: 2px solid transparent; 59 | margin-bottom: -2px; 60 | color: var(--panel-tab-foreground); 61 | background-color: var(--panel-tab-background); 62 | list-style: none; 63 | z-index: 5; 64 | } 65 | 66 | .panelset .panel-tabs .panel-tab > a { 67 | color: currentColor; 68 | text-decoration: none; 69 | } 70 | 71 | .panelset .panel-tabs .panel-tab > a:focus { 72 | outline: none; 73 | } 74 | 75 | .panelset .panel-tabs .panel-tab:hover { 76 | border-bottom-color: var(--panel-tab-hover-border-color); 77 | color: var(--panel-tab-hover-foreground); 78 | background-color: var(--panel-tab-hover-background); 79 | opacity: 1; 80 | cursor: pointer; 81 | z-index: 10; 82 | } 83 | 84 | .panelset .panel-tabs .panel-tab:focus { 85 | outline: none; 86 | color: var(--panel-tab-hover-foreground); 87 | border-bottom-color: var(--panel-tab-hover-border-color); 88 | background-color: var(--panel-tab-hover-background); 89 | } 90 | 91 | .panelset .panel-tabs .panel-tab.panel-tab-active { 92 | border-top-color: var(--panel-tab-active-border-color); 93 | color: var(--panel-tab-active-foreground); 94 | background-color: var(--panel-tab-active-background); 95 | opacity: 1; 96 | } 97 | 98 | .panelset .panel { 99 | display: none; 100 | } 101 | 102 | .panelset .panel-active { 103 | display: block; 104 | } 105 | -------------------------------------------------------------------------------- /Code/libs/panelset/panelset.css: -------------------------------------------------------------------------------- 1 | /* prefixed by https://autoprefixer.github.io (PostCSS: v7.0.23, autoprefixer: v9.7.3) */ 2 | 3 | .panelset { 4 | width: 100%; 5 | position: relative; 6 | --panel-tabs-border-bottom: #ddd; 7 | --panel-tab-foreground: currentColor; 8 | --panel-tab-background: unset; 9 | --panel-tab-active-foreground: currentColor; 10 | --panel-tab-active-background: unset; 11 | --panel-tab-hover-foreground: currentColor; 12 | --panel-tab-hover-background: unset; 13 | --panel-tab-active-border-color: currentColor; 14 | --panel-tab-hover-border-color: currentColor; 15 | --panel-tab-inactive-opacity: 0.5; 16 | --panel-tab-font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; 17 | } 18 | 19 | .panelset * { 20 | box-sizing: border-box; 21 | } 22 | 23 | .panelset .panel-tabs { 24 | display: -webkit-box; 25 | display: flex; 26 | flex-wrap: wrap; 27 | -webkit-box-orient: horizontal; 28 | -webkit-box-direction: normal; 29 | flex-direction: row; 30 | -webkit-box-pack: start; 31 | justify-content: start; 32 | -webkit-box-align: center; 33 | align-items: center; 34 | overflow-y: visible; 35 | overflow-x: auto; 36 | -webkit-overflow-scrolling: touch; 37 | padding: 0 0 2px 0; 38 | box-shadow: inset 0 -2px 0px var(--panel-tabs-border-bottom); 39 | } 40 | 41 | .panelset .panel-tabs * { 42 | -webkit-transition: opacity 0.5s ease; 43 | transition: opacity 0.5s ease; 44 | } 45 | 46 | .panelset .panel-tabs .panel-tab { 47 | min-height: 50px; 48 | display: -webkit-box; 49 | display: flex; 50 | -webkit-box-pack: center; 51 | justify-content: center; 52 | -webkit-box-align: center; 53 | align-items: center; 54 | padding: 0.5em 1em; 55 | font-family: var(--panel-tab-font-family); 56 | opacity: var(--panel-tab-inactive-opacity); 57 | border-top: 2px solid transparent; 58 | border-bottom: 2px solid transparent; 59 | margin-bottom: -2px; 60 | color: var(--panel-tab-foreground); 61 | background-color: var(--panel-tab-background); 62 | list-style: none; 63 | z-index: 5; 64 | } 65 | 66 | .panelset .panel-tabs .panel-tab > a { 67 | color: currentColor; 68 | text-decoration: none; 69 | } 70 | 71 | .panelset .panel-tabs .panel-tab > a:focus { 72 | outline: none; 73 | } 74 | 75 | .panelset .panel-tabs .panel-tab:hover { 76 | border-bottom-color: var(--panel-tab-hover-border-color); 77 | color: var(--panel-tab-hover-foreground); 78 | background-color: var(--panel-tab-hover-background); 79 | opacity: 1; 80 | cursor: pointer; 81 | z-index: 10; 82 | } 83 | 84 | .panelset .panel-tabs .panel-tab:focus { 85 | outline: none; 86 | color: var(--panel-tab-hover-foreground); 87 | border-bottom-color: var(--panel-tab-hover-border-color); 88 | background-color: var(--panel-tab-hover-background); 89 | } 90 | 91 | .panelset .panel-tabs .panel-tab.panel-tab-active { 92 | border-top-color: var(--panel-tab-active-border-color); 93 | color: var(--panel-tab-active-foreground); 94 | background-color: var(--panel-tab-active-background); 95 | opacity: 1; 96 | } 97 | 98 | .panelset .panel { 99 | display: none; 100 | } 101 | 102 | .panelset .panel-active { 103 | display: block; 104 | } 105 | -------------------------------------------------------------------------------- /Code/libs/editable/editable.js: -------------------------------------------------------------------------------- 1 | /* global Cookies */ 2 | (function () { 3 | const ready = function (fn) { 4 | /* MIT License Copyright (c) 2016 Nuclei */ 5 | /* https://github.com/nuclei/readyjs */ 6 | const completed = () => { 7 | document.removeEventListener('DOMContentLoaded', completed) 8 | window.removeEventListener('load', completed) 9 | fn() 10 | } 11 | if (document.readyState !== 'loading') { 12 | setTimeout(fn) 13 | } else { 14 | document.addEventListener('DOMContentLoaded', completed) 15 | window.addEventListener('load', completed) 16 | } 17 | } 18 | 19 | ready(makeEditable) 20 | 21 | function makeEditable () { 22 | const docId = JSON.parse( 23 | document.getElementById('xaringanExtra-editable-docid').textContent 24 | ) 25 | docId.id = 'editable_' + docId.id 26 | 27 | const blockEvents = ev => ev.stopPropagation() 28 | const getKey = el => Array.from(el.classList).filter(c => c.match('key'))[0] 29 | const html2json = el => window.himalaya.parse(el.innerHTML) 30 | const json2html = json => window.himalaya.stringify(json) 31 | 32 | function storeElement (el) { 33 | let stored = Cookies.get(docId.id) 34 | stored = stored ? JSON.parse(stored) : {} 35 | const key = getKey(el) 36 | if (!key) return 37 | stored[key] = html2json(el) 38 | Cookies.set(docId.id, JSON.stringify(stored), { expires: docId.expires, sameSite: 'None', secure: true }) 39 | } 40 | 41 | function updateElement (el) { 42 | const key = getKey(el) 43 | if (!key) return 44 | let stored = Cookies.get(docId.id) 45 | if (!stored) return 46 | stored = JSON.parse(stored) 47 | if (!Object.keys(stored).includes(key)) return 48 | el.innerHTML = json2html(stored[key]) 49 | } 50 | 51 | window.editable = { 52 | clearCookies () { 53 | Cookies.remove(docId.id) 54 | }, 55 | clearCookiesAll () { 56 | Object.keys(Cookies.get()) 57 | .filter(key => key.match(/^editable_/)) 58 | .forEach(key => Cookies.remove(key)) 59 | } 60 | } 61 | 62 | const editables = document.querySelectorAll('.can-edit') 63 | if (!editables.length) return 64 | 65 | editables.forEach(el => { 66 | el.setAttribute('contenteditable', true) 67 | el.setAttribute('autocomplete', 'off') 68 | el.setAttribute('autocorrect', 'off') 69 | el.setAttribute('spellcheck', false) 70 | updateElement(el) 71 | }) 72 | 73 | editables.forEach(function (el) { 74 | el.addEventListener('focus', function () { 75 | if (window.editable.debug) console.log('[editable] blocking shortcuts') 76 | el.addEventListener('keyup', blockEvents) 77 | el.addEventListener('keydown', blockEvents) 78 | el.addEventListener('keypress', blockEvents) 79 | }) 80 | el.addEventListener('input', function () { 81 | el.willStore = true 82 | }) 83 | el.addEventListener('blur', function () { 84 | if (window.editable.debug) console.log('[editable] unblocking shortcuts') 85 | el.removeEventListener('keyup', blockEvents) 86 | el.removeEventListener('keydown', blockEvents) 87 | el.removeEventListener('keypress', blockEvents) 88 | if (el.willStore) { 89 | el.willStore = false 90 | if (window.editable.debug) console.log('[editable] storing update html') 91 | storeElement(el) 92 | } 93 | }) 94 | }) 95 | } 96 | })() 97 | -------------------------------------------------------------------------------- /Code/assets/editable/editable.js: -------------------------------------------------------------------------------- 1 | /* global Cookies */ 2 | (function () { 3 | const ready = function (fn) { 4 | /* MIT License Copyright (c) 2016 Nuclei */ 5 | /* https://github.com/nuclei/readyjs */ 6 | const completed = () => { 7 | document.removeEventListener('DOMContentLoaded', completed) 8 | window.removeEventListener('load', completed) 9 | fn() 10 | } 11 | if (document.readyState !== 'loading') { 12 | setTimeout(fn) 13 | } else { 14 | document.addEventListener('DOMContentLoaded', completed) 15 | window.addEventListener('load', completed) 16 | } 17 | } 18 | 19 | ready(makeEditable) 20 | 21 | function makeEditable () { 22 | const docId = JSON.parse( 23 | document.getElementById('xaringanExtra-editable-docid').textContent 24 | ) 25 | docId.id = 'editable_' + docId.id 26 | 27 | const blockEvents = ev => ev.stopPropagation() 28 | const getKey = el => Array.from(el.classList).filter(c => c.match('key'))[0] 29 | const html2json = el => window.himalaya.parse(el.innerHTML) 30 | const json2html = json => window.himalaya.stringify(json) 31 | 32 | function storeElement (el) { 33 | let stored = Cookies.get(docId.id) 34 | stored = stored ? JSON.parse(stored) : {} 35 | const key = getKey(el) 36 | if (!key) return 37 | stored[key] = html2json(el) 38 | Cookies.set(docId.id, JSON.stringify(stored), { expires: docId.expires, sameSite: 'None', secure: true }) 39 | } 40 | 41 | function updateElement (el) { 42 | const key = getKey(el) 43 | if (!key) return 44 | let stored = Cookies.get(docId.id) 45 | if (!stored) return 46 | stored = JSON.parse(stored) 47 | if (!Object.keys(stored).includes(key)) return 48 | el.innerHTML = json2html(stored[key]) 49 | } 50 | 51 | window.editable = { 52 | clearCookies () { 53 | Cookies.remove(docId.id) 54 | }, 55 | clearCookiesAll () { 56 | Object.keys(Cookies.get()) 57 | .filter(key => key.match(/^editable_/)) 58 | .forEach(key => Cookies.remove(key)) 59 | } 60 | } 61 | 62 | const editables = document.querySelectorAll('.can-edit') 63 | if (!editables.length) return 64 | 65 | editables.forEach(el => { 66 | el.setAttribute('contenteditable', true) 67 | el.setAttribute('autocomplete', 'off') 68 | el.setAttribute('autocorrect', 'off') 69 | el.setAttribute('spellcheck', false) 70 | updateElement(el) 71 | }) 72 | 73 | editables.forEach(function (el) { 74 | el.addEventListener('focus', function () { 75 | if (window.editable.debug) console.log('[editable] blocking shortcuts') 76 | el.addEventListener('keyup', blockEvents) 77 | el.addEventListener('keydown', blockEvents) 78 | el.addEventListener('keypress', blockEvents) 79 | }) 80 | el.addEventListener('input', function () { 81 | el.willStore = true 82 | }) 83 | el.addEventListener('blur', function () { 84 | if (window.editable.debug) console.log('[editable] unblocking shortcuts') 85 | el.removeEventListener('keyup', blockEvents) 86 | el.removeEventListener('keydown', blockEvents) 87 | el.removeEventListener('keypress', blockEvents) 88 | if (el.willStore) { 89 | el.willStore = false 90 | if (window.editable.debug) console.log('[editable] storing update html') 91 | storeElement(el) 92 | } 93 | }) 94 | }) 95 | } 96 | })() 97 | -------------------------------------------------------------------------------- /Code/xaringan-extra.css: -------------------------------------------------------------------------------- 1 | .full-screen { 2 | padding: 0; 3 | } 4 | 5 | .hide-count .remark-slide-number { 6 | display: none; 7 | } 8 | 9 | .grid-3-2 { 10 | display: grid; 11 | height: calc(100%); 12 | grid-template-columns: repeat(3, 1fr); 13 | grid-template-rows: 1fr 1fr; 14 | align-items: center; 15 | text-align: center; 16 | grid-gap: 1em; 17 | padding: 1em; 18 | } 19 | 20 | .extension-tile { 21 | height: 100%; 22 | width: 100%; 23 | align-items: center; 24 | justify-content: center; 25 | display: flex; 26 | font-family: 'Staatliches'; 27 | font-size: 2em; 28 | } 29 | 30 | .tileview { 31 | background: #8380B6; 32 | color: white; 33 | } 34 | 35 | .panelset-tile { 36 | background-color: #F17F29; 37 | color: white; 38 | } 39 | 40 | .editable { 41 | background: #97D8C4; 42 | } 43 | 44 | .animate { 45 | background-color: #795277; 46 | color: white; 47 | } 48 | 49 | .slide { 50 | background-color: #EE6D91; 51 | color: white; 52 | z-index: 100; 53 | } 54 | 55 | .panelset-tile { 56 | background-color: #F38D3F; 57 | } 58 | 59 | .webcam { 60 | background-color: #79C7C5; 61 | background-color: #1381B0; 62 | color: white; 63 | } 64 | 65 | .title-slide h1 { 66 | font-family: Pompiere,var(--header-font-family); 67 | font-size: 5em; 68 | line-height: 1; 69 | margin-bottom: 0; 70 | } 71 | 72 | .title-slide h2 { 73 | font-family: Pompiere,var(--header-font-family); 74 | margin-top: 0; 75 | } 76 | 77 | .pompiere { 78 | font-family: Pompiere,var(--header-font-family); 79 | } 80 | 81 | .title-slide .author { 82 | font-family: Pompiere,var(--header-font-family); 83 | font-size: 2em; 84 | position: absolute; 85 | bottom: 0em; 86 | } 87 | 88 | .title-slide .twitter { 89 | position: absolute; 90 | bottom: 0; 91 | } 92 | 93 | .user-logo { 94 | width: 150px; 95 | position: absolute; 96 | bottom: 1em; 97 | right: 1.5em; 98 | background: white; 99 | } 100 | 101 | .key { 102 | color:#000; 103 | margin:0 5px; 104 | padding:1px 5px; 105 | font-family:courier new; 106 | font-size:1.2em; 107 | border:1px #fff; 108 | -webkit-border-radius:3px; 109 | -moz-border-radius:3px; 110 | border-radius:3px; 111 | background:-webkit-gradient( 112 | linear, 113 | left top, 114 | right top, 115 | color-stop(0%, #fff), 116 | color-stop(25%, #fff) 117 | /* White Keyboard Key Style by Showeblogin */); 118 | background:-o-linear-gradient(left, #fff 0%, #fff 25%); 119 | background:-moz-linear-gradient(left, #fff 0%, #fff 25%); 120 | background:-webkit-linear-gradient(left, #fff 0%, #fff 25%); 121 | background:-ms-linear-gradient(left, #fff 0%, #fff 25%); 122 | background:linear-gradient(left, #fff 0%, #fff 25%); 123 | -webkit-box-shadow:1px 0 1px 0 #999, 0 2px 0 2px lightGray, 0 2px 0 3px #666; 124 | -moz-box-shadow:1px 0 1px 0 #999, 0 2px 0 2px lightGray, 0 2px 0 3px #666; 125 | box-shadow:1px 0 1px 0 #999, 0 2px 0 2px lightGray, 0 2px 0 3px #666; 126 | } 127 | 128 | .remark-slide-content h1 { 129 | margin-bottom: 0.25em; 130 | } 131 | 132 | .bigger { 133 | font-size: 0.9em; 134 | } 135 | 136 | .footnote { 137 | font-size: 0.4em; 138 | position: absolute; 139 | bottom: 1em; 140 | right: 1.5em; 141 | background: white; 142 | } 143 | 144 | .huge p:not(.remark-slide-number) { 145 | font-size: 5em; 146 | } 147 | 148 | .bigger pre code { 149 | font-size: 0.9em; 150 | } 151 | 152 | .remark-code-has-line-highlighted .remark-code-line:not(.remark-code-line-highlighted) { 153 | opacity: 0.4; 154 | } 155 | 156 | .remark-code-line-highlighted { 157 | background-color: inherit; /* Override highlight color */ 158 | /* font-weight: 500; */ 159 | } 160 | 161 | /* Line Hover Indicator */ 162 | .remark-code-line:hover { 163 | font-weight: bold; 164 | opacity: 1 !important; 165 | } 166 | 167 | .remark-code-line:hover:before { 168 | content: "\25B6"; 169 | color: #6d7e8a; 170 | position: absolute; 171 | transform: translateX(-1.2em); 172 | animation: hover 0.66s alternate 8 cubic-bezier(0.445, 0.05, 0.55, 0.95); 173 | } 174 | 175 | @keyframes hover { 176 | 0% { 177 | transform: translateX(-1.2em); 178 | } 179 | 100% { 180 | transform: translateX(-0.8em); 181 | } 182 | } 183 | 184 | ::selection { 185 | background-color: rgba(85, 235, 188, 0.6); 186 | } 187 | 188 | .not-last { 189 | display: none; 190 | } 191 | .has-continuation .not-last { 192 | display: unset; 193 | } 194 | -------------------------------------------------------------------------------- /Code/assets/js-cookie/js.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * JavaScript Cookie v2.2.1 3 | * https://github.com/js-cookie/js-cookie 4 | * 5 | * Copyright 2006, 2015 Klaus Hartl & Fagner Brack 6 | * Released under the MIT license 7 | */ 8 | ;(function (factory) { 9 | var registeredInModuleLoader; 10 | if (typeof define === 'function' && define.amd) { 11 | define(factory); 12 | registeredInModuleLoader = true; 13 | } 14 | if (typeof exports === 'object') { 15 | module.exports = factory(); 16 | registeredInModuleLoader = true; 17 | } 18 | if (!registeredInModuleLoader) { 19 | var OldCookies = window.Cookies; 20 | var api = window.Cookies = factory(); 21 | api.noConflict = function () { 22 | window.Cookies = OldCookies; 23 | return api; 24 | }; 25 | } 26 | }(function () { 27 | function extend () { 28 | var i = 0; 29 | var result = {}; 30 | for (; i < arguments.length; i++) { 31 | var attributes = arguments[ i ]; 32 | for (var key in attributes) { 33 | result[key] = attributes[key]; 34 | } 35 | } 36 | return result; 37 | } 38 | 39 | function decode (s) { 40 | return s.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent); 41 | } 42 | 43 | function init (converter) { 44 | function api() {} 45 | 46 | function set (key, value, attributes) { 47 | if (typeof document === 'undefined') { 48 | return; 49 | } 50 | 51 | attributes = extend({ 52 | path: '/' 53 | }, api.defaults, attributes); 54 | 55 | if (typeof attributes.expires === 'number') { 56 | attributes.expires = new Date(new Date() * 1 + attributes.expires * 864e+5); 57 | } 58 | 59 | // We're using "expires" because "max-age" is not supported by IE 60 | attributes.expires = attributes.expires ? attributes.expires.toUTCString() : ''; 61 | 62 | try { 63 | var result = JSON.stringify(value); 64 | if (/^[\{\[]/.test(result)) { 65 | value = result; 66 | } 67 | } catch (e) {} 68 | 69 | value = converter.write ? 70 | converter.write(value, key) : 71 | encodeURIComponent(String(value)) 72 | .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); 73 | 74 | key = encodeURIComponent(String(key)) 75 | .replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent) 76 | .replace(/[\(\)]/g, escape); 77 | 78 | var stringifiedAttributes = ''; 79 | for (var attributeName in attributes) { 80 | if (!attributes[attributeName]) { 81 | continue; 82 | } 83 | stringifiedAttributes += '; ' + attributeName; 84 | if (attributes[attributeName] === true) { 85 | continue; 86 | } 87 | 88 | // Considers RFC 6265 section 5.2: 89 | // ... 90 | // 3. If the remaining unparsed-attributes contains a %x3B (";") 91 | // character: 92 | // Consume the characters of the unparsed-attributes up to, 93 | // not including, the first %x3B (";") character. 94 | // ... 95 | stringifiedAttributes += '=' + attributes[attributeName].split(';')[0]; 96 | } 97 | 98 | return (document.cookie = key + '=' + value + stringifiedAttributes); 99 | } 100 | 101 | function get (key, json) { 102 | if (typeof document === 'undefined') { 103 | return; 104 | } 105 | 106 | var jar = {}; 107 | // To prevent the for loop in the first place assign an empty array 108 | // in case there are no cookies at all. 109 | var cookies = document.cookie ? document.cookie.split('; ') : []; 110 | var i = 0; 111 | 112 | for (; i < cookies.length; i++) { 113 | var parts = cookies[i].split('='); 114 | var cookie = parts.slice(1).join('='); 115 | 116 | if (!json && cookie.charAt(0) === '"') { 117 | cookie = cookie.slice(1, -1); 118 | } 119 | 120 | try { 121 | var name = decode(parts[0]); 122 | cookie = (converter.read || converter)(cookie, name) || 123 | decode(cookie); 124 | 125 | if (json) { 126 | try { 127 | cookie = JSON.parse(cookie); 128 | } catch (e) {} 129 | } 130 | 131 | jar[name] = cookie; 132 | 133 | if (key === name) { 134 | break; 135 | } 136 | } catch (e) {} 137 | } 138 | 139 | return key ? jar[key] : jar; 140 | } 141 | 142 | api.set = set; 143 | api.get = function (key) { 144 | return get(key, false /* read as raw */); 145 | }; 146 | api.getJSON = function (key) { 147 | return get(key, true /* read as json */); 148 | }; 149 | api.remove = function (key, attributes) { 150 | set(key, '', extend(attributes, { 151 | expires: -1 152 | })); 153 | }; 154 | 155 | api.defaults = {}; 156 | 157 | api.withConverter = init; 158 | 159 | return api; 160 | } 161 | 162 | return init(function () {}); 163 | })); 164 | -------------------------------------------------------------------------------- /Code/libs/js-cookie/js.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * JavaScript Cookie v2.2.1 3 | * https://github.com/js-cookie/js-cookie 4 | * 5 | * Copyright 2006, 2015 Klaus Hartl & Fagner Brack 6 | * Released under the MIT license 7 | */ 8 | ;(function (factory) { 9 | var registeredInModuleLoader; 10 | if (typeof define === 'function' && define.amd) { 11 | define(factory); 12 | registeredInModuleLoader = true; 13 | } 14 | if (typeof exports === 'object') { 15 | module.exports = factory(); 16 | registeredInModuleLoader = true; 17 | } 18 | if (!registeredInModuleLoader) { 19 | var OldCookies = window.Cookies; 20 | var api = window.Cookies = factory(); 21 | api.noConflict = function () { 22 | window.Cookies = OldCookies; 23 | return api; 24 | }; 25 | } 26 | }(function () { 27 | function extend () { 28 | var i = 0; 29 | var result = {}; 30 | for (; i < arguments.length; i++) { 31 | var attributes = arguments[ i ]; 32 | for (var key in attributes) { 33 | result[key] = attributes[key]; 34 | } 35 | } 36 | return result; 37 | } 38 | 39 | function decode (s) { 40 | return s.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent); 41 | } 42 | 43 | function init (converter) { 44 | function api() {} 45 | 46 | function set (key, value, attributes) { 47 | if (typeof document === 'undefined') { 48 | return; 49 | } 50 | 51 | attributes = extend({ 52 | path: '/' 53 | }, api.defaults, attributes); 54 | 55 | if (typeof attributes.expires === 'number') { 56 | attributes.expires = new Date(new Date() * 1 + attributes.expires * 864e+5); 57 | } 58 | 59 | // We're using "expires" because "max-age" is not supported by IE 60 | attributes.expires = attributes.expires ? attributes.expires.toUTCString() : ''; 61 | 62 | try { 63 | var result = JSON.stringify(value); 64 | if (/^[\{\[]/.test(result)) { 65 | value = result; 66 | } 67 | } catch (e) {} 68 | 69 | value = converter.write ? 70 | converter.write(value, key) : 71 | encodeURIComponent(String(value)) 72 | .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); 73 | 74 | key = encodeURIComponent(String(key)) 75 | .replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent) 76 | .replace(/[\(\)]/g, escape); 77 | 78 | var stringifiedAttributes = ''; 79 | for (var attributeName in attributes) { 80 | if (!attributes[attributeName]) { 81 | continue; 82 | } 83 | stringifiedAttributes += '; ' + attributeName; 84 | if (attributes[attributeName] === true) { 85 | continue; 86 | } 87 | 88 | // Considers RFC 6265 section 5.2: 89 | // ... 90 | // 3. If the remaining unparsed-attributes contains a %x3B (";") 91 | // character: 92 | // Consume the characters of the unparsed-attributes up to, 93 | // not including, the first %x3B (";") character. 94 | // ... 95 | stringifiedAttributes += '=' + attributes[attributeName].split(';')[0]; 96 | } 97 | 98 | return (document.cookie = key + '=' + value + stringifiedAttributes); 99 | } 100 | 101 | function get (key, json) { 102 | if (typeof document === 'undefined') { 103 | return; 104 | } 105 | 106 | var jar = {}; 107 | // To prevent the for loop in the first place assign an empty array 108 | // in case there are no cookies at all. 109 | var cookies = document.cookie ? document.cookie.split('; ') : []; 110 | var i = 0; 111 | 112 | for (; i < cookies.length; i++) { 113 | var parts = cookies[i].split('='); 114 | var cookie = parts.slice(1).join('='); 115 | 116 | if (!json && cookie.charAt(0) === '"') { 117 | cookie = cookie.slice(1, -1); 118 | } 119 | 120 | try { 121 | var name = decode(parts[0]); 122 | cookie = (converter.read || converter)(cookie, name) || 123 | decode(cookie); 124 | 125 | if (json) { 126 | try { 127 | cookie = JSON.parse(cookie); 128 | } catch (e) {} 129 | } 130 | 131 | jar[name] = cookie; 132 | 133 | if (key === name) { 134 | break; 135 | } 136 | } catch (e) {} 137 | } 138 | 139 | return key ? jar[key] : jar; 140 | } 141 | 142 | api.set = set; 143 | api.get = function (key) { 144 | return get(key, false /* read as raw */); 145 | }; 146 | api.getJSON = function (key) { 147 | return get(key, true /* read as json */); 148 | }; 149 | api.remove = function (key, attributes) { 150 | set(key, '', extend(attributes, { 151 | expires: -1 152 | })); 153 | }; 154 | 155 | api.defaults = {}; 156 | 157 | api.withConverter = init; 158 | 159 | return api; 160 | } 161 | 162 | return init(function () {}); 163 | })); 164 | -------------------------------------------------------------------------------- /Code/assets/xaringanExtra-webcam/webcam.js: -------------------------------------------------------------------------------- 1 | (function (window, document) { 2 | const videoOpts = { first: true } 3 | 4 | function setVideoOptions () { 5 | if (!videoOpts.width) { 6 | const opts = JSON.parse( 7 | document.getElementById('xaringanExtra-webcam-options').textContent 8 | ) 9 | videoOpts.width = opts.width || 200 10 | videoOpts.height = opts.height || 200 11 | videoOpts.margin = opts.margin || '1em' 12 | } 13 | } 14 | 15 | function createVideoElement () { 16 | setVideoOptions() 17 | const vid = document.createElement('video') 18 | vid.setAttribute('autoplay', true) 19 | vid.setAttribute('draggable', true) 20 | vid.id = 'xaringanExtra-webcam' 21 | vid.width = videoOpts.width 22 | vid.height = videoOpts.height 23 | vid.style.position = 'absolute' 24 | vid.style.top = 0 25 | vid.style.right = 0 26 | vid.style.margin = videoOpts.margin 27 | vid.style.zIndex = 1000 28 | vid.style.transform = 'rotateY(180deg)' 29 | vid.style.cursor = 'move' 30 | return vid 31 | } 32 | 33 | function findVideo () { 34 | return document.getElementById('xaringanExtra-webcam') 35 | } 36 | 37 | function alertNoVideo () { 38 | let msg = 'Webcam is not available in this browser.' 39 | if (!/^https/.test(window.location.protocol)) { 40 | msg += '\n\nAre you using https for this site?' 41 | } 42 | window.alert(msg) 43 | } 44 | 45 | function startVideo () { 46 | if (document.body.classList.contains('remark-presenter-mode')) { 47 | return 48 | } 49 | 50 | if (findVideo()) { 51 | return 52 | } 53 | 54 | if (!navigator.mediaDevices) { 55 | alertNoVideo() 56 | return 57 | } 58 | 59 | navigator 60 | .mediaDevices 61 | .getUserMedia({ 62 | audio: false, 63 | video: { 64 | facingMode: 'user', 65 | width: { ideal: videoOpts.width }, 66 | height: { ideal: videoOpts.height } 67 | } 68 | }) 69 | .then(function (stream) { 70 | const vid = createVideoElement() 71 | videoOpts.el = vid 72 | document.body.appendChild(vid) 73 | 74 | vid.addEventListener('dragstart', dragStart, false) 75 | document.body.addEventListener('dragover', dragOver, false) 76 | document.body.addEventListener('drop', dragDrop, false) 77 | vid.srcObject = stream 78 | }) 79 | .catch(err => { 80 | if (videoOpts.first) { 81 | alertNoVideo() 82 | videoOpts.first = false 83 | } 84 | console.error(err) 85 | }) 86 | } 87 | 88 | function stopVideo () { 89 | const vid = findVideo() 90 | if (!vid) return 91 | vid.srcObject.getTracks().forEach(track => track.stop()) 92 | vid.parentNode.removeChild(vid) 93 | videoOpts.el = null 94 | document.body.removeEventListener('dragover', dragOver) 95 | document.body.removeEventListener('drop', dragDrop) 96 | } 97 | 98 | function toggleVideo () { 99 | findVideo() ? stopVideo() : startVideo() 100 | } 101 | 102 | function moveVideo (top = '0px', right = '0px', bottom = null, left = null) { 103 | videoOpts.el.style.top = top 104 | videoOpts.el.style.right = right 105 | videoOpts.el.style.bottom = bottom 106 | videoOpts.el.style.left = left 107 | } 108 | 109 | function moveVideoNextCorner () { 110 | const { top, right, bottom, left } = videoOpts.el.style 111 | if (top === '0px' && right === '0px') { 112 | moveVideo(null, '0px', '0px', null) 113 | } else if (right === '0px' && bottom === '0px') { 114 | moveVideo(null, null, '0px', '0px') 115 | } else if (bottom === '0px' && left === '0px') { 116 | moveVideo('0px', null, null, '0px') 117 | } else if (top === '0px' && left === '0px') { 118 | moveVideo('0px', '0px', null, null) 119 | } else { 120 | moveVideo() 121 | } 122 | } 123 | 124 | function dragStart (event) { 125 | const { clientX, clientY } = event 126 | const style = window.getComputedStyle(event.target, null) 127 | const x = parseInt(style.getPropertyValue('left'), 10) - clientX 128 | const y = parseInt(style.getPropertyValue('top'), 10) - clientY 129 | event.dataTransfer.setData('text', `${x},${y}`) 130 | } 131 | 132 | function dragOver (event) { 133 | event.preventDefault() 134 | return false 135 | } 136 | 137 | function dragDrop (event) { 138 | let offset = event.dataTransfer.getData('text') 139 | offset = offset.split(',').map(x => parseInt(x, 10)) 140 | const { clientX, clientY } = event 141 | moveVideo(`${clientY + offset[1]}px`, null, null, `${clientX + offset[0]}px`) 142 | event.preventDefault() 143 | return false 144 | } 145 | 146 | document.addEventListener('keydown', function (ev) { 147 | if (ev.code === 'KeyW') { 148 | ev.shiftKey ? moveVideoNextCorner() : toggleVideo() 149 | } 150 | }) 151 | })(window, document) 152 | -------------------------------------------------------------------------------- /Code/libs/xaringanExtra-webcam/webcam.js: -------------------------------------------------------------------------------- 1 | (function (window, document) { 2 | const videoOpts = { first: true } 3 | 4 | function setVideoOptions () { 5 | if (!videoOpts.width) { 6 | const opts = JSON.parse( 7 | document.getElementById('xaringanExtra-webcam-options').textContent 8 | ) 9 | videoOpts.width = opts.width || 200 10 | videoOpts.height = opts.height || 200 11 | videoOpts.margin = opts.margin || '1em' 12 | } 13 | } 14 | 15 | function createVideoElement () { 16 | setVideoOptions() 17 | const vid = document.createElement('video') 18 | vid.setAttribute('autoplay', true) 19 | vid.setAttribute('draggable', true) 20 | vid.id = 'xaringanExtra-webcam' 21 | vid.width = videoOpts.width 22 | vid.height = videoOpts.height 23 | vid.style.position = 'absolute' 24 | vid.style.top = 0 25 | vid.style.right = 0 26 | vid.style.margin = videoOpts.margin 27 | vid.style.zIndex = 1000 28 | vid.style.transform = 'rotateY(180deg)' 29 | vid.style.cursor = 'move' 30 | return vid 31 | } 32 | 33 | function findVideo () { 34 | return document.getElementById('xaringanExtra-webcam') 35 | } 36 | 37 | function alertNoVideo () { 38 | let msg = 'Webcam is not available in this browser.' 39 | if (!/^https/.test(window.location.protocol)) { 40 | msg += '\n\nAre you using https for this site?' 41 | } 42 | window.alert(msg) 43 | } 44 | 45 | function startVideo () { 46 | if (document.body.classList.contains('remark-presenter-mode')) { 47 | return 48 | } 49 | 50 | if (findVideo()) { 51 | return 52 | } 53 | 54 | if (!navigator.mediaDevices) { 55 | alertNoVideo() 56 | return 57 | } 58 | 59 | navigator 60 | .mediaDevices 61 | .getUserMedia({ 62 | audio: false, 63 | video: { 64 | facingMode: 'user', 65 | width: { ideal: videoOpts.width }, 66 | height: { ideal: videoOpts.height } 67 | } 68 | }) 69 | .then(function (stream) { 70 | const vid = createVideoElement() 71 | videoOpts.el = vid 72 | document.body.appendChild(vid) 73 | 74 | vid.addEventListener('dragstart', dragStart, false) 75 | document.body.addEventListener('dragover', dragOver, false) 76 | document.body.addEventListener('drop', dragDrop, false) 77 | vid.srcObject = stream 78 | }) 79 | .catch(err => { 80 | if (videoOpts.first) { 81 | alertNoVideo() 82 | videoOpts.first = false 83 | } 84 | console.error(err) 85 | }) 86 | } 87 | 88 | function stopVideo () { 89 | const vid = findVideo() 90 | if (!vid) return 91 | vid.srcObject.getTracks().forEach(track => track.stop()) 92 | vid.parentNode.removeChild(vid) 93 | videoOpts.el = null 94 | document.body.removeEventListener('dragover', dragOver) 95 | document.body.removeEventListener('drop', dragDrop) 96 | } 97 | 98 | function toggleVideo () { 99 | findVideo() ? stopVideo() : startVideo() 100 | } 101 | 102 | function moveVideo (top = '0px', right = '0px', bottom = null, left = null) { 103 | videoOpts.el.style.top = top 104 | videoOpts.el.style.right = right 105 | videoOpts.el.style.bottom = bottom 106 | videoOpts.el.style.left = left 107 | } 108 | 109 | function moveVideoNextCorner () { 110 | const { top, right, bottom, left } = videoOpts.el.style 111 | if (top === '0px' && right === '0px') { 112 | moveVideo(null, '0px', '0px', null) 113 | } else if (right === '0px' && bottom === '0px') { 114 | moveVideo(null, null, '0px', '0px') 115 | } else if (bottom === '0px' && left === '0px') { 116 | moveVideo('0px', null, null, '0px') 117 | } else if (top === '0px' && left === '0px') { 118 | moveVideo('0px', '0px', null, null) 119 | } else { 120 | moveVideo() 121 | } 122 | } 123 | 124 | function dragStart (event) { 125 | const { clientX, clientY } = event 126 | const style = window.getComputedStyle(event.target, null) 127 | const x = parseInt(style.getPropertyValue('left'), 10) - clientX 128 | const y = parseInt(style.getPropertyValue('top'), 10) - clientY 129 | event.dataTransfer.setData('text', `${x},${y}`) 130 | } 131 | 132 | function dragOver (event) { 133 | event.preventDefault() 134 | return false 135 | } 136 | 137 | function dragDrop (event) { 138 | let offset = event.dataTransfer.getData('text') 139 | offset = offset.split(',').map(x => parseInt(x, 10)) 140 | const { clientX, clientY } = event 141 | moveVideo(`${clientY + offset[1]}px`, null, null, `${clientX + offset[0]}px`) 142 | event.preventDefault() 143 | return false 144 | } 145 | 146 | document.addEventListener('keydown', function (ev) { 147 | if (ev.code === 'KeyW') { 148 | ev.shiftKey ? moveVideoNextCorner() : toggleVideo() 149 | } 150 | }) 151 | })(window, document) 152 | -------------------------------------------------------------------------------- /Code/assets/tile-view/tile-view.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Tile View for remark.js Slides 3 | * 4 | * Garrick Aden-Buie 5 | * 6 | * Inspired and converted to Vanilla JS from 7 | * https://github.com/StephenHesperus/remark-hook/ 8 | * 9 | * Include after remarkjs slides are initialized. 10 | * 11 | */ 12 | 13 | /* global slideshow */ 14 | (function () { 15 | const ready = function (fn) { 16 | /* MIT License Copyright (c) 2016 Nuclei */ 17 | /* https://github.com/nuclei/readyjs */ 18 | const completed = () => { 19 | document.removeEventListener('DOMContentLoaded', completed) 20 | window.removeEventListener('load', completed) 21 | fn() 22 | } 23 | if (document.readyState !== 'loading') { 24 | setTimeout(fn) 25 | } else { 26 | document.addEventListener('DOMContentLoaded', completed) 27 | window.addEventListener('load', completed) 28 | } 29 | } 30 | 31 | ready(function () { 32 | const launchKey = 79 // keycode for O, used to enable tile view 33 | 34 | // Slides container 35 | const remarkSlideShow = document.querySelector('div.remark-slides-area') 36 | 37 | let tileView = document.querySelector('div.remark__tile-view') 38 | if (!tileView) { 39 | tileView = document.createElement('div') 40 | tileView.className = 'remark__tile-view' 41 | } 42 | 43 | const toggleElement = el => { 44 | el.style.display = el.style.display === 'none' ? '' : 'none' 45 | } 46 | 47 | const toggleTileView = function () { 48 | toggleElement(tileView) 49 | toggleElement(remarkSlideShow) 50 | 51 | if (tileView.style.display === 'none') { 52 | // tileView is now hidden, go to current slide 53 | slideshow.gotoSlide(tileVars.currentSlideIdx + 1) 54 | 55 | // remove scroll/mousewheel event blocking 56 | tileView.removeEventListener('mousewheel', blockEvent) 57 | tileView.removeEventListener('DOMMouseScroll', blockEvent) 58 | console.log('removing blockScaling') 59 | document.removeEventListener('keydown', blockScaling) 60 | } else { 61 | // store current slide index prior to launching tile-view 62 | tileVars.currentSlideIdx = slideshow.getCurrentSlideIndex() 63 | 64 | // set class on seen and current slide and scroll into view 65 | const tiles = tileView.querySelectorAll('.remark__tile-view__tile'); 66 | [...tiles].forEach((tile, idx) => { 67 | tile.classList.toggle( 68 | 'remark__tile-view__tile--seen', 69 | idx < tileVars.currentSlideIdx 70 | ) 71 | tile.classList.toggle( 72 | 'remark__tile-view__tile--current', 73 | idx === tileVars.currentSlideIdx 74 | ) 75 | }) 76 | tiles[tileVars.currentSlideIdx].scrollIntoView({ 77 | behavior: 'smooth', 78 | block: 'center' 79 | }) 80 | 81 | // block remarkjs from handling scroll events 82 | tileView.addEventListener('mousewheel', blockEvent) 83 | tileView.addEventListener('DOMMouseScroll', blockEvent) 84 | console.log('adding blockScaling') 85 | document.addEventListener('keydown', blockScaling) 86 | } 87 | } 88 | 89 | const createTileView = ({ minSize = 250, title = document.title } = {}) => { 90 | // Tile view header 91 | const h1 = document.createElement('h1') 92 | h1.className = 'remark__tile-view__header' 93 | h1.innerHTML = title 94 | 95 | tileView.appendChild(h1) 96 | const tiles = document.createElement('div') 97 | tiles.className = 'remark__tile-view__tiles' 98 | tileView.appendChild(tiles) 99 | 100 | // Clone slideshow 101 | const slidesArea = remarkSlideShow.cloneNode(true) 102 | 103 | // Calculate slide scale and tile container size 104 | const slideScaler = slidesArea.querySelector('.remark-slide-scaler') 105 | const slideWidth = parseFloat(slideScaler.style.width.replace('px', '')) 106 | const slideHeight = parseFloat( 107 | slideScaler.style.height.replace('px', '') 108 | ) 109 | const scale = minSize / Math.min(slideWidth, slideHeight) 110 | let tileWidth = Math.round(slideWidth * scale) 111 | let tileHeight = Math.round(slideHeight * scale) 112 | 113 | // convert tileWidth/Height to em relative to base 18px (set in CSS) 114 | tileWidth = tileWidth / 18 115 | tileHeight = tileHeight / 18 116 | 117 | tiles.style.gridTemplateColumns = `repeat(auto-fill, minmax(${tileWidth}em, 1fr))` 118 | 119 | const slides = slidesArea.querySelectorAll('.remark-slide-container') 120 | 121 | slides.forEach((slide, slideIndex) => { 122 | let tile = document.createElement('template') 123 | tile.innerHTML = `
124 |
125 |
` 126 | tile = tile.content.firstChild 127 | 128 | const tileContainer = tile.querySelector( 129 | '.remark__tile-view__slide-container' 130 | ) 131 | tileContainer.style.width = `${tileWidth}em` 132 | tileContainer.style.height = `${tileHeight}em` 133 | 134 | const thisSlideScaler = slide.querySelector('.remark-slide-scaler') 135 | thisSlideScaler.style.top = '0px' 136 | thisSlideScaler.style.left = '0px' 137 | thisSlideScaler.style.transform = `scale(${scale})` 138 | thisSlideScaler.parentElement.classList.add('remark-visible') 139 | 140 | slide.addEventListener('click', () => { 141 | tileVars.currentSlideIdx = slideIndex 142 | toggleTileView() 143 | }) 144 | 145 | tileContainer.appendChild(slide) 146 | tiles.appendChild(tile) 147 | }) 148 | 149 | document.body.appendChild(tileView) 150 | } 151 | 152 | const tileVars = {} 153 | const blockEvent = ev => ev.stopPropagation() 154 | const blockScaling = function (ev) { 155 | if (ev.controlKey || ev.metaKey) { 156 | if (ev.key === '=' || ev.key === '-') { 157 | ev.preventDefault() 158 | console.log('window scaling is not allowed inside the tile overview') 159 | } 160 | } 161 | } 162 | 163 | document.addEventListener('keydown', ev => { 164 | if (ev.keyCode === launchKey) { 165 | toggleTileView() 166 | } 167 | }) 168 | 169 | const addTileViewHelpText = () => { 170 | const helpTable = document.querySelector( 171 | '.remark-help-content table.light-keys' 172 | ) 173 | if (!helpTable) { 174 | console.error( 175 | 'Could not find remark help table, has remark been initialized?' 176 | ) 177 | return 178 | } 179 | const newRow = document.createElement('tr') 180 | newRow.innerHTML += 'o' 181 | newRow.innerHTML += 'Tile View: Overview of Slides' 182 | helpTable.append(newRow) 183 | } 184 | 185 | createTileView({ minSize: 200 }) 186 | toggleElement(tileView) 187 | addTileViewHelpText() 188 | }) 189 | })() 190 | -------------------------------------------------------------------------------- /Code/libs/tile-view/tile-view.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Tile View for remark.js Slides 3 | * 4 | * Garrick Aden-Buie 5 | * 6 | * Inspired and converted to Vanilla JS from 7 | * https://github.com/StephenHesperus/remark-hook/ 8 | * 9 | * Include after remarkjs slides are initialized. 10 | * 11 | */ 12 | 13 | /* global slideshow */ 14 | (function () { 15 | const ready = function (fn) { 16 | /* MIT License Copyright (c) 2016 Nuclei */ 17 | /* https://github.com/nuclei/readyjs */ 18 | const completed = () => { 19 | document.removeEventListener('DOMContentLoaded', completed) 20 | window.removeEventListener('load', completed) 21 | fn() 22 | } 23 | if (document.readyState !== 'loading') { 24 | setTimeout(fn) 25 | } else { 26 | document.addEventListener('DOMContentLoaded', completed) 27 | window.addEventListener('load', completed) 28 | } 29 | } 30 | 31 | ready(function () { 32 | const launchKey = 79 // keycode for O, used to enable tile view 33 | 34 | // Slides container 35 | const remarkSlideShow = document.querySelector('div.remark-slides-area') 36 | 37 | let tileView = document.querySelector('div.remark__tile-view') 38 | if (!tileView) { 39 | tileView = document.createElement('div') 40 | tileView.className = 'remark__tile-view' 41 | } 42 | 43 | const toggleElement = el => { 44 | el.style.display = el.style.display === 'none' ? '' : 'none' 45 | } 46 | 47 | const toggleTileView = function () { 48 | toggleElement(tileView) 49 | toggleElement(remarkSlideShow) 50 | 51 | if (tileView.style.display === 'none') { 52 | // tileView is now hidden, go to current slide 53 | slideshow.gotoSlide(tileVars.currentSlideIdx + 1) 54 | 55 | // remove scroll/mousewheel event blocking 56 | tileView.removeEventListener('mousewheel', blockEvent) 57 | tileView.removeEventListener('DOMMouseScroll', blockEvent) 58 | console.log('removing blockScaling') 59 | document.removeEventListener('keydown', blockScaling) 60 | } else { 61 | // store current slide index prior to launching tile-view 62 | tileVars.currentSlideIdx = slideshow.getCurrentSlideIndex() 63 | 64 | // set class on seen and current slide and scroll into view 65 | const tiles = tileView.querySelectorAll('.remark__tile-view__tile'); 66 | [...tiles].forEach((tile, idx) => { 67 | tile.classList.toggle( 68 | 'remark__tile-view__tile--seen', 69 | idx < tileVars.currentSlideIdx 70 | ) 71 | tile.classList.toggle( 72 | 'remark__tile-view__tile--current', 73 | idx === tileVars.currentSlideIdx 74 | ) 75 | }) 76 | tiles[tileVars.currentSlideIdx].scrollIntoView({ 77 | behavior: 'smooth', 78 | block: 'center' 79 | }) 80 | 81 | // block remarkjs from handling scroll events 82 | tileView.addEventListener('mousewheel', blockEvent) 83 | tileView.addEventListener('DOMMouseScroll', blockEvent) 84 | console.log('adding blockScaling') 85 | document.addEventListener('keydown', blockScaling) 86 | } 87 | } 88 | 89 | const createTileView = ({ minSize = 250, title = document.title } = {}) => { 90 | // Tile view header 91 | const h1 = document.createElement('h1') 92 | h1.className = 'remark__tile-view__header' 93 | h1.innerHTML = title 94 | 95 | tileView.appendChild(h1) 96 | const tiles = document.createElement('div') 97 | tiles.className = 'remark__tile-view__tiles' 98 | tileView.appendChild(tiles) 99 | 100 | // Clone slideshow 101 | const slidesArea = remarkSlideShow.cloneNode(true) 102 | 103 | // Calculate slide scale and tile container size 104 | const slideScaler = slidesArea.querySelector('.remark-slide-scaler') 105 | const slideWidth = parseFloat(slideScaler.style.width.replace('px', '')) 106 | const slideHeight = parseFloat( 107 | slideScaler.style.height.replace('px', '') 108 | ) 109 | const scale = minSize / Math.min(slideWidth, slideHeight) 110 | let tileWidth = Math.round(slideWidth * scale) 111 | let tileHeight = Math.round(slideHeight * scale) 112 | 113 | // convert tileWidth/Height to em relative to base 18px (set in CSS) 114 | tileWidth = tileWidth / 18 115 | tileHeight = tileHeight / 18 116 | 117 | tiles.style.gridTemplateColumns = `repeat(auto-fill, minmax(${tileWidth}em, 1fr))` 118 | 119 | const slides = slidesArea.querySelectorAll('.remark-slide-container') 120 | 121 | slides.forEach((slide, slideIndex) => { 122 | let tile = document.createElement('template') 123 | tile.innerHTML = `
124 |
125 |
` 126 | tile = tile.content.firstChild 127 | 128 | const tileContainer = tile.querySelector( 129 | '.remark__tile-view__slide-container' 130 | ) 131 | tileContainer.style.width = `${tileWidth}em` 132 | tileContainer.style.height = `${tileHeight}em` 133 | 134 | const thisSlideScaler = slide.querySelector('.remark-slide-scaler') 135 | thisSlideScaler.style.top = '0px' 136 | thisSlideScaler.style.left = '0px' 137 | thisSlideScaler.style.transform = `scale(${scale})` 138 | thisSlideScaler.parentElement.classList.add('remark-visible') 139 | 140 | slide.addEventListener('click', () => { 141 | tileVars.currentSlideIdx = slideIndex 142 | toggleTileView() 143 | }) 144 | 145 | tileContainer.appendChild(slide) 146 | tiles.appendChild(tile) 147 | }) 148 | 149 | document.body.appendChild(tileView) 150 | } 151 | 152 | const tileVars = {} 153 | const blockEvent = ev => ev.stopPropagation() 154 | const blockScaling = function (ev) { 155 | if (ev.controlKey || ev.metaKey) { 156 | if (ev.key === '=' || ev.key === '-') { 157 | ev.preventDefault() 158 | console.log('window scaling is not allowed inside the tile overview') 159 | } 160 | } 161 | } 162 | 163 | document.addEventListener('keydown', ev => { 164 | if (ev.keyCode === launchKey) { 165 | toggleTileView() 166 | } 167 | }) 168 | 169 | const addTileViewHelpText = () => { 170 | const helpTable = document.querySelector( 171 | '.remark-help-content table.light-keys' 172 | ) 173 | if (!helpTable) { 174 | console.error( 175 | 'Could not find remark help table, has remark been initialized?' 176 | ) 177 | return 178 | } 179 | const newRow = document.createElement('tr') 180 | newRow.innerHTML += 'o' 181 | newRow.innerHTML += 'Tile View: Overview of Slides' 182 | helpTable.append(newRow) 183 | } 184 | 185 | createTileView({ minSize: 200 }) 186 | toggleElement(tileView) 187 | addTileViewHelpText() 188 | }) 189 | })() 190 | -------------------------------------------------------------------------------- /Code/xaringan-themer.css: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------- 2 | * 3 | * !! This file was generated by xaringanthemer !! 4 | * 5 | * Changes made to this file directly will be overwritten 6 | * if you used xaringanthemer in your xaringan slides Rmd 7 | * 8 | * Issues or likes? 9 | * - https://github.com/gadenbuie/xaringanthemer 10 | * - https://www.garrickadenbuie.com 11 | * 12 | * Need help? Try: 13 | * - vignette(package = "xaringanthemer") 14 | * - ?xaringanthemer::style_xaringan 15 | * - xaringan wiki: https://github.com/yihui/xaringan/wiki 16 | * - remarkjs wiki: https://github.com/gnab/remark/wiki 17 | * 18 | * Version: 0.3.0 19 | * 20 | * ------------------------------------------------------- */ 21 | @import url(https://fonts.googleapis.com/css?family=Noto+Sans:400,400i,700,700i&display=swap); 22 | @import url(https://fonts.googleapis.com/css?family=Cabin:600,600i&display=swap); 23 | @import url(https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700&display=swap); 24 | @import url(https://fonts.googleapis.com/css?family=Staatliches&display=swap); 25 | @import url(https://fonts.googleapis.com/css?family=Megrim&display=swap); 26 | @import url(https://fonts.googleapis.com/css?family=Pompiere&display=swap); 27 | 28 | :root { 29 | /* Fonts */ 30 | --text-font-family: 'Noto Sans'; 31 | --text-font-is-google: 1; 32 | --text-font-family-fallback: -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, Ubuntu, roboto, noto, segoe ui, arial; 33 | --text-font-base: sans-serif; 34 | --header-font-family: Cabin; 35 | --header-font-is-google: 1; 36 | --code-font-family: 'Source Code Pro'; 37 | --code-font-is-google: 1; 38 | --base-font-size: 22px; 39 | --text-font-size: 1rem; 40 | --code-font-size: 0.9rem; 41 | --code-inline-font-size: 1em; 42 | --header-h1-font-size: 2.75rem; 43 | --header-h2-font-size: 2.25rem; 44 | --header-h3-font-size: 1.75rem; 45 | 46 | /* Colors */ 47 | --text-color: #000000; 48 | --header-color: #1381B0; 49 | --background-color: #FFFFFF; 50 | --link-color: #1381B0; 51 | --text-bold-color: #1381B0; 52 | --code-highlight-color: rgba(255,255,0,0.5); 53 | --inverse-text-color: #000000; 54 | --inverse-background-color: #FF961C; 55 | --inverse-header-color: #FFFFFF; 56 | --title-slide-background-color: #1381B0; 57 | --title-slide-text-color: #FFFFFF; 58 | --header-background-color: #1381B0; 59 | --header-background-text-color: #FFFFFF; 60 | --primary: #1381B0; 61 | --secondary: #FF961C; 62 | --white: #FFFFFF; 63 | --black: #000000; 64 | } 65 | 66 | html { 67 | font-size: var(--base-font-size); 68 | } 69 | 70 | body { 71 | font-family: var(--text-font-family), var(--text-font-family-fallback), var(--text-font-base); 72 | font-weight: normal; 73 | color: var(--text-color); 74 | } 75 | h1, h2, h3 { 76 | font-family: var(--header-font-family); 77 | font-weight: 600; 78 | color: var(--header-color); 79 | } 80 | .remark-slide-content { 81 | background-color: var(--background-color); 82 | font-size: 1rem; 83 | padding: 16px 64px 16px 64px; 84 | width: 100%; 85 | height: 100%; 86 | } 87 | .remark-slide-content h1 { 88 | font-size: var(--header-h1-font-size); 89 | } 90 | .remark-slide-content h2 { 91 | font-size: var(--header-h2-font-size); 92 | } 93 | .remark-slide-content h3 { 94 | font-size: var(--header-h3-font-size); 95 | } 96 | .remark-code, .remark-inline-code { 97 | font-family: var(--code-font-family), Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; 98 | } 99 | .remark-code { 100 | font-size: var(--code-font-size); 101 | } 102 | .remark-inline-code { 103 | font-size: var(--code-inline-font-size); 104 | color: #1381B0; 105 | } 106 | .remark-slide-number { 107 | color: #1381B0; 108 | opacity: 1; 109 | font-size: 0.9em; 110 | } 111 | strong { color: var(--text-bold-color); } 112 | a, a > code { 113 | color: var(--link-color); 114 | text-decoration: none; 115 | } 116 | .footnote { 117 | position: absolute; 118 | bottom: 60px; 119 | padding-right: 4em; 120 | font-size: 0.9em; 121 | } 122 | .remark-code-line-highlighted { 123 | background-color: var(--code-highlight-color); 124 | } 125 | .inverse { 126 | background-color: var(--inverse-background-color); 127 | color: var(--inverse-text-color); 128 | 129 | } 130 | .inverse h1, .inverse h2, .inverse h3 { 131 | color: var(--inverse-header-color); 132 | } 133 | .title-slide, .title-slide h1, .title-slide h2, .title-slide h3 { 134 | color: var(--title-slide-text-color); 135 | } 136 | .title-slide { 137 | background-color: var(--title-slide-background-color); 138 | } 139 | .title-slide .remark-slide-number { 140 | display: none; 141 | } 142 | /* Two-column layout */ 143 | .left-column { 144 | width: 20%; 145 | height: 92%; 146 | float: left; 147 | } 148 | .left-column h2, .left-column h3 { 149 | color: #1381B099; 150 | } 151 | .left-column h2:last-of-type, .left-column h3:last-child { 152 | color: #1381B0; 153 | } 154 | .right-column { 155 | width: 75%; 156 | float: right; 157 | padding-top: 1em; 158 | } 159 | .pull-left { 160 | float: left; 161 | width: 47%; 162 | } 163 | .pull-right { 164 | float: right; 165 | width: 47%; 166 | } 167 | .pull-right ~ * { 168 | clear: both; 169 | } 170 | img, video, iframe { 171 | max-width: 100%; 172 | } 173 | blockquote { 174 | border-left: solid 5px #FF961C80; 175 | padding-left: 1em; 176 | } 177 | .remark-slide table { 178 | margin: auto; 179 | border-top: 1px solid #666; 180 | border-bottom: 1px solid #666; 181 | } 182 | .remark-slide table thead th { 183 | border-bottom: 1px solid #ddd; 184 | } 185 | th, td { 186 | padding: 5px; 187 | } 188 | .remark-slide thead, .remark-slide tfoot, .remark-slide tr:nth-child(even) { 189 | background: #FFEAD1; 190 | } 191 | table.dataTable tbody { 192 | background-color: var(--background-color); 193 | color: var(--text-color); 194 | } 195 | table.dataTable.display tbody tr.odd { 196 | background-color: var(--background-color); 197 | } 198 | table.dataTable.display tbody tr.even { 199 | background-color: #FFEAD1; 200 | } 201 | table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover { 202 | background-color: rgba(255, 255, 255, 0.5); 203 | } 204 | .dataTables_wrapper .dataTables_length, .dataTables_wrapper .dataTables_filter, .dataTables_wrapper .dataTables_info, .dataTables_wrapper .dataTables_processing, .dataTables_wrapper .dataTables_paginate { 205 | color: var(--text-color); 206 | } 207 | .dataTables_wrapper .dataTables_paginate .paginate_button { 208 | color: var(--text-color) !important; 209 | } 210 | 211 | /* Slide Header Background for h1 elements */ 212 | .remark-slide-content.header_background > h1 { 213 | display: block; 214 | position: absolute; 215 | top: 0; 216 | left: 0; 217 | width: 100%; 218 | background: var(--header-background-color); 219 | color: var(--header-background-text-color); 220 | padding: 2rem 64px 1.5rem 64px; 221 | margin-top: 0; 222 | box-sizing: border-box; 223 | } 224 | .remark-slide-content.header_background { 225 | padding-top: 7rem; 226 | } 227 | 228 | @page { margin: 0; } 229 | @media print { 230 | .remark-slide-scaler { 231 | width: 100% !important; 232 | height: 100% !important; 233 | transform: scale(1) !important; 234 | top: 0 !important; 235 | left: 0 !important; 236 | } 237 | } 238 | 239 | .primary { 240 | color: var(--primary); 241 | } 242 | .bg-primary { 243 | background-color: var(--primary); 244 | } 245 | .secondary { 246 | color: var(--secondary); 247 | } 248 | .bg-secondary { 249 | background-color: var(--secondary); 250 | } 251 | .white { 252 | color: var(--white); 253 | } 254 | .bg-white { 255 | background-color: var(--white); 256 | } 257 | .black { 258 | color: var(--black); 259 | } 260 | .bg-black { 261 | background-color: var(--black); 262 | } 263 | 264 | -------------------------------------------------------------------------------- /Code/assets/xaringanExtra-shareagain/shareagain.css: -------------------------------------------------------------------------------- 1 | /* Animations by animate.css */ 2 | 3 | @-webkit-keyframes slideOutDown { 4 | from { 5 | -webkit-transform: translate3d(0, 0, 0); 6 | transform: translate3d(0, 0, 0); 7 | } 8 | 9 | to { 10 | visibility: hidden; 11 | -webkit-transform: translate3d(0, 100%, 0); 12 | transform: translate3d(0, 100%, 0); 13 | } 14 | } 15 | 16 | @keyframes slideOutDown { 17 | from { 18 | -webkit-transform: translate3d(0, 0, 0); 19 | transform: translate3d(0, 0, 0); 20 | } 21 | 22 | to { 23 | visibility: hidden; 24 | -webkit-transform: translate3d(0, 100%, 0); 25 | transform: translate3d(0, 100%, 0); 26 | } 27 | } 28 | 29 | @-webkit-keyframes slideInUp { 30 | from { 31 | -webkit-transform: translate3d(0, 100%, 0); 32 | transform: translate3d(0, 100%, 0); 33 | visibility: visible; 34 | } 35 | 36 | to { 37 | -webkit-transform: translate3d(0, 0, 0); 38 | transform: translate3d(0, 0, 0); 39 | } 40 | } 41 | 42 | @keyframes slideInUp { 43 | from { 44 | -webkit-transform: translate3d(0, 100%, 0); 45 | transform: translate3d(0, 100%, 0); 46 | visibility: visible; 47 | } 48 | 49 | to { 50 | -webkit-transform: translate3d(0, 0, 0); 51 | transform: translate3d(0, 0, 0); 52 | } 53 | } 54 | 55 | @-webkit-keyframes slideOutRight { 56 | from { 57 | -webkit-transform: translate3d(0, 0, 0); 58 | transform: translate3d(0, 0, 0); 59 | } 60 | 61 | to { 62 | visibility: hidden; 63 | -webkit-transform: translate3d(100%, 0, 0); 64 | transform: translate3d(100%, 0, 0); 65 | } 66 | } 67 | 68 | @keyframes slideOutRight { 69 | from { 70 | -webkit-transform: translate3d(0, 0, 0); 71 | transform: translate3d(0, 0, 0); 72 | } 73 | 74 | to { 75 | visibility: hidden; 76 | -webkit-transform: translate3d(100%, 0, 0); 77 | transform: translate3d(100%, 0, 0); 78 | } 79 | } 80 | 81 | @-webkit-keyframes slideInRight { 82 | from { 83 | -webkit-transform: translate3d(100%, 0, 0); 84 | transform: translate3d(100%, 0, 0); 85 | visibility: visible; 86 | } 87 | 88 | to { 89 | -webkit-transform: translate3d(0, 0, 0); 90 | transform: translate3d(0, 0, 0); 91 | } 92 | } 93 | 94 | @keyframes slideInRight { 95 | from { 96 | -webkit-transform: translate3d(100%, 0, 0); 97 | transform: translate3d(100%, 0, 0); 98 | visibility: visible; 99 | } 100 | 101 | to { 102 | -webkit-transform: translate3d(0, 0, 0); 103 | transform: translate3d(0, 0, 0); 104 | } 105 | } 106 | 107 | .slideOutDown { 108 | -webkit-animation-name: slideOutDown; 109 | animation-name: slideOutDown; 110 | -webkit-animation-duration: 1s; 111 | animation-duration: 1s; 112 | -webkit-animation-fill-mode: both; 113 | animation-fill-mode: both; 114 | } 115 | 116 | .slideInUp { 117 | -webkit-animation-name: slideInUp; 118 | animation-name: slideInUp; 119 | -webkit-animation-duration: 1s; 120 | animation-duration: 1s; 121 | -webkit-animation-fill-mode: both; 122 | animation-fill-mode: both; 123 | } 124 | 125 | .slideInRight { 126 | -webkit-animation-name: slideInRight; 127 | animation-name: slideInRight; 128 | -webkit-animation-duration: 1s; 129 | animation-duration: 1s; 130 | -webkit-animation-fill-mode: both; 131 | animation-fill-mode: both; 132 | } 133 | 134 | .slideOutRight { 135 | -webkit-animation-name: slideOutRight; 136 | animation-name: slideOutRight; 137 | -webkit-animation-duration: 1s; 138 | animation-duration: 1s; 139 | -webkit-animation-fill-mode: both; 140 | animation-fill-mode: both; 141 | } 142 | 143 | /* shareagain css rules */ 144 | .shareagain-bar { 145 | --shareagain-background: #00000088; 146 | --shareagain-foreground: #FFFFFF; 147 | --shareagain-twitter: inline-block; 148 | --shareagain-facebook: inline-block; 149 | --shareagain-linkedin: inline-block; 150 | --shareagain-pinterest: inline-block; 151 | --shareagain-pocket: inline-block; 152 | --shareagain-reddit: inline-block; 153 | } 154 | 155 | .shareagain-bar { 156 | position: absolute; 157 | bottom: 0; 158 | left: 0; 159 | right: 0; 160 | height: 66px; 161 | z-index: 1000; 162 | } 163 | 164 | .shareagain-bar ul { 165 | background: var(--shareagain-background); 166 | color: var(--shareagain-foreground); 167 | display: grid; 168 | grid-template-columns: auto 1fr auto; 169 | grid-gap: 5px; 170 | justify-items: center; 171 | align-items: center; 172 | font-family: sans-serif; 173 | list-style: none; 174 | padding-left: 0; 175 | margin: 0; 176 | height: 100%; 177 | width: 100%; 178 | } 179 | 180 | .shareagain-buttons { 181 | height: 100%; 182 | } 183 | 184 | .shareagain-button { 185 | height: 100%; 186 | width: 66px; 187 | border: none; 188 | padding: 15px; 189 | color: var(--shareagain-foreground); 190 | background: transparent; 191 | } 192 | 193 | .shareagain-button:hover { 194 | background: rgba(0,0,0,.25); 195 | } 196 | 197 | .shareagain-button.disabled { 198 | color: #aaa; 199 | cursor: not-allowed; 200 | } 201 | 202 | .shareagain-button svg { 203 | height: 100%; 204 | width: 100%; 205 | } 206 | 207 | .shareagain-button svg path { 208 | stroke-width: 1px; 209 | } 210 | 211 | .shareagain-title { 212 | font-size: 24px; 213 | text-align: center; 214 | max-height: 100%; 215 | overflow: hidden; 216 | } 217 | 218 | .shareagain-bar .shareon { 219 | font-size: 0 !important; 220 | position: absolute; 221 | top: -45px; 222 | width: 100vw; 223 | right: 0; 224 | text-align: right; 225 | -webkit-transform: translate3d(100%, 0, 0); 226 | transform: translate3d(100%, 0, 0); 227 | } 228 | 229 | .shareagain-bar .shareon > .link { 230 | background-color: #cacaca; 231 | } 232 | .shareagain-bar .shareon > .link::before { 233 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor' class='clipboard-copy w-6 h-6'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3'%3E%3C/path%3E%3C/svg%3E"); 234 | } 235 | 236 | .shareagain-bar .shareon > .link.success::before { 237 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='currentColor' class='clipboard-check w-6 h-6'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4'%3E%3C/path%3E%3C/svg%3E"); 238 | } 239 | 240 | .shareagain-bar .shareon .twitter { display: var(--shareagain-twitter); } 241 | .shareagain-bar .shareon .facebook { display: var(--shareagain-facebook); } 242 | .shareagain-bar .shareon .linkedin { display: var(--shareagain-linkedin); } 243 | .shareagain-bar .shareon .pinterest { display: var(--shareagain-pinterest); } 244 | .shareagain-bar .shareon .pocket { display: var(--shareagain-pocket); } 245 | .shareagain-bar .shareon .reddit { display: var(--shareagain-reddit); } 246 | 247 | @media screen and (max-width: 600px) { 248 | .shareagain-bar { 249 | font-size: 10px; 250 | height: 50px; 251 | } 252 | .shareagain-buttons .shareagain-button { 253 | width: 50px; 254 | } 255 | .shareagain-title { 256 | font-size: 16px; 257 | } 258 | } 259 | @media screen and (max-width: 400px) { 260 | .shareagain-bar { 261 | height: 30px; 262 | } 263 | .shareagain-bar ul { 264 | grid-template-columns: auto auto; 265 | justify-content: space-between; 266 | } 267 | .shareagain-bar .shareagain-title { 268 | display: none; 269 | } 270 | .shareagain-buttons .shareagain-button { 271 | width: 30px; 272 | padding: 5px; 273 | } 274 | } 275 | 276 | @media screen and (max-width: 400px) { 277 | .shareagain-bar { 278 | height: 30px; 279 | } 280 | .shareagain-bar ul { 281 | grid-template-columns: auto auto; 282 | justify-content: space-between; 283 | } 284 | .shareagain-bar .shareagain-title { 285 | display: none; 286 | } 287 | .shareagain-buttons .shareagain-button { 288 | width: 30px; 289 | padding: 5px; 290 | } 291 | .shareagain-buttons .shareon > * { 292 | width: 15px; 293 | height: 20px; 294 | } 295 | .shareagain-buttons .shareon > ::before { 296 | transform: scale(0.66); 297 | top: 7px; 298 | } 299 | .shareagain-buttons .shareon .link { 300 | width: 23px; 301 | } 302 | .shareagain-buttons .shareon > .link::before { 303 | top: 5px; 304 | left: 6px; 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /Code/assets/clipboard/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.6 3 | * https://clipboardjs.com/ 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return o={},r.m=n=[function(t,e){t.exports=function(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var o=this;function r(){o.off(t,r),e.apply(n,arguments)}return r._=e,this.on(t,r,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;o { 17 | document.removeEventListener('DOMContentLoaded', completed) 18 | window.removeEventListener('load', completed) 19 | fn() 20 | } 21 | if (document.readyState !== 'loading') { 22 | setTimeout(fn) 23 | } else { 24 | document.addEventListener('DOMContentLoaded', completed) 25 | window.addEventListener('load', completed) 26 | } 27 | } 28 | 29 | ready(addShareAgainBar) 30 | 31 | function addShareAgainBar () { 32 | const icons = { 33 | left: '', 34 | right: '', 35 | fullScreen: '', 36 | share: '' 37 | } 38 | 39 | function getAuthor () { 40 | let author = document.head.querySelector('meta[name="author"]') 41 | 42 | if (author) { 43 | author = author.content + ' — ' 44 | } else { 45 | author = '' 46 | } 47 | return author 48 | } 49 | 50 | function truncate (str, n, useWordBoundary = true) { 51 | if (str.length <= n) { return str } 52 | const subString = str.substr(0, n - 1) 53 | return (useWordBoundary 54 | ? subString.substr(0, subString.lastIndexOf(' ')) 55 | : subString) + '…' 56 | }; 57 | 58 | function getShortTitle () { 59 | return truncate(window.document.title || '', 50) 60 | } 61 | 62 | const navbar = document.createElement('nav') 63 | navbar.classList.add('shareagain-bar') 64 | 65 | navbar.innerHTML += `` 85 | 86 | const slidesContainer = document.querySelector(':not(html).remark-container') 87 | slidesContainer.appendChild(navbar) 88 | 89 | const btnSlidePrev = document.getElementById('shareagain-slide-prev') 90 | const btnSlideNext = document.getElementById('shareagain-slide-next') 91 | 92 | function toggleSlideButtons (slideIndex) { 93 | if (typeof slideIndex === 'undefined') { 94 | slideIndex = slideshow.getCurrentSlideIndex() 95 | } 96 | 97 | // Toggle next slide button 98 | if (slideIndex + 1 === slideshow.getSlideCount()) { 99 | btnSlideNext.classList.add('disabled') 100 | btnSlideNext.setAttribute('disabled', true) 101 | } else { 102 | btnSlideNext.classList.remove('disabled') 103 | btnSlideNext.removeAttribute('disabled') 104 | } 105 | 106 | // Toggle prev slide button 107 | if (slideIndex === 0) { 108 | btnSlidePrev.classList.add('disabled') 109 | btnSlidePrev.setAttribute('disabled', true) 110 | } else { 111 | btnSlidePrev.classList.remove('disabled') 112 | btnSlidePrev.removeAttribute('disabled') 113 | } 114 | } 115 | 116 | setTimeout(toggleSlideButtons, 100) 117 | 118 | // button click events 119 | btnSlidePrev.addEventListener('click', function (ev) { 120 | slideshow.gotoPreviousSlide() 121 | }) 122 | 123 | btnSlideNext.addEventListener('click', function (ev) { 124 | slideshow.gotoNextSlide() 125 | }) 126 | 127 | // button touch events (block remarkjs slide change on touch) 128 | btnSlidePrev.addEventListener('touchend', function (ev) { 129 | slideshow.gotoPreviousSlide() 130 | ev.stopPropagation() 131 | }) 132 | 133 | btnSlideNext.addEventListener('touchend', function (ev) { 134 | slideshow.gotoNextSlide() 135 | ev.stopPropagation() 136 | }); 137 | 138 | // show/hide share buttons 139 | ['click', 'touchend'].map(function (evType) { 140 | document.getElementById('shareagain-share').addEventListener(evType, function (ev) { 141 | toggleShareButtons() 142 | ev.preventDefault() 143 | ev.stopPropagation() 144 | }) 145 | }) 146 | 147 | navbar.addEventListener('touchend', function (ev) { 148 | ev.preventDefault() 149 | ev.stopPropagation() 150 | }) 151 | 152 | // copy slides link to clipboard 153 | const shareClip = new ClipboardJS('.shareon .link') 154 | shareClip.on('success', function (e) { 155 | const el = document.querySelector('.shareon .link') 156 | el.classList.add('success') 157 | setTimeout(() => el.classList.remove('success'), 2500) 158 | }) 159 | 160 | slideshow.on('afterShowSlide', function (slide) { 161 | toggleSlideButtons(slide.getSlideIndex()) 162 | }) 163 | 164 | function toggleAnimated ({ selector, show, inClass, outClass }) { 165 | const el = document.querySelector(selector) 166 | const isShown = el.classList.contains(inClass) 167 | if (typeof show === 'undefined') { 168 | show = !isShown 169 | } 170 | if (show === isShown) { 171 | return 172 | } 173 | if (show) { 174 | el.classList.remove(outClass) 175 | el.classList.add(inClass) 176 | } else { 177 | el.classList.remove(inClass) 178 | el.classList.add(outClass) 179 | } 180 | } 181 | 182 | let isCurrentlyFullScreen = false 183 | document.addEventListener('fullscreenchange', (event) => { 184 | if (document.fullscreenElement) { 185 | stopAutoHide(false) 186 | isCurrentlyFullScreen = true 187 | } else { 188 | isCurrentlyFullScreen = false 189 | startAutoHide() 190 | } 191 | }) 192 | 193 | function toggleNavBar (show) { 194 | // do nothing if currently fullscreen 195 | if (isCurrentlyFullScreen) return 196 | 197 | toggleAnimated({ selector: '.shareagain-bar ul', show, inClass: 'slideInUp', outClass: 'slideOutDown' }) 198 | if (!show) toggleShareButtons(false) 199 | } 200 | 201 | function toggleShareButtons (show) { 202 | toggleAnimated({ selector: '.shareagain-bar .shareon', show, inClass: 'slideInRight', outClass: 'slideOutRight' }) 203 | } 204 | 205 | // auto hide the share bar when focus is in the slides 206 | let mouseMoveTimer = null 207 | function hideNavDelayed (ev) { 208 | if (mouseMoveTimer) { 209 | clearTimeout(mouseMoveTimer) 210 | } 211 | toggleNavBar(true) 212 | mouseMoveTimer = setTimeout(function () { toggleNavBar(false) }, 2000) 213 | }; 214 | 215 | // toggle toggle full screen 216 | ['click', 'touchend'].map(function (evType) { 217 | document.getElementById('shareagain-fullscreen').addEventListener(evType, function (ev) { 218 | slideshow.toggleFullscreen() 219 | ev.stopPropagation() 220 | }) 221 | }) 222 | 223 | function startAutoHide () { 224 | hideNavDelayed() 225 | slidesContainer.addEventListener('mousemove', hideNavDelayed) 226 | } 227 | 228 | function stopAutoHide (showAfter = false) { 229 | slidesContainer.removeEventListener('mousemove', hideNavDelayed) 230 | clearTimeout(mouseMoveTimer) 231 | toggleNavBar(showAfter) 232 | } 233 | 234 | // auto hide turns on when the mouse comes into the slides area 235 | slidesContainer.addEventListener('mouseenter', function () { 236 | startAutoHide() 237 | }) 238 | 239 | // turn off auto hide when the mouse leaves the slides area 240 | slidesContainer.addEventListener('mouseleave', function () { 241 | stopAutoHide(true) 242 | }); 243 | 244 | // turn off auto hide if the mouse or focus is in the share bar 245 | ['focusin', 'mouseenter'].map(function (evType) { 246 | navbar.addEventListener(evType, function () { 247 | stopAutoHide(true) 248 | }) 249 | }); 250 | 251 | // and turn auto hide back on whne the mouse leaves the share bar 252 | // (if mouse exits out of the slides area, the slidesContainer should fire later) 253 | ['focusout', 'mouseleave'].map(function (evType) { 254 | navbar.addEventListener(evType, function () { 255 | startAutoHide() 256 | }) 257 | }) 258 | } 259 | })() 260 | -------------------------------------------------------------------------------- /Code/assets/shareon/shareon.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * shareon v1.4.1 by Nikita Karamov 3 | * https://shareon.js.org 4 | */ 5 | .shareon{font-size:0!important}.shareon>*{display:inline-block;position:relative;height:24px;min-width:16px;margin:3px;padding:6px 10px;background-color:#ccc;border-radius:3.33333px;border:none;box-sizing:content-box;color:#fff;line-height:1.5;transition:opacity .3s ease;vertical-align:middle}.shareon>:hover{border:none;cursor:pointer;opacity:.7}.shareon>:not(:empty){font-size:16px;text-decoration:none}.shareon>:not(:empty):before{position:relative;height:100%;width:28px;top:0;left:0;background-position:0 50%}.shareon>:before{display:inline-block;position:absolute;height:20px;width:20px;top:8px;left:8px;background-repeat:no-repeat;background-size:20px 20px;content:"";vertical-align:bottom}.shareon>.facebook{background-color:#1877f2}.shareon>.facebook:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M23.998 12c0-6.628-5.372-12-11.999-12C5.372 0 0 5.372 0 12c0 5.988 4.388 10.952 10.124 11.852v-8.384H7.078v-3.469h3.046V9.356c0-3.008 1.792-4.669 4.532-4.669 1.313 0 2.686.234 2.686.234v2.953H15.83c-1.49 0-1.955.925-1.955 1.874V12h3.328l-.532 3.469h-2.796v8.384c5.736-.9 10.124-5.864 10.124-11.853z'/%3E%3C/svg%3E")}.shareon>.linkedin{background-color:#2867b2}.shareon>.linkedin:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M23.722 23.72h-4.91v-7.692c0-1.834-.038-4.194-2.559-4.194-2.56 0-2.95 1.995-2.95 4.06v7.827H8.394V7.902h4.716v2.157h.063c.659-1.244 2.261-2.556 4.655-2.556 4.974 0 5.894 3.274 5.894 7.535zM.388 7.902h4.923v15.819H.388zM2.85 5.738A2.849 2.849 0 010 2.886a2.851 2.851 0 112.85 2.852z'/%3E%3C/svg%3E")}.shareon>.linkedin:not(:empty):before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z'/%3E%3C/svg%3E")}.shareon>.messenger{background-color:#09f}.shareon>.messenger:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 11.64C0 4.95 5.24 0 12 0s12 4.95 12 11.64-5.24 11.64-12 11.64c-1.21 0-2.38-.16-3.47-.46a.96.96 0 00-.64.05L5.5 23.92a.96.96 0 01-1.35-.85l-.07-2.14a.97.97 0 00-.32-.68A11.39 11.39 0 010 11.64zm8.32-2.19l-3.52 5.6c-.35.53.32 1.14.82.75l3.79-2.87c.26-.2.6-.2.87 0l2.8 2.1c.84.63 2.04.4 2.6-.48l3.52-5.6c.35-.53-.32-1.13-.82-.75l-3.79 2.87c-.25.2-.6.2-.86 0l-2.8-2.1a1.8 1.8 0 00-2.61.48z'/%3E%3C/svg%3E")}.shareon>.odnoklassniki{background-color:#ee8208}.shareon>.odnoklassniki:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M14.505 17.44a11.599 11.599 0 003.6-1.49 1.816 1.816 0 00-1.935-3.073 7.866 7.866 0 01-8.34 0 1.814 1.814 0 00-2.5.565c0 .002 0 .004-.002.005a1.812 1.812 0 00.567 2.5l.002.002c1.105.695 2.322 1.2 3.596 1.488l-3.465 3.465A1.796 1.796 0 006 23.439l.03.03c.344.354.81.53 1.274.53.465 0 .93-.176 1.275-.53L12 20.065l3.404 3.406a1.815 1.815 0 002.566-2.565l-3.465-3.466zM12 12.388a6.202 6.202 0 006.195-6.193C18.195 2.78 15.415 0 12 0S5.805 2.78 5.805 6.197A6.2 6.2 0 0012 12.389zm0-8.757a2.566 2.566 0 010 5.13 2.569 2.569 0 01-2.565-2.564A2.57 2.57 0 0112 3.63z'/%3E%3C/svg%3E")}.shareon>.pinterest{background-color:#ee0023}.shareon>.pinterest:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.162-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.099.12.112.225.085.345-.09.375-.293 1.199-.334 1.363-.053.225-.172.271-.401.165-1.495-.69-2.433-2.878-2.433-4.646 0-3.776 2.748-7.252 7.92-7.252 4.158 0 7.392 2.967 7.392 6.923 0 4.135-2.607 7.462-6.233 7.462-1.214 0-2.354-.629-2.758-1.379l-.749 2.848c-.269 1.045-1.004 2.352-1.498 3.146 1.123.345 2.306.535 3.55.535 6.607 0 11.985-5.365 11.985-11.987C23.97 5.39 18.592.026 11.985.026z'/%3E%3C/svg%3E")}.shareon>.pocket{background-color:#ef4154}.shareon>.pocket:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.813 10.259l-5.646 5.419a1.649 1.649 0 01-2.282 0l-5.646-5.419a1.645 1.645 0 012.276-2.376l4.511 4.322 4.517-4.322a1.643 1.643 0 012.326.049 1.64 1.64 0 01-.045 2.326zm5.083-7.546a2.163 2.163 0 00-2.041-1.436H2.179c-.9 0-1.717.564-2.037 1.405-.094.25-.142.511-.142.774v7.245l.084 1.441c.348 3.277 2.047 6.142 4.682 8.139.045.036.094.07.143.105l.03.023a11.899 11.899 0 004.694 2.072c.786.158 1.591.24 2.389.24.739 0 1.481-.067 2.209-.204.088-.029.176-.045.264-.06.023 0 .049-.015.074-.029a12.002 12.002 0 004.508-2.025l.029-.031.135-.105c2.627-1.995 4.324-4.862 4.686-8.148L24 10.678V3.445c0-.251-.031-.5-.121-.742z'/%3E%3C/svg%3E")}.shareon>.reddit{background-color:#ff4500}.shareon>.reddit:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M19.53 1.148a1.83 1.83 0 00-1.667 1.054l-4.372-.928a.522.522 0 00-.358.063.515.515 0 00-.21.297L11.59 7.925c-2.807.086-5.32.909-7.137 2.237a2.668 2.668 0 00-1.815-.737A2.64 2.64 0 000 12.064a2.634 2.634 0 001.563 2.407 4.95 4.95 0 00-.065.803c0 4.053 4.71 7.326 10.537 7.326s10.537-3.273 10.537-7.326a4.548 4.548 0 00-.063-.782 2.732 2.732 0 001.519-2.428 2.64 2.64 0 00-2.639-2.64 2.53 2.53 0 00-1.816.74c-1.796-1.288-4.287-2.134-7.031-2.239l1.204-5.637 3.906.823a1.888 1.888 0 001.878 1.777c1.024 0 1.87-.837 1.88-1.861a1.884 1.884 0 00-1.88-1.88zM7.907 18.066c-.13 0-.254.05-.347.141a.498.498 0 000 .697c1.266 1.267 3.736 1.373 4.454 1.373s3.167-.084 4.454-1.373a.546.546 0 00.044-.697.5.5 0 00-.698 0c-.823.802-2.533 1.099-3.779 1.099s-2.977-.295-3.779-1.099a.49.49 0 00-.349-.142zm-1.932-4.122c0-1.035.844-1.88 1.88-1.88 1.034 0 1.878.843 1.878 1.879S8.89 15.82 7.856 15.82a1.882 1.882 0 01-1.88-1.877zm10.155-1.88c1.035 0 1.88.845 1.88 1.879 0 1.035-.844 1.878-1.879 1.878s-1.879-.843-1.879-1.877c0-1.037.844-1.88 1.878-1.88z' fill-rule='evenodd'/%3E%3C/svg%3E")}.shareon>.telegram{background-color:#179cde}.shareon>.telegram:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.517 11.31c-.962.382-1.466.756-1.512 1.122-.088.702.924.921 2.196 1.335 1.037.337 2.433.731 3.158.747.658.014 1.393-.257 2.204-.814 5.533-3.735 8.39-5.622 8.57-5.663.126-.029.301-.065.42.04.119.106.107.306.095.36-.101.429-5.3 5.156-5.599 5.467-1.143 1.187-2.443 1.913-.437 3.235 1.735 1.144 2.746 1.873 4.534 3.045 1.142.75 2.039 1.637 3.218 1.529.543-.05 1.104-.56 1.389-2.083.673-3.598 1.996-11.392 2.302-14.604a3.585 3.585 0 00-.034-.8c-.027-.158-.084-.383-.29-.55-.243-.197-.619-.24-.787-.236-.764.013-1.936.42-7.579 2.767C11.39 7.03 7.44 8.73 1.517 11.31z'/%3E%3C/svg%3E")}.shareon>.twitter{background-color:#1da1f2}.shareon>.twitter:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M23.954 4.569a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.691 8.094 4.066 6.13 1.64 3.161a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.061a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.937 4.937 0 004.604 3.417 9.868 9.868 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.054 0 13.999-7.496 13.999-13.986 0-.209 0-.42-.015-.63a9.936 9.936 0 002.46-2.548l-.047-.02z'/%3E%3C/svg%3E")}.shareon>.viber{background-color:#7360f2}.shareon>.viber:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M11.398.002C9.473.028 5.331.344 3.014 2.467 1.294 4.177.693 6.698.623 9.82c-.06 3.11-.13 8.95 5.5 10.541v2.42s-.038.97.602 1.17c.79.25 1.24-.499 1.99-1.299l1.4-1.58c3.85.32 6.8-.419 7.14-.529.78-.25 5.181-.811 5.901-6.652.74-6.031-.36-9.831-2.34-11.551l-.01-.002c-.6-.55-3-2.3-8.37-2.32 0 0-.396-.025-1.038-.016zm.067 1.697c.545-.003.88.02.88.02 4.54.01 6.711 1.38 7.221 1.84 1.67 1.429 2.528 4.856 1.9 9.892-.6 4.88-4.17 5.19-4.83 5.4-.28.09-2.88.73-6.152.52 0 0-2.439 2.941-3.199 3.701-.12.13-.26.17-.35.15-.13-.03-.17-.19-.16-.41l.02-4.019c-4.771-1.32-4.491-6.302-4.441-8.902.06-2.6.55-4.732 2-6.172 1.957-1.77 5.475-2.01 7.11-2.02zm.36 2.6a.299.299 0 00-.3.299.3.3 0 00.3.3 5.631 5.631 0 014.03 1.59c1.09 1.06 1.621 2.48 1.641 4.34a.3.3 0 00.3.3v-.009a.3.3 0 00.3-.3 6.451 6.451 0 00-1.81-4.76c-1.19-1.16-2.692-1.76-4.462-1.76zm-3.954.69a.955.955 0 00-.615.12h-.012c-.41.24-.788.54-1.148.94-.27.32-.421.639-.461.949a1.24 1.24 0 00.05.541l.02.01a13.722 13.722 0 001.2 2.6 15.383 15.383 0 002.32 3.171l.03.04.04.03.03.03.03.03a15.603 15.603 0 003.18 2.33c1.32.72 2.122 1.06 2.602 1.2v.01c.14.04.268.06.398.06a1.84 1.84 0 001.102-.472c.39-.35.7-.738.93-1.148v-.01c.23-.43.15-.841-.18-1.121a13.632 13.632 0 00-2.15-1.54c-.51-.28-1.03-.11-1.24.17l-.45.569c-.23.28-.65.24-.65.24l-.012.01c-3.12-.8-3.95-3.959-3.95-3.959s-.04-.43.25-.65l.56-.45c.27-.22.46-.74.17-1.25a13.522 13.522 0 00-1.54-2.15.843.843 0 00-.504-.3zm4.473.89a.3.3 0 00.002.6 3.78 3.78 0 012.65 1.15 3.5 3.5 0 01.9 2.57.3.3 0 00.3.299l.01.012a.3.3 0 00.3-.301c.03-1.19-.34-2.19-1.07-2.99-.73-.8-1.75-1.25-3.05-1.34a.3.3 0 00-.042 0zm.49 1.619a.305.305 0 00-.018.611c.99.05 1.47.55 1.53 1.58a.3.3 0 00.3.29h.01a.3.3 0 00.29-.32c-.07-1.34-.8-2.091-2.1-2.161a.305.305 0 00-.012 0z'/%3E%3C/svg%3E")}.shareon>.vkontakte{background-color:#4680c2}.shareon>.vkontakte:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M23.058 19.504h-2.616c-.99 0-1.297-.787-3.076-2.59-1.55-1.501-2.236-1.704-2.617-1.704-.534 0-.687.153-.687.89v2.363c0 .636-.202 1.017-1.88 1.017-2.77 0-5.845-1.677-8.004-4.804C.925 10.103.034 6.672.034 5.961c0-.381.153-.737.89-.737H3.54c.66 0 .915.305 1.17 1.016 1.295 3.736 3.456 7.014 4.345 7.014.33 0 .483-.153.483-.99V8.399c-.102-1.78-1.042-1.931-1.042-2.566 0-.306.255-.61.66-.61h4.117c.56 0 .762.304.762.964v5.211c0 .558.255.762.407.762.33 0 .61-.204 1.22-.813 1.88-2.11 3.227-5.362 3.227-5.362.178-.381.483-.737 1.145-.737h2.616c.788 0 .966.405.788.965-.33 1.526-3.532 6.048-3.532 6.048-.28.457-.381.66 0 1.17.28.381 1.194 1.169 1.805 1.88 1.118 1.27 1.98 2.338 2.21 3.076.255.735-.128 1.116-.864 1.116z'/%3E%3C/svg%3E")}.shareon>.whatsapp{background-color:#25d366}.shareon>.whatsapp:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg fill='%23fff' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51a12.8 12.8 0 00-.57-.01c-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z'/%3E%3C/svg%3E")} -------------------------------------------------------------------------------- /Code/libs/panelset/panelset.js: -------------------------------------------------------------------------------- 1 | /* global slideshow */ 2 | (function () { 3 | const ready = function (fn) { 4 | /* MIT License Copyright (c) 2016 Nuclei */ 5 | /* https://github.com/nuclei/readyjs */ 6 | const completed = () => { 7 | document.removeEventListener('DOMContentLoaded', completed) 8 | window.removeEventListener('load', completed) 9 | fn() 10 | } 11 | if (document.readyState !== 'loading') { 12 | setTimeout(fn) 13 | } else { 14 | document.addEventListener('DOMContentLoaded', completed) 15 | window.addEventListener('load', completed) 16 | } 17 | } 18 | 19 | ready(function () { 20 | [...document.querySelectorAll('.panel-name')] 21 | .map(el => el.textContent.trim()) 22 | 23 | const panelIds = {} 24 | 25 | const uniquePanelId = (name) => { 26 | name = encodeURIComponent(name.toLowerCase().replace(/[\s]/g, '-')) 27 | if (Object.keys(panelIds).includes(name)) { 28 | name += ++panelIds[name] 29 | } else { 30 | panelIds[name] = 1 31 | } 32 | return name 33 | } 34 | 35 | const identifyPanelName = (item) => { 36 | let name = 'Panel' 37 | 38 | // If the item doesn't have a parent element, then we've already processed 39 | // it, probably because we're in an Rmd, and it's been removed from the DOM 40 | if (!item.parentElement) { 41 | return 42 | } 43 | 44 | // In R Markdown when header-attrs.js is present, we may have found a 45 | // section header but the class attributes won't be duplicated on the tag 46 | if ( 47 | (item.tagName === 'SECTION' || item.classList.contains('section')) && 48 | /^H[1-6]/.test(item.children[0].tagName) 49 | ) { 50 | name = item.children[0].textContent 51 | item.classList.remove('panel-name') 52 | item.removeChild(item.children[0]) 53 | return name 54 | } 55 | 56 | const nameDiv = item.querySelector('.panel-name') 57 | if (!nameDiv) return name 58 | 59 | // In remarkjs the .panel-name span might be in a paragraph tag 60 | // and if the

is empty, we'll remove it 61 | if ( 62 | nameDiv.tagName === 'SPAN' && 63 | nameDiv.parentNode.tagName === 'P' && 64 | nameDiv.textContent === nameDiv.parentNode.textContent 65 | ) { 66 | name = nameDiv.textContent 67 | item.removeChild(nameDiv.parentNode) 68 | return name 69 | } 70 | 71 | // If none of the above, remove the nameDiv and return the name 72 | name = nameDiv.textContent 73 | nameDiv.parentNode.removeChild(nameDiv) 74 | return name 75 | } 76 | 77 | const processPanelItem = (item) => { 78 | const name = identifyPanelName(item) 79 | if (!name) { 80 | return null 81 | } 82 | return { name, content: item.children, id: uniquePanelId(name) } 83 | } 84 | 85 | const getCurrentPanelFromUrl = (panelset) => { 86 | const params = new URLSearchParams(window.location.search) 87 | return params.get(panelset) 88 | } 89 | 90 | const reflowPanelSet = (panels, idx) => { 91 | const res = document.createElement('div') 92 | res.className = 'panelset' 93 | res.id = 'panelset' + (idx > 0 ? idx : '') 94 | const panelSelected = getCurrentPanelFromUrl(res.id) 95 | 96 | // create header row 97 | const headerRow = document.createElement('ul') 98 | headerRow.className = 'panel-tabs' 99 | headerRow.setAttribute('role', 'tablist') 100 | panels 101 | .map((p, idx) => { 102 | const panelHeaderItem = document.createElement('li') 103 | panelHeaderItem.className = 'panel-tab' 104 | panelHeaderItem.setAttribute('role', 'tab') 105 | const thisPanelIsActive = panelSelected ? panelSelected === p.id : idx === 0 106 | if (thisPanelIsActive) { 107 | panelHeaderItem.classList.add('panel-tab-active') 108 | panelHeaderItem.setAttribute('aria-selected', true) 109 | } 110 | panelHeaderItem.tabIndex = 0 111 | panelHeaderItem.id = res.id + '_' + p.id // #panelsetid_panelid 112 | 113 | const panelHeaderLink = document.createElement('a') 114 | panelHeaderLink.href = '?' + res.id + '=' + p.id + '#' + panelHeaderItem.id 115 | panelHeaderLink.setAttribute('onclick', 'return false;') 116 | panelHeaderLink.tabIndex = -1 // list item is tabable, not link 117 | panelHeaderLink.innerHTML = p.name 118 | panelHeaderLink.setAttribute('aria-controls', p.id) 119 | 120 | panelHeaderItem.appendChild(panelHeaderLink) 121 | return panelHeaderItem 122 | }) 123 | .forEach(el => headerRow.appendChild(el)) 124 | 125 | res.appendChild(headerRow) 126 | 127 | panels 128 | .map((p, idx) => { 129 | const panelContent = document.createElement('section') 130 | panelContent.className = 'panel' 131 | panelContent.setAttribute('role', 'tabpanel') 132 | const thisPanelIsActive = panelSelected ? panelSelected === p.id : idx === 0 133 | panelContent.classList.toggle('panel-active', thisPanelIsActive) 134 | panelContent.id = p.id 135 | panelContent.setAttribute('aria-labelledby', p.id) 136 | Array.from(p.content).forEach(el => panelContent.appendChild(el)) 137 | return panelContent 138 | }) 139 | .forEach(el => res.appendChild(el)) 140 | 141 | return res 142 | } 143 | 144 | /* 145 | * Update selected panel for panelset or delete panelset from query string 146 | * 147 | * @param panelset Panelset ID to update in the search params 148 | * @param panel Panel ID of selected panel in panelset, or null to delete from search params 149 | * @param params Current params object, or params from window.location.search 150 | */ 151 | function updateSearchParams (panelset, panel, params = new URLSearchParams(window.location.search)) { 152 | if (panel) { 153 | params.set(panelset, panel) 154 | } else { 155 | params.delete(panelset) 156 | } 157 | return params 158 | } 159 | 160 | /* 161 | * Update the URL to match params 162 | */ 163 | const updateUrl = (params) => { 164 | if (typeof params === 'undefined') return 165 | params = params.toString() ? ('?' + params.toString()) : '' 166 | const { pathname, hash } = window.location 167 | const uri = pathname + params + hash 168 | window.history.replaceState(uri, '', uri) 169 | } 170 | 171 | const togglePanel = (clicked) => { 172 | if (clicked.nodeName.toUpperCase() === 'A') { 173 | clicked = clicked.parentElement 174 | } 175 | if (!clicked.classList.contains('panel-tab')) return 176 | if (clicked.classList.contains('panel-tab-active')) return 177 | 178 | const tabs = clicked.parentNode 179 | .querySelectorAll('.panel-tab') 180 | const panels = clicked.parentNode.parentNode 181 | .querySelectorAll('.panel') 182 | const panelTabClicked = clicked.children[0].getAttribute('aria-controls') 183 | const panelClicked = clicked.parentNode.parentNode.id 184 | 185 | Array.from(tabs) 186 | .forEach(t => { 187 | t.classList.remove('panel-tab-active') 188 | t.removeAttribute('aria-selected') 189 | }) 190 | Array.from(panels) 191 | .forEach(p => { 192 | const active = p.id === panelTabClicked 193 | p.classList.toggle('panel-active', active) 194 | // make inactive panels inaccessible by keyboard navigation 195 | if (active) { 196 | p.removeAttribute('tabIndex') 197 | p.removeAttribute('aria-hidden') 198 | } else { 199 | p.setAttribute('tabIndex', -1) 200 | p.setAttribute('aria-hidden', true) 201 | } 202 | }) 203 | 204 | clicked.classList.add('panel-tab-active') 205 | clicked.setAttribute('aria-selected', true) 206 | 207 | // update query string 208 | const params = updateSearchParams(panelClicked, panelTabClicked) 209 | updateUrl(params) 210 | } 211 | 212 | const initPanelSet = (panelset, idx) => { 213 | let panels = Array.from(panelset.querySelectorAll('.panel')) 214 | if (!panels.length && panelset.matches('.section[class*="level"]')) { 215 | // we're in tabset-alike R Markdown 216 | const panelsetLevel = [...panelset.classList] 217 | .filter(s => s.match(/^level/))[0] 218 | .replace('level', '') 219 | 220 | // move children that aren't inside a section up above the panelset 221 | Array.from(panelset.children).forEach(function (el) { 222 | if (el.matches('div.section[class*="level"]')) return 223 | panelset.parentElement.insertBefore(el, panelset) 224 | }) 225 | 226 | // panels are all .sections with .level 227 | const panelLevel = +panelsetLevel + 1 228 | panels = Array.from(panelset.querySelectorAll(`.section.level${panelLevel}`)) 229 | } 230 | 231 | if (!panels.length) return 232 | 233 | const contents = panels.map(processPanelItem).filter(o => o !== null) 234 | const newPanelSet = reflowPanelSet(contents, idx) 235 | panelset.parentNode.insertBefore(newPanelSet, panelset) 236 | panelset.parentNode.removeChild(panelset) 237 | 238 | // click and touch events 239 | const panelTabs = newPanelSet.querySelector('.panel-tabs'); 240 | ['click', 'touchend'].forEach(eventType => { 241 | panelTabs.addEventListener(eventType, function (ev) { 242 | togglePanel(ev.target) 243 | ev.stopPropagation() 244 | }) 245 | }) 246 | panelTabs.addEventListener('touchmove', function (ev) { 247 | ev.preventDefault() 248 | }) 249 | 250 | // key events 251 | newPanelSet 252 | .querySelector('.panel-tabs') 253 | .addEventListener('keydown', (ev) => { 254 | const self = ev.currentTarget.querySelector('.panel-tab-active') 255 | if (ev.code === 'Space' || ev.code === 'Enter') { 256 | togglePanel(ev.target) 257 | ev.stopPropagation() 258 | } else if (ev.code === 'ArrowLeft' && self.previousSibling) { 259 | togglePanel(self.previousSibling) 260 | self.previousSibling.focus() 261 | ev.stopPropagation() 262 | } else if (ev.code === 'ArrowRight' && self.nextSibling) { 263 | togglePanel(self.nextSibling) 264 | self.nextSibling.focus() 265 | ev.stopPropagation() 266 | } 267 | }) 268 | 269 | return panels 270 | } 271 | 272 | // initialize panels 273 | Array.from(document.querySelectorAll('.panelset')).map(initPanelSet) 274 | 275 | if (typeof slideshow !== 'undefined') { 276 | const getVisibleActivePanelInfo = () => { 277 | const slidePanels = document.querySelectorAll('.remark-visible .panel-tab-active') 278 | 279 | if (!slidePanels.length) return null 280 | 281 | return slidePanels.map(panel => { 282 | return { 283 | panel, 284 | panelId: panel.children[0].getAttribute('aria-controls'), 285 | panelSetId: panel.parentNode.parentNode.id 286 | } 287 | }) 288 | } 289 | 290 | slideshow.on('hideSlide', slide => { 291 | // clear focus if we had a panel-tab selected 292 | document.activeElement.blur() 293 | 294 | // clear search query for panelsets in current slide 295 | const params = [...document.querySelectorAll('.remark-visible .panelset')] 296 | .reduce(function (params, panelset) { 297 | return updateSearchParams(panelset.id, null, params) 298 | }, new URLSearchParams(window.location.search)) 299 | 300 | updateUrl(params) 301 | }) 302 | 303 | slideshow.on('afterShowSlide', slide => { 304 | const slidePanels = getVisibleActivePanelInfo() 305 | 306 | if (slidePanels) { 307 | // only first panel gets focus 308 | slidePanels[0].panel.focus() 309 | // but still update the url to reflect all active panels 310 | const params = slidePanels.reduce( 311 | function (params, { panelId, panelSetId }) { 312 | return updateSearchParams(panelSetId, panelId, params) 313 | }, 314 | new URLSearchParams(window.location.search) 315 | ) 316 | updateUrl(params) 317 | } 318 | }) 319 | } 320 | }) 321 | })() 322 | -------------------------------------------------------------------------------- /Code/assets/panelset/panelset.js: -------------------------------------------------------------------------------- 1 | /* global slideshow */ 2 | (function () { 3 | const ready = function (fn) { 4 | /* MIT License Copyright (c) 2016 Nuclei */ 5 | /* https://github.com/nuclei/readyjs */ 6 | const completed = () => { 7 | document.removeEventListener('DOMContentLoaded', completed) 8 | window.removeEventListener('load', completed) 9 | fn() 10 | } 11 | if (document.readyState !== 'loading') { 12 | setTimeout(fn) 13 | } else { 14 | document.addEventListener('DOMContentLoaded', completed) 15 | window.addEventListener('load', completed) 16 | } 17 | } 18 | 19 | ready(function () { 20 | [...document.querySelectorAll('.panel-name')] 21 | .map(el => el.textContent.trim()) 22 | 23 | const panelIds = {} 24 | 25 | const uniquePanelId = (name) => { 26 | name = encodeURIComponent(name.toLowerCase().replace(/[\s]/g, '-')) 27 | if (Object.keys(panelIds).includes(name)) { 28 | name += ++panelIds[name] 29 | } else { 30 | panelIds[name] = 1 31 | } 32 | return name 33 | } 34 | 35 | const identifyPanelName = (item) => { 36 | let name = 'Panel' 37 | 38 | // If the item doesn't have a parent element, then we've already processed 39 | // it, probably because we're in an Rmd, and it's been removed from the DOM 40 | if (!item.parentElement) { 41 | return 42 | } 43 | 44 | // In R Markdown when header-attrs.js is present, we may have found a 45 | // section header but the class attributes won't be duplicated on the tag 46 | if ( 47 | (item.tagName === 'SECTION' || item.classList.contains('section')) && 48 | /^H[1-6]/.test(item.children[0].tagName) 49 | ) { 50 | name = item.children[0].textContent 51 | item.classList.remove('panel-name') 52 | item.removeChild(item.children[0]) 53 | return name 54 | } 55 | 56 | const nameDiv = item.querySelector('.panel-name') 57 | if (!nameDiv) return name 58 | 59 | // In remarkjs the .panel-name span might be in a paragraph tag 60 | // and if the

is empty, we'll remove it 61 | if ( 62 | nameDiv.tagName === 'SPAN' && 63 | nameDiv.parentNode.tagName === 'P' && 64 | nameDiv.textContent === nameDiv.parentNode.textContent 65 | ) { 66 | name = nameDiv.textContent 67 | item.removeChild(nameDiv.parentNode) 68 | return name 69 | } 70 | 71 | // If none of the above, remove the nameDiv and return the name 72 | name = nameDiv.textContent 73 | nameDiv.parentNode.removeChild(nameDiv) 74 | return name 75 | } 76 | 77 | const processPanelItem = (item) => { 78 | const name = identifyPanelName(item) 79 | if (!name) { 80 | return null 81 | } 82 | return { name, content: item.children, id: uniquePanelId(name) } 83 | } 84 | 85 | const getCurrentPanelFromUrl = (panelset) => { 86 | const params = new URLSearchParams(window.location.search) 87 | return params.get(panelset) 88 | } 89 | 90 | const reflowPanelSet = (panels, idx) => { 91 | const res = document.createElement('div') 92 | res.className = 'panelset' 93 | res.id = 'panelset' + (idx > 0 ? idx : '') 94 | const panelSelected = getCurrentPanelFromUrl(res.id) 95 | 96 | // create header row 97 | const headerRow = document.createElement('ul') 98 | headerRow.className = 'panel-tabs' 99 | headerRow.setAttribute('role', 'tablist') 100 | panels 101 | .map((p, idx) => { 102 | const panelHeaderItem = document.createElement('li') 103 | panelHeaderItem.className = 'panel-tab' 104 | panelHeaderItem.setAttribute('role', 'tab') 105 | const thisPanelIsActive = panelSelected ? panelSelected === p.id : idx === 0 106 | if (thisPanelIsActive) { 107 | panelHeaderItem.classList.add('panel-tab-active') 108 | panelHeaderItem.setAttribute('aria-selected', true) 109 | } 110 | panelHeaderItem.tabIndex = 0 111 | panelHeaderItem.id = res.id + '_' + p.id // #panelsetid_panelid 112 | 113 | const panelHeaderLink = document.createElement('a') 114 | panelHeaderLink.href = '?' + res.id + '=' + p.id + '#' + panelHeaderItem.id 115 | panelHeaderLink.setAttribute('onclick', 'return false;') 116 | panelHeaderLink.tabIndex = -1 // list item is tabable, not link 117 | panelHeaderLink.innerHTML = p.name 118 | panelHeaderLink.setAttribute('aria-controls', p.id) 119 | 120 | panelHeaderItem.appendChild(panelHeaderLink) 121 | return panelHeaderItem 122 | }) 123 | .forEach(el => headerRow.appendChild(el)) 124 | 125 | res.appendChild(headerRow) 126 | 127 | panels 128 | .map((p, idx) => { 129 | const panelContent = document.createElement('section') 130 | panelContent.className = 'panel' 131 | panelContent.setAttribute('role', 'tabpanel') 132 | const thisPanelIsActive = panelSelected ? panelSelected === p.id : idx === 0 133 | panelContent.classList.toggle('panel-active', thisPanelIsActive) 134 | panelContent.id = p.id 135 | panelContent.setAttribute('aria-labelledby', p.id) 136 | Array.from(p.content).forEach(el => panelContent.appendChild(el)) 137 | return panelContent 138 | }) 139 | .forEach(el => res.appendChild(el)) 140 | 141 | return res 142 | } 143 | 144 | /* 145 | * Update selected panel for panelset or delete panelset from query string 146 | * 147 | * @param panelset Panelset ID to update in the search params 148 | * @param panel Panel ID of selected panel in panelset, or null to delete from search params 149 | * @param params Current params object, or params from window.location.search 150 | */ 151 | function updateSearchParams (panelset, panel, params = new URLSearchParams(window.location.search)) { 152 | if (panel) { 153 | params.set(panelset, panel) 154 | } else { 155 | params.delete(panelset) 156 | } 157 | return params 158 | } 159 | 160 | /* 161 | * Update the URL to match params 162 | */ 163 | const updateUrl = (params) => { 164 | if (typeof params === 'undefined') return 165 | params = params.toString() ? ('?' + params.toString()) : '' 166 | const { pathname, hash } = window.location 167 | const uri = pathname + params + hash 168 | window.history.replaceState(uri, '', uri) 169 | } 170 | 171 | const togglePanel = (clicked) => { 172 | if (clicked.nodeName.toUpperCase() === 'A') { 173 | clicked = clicked.parentElement 174 | } 175 | if (!clicked.classList.contains('panel-tab')) return 176 | if (clicked.classList.contains('panel-tab-active')) return 177 | 178 | const tabs = clicked.parentNode 179 | .querySelectorAll('.panel-tab') 180 | const panels = clicked.parentNode.parentNode 181 | .querySelectorAll('.panel') 182 | const panelTabClicked = clicked.children[0].getAttribute('aria-controls') 183 | const panelClicked = clicked.parentNode.parentNode.id 184 | 185 | Array.from(tabs) 186 | .forEach(t => { 187 | t.classList.remove('panel-tab-active') 188 | t.removeAttribute('aria-selected') 189 | }) 190 | Array.from(panels) 191 | .forEach(p => { 192 | const active = p.id === panelTabClicked 193 | p.classList.toggle('panel-active', active) 194 | // make inactive panels inaccessible by keyboard navigation 195 | if (active) { 196 | p.removeAttribute('tabIndex') 197 | p.removeAttribute('aria-hidden') 198 | } else { 199 | p.setAttribute('tabIndex', -1) 200 | p.setAttribute('aria-hidden', true) 201 | } 202 | }) 203 | 204 | clicked.classList.add('panel-tab-active') 205 | clicked.setAttribute('aria-selected', true) 206 | 207 | // update query string 208 | const params = updateSearchParams(panelClicked, panelTabClicked) 209 | updateUrl(params) 210 | } 211 | 212 | const initPanelSet = (panelset, idx) => { 213 | let panels = Array.from(panelset.querySelectorAll('.panel')) 214 | if (!panels.length && panelset.matches('.section[class*="level"]')) { 215 | // we're in tabset-alike R Markdown 216 | const panelsetLevel = [...panelset.classList] 217 | .filter(s => s.match(/^level/))[0] 218 | .replace('level', '') 219 | 220 | // move children that aren't inside a section up above the panelset 221 | Array.from(panelset.children).forEach(function (el) { 222 | if (el.matches('div.section[class*="level"]')) return 223 | panelset.parentElement.insertBefore(el, panelset) 224 | }) 225 | 226 | // panels are all .sections with .level 227 | const panelLevel = +panelsetLevel + 1 228 | panels = Array.from(panelset.querySelectorAll(`.section.level${panelLevel}`)) 229 | } 230 | 231 | if (!panels.length) return 232 | 233 | const contents = panels.map(processPanelItem).filter(o => o !== null) 234 | const newPanelSet = reflowPanelSet(contents, idx) 235 | panelset.parentNode.insertBefore(newPanelSet, panelset) 236 | panelset.parentNode.removeChild(panelset) 237 | 238 | // click and touch events 239 | const panelTabs = newPanelSet.querySelector('.panel-tabs'); 240 | ['click', 'touchend'].forEach(eventType => { 241 | panelTabs.addEventListener(eventType, function (ev) { 242 | togglePanel(ev.target) 243 | ev.stopPropagation() 244 | }) 245 | }) 246 | panelTabs.addEventListener('touchmove', function (ev) { 247 | ev.preventDefault() 248 | }) 249 | 250 | // key events 251 | newPanelSet 252 | .querySelector('.panel-tabs') 253 | .addEventListener('keydown', (ev) => { 254 | const self = ev.currentTarget.querySelector('.panel-tab-active') 255 | if (ev.code === 'Space' || ev.code === 'Enter') { 256 | togglePanel(ev.target) 257 | ev.stopPropagation() 258 | } else if (ev.code === 'ArrowLeft' && self.previousSibling) { 259 | togglePanel(self.previousSibling) 260 | self.previousSibling.focus() 261 | ev.stopPropagation() 262 | } else if (ev.code === 'ArrowRight' && self.nextSibling) { 263 | togglePanel(self.nextSibling) 264 | self.nextSibling.focus() 265 | ev.stopPropagation() 266 | } 267 | }) 268 | 269 | return panels 270 | } 271 | 272 | // initialize panels 273 | Array.from(document.querySelectorAll('.panelset')).map(initPanelSet) 274 | 275 | if (typeof slideshow !== 'undefined') { 276 | const getVisibleActivePanelInfo = () => { 277 | const slidePanels = document.querySelectorAll('.remark-visible .panel-tab-active') 278 | 279 | if (!slidePanels.length) return null 280 | 281 | return slidePanels.map(panel => { 282 | return { 283 | panel, 284 | panelId: panel.children[0].getAttribute('aria-controls'), 285 | panelSetId: panel.parentNode.parentNode.id 286 | } 287 | }) 288 | } 289 | 290 | slideshow.on('hideSlide', slide => { 291 | // clear focus if we had a panel-tab selected 292 | document.activeElement.blur() 293 | 294 | // clear search query for panelsets in current slide 295 | const params = [...document.querySelectorAll('.remark-visible .panelset')] 296 | .reduce(function (params, panelset) { 297 | return updateSearchParams(panelset.id, null, params) 298 | }, new URLSearchParams(window.location.search)) 299 | 300 | updateUrl(params) 301 | }) 302 | 303 | slideshow.on('afterShowSlide', slide => { 304 | const slidePanels = getVisibleActivePanelInfo() 305 | 306 | if (slidePanels) { 307 | // only first panel gets focus 308 | slidePanels[0].panel.focus() 309 | // but still update the url to reflect all active panels 310 | const params = slidePanels.reduce( 311 | function (params, { panelId, panelSetId }) { 312 | return updateSearchParams(panelSetId, panelId, params) 313 | }, 314 | new URLSearchParams(window.location.search) 315 | ) 316 | updateUrl(params) 317 | } 318 | }) 319 | } 320 | }) 321 | })() 322 | -------------------------------------------------------------------------------- /Code/index.rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Gaussian Mixture Models (GMM)" 3 | subtitle: "Fitting GMMs using the Expectation-Maximization (E-M) Algorithm in R" 4 | author: 5 | - "Pavitra Chakravarty" 6 | date: '2020-09-13' 7 | output: 8 | xaringan::moon_reader: 9 | lib_dir: assets 10 | chakra: assets/remark-0.14.0.min.js 11 | css: [xaringan-themer.css, xaringan-extra.css] 12 | seal: false 13 | nature: 14 | highlightStyle: atom-one-light 15 | highlightLines: true 16 | ratio: "16:9" 17 | countIncrementalSlides: true 18 | slideNumberFormat: "%current%" 19 | 20 | bibliography: refs.bib 21 | biblio-style: apalike 22 | link-citations: yes 23 | --- 24 | 25 | class: title-slide middle 26 | 27 | # `r rmarkdown::metadata$title` 28 | 29 | ## `r rmarkdown::metadata$subtitle` 30 | 31 | .author[ 32 | `r rmarkdown::metadata$author` 33 | ] 34 | 35 | .twitter[ 36 | @genomixgmailcom 37 | ] 38 | 39 | 40 | 41 | ```{r setup, include=FALSE} 42 | options(htmltools.dir.version = FALSE) 43 | ``` 44 | 45 | ```{r global.options, include = FALSE} 46 | knitr::opts_chunk$set( 47 | fig.width = 15, # the width for plots created by code chunk 48 | fig.height = 8, # the height for plots created by code chunk 49 | fig.align = 'center', # how to align graphics in the final doc. 'left', 'right', 'center' 50 | fig.path = 'figs/', # file path to the directory where knitr shall store the graphics files 51 | results = 'asis', # knitr will pass through results without reformatting them 52 | echo = TRUE, # in FALSE knitr will not display code in the code chunk above it's results 53 | message = TRUE, # if FALSE knitr will not display any messages generated by code 54 | strip.white = TRUE, # if FALSE knitr will not remove white spaces at the beg or end of code chunk 55 | warning = FALSE) # if FALSE knitr will not display any warning messages in the final document 56 | ``` 57 | 58 | ```{r xaringan-themer, include=FALSE, warning=FALSE} 59 | library(xaringanthemer) 60 | library(tidyverse) 61 | library(ggplot2) 62 | library(palmerpenguins) 63 | library(mixtools) 64 | 65 | style_duo_accent( 66 | base_font_size = "22px", 67 | primary_color = "#1381B0", 68 | secondary_color = "#FF961C", 69 | inverse_header_color = "#FFFFFF", 70 | title_slide_text_color = "#FFFFFF", 71 | code_inline_font_size = "1em", 72 | extra_fonts = list( 73 | google_font("Staatliches"), 74 | google_font("Megrim"), 75 | google_font("Pompiere") 76 | ) 77 | ) 78 | 79 | ``` 80 | 81 | ```{r setup extra, include=FALSE} 82 | knitr::opts_chunk$set(echo = TRUE, 83 | warning = FALSE, 84 | message = FALSE, 85 | comment = "") 86 | xaringanExtra::use_tile_view() 87 | xaringanExtra::use_panelset() 88 | xaringanExtra::use_webcam() 89 | xaringanExtra::use_editable() 90 | xaringanExtra::use_extra_styles( 91 | hover_code_line = TRUE, 92 | mute_unhighlighted_code = TRUE 93 | ) 94 | ``` 95 | --- 96 | 97 | 98 | class: middle 99 | 100 | # What are Gaussian Mixture Models (GMM)? 101 | 102 | 103 | * A mixture model is a mixture of k component distributions that collectively make a mixture distribution $f(x)$: 104 | 105 | $$f(x) = \sum_{k=1}^{K}\alpha_{k}f_{k}(x)$$ 106 | * $\alpha_{k}$: mixing weight for the $k^{th}$ component where $\sum_{k=1}^{K}\alpha_{k} = 1$ 107 | 108 | * $f_k(x)$: any sort of distribution (preferably, parametric). If you choose gaussian, you get GMM 109 | 110 | * $\theta_{k}$: $$f(x) = \sum_{k=1}^{K}\alpha_{k}f_{k}(x;\theta_{k})$$ 111 | 112 | --- 113 | 114 | class: middle 115 | 116 | # Features of GMMs 117 | 118 | $$ \mathcal{N}(\mu, \sigma^2) $$ 119 | 120 | The normal distribution is parameterized by two variables: 121 | 122 | * $\mu$: Mean; Center of the mass 123 | 124 | * $\sigma^2$: Variance; Spread of the mass 125 | 126 | * Entire data can be represented as a k-mixture GMM 127 | 128 | 129 | --- 130 | 131 | # Meet the Palmer penguins 132 | 133 | ```{r, echo = FALSE, fig.align='center'} 134 | knitr::include_graphics("figs/lter_penguins.png", dpi = 300) 135 | ``` 136 | 137 | # 138 | 139 | --- 140 | 141 | ```{r, eval = TRUE, echo=FALSE, fig.align='center'} 142 | knitr::include_graphics("figs/all_peng-1.png", dpi = 100) 143 | ``` 144 | 145 | # 146 | 147 | 148 | --- 149 | 150 | class: middle 151 | 152 | # "Complete Data" Scenario 153 | 154 | --- 155 | 156 | class: bigger 157 | 158 | .panelset[ 159 | .panel[.panel-name[Penguins] 160 | 161 | ```{r all_peng, eval=TRUE , fig.show='hide'} 162 | flipper_bill <- ggplot(data = penguins, 163 | aes(x = flipper_length_mm, 164 | y = bill_length_mm)) + 165 | geom_point(aes(color = species, 166 | shape = species), 167 | size = 3, 168 | alpha = 0.8) + 169 | theme_minimal() + 170 | scale_color_manual(values = c("darkorange","purple","cyan4")) + 171 | labs(title = "Flipper and bill length", 172 | subtitle = "Dimensions for Adelie, Chinstrap and Gentoo Penguins at Palmer Station LTER", 173 | x = "Flipper length (mm)", 174 | y = "Bill length (mm)", 175 | color = "Penguin species", 176 | shape = "Penguin species") + 177 | theme(legend.position = c(0.85, 0.15), 178 | legend.background = element_rect(fill = "white", color = NA), 179 | plot.title.position = "plot", 180 | plot.caption = element_text(hjust = 0, face= "italic"), 181 | plot.caption.position = "plot") 182 | 183 | flipper_bill 184 | 185 | ``` 186 | ] 187 | 188 | 189 | .panel[.panel-name[Plot] 190 | ```{r, eval = TRUE, echo=FALSE} 191 | knitr::include_graphics("figs/README-flipper-hist-1.png", dpi = 225) 192 | ``` 193 | ] 194 | ] 195 | --- 196 | 197 | class: middle 198 | 199 | # "Incomplete Data" Scenario 200 | 201 | --- 202 | 203 | class: bigger 204 | 205 | .panelset[ 206 | .panel[.panel-name[Density of Flipper Length] 207 | 208 | ```{r peng_flipper_len_density, eval=TRUE , fig.show='hide'} 209 | flipper_len_density <- ggplot(data = penguins, 210 | aes(x = flipper_length_mm)) + 211 | geom_density() + 212 | theme_minimal() + 213 | 214 | labs(title = "Flipper length density", 215 | subtitle = "Dimensions for Adelie, Chinstrap and Gentoo Penguins at Palmer Station LTER", 216 | x = "Flipper length (mm)", 217 | y = "Density", 218 | color = "Penguin species", 219 | shape = "Penguin species") + 220 | theme(legend.position = c(0.85, 0.15), 221 | legend.background = element_rect(fill = "white", color = NA), 222 | plot.title.position = "plot", 223 | plot.caption = element_text(hjust = 0, face= "italic"), 224 | plot.caption.position = "plot") 225 | 226 | flipper_len_density <- flipper_len_density + 227 | geom_vline(xintercept = 192, col = "red", size = 2) + 228 | geom_vline(xintercept = 215, col = "blue", size = 2) 229 | 230 | flipper_len_density 231 | ``` 232 | ] 233 | 234 | 235 | .panel[.panel-name[Plot] 236 | ![](`r knitr::fig_chunk("peng_flipper_len_density", "png") `) 237 | ] 238 | ] 239 | 240 | --- 241 | 242 | class: middle 243 | 244 | # Black-box approach to get GMM 245 | 246 | --- 247 | 248 | class: bigger 249 | 250 | .panelset[ 251 | .panel[.panel-name[MixEM_Blackbox_code] 252 | 253 | ```{r code, eval=TRUE , fig.show='hide'} 254 | plot_mix_comps <- function(x, mu, sigma, lam) { 255 | lam * dnorm(x, mu, sigma) 256 | } 257 | 258 | set.seed(1) 259 | wait <- penguins$flipper_length_mm 260 | wait <- na.omit(penguins$flipper_length_mm) 261 | mixmdl <- normalmixEM(wait, k = 2) 262 | 263 | data.frame(x = mixmdl$x) %>% 264 | ggplot() + 265 | geom_histogram(aes(x, ..density..), binwidth = 1, colour = "black", 266 | fill = "white") + 267 | stat_function(geom = "line", fun = plot_mix_comps, 268 | args = list(mixmdl$mu[1], mixmdl$sigma[1], lam = mixmdl$lambda[1]), 269 | colour = "red", lwd = 1.5) + 270 | stat_function(geom = "line", fun = plot_mix_comps, 271 | args = list(mixmdl$mu[2], mixmdl$sigma[2], lam = mixmdl$lambda[2]), 272 | colour = "blue", lwd = 1.5) + 273 | ylab("Density") 274 | 275 | ``` 276 | ] 277 | 278 | .panel[.panel-name[Output 1] 279 | 280 | ```{r MixEM_Blackbox_1, eval=TRUE , fig.show='hide'} 281 | 282 | mixmdl$lambda 283 | 284 | mixmdl$mu 285 | 286 | mixmdl$sigma 287 | 288 | ``` 289 | ] 290 | 291 | .panel[.panel-name[Output 2] 292 | 293 | ```{r MixEM_Blackbox_2, eval=TRUE , fig.show='hide'} 294 | 295 | mixmdl$loglik 296 | 297 | head(mixmdl$posterior, n = 25L) 298 | ``` 299 | ] 300 | 301 | .panel[.panel-name[Plot] 302 | ![](`r knitr::fig_chunk("code", "png") `) 303 | ] 304 | ] 305 | 306 | --- 307 | 308 | class: middle 309 | 310 | # Refresher 311 | 312 | --- 313 | 314 | 315 | # K-means algorithm 316 | 317 | Goal: represent dataset as K clusters, each summarized by point estimate:$$\mu$$ 318 | 319 | Initialize prototypes and iterate between: 320 | 321 | * E-step: assign data point to nearest prototype 322 | 323 | * M-step: update prototypes to be cluster means 324 | 325 | * Minimize Cost function 326 | 327 | ```{r, eval = TRUE, echo=FALSE} 328 | knitr::include_graphics("figs/Kmeans_loss.png", dpi = 150) 329 | ``` 330 | 331 | --- 332 | 333 | # Gaussian Probabilistic Clustering 334 | 335 | Goal: represent probability distribution of data as ***Mixture Model*** 336 | 337 | * Mixtures of Gaussians 338 | 339 | * Capture uncertainty in cluster assignments 340 | 341 | * Maximize Likelihood 342 | 343 | $$P(X|\mu,\sigma,\alpha) = \sum_{k=1}^{K}\alpha_k\mathcal{N}(X|\mu_{k},\sigma_{k}^{2})$$ 344 | $$P(X|\mu,\sigma,\alpha) = \alpha_1\mathcal{N}(X|\mu_{1},\sigma_{1}^{2}) + \alpha_2\mathcal{N}(X|\mu_{2},\sigma_{2}^{2})$$ 345 | 346 | --- 347 | 348 | class: middle 349 | 350 | 351 | The expectation maximization algorithm is a natural generalization of maximum likelihood estimation to the incomplete data case 352 | 353 | Chuong B Do & Serafim Batzoglou. 354 | 355 | *What is the expectation maximization algorithm? Nature Biotechnology. 2008* 356 | 357 | --- 358 | 359 | .pull-left[ 360 | 361 | ### Maximum Likelihood Estimation 362 | 363 | $\mu_{k} = \frac{\sum_{i}^{N_{k}}x_{i,k}}{N_{k}}$ 364 | 365 | $\sigma_{k} = \frac{\sum_{i}^{N_{k}}(x_{i,k} - \mu_{k})^2}{N_{k}}$ 366 | 367 | $\alpha_{k} = \frac{N_{k}}{N}$ 368 | 369 | ] 370 | 371 | .pull-right[ 372 | 373 | ### Expectation Maximization 374 | 375 | GMM equation: 376 | 377 | $$P(X|\mu,\sigma,\alpha) = \sum_{k=1}^{K}\alpha_k\mathcal{N}(X|\mu_{k},\sigma_{k}^{2})$$ 378 | 379 | Bayes' Rule: 380 | 381 | $$P(x_{i} \in k_{j} | x_{i}) = \frac{P(x_{i} | x_{i} \in k_{j})P(k_{j})}{P(x_{i})}$$ 382 | What we are interested in is $P(x_{i} \in k_{j} | x_{i})$ "posterior probability" 383 | 384 | ***Expectation***: calculate "posterior probability" 385 | 386 | ***Maximization***: re-estimate parameters using variant of MLE 387 | 388 | ] 389 | 390 | --- 391 | 392 | ```{r, eval = TRUE, echo=FALSE} 393 | knitr::include_graphics("figs/NBT_EM.png", dpi = 115) 394 | ``` 395 | 396 | --- 397 | 398 | class: middle 399 | 400 | # How do you test if MLE/EM performed well? 401 | 402 | ***USING LOSS FUNCTION*** 403 | 404 | --- 405 | 406 | .pull-left[ 407 | 408 | ### MLE 409 | 410 | Minimize Loss 411 | 412 | ```{r, eval = TRUE, echo=FALSE} 413 | knitr::include_graphics("figs/Kmeans_loss.png", dpi = 150) 414 | ``` 415 | 416 | ] 417 | 418 | .pull-right[ 419 | 420 | ### E-M 421 | 422 | Maximize Log Likelihood 423 | 424 | 425 | ```{r, eval = TRUE, echo=FALSE} 426 | knitr::include_graphics("figs/EM_loss.png", dpi = 50) 427 | ``` 428 | 429 | ] 430 | 431 | --- 432 | 433 | class: middle 434 | 435 | # What is going on inside the E-M? 436 | 437 | ## Imagine when our data is 'unlabeled' or 'incomplete' 438 | 439 | --- 440 | 441 | class: bigger 442 | 443 | .panelset[ 444 | .panel[.panel-name[EM_Initial] 445 | 446 | ```{r EM_init, eval=TRUE , fig.show='hide'} 447 | wait <- na.omit(penguins$flipper_length_mm) 448 | 449 | wait.kmeans <- kmeans(wait, 2) 450 | wait.kmeans.cluster <- wait.kmeans$cluster 451 | 452 | wait.df <- data_frame(x = wait, cluster = wait.kmeans.cluster) 453 | 454 | wait1.df <- wait.df %>% 455 | mutate(num = row_number()) %>% 456 | ggplot(aes(y = num, x = x, color = factor(cluster))) + 457 | geom_point() + 458 | ylab("Values") + 459 | ylab("Data Point Number") + 460 | scale_color_discrete(name = "Cluster") + 461 | ggtitle("K-means Clustering") 462 | 463 | wait1.df 464 | ``` 465 | ] 466 | 467 | .panel[.panel-name[Plot] 468 | ![](`r knitr::fig_chunk("EM_init", "png") `) 469 | ] 470 | ] 471 | 472 | --- 473 | 474 | class: middle 475 | 476 | # E-M: "Initialization" step 477 | 478 | --- 479 | 480 | class: bigger 481 | 482 | .panelset[ 483 | .panel[.panel-name[code] 484 | 485 | ```{r code_EM_init, eval=TRUE , fig.show='hide'} 486 | wait.summary.df <- wait.df %>% 487 | group_by(cluster) %>% 488 | summarize(mu = mean(x), variance = var(x), std = sd(x), size = n(), .groups = 'drop') 489 | wait.summary.df <- wait.summary.df %>% 490 | mutate(alpha = size / sum(size)) 491 | 492 | ``` 493 | ] 494 | 495 | .panel[.panel-name[Output] 496 | 497 | ```{r code_EM_init_output, eval=TRUE , fig.show='hide'} 498 | 499 | wait.summary.df$cluster 500 | 501 | wait.summary.df$size 502 | 503 | wait.summary.df$alpha 504 | ``` 505 | ] 506 | ] 507 | 508 | --- 509 | 510 | class: middle 511 | 512 | # E-M: "Expectation" step 513 | 514 | --- 515 | 516 | class: bigger 517 | 518 | .panelset[ 519 | .panel[.panel-name[code] 520 | 521 | ```{r code_EM_Expect, eval=TRUE , fig.show='hide'} 522 | e_step <- function(x, mu.vector, sd.vector, alpha.vector) { 523 | comp1.prod <- dnorm(x, mu.vector[1], sd.vector[1]) * alpha.vector[1] 524 | comp2.prod <- dnorm(x, mu.vector[2], sd.vector[2]) * alpha.vector[2] 525 | sum.of.comps <- comp1.prod + comp2.prod 526 | comp1.post <- comp1.prod / sum.of.comps 527 | comp2.post <- comp2.prod / sum.of.comps 528 | 529 | sum.of.comps.ln <- log(sum.of.comps, base = exp(1)) 530 | sum.of.comps.ln.sum <- sum(sum.of.comps.ln) 531 | 532 | list("loglik" = sum.of.comps.ln.sum, 533 | "posterior.df" = cbind(comp1.post, comp2.post)) 534 | } 535 | ``` 536 | ] 537 | 538 | .panel[.panel-name[Output] 539 | 540 | ```{r code_EM_Expect_output, eval=TRUE, echo = FALSE} 541 | knitr::include_graphics("figs/posterior_df.png", dpi = 75) 542 | ``` 543 | ] 544 | ] 545 | 546 | --- 547 | 548 | class: middle 549 | 550 | # E-M: "Maximization" step 551 | 552 | --- 553 | 554 | class: bigger 555 | 556 | .panelset[ 557 | .panel[.panel-name[code] 558 | 559 | ```{r code_EM_Maximization, eval=TRUE , fig.show='hide'} 560 | m_step <- function(x, posterior.df) { 561 | comp1.n <- sum(posterior.df[, 1]) 562 | comp2.n <- sum(posterior.df[, 2]) 563 | 564 | comp1.mu <- 1/comp1.n * sum(posterior.df[, 1] * x) 565 | comp2.mu <- 1/comp2.n * sum(posterior.df[, 2] * x) 566 | 567 | comp1.var <- sum(posterior.df[, 1] * (x - comp1.mu)^2) * 1/comp1.n 568 | comp2.var <- sum(posterior.df[, 2] * (x - comp2.mu)^2) * 1/comp2.n 569 | 570 | comp1.alpha <- comp1.n / length(x) 571 | comp2.alpha <- comp2.n / length(x) 572 | 573 | list("mu" = c(comp1.mu, comp2.mu), 574 | "var" = c(comp1.var, comp2.var), 575 | "alpha" = c(comp1.alpha, comp2.alpha)) 576 | } 577 | 578 | ``` 579 | 580 | ] 581 | 582 | .panel[.panel-name[EM-Loop] 583 | 584 | ```{r code_EM_Loop, eval=TRUE , fig.show='hide'} 585 | 586 | for (i in 1:50) { 587 | if (i == 1) { 588 | # Initialization 589 | e.step <- e_step(wait, wait.summary.df[["mu"]], wait.summary.df[["std"]], 590 | wait.summary.df[["alpha"]]) 591 | m.step <- m_step(wait, e.step[["posterior.df"]]) 592 | cur.loglik <- e.step[["loglik"]] 593 | loglik.vector <- e.step[["loglik"]] 594 | } else { 595 | # Repeat E and M steps till convergence 596 | e.step <- e_step(wait, m.step[["mu"]], sqrt(m.step[["var"]]), 597 | m.step[["alpha"]]) 598 | m.step <- m_step(wait, e.step[["posterior.df"]]) 599 | loglik.vector <- c(loglik.vector, e.step[["loglik"]]) 600 | 601 | loglik.diff <- abs((cur.loglik - e.step[["loglik"]])) 602 | if(loglik.diff < 1e-6) { 603 | break 604 | } else { 605 | cur.loglik <- e.step[["loglik"]] 606 | } 607 | } 608 | } 609 | 610 | loglik.vector 611 | 612 | 613 | ``` 614 | ] 615 | 616 | .panel[.panel-name[Output] 617 | 618 | ```{r code_EM_Maximization_output, eval=TRUE, echo = FALSE} 619 | knitr::include_graphics("figs/loglik.png", dpi = 75) 620 | ``` 621 | ] 622 | ] 623 | 624 | --- 625 | 626 | class: middle 627 | 628 | # Final GMM from running E-M 629 | 630 | --- 631 | 632 | class: bigger 633 | 634 | .panelset[ 635 | .panel[.panel-name[code] 636 | 637 | ```{r code_EM_GMM, eval=TRUE , fig.show='hide'} 638 | plot_mix_comps <- function(x, mu, sigma, lam) { 639 | lam * dnorm(x, mu, sigma) 640 | } 641 | 642 | final_gmm_plot <- data.frame(x = wait) %>% 643 | ggplot() + 644 | geom_histogram(aes(x, ..density..), binwidth = 1, colour = "black", 645 | fill = "white") + 646 | stat_function(geom = "line", fun = plot_mix_comps, 647 | args = list(m.step$mu[1], sqrt(m.step$var[1]), 648 | lam = m.step$alpha[1]), 649 | colour = "red", lwd = 1.5) + 650 | stat_function(geom = "line", fun = plot_mix_comps, 651 | args = list(m.step$mu[2], sqrt(m.step$var[2]), 652 | lam = m.step$alpha[2]), 653 | colour = "blue", lwd = 1.5) + 654 | ylab("Density") + 655 | xlab("Values") + 656 | ggtitle("Final GMM Fit") 657 | 658 | final_gmm_plot 659 | ``` 660 | 661 | ] 662 | 663 | .panel[.panel-name[Plot] 664 | ![](`r knitr::fig_chunk("code_EM_GMM", "png") `) 665 | ] 666 | ] 667 | 668 | --- 669 | 670 | class: middle 671 | 672 | # Acknowledgments 673 | 674 | ### I would like to specially acknowledge the work of [***Dr. Fong Chun Chan***](https://tinyheero.github.io/about/). He had already developed all the code that was used in these slides. 675 | ### I only adapted his code to work with the [***penguins***](https://education.rstudio.com/blog/2020/07/palmerpenguins-cran/) dataset 676 | 677 | --- 678 | 679 | # References 680 | 681 | ```{r, load_refs, echo=FALSE} 682 | library(RefManageR) 683 | bib <- ReadBib("refs.bib", check = FALSE) 684 | ui <- "- " 685 | ``` 686 | 687 | ```{r, print_refs, results='asis', echo=FALSE, warning=FALSE, message=FALSE} 688 | writeLines(ui) 689 | print(bib[key = "FongEMAlgo"], 690 | .opts = list(check.entries = FALSE, 691 | style = "html", 692 | bib.style = "authoryear")) 693 | writeLines(ui) 694 | print(bib[key = "FongKMeans"], 695 | .opts = list(check.entries = FALSE, 696 | style = "html", 697 | bib.style = "authoryear")) 698 | writeLines(ui) 699 | print(bib[key = "GarrickSlidesExtra"], 700 | .opts = list(check.entries = FALSE, 701 | style = "html", 702 | bib.style = "authoryear")) 703 | writeLines(ui) 704 | print(bib[key = "AllisonPP"], 705 | .opts = list(check.entries = FALSE, 706 | style = "html", 707 | bib.style = "authoryear")) 708 | writeLines(ui) 709 | print(bib[key = "RolandKrasser"], 710 | .opts = list(check.entries = FALSE, 711 | style = "html", 712 | bib.style = "authoryear")) 713 | writeLines(ui) 714 | print(bib[key = "ChuongSerfim"], 715 | .opts = list(check.entries = FALSE, 716 | style = "html", 717 | bib.style = "authoryear")) 718 | 719 | ``` 720 | 721 | --- 722 | 723 | layout: false 724 | class: middle left 725 | 726 | 727 | 728 |

🌟 github.com/genomix

729 | 730 | -- 731 | 732 |

🗣 @genomix

733 | 734 | -- 735 | 736 |

👨🏼‍💻 genomix.netlify.app

737 | -------------------------------------------------------------------------------- /Code/libs/himalaya/himalaya.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"names":[],"mappings":"","sources":["himalaya.js"],"sourcesContent":["(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"function\"&&define.amd){define([],f)}else{var g;if(typeof window!==\"undefined\"){g=window}else if(typeof global!==\"undefined\"){g=global}else if(typeof self!==\"undefined\"){g=self}else{g=this}g.himalaya = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o= 0 ? lookupIndex : len + lookupIndex;\n while (searchIndex < len) {\n var element = array[searchIndex++];\n if (element === searchElement) return true;\n if (isNaNElement && isRealNaN(element)) return true;\n }\n\n return false;\n}\n\n},{}],2:[function(require,module,exports){\n'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.splitHead = splitHead;\nexports.unquote = unquote;\nexports.format = format;\nexports.formatAttributes = formatAttributes;\nfunction splitHead(str, sep) {\n var idx = str.indexOf(sep);\n if (idx === -1) return [str];\n return [str.slice(0, idx), str.slice(idx + sep.length)];\n}\n\nfunction unquote(str) {\n var car = str.charAt(0);\n var end = str.length - 1;\n var isQuoteStart = car === '\"' || car === \"'\";\n if (isQuoteStart && car === str.charAt(end)) {\n return str.slice(1, end);\n }\n return str;\n}\n\nfunction format(nodes, options) {\n return nodes.map(function (node) {\n var type = node.type;\n var outputNode = type === 'element' ? {\n type: type,\n tagName: node.tagName.toLowerCase(),\n attributes: formatAttributes(node.attributes),\n children: format(node.children, options)\n } : { type: type, content: node.content };\n if (options.includePositions) {\n outputNode.position = node.position;\n }\n return outputNode;\n });\n}\n\nfunction formatAttributes(attributes) {\n return attributes.map(function (attribute) {\n var parts = splitHead(attribute.trim(), '=');\n var key = parts[0];\n var value = typeof parts[1] === 'string' ? unquote(parts[1]) : null;\n return { key: key, value: value };\n });\n}\n\n},{}],3:[function(require,module,exports){\n'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.parseDefaults = undefined;\nexports.parse = parse;\nexports.stringify = stringify;\n\nvar _lexer = require('./lexer');\n\nvar _lexer2 = _interopRequireDefault(_lexer);\n\nvar _parser = require('./parser');\n\nvar _parser2 = _interopRequireDefault(_parser);\n\nvar _format = require('./format');\n\nvar _stringify = require('./stringify');\n\nvar _tags = require('./tags');\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar parseDefaults = exports.parseDefaults = {\n voidTags: _tags.voidTags,\n closingTags: _tags.closingTags,\n childlessTags: _tags.childlessTags,\n closingTagAncestorBreakers: _tags.closingTagAncestorBreakers,\n includePositions: false\n};\n\nfunction parse(str) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : parseDefaults;\n\n var tokens = (0, _lexer2.default)(str, options);\n var nodes = (0, _parser2.default)(tokens, options);\n return (0, _format.format)(nodes, options);\n}\n\nfunction stringify(ast) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : parseDefaults;\n\n return (0, _stringify.toHTML)(ast, options);\n}\n\n},{\"./format\":2,\"./lexer\":4,\"./parser\":5,\"./stringify\":6,\"./tags\":7}],4:[function(require,module,exports){\n'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.feedPosition = feedPosition;\nexports.jumpPosition = jumpPosition;\nexports.makeInitialPosition = makeInitialPosition;\nexports.copyPosition = copyPosition;\nexports.default = lexer;\nexports.lex = lex;\nexports.findTextEnd = findTextEnd;\nexports.lexText = lexText;\nexports.lexComment = lexComment;\nexports.lexTag = lexTag;\nexports.isWhitespaceChar = isWhitespaceChar;\nexports.lexTagName = lexTagName;\nexports.lexTagAttributes = lexTagAttributes;\nexports.lexSkipTag = lexSkipTag;\n\nvar _compat = require('./compat');\n\nfunction feedPosition(position, str, len) {\n var start = position.index;\n var end = position.index = start + len;\n for (var i = start; i < end; i++) {\n var char = str.charAt(i);\n if (char === '\\n') {\n position.line++;\n position.column = 0;\n } else {\n position.column++;\n }\n }\n}\n\nfunction jumpPosition(position, str, end) {\n var len = end - position.index;\n return feedPosition(position, str, len);\n}\n\nfunction makeInitialPosition() {\n return {\n index: 0,\n column: 0,\n line: 0\n };\n}\n\nfunction copyPosition(position) {\n return {\n index: position.index,\n line: position.line,\n column: position.column\n };\n}\n\nfunction lexer(str, options) {\n var state = {\n str: str,\n options: options,\n position: makeInitialPosition(),\n tokens: []\n };\n lex(state);\n return state.tokens;\n}\n\nfunction lex(state) {\n var str = state.str,\n childlessTags = state.options.childlessTags;\n\n var len = str.length;\n while (state.position.index < len) {\n var start = state.position.index;\n lexText(state);\n if (state.position.index === start) {\n var isComment = (0, _compat.startsWith)(str, '!--', start + 1);\n if (isComment) {\n lexComment(state);\n } else {\n var tagName = lexTag(state);\n var safeTag = tagName.toLowerCase();\n if ((0, _compat.arrayIncludes)(childlessTags, safeTag)) {\n lexSkipTag(tagName, state);\n }\n }\n }\n }\n}\n\nvar alphanumeric = /[A-Za-z0-9]/;\nfunction findTextEnd(str, index) {\n while (true) {\n var textEnd = str.indexOf('<', index);\n if (textEnd === -1) {\n return textEnd;\n }\n var char = str.charAt(textEnd + 1);\n if (char === '/' || char === '!' || alphanumeric.test(char)) {\n return textEnd;\n }\n index = textEnd + 1;\n }\n}\n\nfunction lexText(state) {\n var type = 'text';\n var str = state.str,\n position = state.position;\n\n var textEnd = findTextEnd(str, position.index);\n if (textEnd === position.index) return;\n if (textEnd === -1) {\n textEnd = str.length;\n }\n\n var start = copyPosition(position);\n var content = str.slice(position.index, textEnd);\n jumpPosition(position, str, textEnd);\n var end = copyPosition(position);\n state.tokens.push({ type: type, content: content, position: { start: start, end: end } });\n}\n\nfunction lexComment(state) {\n var str = state.str,\n position = state.position;\n\n var start = copyPosition(position);\n feedPosition(position, str, 4); // \"', position.index);\n var commentEnd = contentEnd + 3; // \"-->\".length\n if (contentEnd === -1) {\n contentEnd = commentEnd = str.length;\n }\n\n var content = str.slice(position.index, contentEnd);\n jumpPosition(position, str, commentEnd);\n state.tokens.push({\n type: 'comment',\n content: content,\n position: {\n start: start,\n end: copyPosition(position)\n }\n });\n}\n\nfunction lexTag(state) {\n var str = state.str,\n position = state.position;\n\n {\n var secondChar = str.charAt(position.index + 1);\n var close = secondChar === '/';\n var start = copyPosition(position);\n feedPosition(position, str, close ? 2 : 1);\n state.tokens.push({ type: 'tag-start', close: close, position: { start: start } });\n }\n var tagName = lexTagName(state);\n lexTagAttributes(state);\n {\n var firstChar = str.charAt(position.index);\n var _close = firstChar === '/';\n feedPosition(position, str, _close ? 2 : 1);\n var end = copyPosition(position);\n state.tokens.push({ type: 'tag-end', close: _close, position: { end: end } });\n }\n return tagName;\n}\n\n// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#special-white-space\nvar whitespace = /\\s/;\nfunction isWhitespaceChar(char) {\n return whitespace.test(char);\n}\n\nfunction lexTagName(state) {\n var str = state.str,\n position = state.position;\n\n var len = str.length;\n var start = position.index;\n while (start < len) {\n var char = str.charAt(start);\n var isTagChar = !(isWhitespaceChar(char) || char === '/' || char === '>');\n if (isTagChar) break;\n start++;\n }\n\n var end = start + 1;\n while (end < len) {\n var _char = str.charAt(end);\n var _isTagChar = !(isWhitespaceChar(_char) || _char === '/' || _char === '>');\n if (!_isTagChar) break;\n end++;\n }\n\n jumpPosition(position, str, end);\n var tagName = str.slice(start, end);\n state.tokens.push({\n type: 'tag',\n content: tagName\n });\n return tagName;\n}\n\nfunction lexTagAttributes(state) {\n var str = state.str,\n position = state.position,\n tokens = state.tokens;\n\n var cursor = position.index;\n var quote = null; // null, single-, or double-quote\n var wordBegin = cursor; // index of word start\n var words = []; // \"key\", \"key=value\", \"key='value'\", etc\n var len = str.length;\n while (cursor < len) {\n var char = str.charAt(cursor);\n if (quote) {\n var isQuoteEnd = char === quote;\n if (isQuoteEnd) {\n quote = null;\n }\n cursor++;\n continue;\n }\n\n var isTagEnd = char === '/' || char === '>';\n if (isTagEnd) {\n if (cursor !== wordBegin) {\n words.push(str.slice(wordBegin, cursor));\n }\n break;\n }\n\n var isWordEnd = isWhitespaceChar(char);\n if (isWordEnd) {\n if (cursor !== wordBegin) {\n words.push(str.slice(wordBegin, cursor));\n }\n wordBegin = cursor + 1;\n cursor++;\n continue;\n }\n\n var isQuoteStart = char === '\\'' || char === '\"';\n if (isQuoteStart) {\n quote = char;\n cursor++;\n continue;\n }\n\n cursor++;\n }\n jumpPosition(position, str, cursor);\n\n var wLen = words.length;\n var type = 'attribute';\n for (var i = 0; i < wLen; i++) {\n var word = words[i];\n var isNotPair = word.indexOf('=') === -1;\n if (isNotPair) {\n var secondWord = words[i + 1];\n if (secondWord && (0, _compat.startsWith)(secondWord, '=')) {\n if (secondWord.length > 1) {\n var newWord = word + secondWord;\n tokens.push({ type: type, content: newWord });\n i += 1;\n continue;\n }\n var thirdWord = words[i + 2];\n i += 1;\n if (thirdWord) {\n var _newWord = word + '=' + thirdWord;\n tokens.push({ type: type, content: _newWord });\n i += 1;\n continue;\n }\n }\n }\n if ((0, _compat.endsWith)(word, '=')) {\n var _secondWord = words[i + 1];\n if (_secondWord && !(0, _compat.stringIncludes)(_secondWord, '=')) {\n var _newWord3 = word + _secondWord;\n tokens.push({ type: type, content: _newWord3 });\n i += 1;\n continue;\n }\n\n var _newWord2 = word.slice(0, -1);\n tokens.push({ type: type, content: _newWord2 });\n continue;\n }\n\n tokens.push({ type: type, content: word });\n }\n}\n\nvar push = [].push;\n\nfunction lexSkipTag(tagName, state) {\n var str = state.str,\n position = state.position,\n tokens = state.tokens;\n\n var safeTagName = tagName.toLowerCase();\n var len = str.length;\n var index = position.index;\n while (index < len) {\n var nextTag = str.indexOf('= 0) {\n var parentTagName = stack[currentIndex].tagName;\n if (parentTagName === tagName) {\n break;\n }\n if ((0, _compat.arrayIncludes)(tagParents, parentTagName)) {\n return true;\n }\n currentIndex--;\n }\n }\n return false;\n}\n\nfunction rewindStack(stack, newLength, childrenEndPosition, endPosition) {\n stack[newLength].position.end = endPosition;\n for (var i = newLength + 1, len = stack.length; i < len; i++) {\n stack[i].position.end = childrenEndPosition;\n }\n stack.splice(newLength);\n}\n\nfunction parse(state) {\n var tokens = state.tokens,\n options = state.options;\n var stack = state.stack;\n\n var nodes = stack[stack.length - 1].children;\n var len = tokens.length;\n var cursor = state.cursor;\n\n while (cursor < len) {\n var token = tokens[cursor];\n if (token.type !== 'tag-start') {\n nodes.push(token);\n cursor++;\n continue;\n }\n\n var tagToken = tokens[++cursor];\n cursor++;\n var tagName = tagToken.content.toLowerCase();\n if (token.close) {\n var index = stack.length;\n var shouldRewind = false;\n while (--index > -1) {\n if (stack[index].tagName === tagName) {\n shouldRewind = true;\n break;\n }\n }\n while (cursor < len) {\n var endToken = tokens[cursor];\n if (endToken.type !== 'tag-end') break;\n cursor++;\n }\n if (shouldRewind) {\n rewindStack(stack, index, token.position.start, tokens[cursor - 1].position.end);\n break;\n } else {\n continue;\n }\n }\n\n var isClosingTag = (0, _compat.arrayIncludes)(options.closingTags, tagName);\n var shouldRewindToAutoClose = isClosingTag;\n if (shouldRewindToAutoClose) {\n var terminals = options.closingTagAncestorBreakers;\n\n shouldRewindToAutoClose = !hasTerminalParent(tagName, stack, terminals);\n }\n\n if (shouldRewindToAutoClose) {\n // rewind the stack to just above the previous\n // closing tag of the same name\n var currentIndex = stack.length - 1;\n while (currentIndex > 0) {\n if (tagName === stack[currentIndex].tagName) {\n rewindStack(stack, currentIndex, token.position.start, token.position.start);\n var previousIndex = currentIndex - 1;\n nodes = stack[previousIndex].children;\n break;\n }\n currentIndex = currentIndex - 1;\n }\n }\n\n var attributes = [];\n var attrToken = void 0;\n while (cursor < len) {\n attrToken = tokens[cursor];\n if (attrToken.type === 'tag-end') break;\n attributes.push(attrToken.content);\n cursor++;\n }\n\n cursor++;\n var children = [];\n var position = {\n start: token.position.start,\n end: attrToken.position.end\n };\n var elementNode = {\n type: 'element',\n tagName: tagToken.content,\n attributes: attributes,\n children: children,\n position: position\n };\n nodes.push(elementNode);\n\n var hasChildren = !(attrToken.close || (0, _compat.arrayIncludes)(options.voidTags, tagName));\n if (hasChildren) {\n var size = stack.push({ tagName: tagName, children: children, position: position });\n var innerState = { tokens: tokens, options: options, cursor: cursor, stack: stack };\n parse(innerState);\n cursor = innerState.cursor;\n var rewoundInElement = stack.length === size;\n if (rewoundInElement) {\n elementNode.position.end = tokens[cursor - 1].position.end;\n }\n }\n }\n state.cursor = cursor;\n}\n\n},{\"./compat\":1}],6:[function(require,module,exports){\n'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.formatAttributes = formatAttributes;\nexports.toHTML = toHTML;\n\nvar _compat = require('./compat');\n\nfunction formatAttributes(attributes) {\n return attributes.reduce(function (attrs, attribute) {\n var key = attribute.key,\n value = attribute.value;\n\n if (value === null) {\n return attrs + ' ' + key;\n }\n var quoteEscape = value.indexOf('\\'') !== -1;\n var quote = quoteEscape ? '\"' : '\\'';\n return attrs + ' ' + key + '=' + quote + value + quote;\n }, '');\n}\n\nfunction toHTML(tree, options) {\n return tree.map(function (node) {\n if (node.type === 'text') {\n return node.content;\n }\n if (node.type === 'comment') {\n return '';\n }\n var tagName = node.tagName,\n attributes = node.attributes,\n children = node.children;\n\n var isSelfClosing = (0, _compat.arrayIncludes)(options.voidTags, tagName.toLowerCase());\n return isSelfClosing ? '<' + tagName + formatAttributes(attributes) + '>' : '<' + tagName + formatAttributes(attributes) + '>' + toHTML(children, options) + '';\n }).join('');\n}\n\nexports.default = { toHTML: toHTML };\n\n},{\"./compat\":1}],7:[function(require,module,exports){\n'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n/*\n Tags which contain arbitary non-parsed content\n For example: