├── .hugo_build.lock
├── CNAME
├── .gitmodules
├── archetypes
└── default.md
├── content
├── docs
│ └── chapter01
│ │ └── _index.md
└── _index.md
├── resources
└── _gen
│ └── assets
│ └── scss
│ ├── book.scss_50fc8c04e12a2f59027287995557ceff.json
│ └── book.scss_50fc8c04e12a2f59027287995557ceff.content
├── config.toml
├── public
├── docs
│ ├── index.xml
│ └── chapter01
│ │ └── index.xml
├── index.xml
├── tags
│ └── index.xml
├── categories
│ └── index.xml
└── sitemap.xml
├── .github
└── workflows
│ └── gh-pages.yaml
└── README.md
/.hugo_build.lock:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | fastfsharp.com
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "themes/hugo-book"]
2 | path = themes/hugo-book
3 | url = https://github.com/alex-shpak/hugo-book
4 |
--------------------------------------------------------------------------------
/archetypes/default.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "{{ replace .Name "-" " " | title }}"
3 | date: {{ .Date }}
4 | draft: true
5 | ---
6 |
7 |
--------------------------------------------------------------------------------
/content/docs/chapter01/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 1
3 | bookFlatSection: true
4 | title: "Chapter 1: Why?"
5 | ---
6 |
7 | # Why We Care About Performance
8 |
--------------------------------------------------------------------------------
/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.json:
--------------------------------------------------------------------------------
1 | {"Target":"book.min.82c5dbd23447cee0b4c2aa3ed08ce0961faa40e1fa370eee4f8c9f02e0d46b5f.css","MediaType":"text/css","Data":{"Integrity":"sha256-gsXb0jRHzuC0wqo+0Izglh+qQOH6Nw7uT4yfAuDUa18="}}
--------------------------------------------------------------------------------
/config.toml:
--------------------------------------------------------------------------------
1 | baseURL = '/'
2 | languageCode = 'en-us'
3 | title = 'Fast F#'
4 | theme = 'hugo-book'
5 |
6 | [params]
7 |
8 | # Set source repository location.
9 | # Used for 'Last Modified' and 'Edit this page' links.
10 | BookRepo = 'https://github.com/matthewcrews/fast-fsharp'
11 |
12 | # Enable 'Edit this page' links for 'doc' page type.
13 | # Disabled by default. Uncomment to enable. Requires 'BookRepo' param.
14 | # Path must point to the site directory.
15 | BookEditPath = 'blob/main'
--------------------------------------------------------------------------------
/public/docs/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Docs on Fast F#
5 | https://fastfsharp.com/docs/
6 | Recent content in Docs on Fast F#
7 | Hugo -- gohugo.io
8 | en-us
9 |
10 |
11 |
--------------------------------------------------------------------------------
/public/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Introduction on Fast F#
5 | https://fastfsharp.com/
6 | Recent content in Introduction on Fast F#
7 | Hugo -- gohugo.io
8 | en-us
9 |
10 |
11 |
--------------------------------------------------------------------------------
/public/tags/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Tags on Fast F#
5 | https://fastfsharp.com/tags/
6 | Recent content in Tags on Fast F#
7 | Hugo -- gohugo.io
8 | en-us
9 |
10 |
11 |
--------------------------------------------------------------------------------
/public/categories/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Categories on Fast F#
5 | https://fastfsharp.com/categories/
6 | Recent content in Categories on Fast F#
7 | Hugo -- gohugo.io
8 | en-us
9 |
10 |
11 |
--------------------------------------------------------------------------------
/public/sitemap.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | https://fastfsharp.com/docs/chapter01/
6 |
7 | https://fastfsharp.com/categories/
8 |
9 | https://fastfsharp.com/docs/
10 |
11 | https://fastfsharp.com/
12 |
13 | https://fastfsharp.com/tags/
14 |
15 |
16 |
--------------------------------------------------------------------------------
/public/docs/chapter01/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Chapter 1: Why? on Fast F#
5 | https://fastfsharp.com/docs/chapter01/
6 | Recent content in Chapter 1: Why? on Fast F#
7 | Hugo -- gohugo.io
8 | en-us
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages.yaml:
--------------------------------------------------------------------------------
1 | name: github pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main # Set a branch to deploy
7 | pull_request:
8 |
9 | jobs:
10 | deploy:
11 | runs-on: ubuntu-20.04
12 | steps:
13 | - uses: actions/checkout@v2
14 | with:
15 | submodules: true # Fetch Hugo themes (true OR recursive)
16 | fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
17 |
18 | - name: Setup Hugo
19 | uses: peaceiris/actions-hugo@v2
20 | with:
21 | hugo-version: 'latest'
22 | extended: true
23 |
24 | - name: Build
25 | run: hugo --minify
26 |
27 | - name: Deploy
28 | uses: peaceiris/actions-gh-pages@v3
29 | if: github.ref == 'refs/heads/main'
30 | with:
31 | github_token: ${{ secrets.GITHUB_TOKEN }}
32 | publish_dir: ./public
33 | cname: fastfsharp.com
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fast F#
2 |
3 | This is a book under development on how to write fast F# code. The books website can be found [here](https://fastfsharp.com). I welcome contribution in the form of Case Study chapters or theory chapters where you go into detail on how F# works and the performance implications.
4 |
5 | ## Building Locally
6 |
7 | The book is built using Hugo and utilizes the [Hugo Book](https://github.com/alex-shpak/hugo-book#installation) theme. If you want to edit locally, I recommend cloning this repo then cloning the theme using a git submodule.
8 |
9 | > **Note:** You will need to have a version of Hugo which meets the requirements of the theme
10 |
11 | 1. Clone this repo locally
12 | 2. Open a terminal in the cloned repo
13 | 3. Clone the theme using `git submodule add https://github.com/alex-shpak/hugo-book themes/hugo-book`
14 | 4. Run the hugo server locally using `hugo server --minify --theme hugo-book`
15 |
16 |
--------------------------------------------------------------------------------
/content/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | type: docs
4 | ---
5 |
6 | # Fast F# - A Book for the Community
7 |
8 | I am obsessed with performance, and I have longed for a book on writing fast F# code. There are some resources out there, but nothing that went to the depth I am interested in. I want to take the same passion that C++ developers take to low latency code and apply it to the F# language. Some may scoff and say this is foolish. I find the challenge interesting.
9 |
10 | I hope this book will result from collaboration across the F# community. I plan to publish new chapters at a steady pace and solicit feedback from the community. This book will evolve as I learn. I plan to put my best ideas out there and get feedback and correction from the community. I fully anticipate being wrong a great deal of the time. I would rather present my ideas and learn from my mistakes than remain in my ignorance. If you have an idea or suggestion, I want to hear it!
11 |
12 | ## Why am I writing this book?
13 |
14 | There are two reasons: I want to learn and offer something to the community. I'm spending most of my time focusing on writing fast code these days. I want to be a world-class developer someday, and that is not going to happen without me putting my ideas out there and letting others provide feedback. I expect my understanding to be entirely rewritten by the time I'm done with this.
15 |
16 | I'm willing to be the person who's wrong so that I can hear from others how to do it right. Nothing invites feedback like stating something strongly on the internet. I'm hoping to save younger developers from having to ask all of the silly questions I have and will continue to ask. By documenting this understanding, the community will have a resource to understand how to get the most performance out of F#.
17 |
18 | ## Who's this for?
19 |
20 | This book is targeted at intermediate F# developers. You've already learned a bit of the language, and now you want to know how to get the most performance out of F#. When I say performance, I refer to single-threaded code that runs in the least amount of time possible. I don't plan to cover things like `async` or multi-threading. I don't use them in my work, so I don't have as much experience. I am primarily concerned with how fast code runs. I would welcome collaborators who feel like they could provide an expert's opinion 😊.
21 |
22 | ## What this book is NOT!
23 |
24 | I think it's important to state upfront that this is not a book on "proper" F# style. We may choose to model things in a way that is less elegant for the sake of achieving higher performance. There are already several excellent books that teach how to write concise, beautiful, and clean code. I refer you to them and their superior teaching. If you want to make F# go fast, though... I'll be waiting 😉.
25 |
26 | ## Layout and Structure
27 |
28 | This book will be a blend of theory and practice. The first half will focus on the internals of the F# language, the F# compiler, and the CLR. We will gain a deep knowledge of exactly how types work in F# and the implication on the performance of our code.
29 |
30 | Once we have laid the groundwork, we will go into case studies. The case studies I will write are motivated by real-world problems. They will show how I went from an initial first draft and then used profiling and benchmarking to improve the performance.
31 |
32 | ## Tentative Table of Contents
33 |
34 | The following are just some of what I hope to cover in this book. These aren't necessarily chapters but a rough list of what I want to cover.
35 |
36 | 1. Array vs. Map vs. Dictionary for lookup performance
37 | 2. Data Layout of Ref and Struct Types
38 | 1. Default Tuple vs Struct Tuple
39 | 2. Records vs. Struct Records
40 | 3. Default DU vs Struct DU
41 | 3. Equality and Comparison
42 | 1. When the defaults are good enough and when they need to be overloaded
43 | 4. Array + Units of Measure for typed indexing
44 | 1. How to use Units of Measure on indices to get zero-cost abstraction on indexing arrays while still getting type checking
45 | 5. Byref: What is it and Where to Use It
46 | 1. Where can they help, where do they hurt,
47 | 6. Active Patterns and Partial Active Patterns
48 | 7. Types of Loops and which to use
49 | 8. Recursion: When it's slow, when it's fast
50 | 9. Minimizing GC
51 | 1. Patterns for how to minimize the amount of GC you incur
52 | 10. Mutability: How to use it while not blowing your foot off
53 | 11. Data-Oriented Design: Some ideas on how to layout data that is both F# friendly but high-performance
54 | 12. SIMD: How to use SSE/AVX instructions from F# and when to use them
55 | 13. Stupid Bit Hacks: Wacky things you can do for speed
56 | 14. Case Study: Generic Topological Sort
57 | 15. Case Study: Topological Sort with Heterogeneous Node Types
58 |
59 | More to come...
60 |
61 | ## Want to help?
62 |
63 | I welcome other authors and editors. If you have something that you think would add value, I want it! Each chapter will note the author, so readers will know when they hear from a different voice. The book is not meant to be formal, so don't worry about having to create the most polished content. I hope that this feels like a conversation for those reading it.
64 |
65 | If you would like to contribute, the books repo is [here](https://github.com/matthewcrews/fast-fsharp). You can reach out to me at my email (matthew@crews.email) or on Twitter, @McCrews.
66 |
67 | This book is a Hugo website using the [Hugo-Book](https://github.com/alex-shpak/hugo-book#installation) theme, so all you need to contribute is to be able to write some Markdown!
68 |
69 | I also welcome technical review. I want to know if I have misstated something or could dramatically improve my algorithms!
70 |
71 | ## Publishing Plan
72 |
73 | This book will be authored in the open, and this digital version will **always** remain free. This book is my gift to the F# community for tolerating my endless questions. I may offer it as a physical book just because I'd like to have a physical copy, but that is a long way.
--------------------------------------------------------------------------------
/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.content:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";:root{--gray-100:#f8f9fa;--gray-200:#e9ecef;--gray-500:#adb5bd;--color-link:#0055bb;--color-visited-link:#8440f1;--body-background:white;--body-font-color:black;--icon-filter:none;--hint-color-info:#6bf;--hint-color-warning:#fd6;--hint-color-danger:#f66}/*!normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css*/html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}.flex{display:flex}.flex-auto{flex:auto}.flex-even{flex:1 1}.flex-wrap{flex-wrap:wrap}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.align-center{align-items:center}.mx-auto{margin:0 auto}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.hidden{display:none}input.toggle{height:0;width:0;overflow:hidden;opacity:0;position:absolute}.clearfix::after{content:"";display:table;clear:both}html{font-size:16px;scroll-behavior:smooth;touch-action:manipulation}body{min-width:20rem;color:var(--body-font-color);background:var(--body-background);letter-spacing:.33px;font-weight:400;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box}body *{box-sizing:inherit}h1,h2,h3,h4,h5{font-weight:400}a{text-decoration:none;color:var(--color-link)}img{vertical-align:baseline}:focus{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}aside nav ul{padding:0;margin:0;list-style:none}aside nav ul li{margin:1em 0;position:relative}aside nav ul a{display:block}aside nav ul a:hover{opacity:.5}aside nav ul ul{padding-inline-start:1rem}ul.pagination{display:flex;justify-content:center;list-style-type:none}ul.pagination .page-item a{padding:1rem}.container{max-width:80rem;margin:0 auto}.book-icon{filter:var(--icon-filter)}.book-brand{margin-top:0;margin-bottom:1rem}.book-brand img{height:1.5em;width:1.5em;margin-inline-end:.5rem}.book-menu{flex:0 0 16rem;font-size:.875rem}.book-menu .book-menu-content{width:16rem;padding:1rem;background:var(--body-background);position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-menu a,.book-menu label{color:inherit;cursor:pointer;word-wrap:break-word}.book-menu a.active{color:var(--color-link)}.book-menu input.toggle+label+ul{display:none}.book-menu input.toggle:checked+label+ul{display:block}.book-menu input.toggle+label::after{content:"▸"}.book-menu input.toggle:checked+label::after{content:"▾"}body[dir=rtl] .book-menu input.toggle+label::after{content:"◂"}body[dir=rtl] .book-menu input.toggle:checked+label::after{content:"▾"}.book-section-flat{margin:2rem 0}.book-section-flat>a,.book-section-flat>span,.book-section-flat>label{font-weight:bolder}.book-section-flat>ul{padding-inline-start:0}.book-page{min-width:20rem;flex-grow:1;padding:1rem}.book-post{margin-bottom:3rem}.book-header{display:none;margin-bottom:1rem}.book-header label{line-height:0}.book-header img.book-icon{height:1.5em;width:1.5em}.book-search{position:relative;margin:1rem 0;border-bottom:1px solid transparent}.book-search input{width:100%;padding:.5rem;border:0;border-radius:.25rem;background:var(--gray-100);color:var(--body-font-color)}.book-search input:required+.book-search-spinner{display:block}.book-search .book-search-spinner{position:absolute;top:0;margin:.5rem;margin-inline-start:calc(100% - 1.5rem);width:1rem;height:1rem;border:1px solid transparent;border-top-color:var(--body-font-color);border-radius:50%;animation:spin 1s ease infinite}@keyframes spin{100%{transform:rotate(360deg)}}.book-search small{opacity:.5}.book-toc{flex:0 0 16rem;font-size:.75rem}.book-toc .book-toc-content{width:16rem;padding:1rem;position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-toc img{height:1em;width:1em}.book-toc nav>ul>li:first-child{margin-top:0}.book-footer{padding-top:1rem;font-size:.875rem}.book-footer img{height:1em;width:1em;margin-inline-end:.5rem}.book-comments{margin-top:1rem}.book-languages{margin-block-end:2rem}.book-languages .book-icon{height:1em;width:1em;margin-inline-end:.5em}.book-languages ul{padding-inline-start:1.5em}.book-menu-content,.book-toc-content,.book-page,.book-header aside,.markdown{transition:.2s ease-in-out;transition-property:transform,margin,opacity,visibility;will-change:transform,margin,opacity}@media screen and (max-width:56rem){#menu-control,#toc-control{display:inline}.book-menu{visibility:hidden;margin-inline-start:-16rem;font-size:16px;z-index:1}.book-toc{display:none}.book-header{display:block}#menu-control:focus~main label[for=menu-control]{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}#menu-control:checked~main .book-menu{visibility:initial}#menu-control:checked~main .book-menu .book-menu-content{transform:translateX(16rem);box-shadow:0 0 .5rem rgba(0,0,0,.1)}#menu-control:checked~main .book-page{opacity:.25}#menu-control:checked~main .book-menu-overlay{display:block;position:absolute;top:0;bottom:0;left:0;right:0}#toc-control:focus~main label[for=toc-control]{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}#toc-control:checked~main .book-header aside{display:block}body[dir=rtl] #menu-control:checked~main .book-menu .book-menu-content{transform:translateX(-16rem)}}@media screen and (min-width:80rem){.book-page,.book-menu .book-menu-content,.book-toc .book-toc-content{padding:2rem 1rem}}@font-face{font-family:roboto;font-style:normal;font-weight:400;font-display:swap;src:local(""),url(fonts/roboto-v27-latin-regular.woff2)format("woff2"),url(fonts/roboto-v27-latin-regular.woff)format("woff")}@font-face{font-family:roboto;font-style:normal;font-weight:700;font-display:swap;src:local(""),url(fonts/roboto-v27-latin-700.woff2)format("woff2"),url(fonts/roboto-v27-latin-700.woff)format("woff")}@font-face{font-family:roboto mono;font-style:normal;font-weight:400;font-display:swap;src:local(""),url(fonts/roboto-mono-v13-latin-regular.woff2)format("woff2"),url(fonts/roboto-mono-v13-latin-regular.woff)format("woff")}body{font-family:roboto,sans-serif}code{font-family:roboto mono,monospace}@media print{.book-menu,.book-footer,.book-toc{display:none}.book-header,.book-header aside{display:block}main{display:block!important}}.markdown{line-height:1.6}.markdown>:first-child{margin-top:0}.markdown h1,.markdown h2,.markdown h3,.markdown h4,.markdown h5,.markdown h6{font-weight:400;line-height:1;margin-top:1.5em;margin-bottom:1rem}.markdown h1 a.anchor,.markdown h2 a.anchor,.markdown h3 a.anchor,.markdown h4 a.anchor,.markdown h5 a.anchor,.markdown h6 a.anchor{opacity:0;font-size:.75em;vertical-align:middle;text-decoration:none}.markdown h1:hover a.anchor,.markdown h1 a.anchor:focus,.markdown h2:hover a.anchor,.markdown h2 a.anchor:focus,.markdown h3:hover a.anchor,.markdown h3 a.anchor:focus,.markdown h4:hover a.anchor,.markdown h4 a.anchor:focus,.markdown h5:hover a.anchor,.markdown h5 a.anchor:focus,.markdown h6:hover a.anchor,.markdown h6 a.anchor:focus{opacity:initial}.markdown h4,.markdown h5,.markdown h6{font-weight:bolder}.markdown h5{font-size:.875em}.markdown h6{font-size:.75em}.markdown b,.markdown optgroup,.markdown strong{font-weight:bolder}.markdown a{text-decoration:none}.markdown a:hover{text-decoration:underline}.markdown a:visited{color:var(--color-visited-link)}.markdown img{max-width:100%;height:auto}.markdown code{padding:0 .25rem;background:var(--gray-200);border-radius:.25rem;font-size:.875em}.markdown pre{padding:1rem;background:var(--gray-100);border-radius:.25rem;overflow-x:auto}.markdown pre code{padding:0;background:0 0}.markdown p{word-wrap:break-word}.markdown blockquote{margin:1rem 0;padding:.5rem 1rem .5rem .75rem;border-inline-start:.25rem solid var(--gray-200);border-radius:.25rem}.markdown blockquote :first-child{margin-top:0}.markdown blockquote :last-child{margin-bottom:0}.markdown table{overflow:auto;display:block;border-spacing:0;border-collapse:collapse;margin-top:1rem;margin-bottom:1rem}.markdown table tr th,.markdown table tr td{padding:.5rem 1rem;border:1px solid var(--gray-200)}.markdown table tr:nth-child(2n){background:var(--gray-100)}.markdown hr{height:1px;border:none;background:var(--gray-200)}.markdown ul,.markdown ol{padding-inline-start:2rem}.markdown dl dt{font-weight:bolder;margin-top:1rem}.markdown dl dd{margin-inline-start:0;margin-bottom:1rem}.markdown .highlight table tr td:nth-child(1) pre{margin:0;padding-inline-end:0}.markdown .highlight table tr td:nth-child(2) pre{margin:0;padding-inline-start:0}.markdown details{padding:1rem;border:1px solid var(--gray-200);border-radius:.25rem}.markdown details summary{line-height:1;padding:1rem;margin:-1rem;cursor:pointer}.markdown details[open] summary{margin-bottom:0}.markdown figure{margin:1rem 0}.markdown figure figcaption p{margin-top:0}.markdown-inner>:first-child{margin-top:0}.markdown-inner>:last-child{margin-bottom:0}.markdown .book-expand{margin-top:1rem;margin-bottom:1rem;border:1px solid var(--gray-200);border-radius:.25rem;overflow:hidden}.markdown .book-expand .book-expand-head{background:var(--gray-100);padding:.5rem 1rem;cursor:pointer}.markdown .book-expand .book-expand-content{display:none;padding:1rem}.markdown .book-expand input[type=checkbox]:checked+.book-expand-content{display:block}.markdown .book-tabs{margin-top:1rem;margin-bottom:1rem;border:1px solid var(--gray-200);border-radius:.25rem;overflow:hidden;display:flex;flex-wrap:wrap}.markdown .book-tabs label{display:inline-block;padding:.5rem 1rem;border-bottom:1px transparent;cursor:pointer}.markdown .book-tabs .book-tabs-content{order:999;width:100%;border-top:1px solid var(--gray-100);padding:1rem;display:none}.markdown .book-tabs input[type=radio]:checked+label{border-bottom:1px solid var(--color-link)}.markdown .book-tabs input[type=radio]:checked+label+.book-tabs-content{display:block}.markdown .book-tabs input[type=radio]:focus+label{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}.markdown .book-columns{margin-left:-1rem;margin-right:-1rem}.markdown .book-columns>div{margin:1rem 0;min-width:10rem;padding:0 1rem}.markdown a.book-btn{display:inline-block;font-size:.875rem;color:var(--color-link);line-height:2rem;padding:0 1rem;border:1px solid var(--color-link);border-radius:.25rem;cursor:pointer}.markdown a.book-btn:hover{text-decoration:none}.markdown .book-hint.info{border-color:#6bf;background-color:rgba(102,187,255,.1)}.markdown .book-hint.warning{border-color:#fd6;background-color:rgba(255,221,102,.1)}.markdown .book-hint.danger{border-color:#f66;background-color:rgba(255,102,102,.1)}
--------------------------------------------------------------------------------