diff --git a/book-source/.Rprofile b/book-source/.Rprofile
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/book-source/.Rprofile
@@ -0,0 +1 @@
+#
diff --git a/book-source/.github/.gitignore b/book-source/.github/.gitignore
new file mode 100644
index 0000000..2d19fc7
--- /dev/null
+++ b/book-source/.github/.gitignore
@@ -0,0 +1 @@
+*.html
diff --git a/book-source/.github/ISSUE_TEMPLATE/a--typos.md b/book-source/.github/ISSUE_TEMPLATE/a--typos.md
new file mode 100644
index 0000000..fb828b1
--- /dev/null
+++ b/book-source/.github/ISSUE_TEMPLATE/a--typos.md
@@ -0,0 +1,16 @@
+---
+name: a) Typos
+about: Please report typos here
+title: Typo in book
+labels: ''
+assignees: ''
+
+---
+
+Please let us know about the typo:
+
+* Location/Chapter Number:
+* Incorrect text:
+* Correct text:
+
+Or you can send a GitHub Pull Request with the fix.
diff --git a/book-source/.github/ISSUE_TEMPLATE/b--all-other-issues.md b/book-source/.github/ISSUE_TEMPLATE/b--all-other-issues.md
new file mode 100644
index 0000000..1e4579b
--- /dev/null
+++ b/book-source/.github/ISSUE_TEMPLATE/b--all-other-issues.md
@@ -0,0 +1,10 @@
+---
+name: b) All other issues
+about: Please report all other issues here
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+
diff --git a/book-source/.github/workflows/deploy_bookdown.yml b/book-source/.github/workflows/deploy_bookdown.yml
new file mode 100644
index 0000000..1e697c1
--- /dev/null
+++ b/book-source/.github/workflows/deploy_bookdown.yml
@@ -0,0 +1,50 @@
+# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
+# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
+
+# Set up renv following instructions at https://www.emilhvitfeldt.com/post/bookdown-netlify-github-actions/
+on:
+ push:
+ branches: [main, master]
+ pull_request:
+ branches: [main, master]
+ workflow_dispatch:
+
+name: bookdown
+
+jobs:
+ bookdown:
+ runs-on: ubuntu-latest
+ # Only restrict concurrency for non-PR jobs
+ concurrency:
+ group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
+ env:
+ GITHUB_PAT: ${{ secrets.GH_PAT }}
+ EMAIL: ${{ secrets.EMAIL }}
+ steps:
+ - uses: actions/checkout@v3
+
+ - uses: r-lib/actions/setup-pandoc@v2
+
+ - uses: r-lib/actions/setup-r@v2
+ with:
+ use-public-rspm: true
+
+ - uses: r-lib/actions/setup-renv@v2
+
+ - name: Cache bookdown results
+ uses: actions/cache@v3
+ with:
+ path: _bookdown_files
+ key: bookdown-${{ hashFiles('**/*Rmd') }}
+ restore-keys: bookdown-
+
+ - name: Build site
+ run: bookdown::render_book("index.Rmd", quiet = TRUE)
+ shell: Rscript {0}
+
+ - name: Deploy to GitHub pages 🚀
+ if: github.event_name != 'pull_request'
+ uses: JamesIves/github-pages-deploy-action@v4.4.1
+ with:
+ branch: gh-pages
+ folder: docs
diff --git a/book-source/.gitignore b/book-source/.gitignore
new file mode 100644
index 0000000..bb5ded8
--- /dev/null
+++ b/book-source/.gitignore
@@ -0,0 +1,38 @@
+# R files
+.Rproj.user
+.Rhistory
+.RData
+.Ruserdata
+.DS_Store
+.Rbuildignore
+
+# bookdown output files
+*_bookdown_files
+HostingShiny.Rmd
+HostingShiny.pdf
+HostingShiny.tex
+HostingShiny.auc
+HostingShiny.aux
+HostingShiny.idx
+HostingShiny.log
+HostingShiny.out
+HostingShiny.toc
+HostingShiny.aux
+HostingShiny.ind
+HostingShiny.ilg
+purl.Rout
+
+# Other files
+.httr-oauth
+*rsconnect/
+
+# Comment out (and thus do not ignore these files) only when version bumping
+# the release branch
+docs/*
+# bib/packages.bib
+
+# generated figures
+figures/*
+
+spelling.csv
+urlcheck.txt
diff --git a/book-source/00-01-foreword.Rmd b/book-source/00-01-foreword.Rmd
new file mode 100644
index 0000000..fb94aa1
--- /dev/null
+++ b/book-source/00-01-foreword.Rmd
@@ -0,0 +1,11 @@
+This is an awesome book! I can recommend it.
+
+```{r echo=FALSE, eval=TRUE, results="asis"}
+if (is_latex_output()) {
+ cat("\\begin{flushright}
+ \\textit{John Doe, Affiliation}
+ \\end{flushright}")
+} else {
+ cat(" *John Doe, Affiliation*")
+}
+```
diff --git a/book-source/00-02-preface.Rmd b/book-source/00-02-preface.Rmd
new file mode 100644
index 0000000..5d05db7
--- /dev/null
+++ b/book-source/00-02-preface.Rmd
@@ -0,0 +1,154 @@
+# Preface {-}
+
+## Motivation {-}
+
+Shiny is a reactive web application framework used by R\index{R} and
+Python\index{Python} users to communicate their results to others. Whether
+you are just starting out with Shiny, or you are a seasoned programmer, at
+some point you'll need to share your app with your colleagues and users.
+You might decide to send them scripts or deploy the app and share the URL
+that takes people to your app. The documentation and books about the Shiny
+ecosystem are phenomenal resources. However, these resources focus on the
+development of the applications and decidedly cover very little about what to
+do once the app is deployed.
+
+It might surprise you, but there are at least /FIXME: CHECK THIS NUMBER/ 20 hosting options for Shiny.
+Navigating these options and finding up-to-date advice on choosing one and
+getting started is not easy or trivial. Information about Shiny hosting is
+scattered around the Internet and is often incomplete. Recommendations are
+usually narrowly focused and never really ask such important questions as:
+Why do you want this app deployed? Who is this app for? What is your budget?
+The goal of the book is to help you learn about the hosting options and what
+it takes to host your apps securely.
+
+Currently available books also focus on the R aspect of Shiny,
+which is not surprising given that Shiny for Python is a relatively recent
+addition to the Shiny ecosystem. Luckily, Shiny
+for R and Python have very similar deployment and hosting patterns. Therefore,
+we can cover both in the same book. We think this is important, because with the
+increasing popularity of Shiny available for R and Python, learning more about
+the hosting side of the application life-cycle will be a critical need for
+programming enthusiasts and data science professionals alike.
+
+## Who Is This Book For? {-}
+
+If you are asking the following questions, this book is definitely for you:
+What Shiny hosting solutions are out there? How do I decide which of these
+options suits my needs, my budget, and my skill level the best? How can I get
+started, and where can I get help if I get stuck? If you are not (yet) asking
+these questions, but want to learn about servers, cloud instances, and
+containers from the perspective of Shiny apps, this book is still for you.
+
+You might be a researchers in academia, government, and at a nonprofit
+organization who wants to self-host Shiny apps so that you can show your
+results to your colleagues, your stakeholders, or to the public.
+You might work at a small agency or a startups and want to securely host
+apps for your clients and users. Or you might be a data scientists who wants
+to effectively collaborate with people at the IT and engineering department
+about Shiny app deployment and hosting.
+
+## What Will You Learn? {-}
+
+The book is structured into four main parts plus appendices. This is what the
+four main parts will cover:
+
+- Part I. Getting started -- This will give you the fundamentals for the rest
+ of the book, including some background, concepts, and setup instructions.
+- Part II. Shiny Apps -- This part deals with everything that happens on your
+ **local** machine, including developing and organizing Shiny apps, and ways
+ of running the apps locally.
+- Part III. Hosting Shiny Apps -- This part describes all the ways how Shiny
+ apps can be hosted on **remote** machines, like cloud servers. This part is
+ the longest, because there are quite a few hosting options.
+- Part IV. What is next -- You'll find a summary of all the things you might
+ want to do next with your Shiny app, things that might either be too advanced
+ or are harder to describe in a physical book format.
+
+The book does not end on the last page. You can find online supplements,
+including the HTML version of the book and all the code that we present
+in the book and more on the book's website <`r book_website_link`>.
+
+## What Will You Not Learn? {-}
+
+This book is focused on the many different ways of hosting Shiny apps. This
+topic is at the intersection of R & Python programming, Shiny app development,
+and DevOps. We assume you are already familiar with either R or Python.
+If this is not the case, we suggest you start with accessible introductions
+to R or Python before going further. You don't need to know both languages,
+but knowing at least one is definitely needed.
+
+We will use example Shiny apps to test and demonstrate the different hosting
+options, but we are not going to teach you how to master Shiny,
+how to engineer production-grade apps, and how to make outstanding user
+interfaces with Shiny. You can find all those in these excellent books:
+
+- [_Mastering Shiny_](https://mastering-shiny.org/) [@Wickham2021]
+- [_Engineering Production-Grade Shiny Apps_](https://engineering-shiny.org/) [@Fay2021]
+- [_Outstanding User Interfaces with Shiny_](https://unleash-shiny.rinterface.com/) [@Granjon2022]
+
+There is also quite a bit that can be said about servers and cloud
+infrastructure that we won't cover in this book.
+[_DevOps for Data Science_](https://do4ds.com/) [@Gold2024]
+gives an accessible overview of DevOps conventions, tools, and practices,
+and provides pointers for further broadening your knowledge in these areas.
+
+## Conventions {-}
+
+Throughout the book, we will use title case and all caps for software and
+frameworks, e.g. Shiny, R, Python SSH, HTML, CSS, etc.
+We will use monospace code font for inline mentions of libraries and extension
+packages (e.g. `shiny`, `pandas`), also for functions and methods
+(e.g. `summary()`, `pandas.read_csv()`), and file names (`image.png`).
+_Italicized_ text will be preserved for publication titles
+(e.g. _Mastering Shiny_) and for cases when we imitate self talk.
+FOr example: _I feel a little bit lost, what do I do now?_
+We will use **bold** when highlighting or emphasizing words.
+URLs when pointing to Internet resources, like websites, will be in
+normal font (e.g. ), URLs when mentioning with
+regards to examples will be in code font (e.g. `http://127.0.0.1`).
+We will use angle brackets for variable values that you should replace
+in your code, e.g. `https://`. Dot-dot-dot within brackets,
+`[...]`, will indicate that we trimmed the output to save space by not
+showing too verbose or repetitive text in the book.
+We will use backslash (`\`) to break up long commands to help readability and
+to fit on the printed page.
+
+We use the _h10y_ as a shorthand for _Hosting Shiny_, and it means that between
+the first letter _H_ and the last letter _Y_ there are 10 other letters.
+You will see the domain [h10y.com](https://h10y.com) used to shorten links and to
+prevent link rot after the print version of the book is out. We will also use
+`h10y` in Docker image namespaces to make image tags shorter.
+
+## About the Authors {-}
+
+Péter Sólymos is a senior data scientist with 20 years of experience in
+wildlife, environmental, and utilities sectors. He holds a PhD in biology,
+he has authored 70 peer-reviewed publications and several statistical software
+packages. He is passionate about using statistics and data science to bridge
+the gap between data and decision making. His focus is on enabling this by
+helping organizations adopting cloud-native practices into their operations.
+FIXME: PETER to revise.
+
+
+Kalvin Eng is a PhD candidate in Computing Science at the University of Alberta with over 15 years of software development experience. His research involves understanding and improving the processes of software engineering. He is passionate about helping organizations develop efficient processes to build software more effectively.
+FIXME: KALVIN to revise.
+
+
+## Acknowledgements {-}
+
+We are grateful for Kahlid Lemzouji for continuously finding new reasons to
+up our game with Shiny hosting. Thanks to Kalob Taulien for helping with our
+early endeavors with Shiny hosting.
+
+Some of the content of this book was developed as part of workshops
+held by the authors at the _Edmonton R User Group (YEGRUG)_. We are thankful
+for the Statistical Society of Canada for inviting us to teach the course
+_Delivering applied statistics from concept to production_.
+
+FIXME: add here more names as needed
+- The authors would like to thank a whole bunch more people.
+- Who provided reviews and we know there names.
+
+
+
+\mainmatter
diff --git a/book-source/01-01-background.Rmd b/book-source/01-01-background.Rmd
new file mode 100644
index 0000000..f850ff5
--- /dev/null
+++ b/book-source/01-01-background.Rmd
@@ -0,0 +1,277 @@
+# (PART) Getting Started {-}
+
+# Background
+
+As a data professional working with R or Python, you might have a workflow
+that loads and transforms some data. You might have to explore and visualize
+this data, run some analyses and summarize your findings in reports.
+
+You can send the report, or even the data and reproducible scripts to others.
+In which case, they will have to be able to load and run you code.
+Wouldn't it be easier to deliver your findings as interactive documents
+or web applications?
+
+In this chapter, you'll see what Shiny can offer when it comes to
+data-intensive web applications, and you will be familiar with the
+workflow of developing and operating such web applications.
+
+## Data-intensive Apps
+
+Data-intensive applications, or data apps for short, are modern web applications
+that are centered around operations on data. This is different from traditional
+websites that focus on providing information for a user. The primary goal of data apps is to make data-intensive operations
+approachable. For non-technical users, this might mean simplifying a
+sophisticated/complex task like processing a CSV data file to a PDF report. For technical users, it can offer an intuitive
+understanding of complex relationships through interactive
+visualization, or it can help in institutionalizing the workflows that are
+incorporated in the apps.
+
+The steps involved in simplifying data-intensive operations resemble
+more traditional workflows used by analysts through the command line:
+
+1. Data import (files, databases)
+2. Data transformation (extract--transform--load; ETL)
+3. Summarizing the data (descriptive statistics)
+4. Visualization and exploratory data analysis (EDA)
+5. Repeating these steps for different subsets or slices of the data
+ (interactive and reactive)
+6. Saving, storing, or exporting these results (files, databases)
+
+Because these apps allow users to interact with the data, these apps
+share some similarities with desktop software applications, like Excel.
+But unlike desktop applications, data apps are accessed through the browser. This is advantageous as your application is able to run on any device that has a web browser like a desktop, tablet, or phone.
+There is also a distinction between our definition of data apps and
+general-purpose analytics tools, like Microsoft PowerBI or Tableau.
+Such tools are primarily used for exploratory purposes, whereas data apps are
+custom built and capable of answering questions in lot more depth.
+This is possible because of all the analytics capabilities of R and Python
+can be called upon in real time. In other words, data apps can be
+exploratory and explanatory at the same time.
+
+## Enter Shiny
+
+If your analytics workflow is built using R or Python,
+Shiny is the easiest way to create data science related web applications.
+Shiny can also power dynamic documents. As advertized by the `shiny` R
+package description [@R-shiny]:
+
+> [Shiny] Makes it incredibly easy to build **interactive** web applications with R.
+> Automatic "**reactive**" binding between inputs and outputs and extensive
+> prebuilt widgets make it possible to build beautiful, **responsive**, and
+> powerful applications with minimal effort.
+
+In other words, you can turn your data and scripts into a web application
+with limited web development skills. Shiny lets you to create a web app without knowing HTML, JavaScript and CSS. But more importantly, it will also let you
+include **reactivity**, so your users can dynamically explore the results.
+
+**Reactivity** is the most important and distinctive feature of Shiny, compared
+to similar [web application frameworks](https://shiny.posit.co/py/docs/comp-streamlit.html)
+know mostly in Python. Reactivity responsible for re-executing parts of the
+code when things change as the result of users interacting with the app.
+This is possible through reactive bindings between inputs and outputs.
+
+The apps are also **interactive**, which is different from being reactive in
+the sense that during interactive behaviour, the state of the application
+stays the same on the backend, changes are only rendered on the client side
+(i.e. in your browser). Think of popups, hover labels, transitions, and
+animations. This behavior is usually powered by the front-end JavaScript code.
+
+The thirds feature mentioned in the Shiny R package description is
+**responsiveness**. It refers to the styling of the rendered web application.
+Again, client-side JavaScript and CSS code used by the Shiny user interface
+is responsible for the app responding to various device sizes (desktop, tablet,
+phone), or viewports (resizing the window). Size responsive systems allow
+the web page to respond to changes according to well thought-out rules
+without the interface elements falling apart.
+
+## Application Development
+
+Application development begins with an idea of what you would like your app to do. In a first iteration, you will have a proof-of-concept version of
+the app that will likely have bugs and need more refinement. It might take several iterations of fixing bugs and incorporating
+feedback from the users until the data app reaches its final form.
+Even after that, you might have to update the data behind the app,
+incorporate latest technologies, and provide security updates for the users.
+You might not make fundamental changes to the app any more, but you are
+still releasing new versions from time to time.
+
+This also means that you might want to test your changes before releasing
+the new version to your users. App development is usually based on
+accumulating small changes, continuously testing these changes, and
+releasing these changes to the users at a pre-determined cadence,
+depending on the criticality of the changes made to the app. You might
+not wait too long with a security update. However, new features
+might get released monthly or yearly.
+
+If you are using Shiny, you are participating in application development. Shiny is a web application framework that can help with the application development process. As a framework, it abstracts most of the complicated code needed for user interfaces, leaving you more time to focus on developing data features for your data app. Shiny is integral in simplifying the application development process for data-intensive apps.
+
+## Application Hosting
+
+Once you've developed your Shiny application, you might be wondering how you can share the application to your colleagues or a wider audience. This is where the concept of application hosting comes in.
+
+Hosting an app includes operations required for successfully and securely running the
+app online, so that the users of the app get access to it without interruption.
+It starts with deploying the application. What deployment means, is that
+a person or an automated process moves some files from one computer to
+another and refreshes the hosting server to display the new version of the app.
+
+You might want to monitor the usage of the app, i.e. how many users are
+active, when and for how long they are using the app. You should also
+monitor resource usage on the hosting server, for example CPU, memory,
+and disk space. This kind of visibility into the system and the ability to
+retrieve system and application level logs will be critical in case
+something goes wrong.
+
+All these require some up-front investment in learning about and setting
+up the systems that you will host you app on. After the resources spent on
+app development and the initial setup, operating costs will be
+determined by the kind of setup and hosting that is required for delivering
+the value to your users. The user experience also largely depends
+on the performance of the servers that the apps are running on, because most
+of the computations happen on the server-side -- i.e. on a remote
+computer or in the cloud.
+
+Other requirements that are less important include the client's machine specifications are less important. Data apps are
+expected to run on desktops, laptops, tablets, and phones via a web browser. This is good
+news for app developers because data apps can be written in
+different programming languages and still can result in a comparable
+user experience. One might prefer Python or R, others might use
+JavaScript. The end result will be very similar: a data app running in
+the browser.
+
+This book will help you gain a better understanding of how to host your Shiny applications walking you through different solutions that can best fit your requirements.
+
+## The DevOps Cycle
+
+An important part of application development and hosting is DevOps.
+DevOps is a set of practices and tools
+that integrates and in most cases automates the work of software development
+(Dev) and information technology (IT) operations (Ops).
+The Dev side is concerned with building a better app, while the
+Ops side is concerned with providing the best infrastructure to run the app on.
+DevOps therefore refers to the collaboration culture between that people on
+both sides to make this process as smooth as possible through the use of
+monitoring and logging to identify and quickly resolve issues.
+Figure \@ref(fig:part1-devops-cycle) shows the of DevOps cycle
+as it is often visualized, capturing the development and operations that we
+described in the previous sections.
+
+```{r part1-devops-cycle, eval=TRUE, echo=FALSE,out.width="80%", fig.cap="The DevOps cycle."}
+if (is_latex_output()) {
+ include_graphics("images/01/devops-cycle.pdf")
+} else {
+ include_graphics("images/01/devops-cycle.png")
+}
+```
+
+DevOps is also often associated with Continuous Integration and Continuous
+Deployment (CI/CD). CI/CD practices ensure the reliable delivery of frequent
+code changes. It emphasizes the use of automation to reduce human error.
+You can view DevOps and CI/CD as two sides of the same coin. CI/CD is
+pro-active, and tries to eliminate sources of errors through automation.
+DevOps is a broader framework that codifies reactive practices when we
+notice deviations from the plan.
+
+Figure \@ref(fig:part1-devops-cycle) might suggest that DevOps is a never
+ending cycle, and that changes can only originate from within the cycle.
+All of those steps are under the control of the development and operations
+teams. But in reality, we can imagine many factors that can perturb
+the ideal system from the outside. These would be factors that we cannot
+of do not want to control. This is when things do not go according to plan.
+
+For example, the system being hacked is never a pleasant experience.
+Paying ransom, or highjacked servers sending spam emails or mining Bitcoin
+is nobody's idea of having fun. Or even the biggest cloud provider
+can experience outages. These are all events that we can prepare for, but
+cannot control.
+
+Other events throwing the DevOps cycle off the plan might be more benign.
+Having too many users is often considered a good kind of problem. But
+unexpected surges in usage might lead to unresponsive servers.
+Adequate monitoring and alerting can prevent these unfortunate events.
+But at some point, the team might be faced with a decision.
+How can we support more users? How can we improve user experience by
+reducing loading time of the apps?
+
+Adapting to changing needs and circumstances often leads to changes in the
+way how the application is hosted. Changes can be incremental, like you
+need more CPU or memory for the server. Or it can be more drastic, for example
+moving from a hosting platform to self hosting your own cloud server.
+
+The DevOps cycle illustrates how the development and deployment of an application
+is iterative. Our book presents the hosting of Shiny applications in a similar
+fashion that we coin as the **hosting cycle** described in the next section.
+
+## The Hosting Cycle
+
+The hosting cycle is inspired by the DevOps cycle. It is meant to illustrate how you hosting your Shiny application might take many iterations. Let's give an example. You have a Shiny app that you'd like to deploy.
+You have read the documentation on the Shiny website and now you are thinking:
+_Posit Connect is pricy, Shiny Server has no licensing fee, and both require self hosting on my own server or in the cloud. I don't know how to do that.
+So I will go with [Shinyapps](https://www.shinyapps.io/).
+It is free to start with, I can deploy my
+app with a click of a button from my RStudio Desktop._
+
+Shinyapps definitely seems like the best option when starting out.
+It is free for the first five apps (with limited hours of app usage).
+You host your app in a cloud platform, i.e. on someone else's server,
+that is fully managed. This means less headache for you.
+You sign up to Shinyapps, you click the deploy button in RStudio Desktop,
+and you app opens up in the browser a few of minutes later.
+
+Let's take a look at Figure \@ref(fig:part1-hosting-cycle) that illustrates the
+cycle of evaluating the Shiny hosting options from time to time.
+In our example above, we assumed that you are new to Shiny hosting.
+You did some research, you made your choice based on some criteria,
+e.g. pricing (free) and operational complexity (managed hosting with push
+button publishing).
+
+Now you are at a stage where the option that you picked is working just fine.
+This could be the end of the story for hosting your Shiny apps. But more often
+than not, questions will pop into your head as time goes by.
+
+- _I have many active users an I am running out of free app hours on Shinyapps._
+- _I want to host more than >5 apps._
+- _I want my custom domain with HTTPS._
+- _I need better performance, more memory._
+- _I need specialized software libraries installed._
+- _I need fine grained access control to my apps._
+
+Should you upgrade your Shinyapps subscription? Should you set up your own
+server and try Shiny Server? Should you convince your boss to foot the bill
+and buy Posit Connect for your team? There could also be other options out there.
+
+This is where the contents of this book will help you make a decision. Your
+answer might depend on many factors, or your needs might evolve over time.
+Having an understanding of what it takes to go with certain hosting options
+will provide the foundation for that.
+
+```{r part1-hosting-cycle, eval=TRUE, echo=FALSE, fig.cap="Shiny hosting cycle."}
+if (is_latex_output()) {
+ include_graphics("images/01/hosting-cycle.pdf")
+} else {
+ include_graphics("images/01/hosting-cycle.png")
+}
+```
+
+Reading this book and trying some of the examples yourself will help you to
+build confidence when it comes to evaluating you Shiny hosting needs and
+options the next time you arrive to a crossroad.
+
+## Summary
+
+We outlined a very generic outline for how the need for a data-intensive
+application might arise, and the reasons why Shiny might be the right tool
+for that. You might have your own reasons for picking Shiny and you
+might also have your unique circumstances and needs. But the DevOps
+principles will likely apply to you too when developing your app.
+And the hosting related considerations and options outlined in the
+"hosting cycle" section will also apply to you. That is why we thing
+that by reading this book, you will be able to make an informed decision
+regarding your next steps around hosting your Shiny apps.
+
+In the next chapter, we will review important concepts that you might already
+be familiar with or at least heard about those concepts. It is very important
+to have a working knowledge of these concepts because as you browse the web
+or read more advanced materials, you will run into these without much explanation.
+Shiny simplifies web technology for data professionals, but those web technologies
+sit under the hood of Shiny's abstraction layer. Hosting directly interfaces
+with these fundamentals, so understanding those concepts will help you.
diff --git a/book-source/01-02-hosting-concepts.Rmd b/book-source/01-02-hosting-concepts.Rmd
new file mode 100755
index 0000000..734c1d2
--- /dev/null
+++ b/book-source/01-02-hosting-concepts.Rmd
@@ -0,0 +1,365 @@
+# Hosting Concepts
+
+To better understand this book, you will need to first gain a general understanding about how web applications are hosted. This includes: domains and networking, website technologies, servers, and hosting environments.
+
+Imagine accessing a Shiny application on Internet. At a high level, you visit the application by clicking a link or typing a URL in browser. By visiting the application, the browser performs operations related to domains and networking to serve you the application. The application itself is run in the browser with website technologies. The actual data of the application is hosted on a server that has a hosting environment catered to the Shiny application.
+
+Users accessing a Shiny application primarily remember only the website link, abstracting them from understanding the concepts needed for the application to run on the Internet. This chapter delves into how Shiny applications are hosted to, using accessing a Shiny application as a motivating example.\
+
+By the end of this chapter, you should be able to grasp why an aplication mght not be loading, or why it is taking longer than you have anticipated for an application to load.
+
+
+
+## Domains and Networking
+To access a Shiny application, it begins with a URL like: .
+FIXME: Add better URL
+
+There is a lot to unpack with this URL. First, there is the **protocol** which is the **https** part. Next is the **domain** which is the **analythium.shinyapps.io**. Finally, there is the **path** part which is **/covid-19-alberta/**.
+
+* The **protocol** specifies how data will be transferred to you. If you use **https**, which is shorthand for "Hypertext Transfer Protocol Secure", it means that you will transfer data securely with encryption. If you use **http** (Hypertext Transfer Protocol), it means that data will transfer data without any encryption.
+
+* The **domain** specifies where data will be transferred from. It is a reference to an IP address (Internet Protocol address) that identifies a computer (server) that is available on the Internet. The domain lets your computer know which computer to request data from.
+
+* The **path** lets you specify what resource you want from the server.
+
+Sometimes, you might see a URL like: which is different from the previous URL we introduced (note the ":443"). This new part specifies a **port**.
+FIXME: Add better URL
+
+* The **port** specifies the connection point of a server. There are common ports for different protocols. For example, for "https" it is usually 443. For "http", it is usually 80.
+
+The URL just specifies how a client connects to a server. The actual connection, requires networking between the client and server using TCP/IP (Transmission Control Protocol/Internet Protocol). TCP/IP is a suite of communication protocols to help computers connect with each other with forms a network of computers.
+
+In the case of the Shiny application, a client computer would **request** data from a server computer and the server sends a **response** with the requested data. This data contains all the necessary information to serve your application. These request/response operations happen everyday on the Internet to provide access to applications like your Bank portal or social media app.
+
+
+
+## Website Technologies
+The data that comes from a server is provided in a raw format that needs to be interpreted by a client. In the case of a Shiny application, the data provided is meant to be interpreted by a web browser that serves a website. A website uses many technologies to interpret which commonly includes: HTML (HyperText Markup Language), CSS (Cascading Style Sheets), and JavaScript.
+
+Shiny applications use HTML, CSS, and JavaScript to render a web application. The HTML contains textual information about a website. While the CSS provides styling for a website. The JavaScript enables interactivity for a website by communicating with the Shiny application backend.
+
+The Shiny applciation backend runs on either R or Python and renders the data needed to render the application on a web browser.
+
+## Servers
+A server is a computer that runs indefinitely, and is available to serve content to anybody that requests it. Servers can be costly to run and are often shared with other users. To alleviate any concerns of security and resource sharing a server can be shared through a virtual layer.
+
+By using a virtual layer, a virtual private server can be created. This is where a server's resources can be allocated to be used exclusively by a user.
+
+Servers can be offered as either as a IaaS (infrastructure as a service) or PaaS (platform as a service). IaaS requires more configuration and requires some server administration knowledge. While PaaS simplifies configuration by abstracting management to a platform.
+
+FIXME: Expand on IaaS and PaaS
+
+
+
+## Hosting Environments
+On servers that are meant to serve web applications, environments are specifically setup to help run web applications.
+A lot of consideration of security, compatibility, and optimization goes into hosting environments to ensure that a web application runs as intended.
+
+In terms of security, you must consider firewalls, how data is transmitted, and who can access your app. These considerations may not be known to the end user of your Shiny application, but it is important for you to be aware of when hosting a Shiny application.
+
+In terms of compatibility, virtualization and containers can help make your application run on any platform. Otherwise, you might spend a lot of time installing the right software to run your Shiny application. This also helps make your application scalable by being able to easily deploy on multiple instances of a platform.
+
+In terms of optimization, you must consider how to route your traffic to ensure that your app runs smoothly. Many of the times, there might be high demand for your application. This is where multiple instances of your Shiny application might come in handy, where you are able to distribute the requests for your application.
+
+
+
+FIXME: We will leave pointers to:
+
+- other chapters
+- other books/websites
+
+
+
+## Summary
+
+In this chapter, we have covered the basic concepts needed for understanding the subsequent sections. In short, we have explained at a high level what goes into hosting a Shiny application and how it is served over the Internet.
+
+In the next chapters, you will learn more details about how hosting a Shiny application including more advanced concepts such as:
+
+* Creating a virtual environment for your Shiny application with a container
+* Where to host your Shiny Application in the Cloud
+* Considerations for making your Shiny Application production ready
+
+
+
+
diff --git a/book-source/01-03-tools.Rmd b/book-source/01-03-tools.Rmd
new file mode 100755
index 0000000..049d233
--- /dev/null
+++ b/book-source/01-03-tools.Rmd
@@ -0,0 +1,461 @@
+# Local Setup
+
+To make your Shiny applications accessible for a wider audience, you must
+have some tools available locally on your laptop or desktop --- sorry, a
+tablet or a phone are not recommended devices for this.
+You will become familiar with several tools used to manage your code
+locally or communicate with remote machines.
+
+By the end of this chapter, you should have obtained the knowledge and the
+local setup of tools to connect to a remote server and run a Shiny app from
+the command line.
+
+## Installing Your Developer Tools
+
+### R
+
+R is a programming language designed mainly for statistical computation, but it
+has been extended with packages to support tasks like developing web apps with
+Shiny.
+
+To begin, you must install the appropriate binary for your operating system
+available at under the "Download and Install R"
+section.
+
+Next, you should install an integrated development environment or IDE of your choice.
+One of the most popular ways to develop in R is to use the [RStudio Desktop]()
+developed by Posit (formerly RStudio). You can download the binary from
+.
+
+Another increasingly popular IDE option for R is Microsoft's VisualStudio Code,
+or VS Code for short. You can download the installer from
+.
+It is recommended to use the R extension for VS Code that relies on the
+`languageserver` R package [@R-languageserver].
+You can find setup instructions in the
+[REditorSupport/vscode-R](https://github.com/REditorSupport/vscode-R)
+GitHub repository.
+Another useful extension is the Shiny VS Code extension from the
+VS Code Marketplace or the Open VSX Registry.
+
+To install Shiny, you can run the command `install.packages("shiny")` in the
+R console. You will need a few other packages too:
+
+```R
+install.packages(c("bslib", "shinylive", "htmltools", "rmarkdown"))
+```
+FIXME: list other R packages.
+
+### Install Python
+
+As a scripting language, Python is versatile and can be developed in different
+environments including text editors and IDEs. The recommended IDE for developing
+in Shiny with Python is VS Code.
+
+To use the IDE for Python, you must ensure that Python can run on your operating
+system. Operating systems like MacOS and Linux should already have come bundled
+with a version of Python, but it may be an older version. Therefore, we recommend
+installing the latest Python version for MacOS and Windows from
+. For Linux/Unix based operating systems,
+we recommend installing Python using the command line with the following
+commands:
+
+Install Shiny with `pip install shiny`. Some of the examples will use Shinylive,
+you can install it with `pip install shinylive`.
+FIXME: CHECK this, how to install Python and `pip` etc.
+
+
+The Shiny VS Code extension from the
+VS Code Marketplace or the Open VSX Registry works with R and Python as well.
+Python itself also has VS Code extensions that can be used.
+
+A rathe new IDE option that R and Python users should keep an eye on is
+[Positron](https://github.com/posit-dev/positron) by Posit as it promises to be
+an extensible, polyglot tool for writing code and exploring data.
+
+### Web Browser
+
+You likely already have a web browser on your local machine. Most people use
+Chrome, Saferi, Edge, Firefox.
+
+### Quarto
+
+Quarto is a open-source scientific and technical publishing system by Posit that
+can include Shiny apps. You can find install instructions at .
+
+### Docker Desktop
+
+To download and install Docker for your platform of choice, visit the
+[Docker download page](https://docs.docker.com/get-docker/) and
+follow the instructions.
+
+If you are a Mac OS X user on Apple Silicone (M1 chip and above), you might want
+to enable virtualization to be able to run and build images for AMD64 architecture.
+Otherwise the images might have poor performance or fail on other platforms.
+Go to the Settings in Docker Desktop, and under the General tab check
+the "Use Virtualization framework" and
+"Use Rosetta for x86_64/amd64 emulation on Apple Silicon" boxes.
+
+## The Command Line
+
+The command line is a text-based user interface to control a computer. Instead of
+using a mouse, you use your keyboard to type commands to control the computer. This is
+different than the graphical user interfaces (GUIs) used in modern day operating systems
+like Windows, Mac OSX, and Ubuntu Desktop Linux. The command line is important for
+managing your Shiny app deployment on a remote server where there might not be a GUI
+for you to control the remote server.
+
+Depending on your operating system, there are different options for the command line.
+
+On Windows, we recommend installing [Windows Subsytem Linux](https://learn.microsoft.com/en-us/windows/wsl/install)
+in order to use a Linux based command line. This is because some commands that
+we describe in this book may be incompatible with Windows based command lines
+like PowerShell (comes preinstalled on Windows) or [Git Bash](https://git-scm.com/downloads).
+
+For Mac OS X, we recommend using the built-in terminal application. You will be
+able to run the commands that we describe in the book.
+
+For Linux-based systems like Ubuntu , you can also use the built-in terminal application.
+
+Our book will cover commands using a "*nix based command line". This means the
+Windows Subsytem Linux terminal or the built-in terminals with Mac OS X or Ubuntu.
+
+The terminal is also available from inside your IDE (RStudio or VS Code),
+which means that you won't have to change windows to access it.
+
+Now let's review some of the most common shell commands that we'll need in the
+absence of a graphical user interface (GUI).
+
+### Navigation
+
+Navigating through the command line might seem daunting at first.
+However, it is quite simple once you memorize 3 key commands involving directories.
+
+#### Current Working Directory
+
+To know which directory you are in, you can type the `pwd` command. This will give
+you the path of the "working directory" that you are in.
+
+#### Listing Directory
+
+The `ls` command is used to list all files in a directory.
+This is important to know when you are trying to find a file on the command line.
+
+#### Change Directory
+
+The `cd` command is used to change directories and traverse a computer. For
+example, you may want to change your working directory to a nested folder of your
+current working directory. In order to do so, you would type `cd` in the command
+line and typing a space after, and then press `tab` to see the available
+directories to traverse to. With the correct directory in mind, you can type
+the directory name after and press enter to change directories.
+
+To verify that you are in the new directory, your command line should indicate
+the current directory that you are in. If you are still unsure, you can use the
+`pwd` command to get the path of your current working directory.
+
+By running `cd ..` you can go up one directory. To go up 2 directories, you can
+run: `cd ../..`.
+
+### Editing Files
+
+Once you can navigate through the computer with command line. The next important
+task to know about is editing files with a text editor. There are many
+popular text editors that usually come bundled with your *nix based command line
+including: `nano`, `vim`, and `emacs`. Vim and Emacs are much more complex
+and require more technical knowledge to edit and save files. Therefore,
+if your are just staring out, we recommend using Nano.
+
+Nano is a user-friendly text editor that allows you to edit files from the
+command line. To begin, you can type `nano` followed by a space and the name of
+the file you want to edit, e.g. `nano app.R`. If the name of the file does not
+exist, nano will create the file for you when you save it.
+
+To navigate through a file with Nano, you can use your arrow keys to move the
+cursor. You can add text to the file by typing on your keyboard.
+
+The instructions for nano are at the bottom of the editor. The `^` stands for
+the CTRL key and the letter beside it stands for the key you press to run the
+command. Press the CTRL key together with the letter on the keyboard to execute
+a command.
+
+To save the file, you would press `CTRL + O` and confirm the prompts with enter.
+To exit nano, you would press `CTRL + X`.
+
+### File and Directory Operations
+
+It is also important to know about how to move, copy, and delete files and
+directories. Furthermore it is also important to know how to create directories.
+
+#### Making Directories
+
+To make a directory you would type `mkdir` followed by a space and the directory name.
+To confirm that the directory is created, you can run the `ls` command and see that
+the folder has been created. You can even try to navigate to it with the `cd` command.
+
+#### Moving Files and Directories
+
+To move a file or directory, you would simply type
+`mv `.
+You can verify that a file or directory has moved with `ls `
+and you should see that in its new location.
+
+#### Copying Files and Directories
+
+Copying files is similar to moving files. To copy a file, you would run
+`cp `.
+
+To copy a directory, you will need to add the `-r` flag so that the whole
+directory is copied "recursively". The command you would run would be:
+`cp -r `.
+
+You can verify that a file or directory has been copied with
+`ls ` and you should see that in its new location.
+
+#### Deleting Files and Directories
+
+To delete files run: `rm `.
+
+For deleting directories, you will need to add the `-r` flag so that the whole
+directory is deleted "recursively". The command to delete directories is:
+`rm -r `.
+
+You can verify that a file or directory has been removed with
+`ls `. There should be an error saying that the
+file and directory do not exist.
+
+### Super User Access
+
+At times you may find that you do not have the correct permissions to edit a
+file or even view a directory. Therefore, you must use super user access to gain
+the correct permissions for your current user.
+
+If you are looking for super user permissions for a single command you can run:
+`sudo `. Command in this case can be any command line command including
+the ones we have described earlier in the chapter like `rm`.
+For example, you could run `sudo rm test.txt` where we are using super user access
+to delete the test file.
+
+You may also find that you need super user permissions for multiple commands.
+In this case you can start a command line as a super user by running `sudo -i`
+and following the prompts. To exit out of the super user command line you can
+press `CTRL + D` or run the command `logout` or `exit`.
+
+## Source Code Management with Git
+
+Source code management is important to keep centralized versions of your Shiny
+application source code. By centralizing your source code, you can have one
+source truth of your application code that can be duplicated anywhere. Source
+code management also helps to save different versions of a file without creating
+multiple copies of a file.
+
+Git is one of the most popular version control systems for software. In this
+section, we will be describing how to get started with Git.
+
+Git comes installed by default on most Mac OS X and Linux machines.
+On Windows you can install Git from .
+Try `git version` in the terminal to test if Git is installed properly.
+
+### Git Services
+
+Git services help host your source code remotely. Two of the most popular Git
+services are GitHub and GitLab. Both are good choices and the instructions in
+this section apply to both services. However, we recommend using GitHub as later
+in the book we describe continuous deployment and continuous integration (CICD)
+that relies on GitHub Actions.
+
+### Git Commands
+
+To follow along with the commands, you will first need to sign up for your Git
+service. Following sign up, you should create an empty repository which will
+host your source code.
+
+In this book, we will outline how to use Git from the command line. There are
+also graphical user interfaces (GUIs) for git. However, when connecting remotely
+to a server, a GUI to use the server is unavailable.
+
+We recommend that you set your default Git text editor to nano with this command:
+`git config --global core.editor "nano"`. Often the default editor is set to
+Vim or emacs which is harder to use.
+
+#### Cloning Repositories
+
+Once you have created a repository, you can clone it to your computer.
+To do so, you would run `git clone ` and enter the appropriate
+credentials. For example, you can clone the GitHub repository with
+one of our Shiny app examples, the Old Faithful example as:
+
+```bash
+git clone https://github.com/h10y/faithful.git
+```
+
+If you are cloning a _private_ GitHub repository, you must setup an access token
+to be used as a password for the `git clone` command.
+For private GitLab repositories, you would use the same password to be used as
+you would sign in on the website, but you can also create an access token.
+
+#### Creating a Commit
+
+When you have cloned the repository, you can begin adding file changes via a
+commit. To begin, you would add or modify files in the repository directory.
+After you have made changes, you can create a commit using three commands.
+
+First, you would run `git add .` from the root directory of your repository.
+This command stages your changes for the commit. The `.` signifies the current
+directory and all its files and directories in it.
+
+Next, you would run `git commit -m ""` to create a
+commit with a message. The `-m` flag indicates that you will have a message for
+your commit. You must enclose your message within quotation marks. A commit must
+always have a message.
+
+Finally, once you have created your commit, you can push your changes with
+`git push`.
+
+#### Pulling Changes
+
+You might have multiple copies of your repository on different computers.
+In such cases, to keep the changes of files and directories in sync, you can
+run the `git pull` command.
+
+It should be noted that if you make changes in your repository without
+committing them, you must commit your changes before performing a pull.
+
+If you have committed changes and the repository has been updated elsewhere,
+merge conflicts may arise. To detect which files have conflicts, you can run
+the `git diff --check` command which shows the files and line numbers that
+have conflicts. The conflicts in the files will appear as `<<<<<<< HEAD` at the
+top with the current changes you have under it. This is separated with `=======`
+and the changes that are conflicting under it. The conflicting changes are
+concluded with `>>>>>>> `.
+
+You must delete the `<<<<<<< HEAD` and enclosing `>>>>>>> `,
+keeping only the change that you want and running `git commit` to resolve any
+conflicts for your repository.
+
+#### Status of Repository
+
+To determine what files you changed and are not tracking, you can run the
+`git status` command. This command will give you the files you have modified and
+files that are not yet tracked by git. This command will also tell you how many
+changes you have locally that haven't been pushed to the git service yet.
+
+### .gitignore
+
+Often there are extraneous files that are artifacts of running your Shiny
+application or contain sensitive information, like passwords or access tokens,
+and do not need to be committed. To ignore these files, you can create a
+`.gitignore` file in the root of your repository.
+
+To generate a `.gitignore` file, you can visit [gitignore.io](https://gitignore.io)
+which will generate a file with the files and directories to ignore for an
+operating system, programming language, or tool. We recommend selecting:
+linux, macOS, windows, R, and Python as a base `.gitignore`.
+
+### Branching
+
+Branching is a more advanced concept in Git that is useful when you have
+multiple people working on the same files. In this subsection, we will briefly
+touch on the basics of git branching.
+
+A branch is a copy of a repository that you can work on and make changes for a
+specific feature. Once that feature has been completed you can merge it back to
+your `main` branch. This allows multiple people to work on the same codebase.
+
+To see the branches that exist in your repository, run the `git branch` command.
+
+To create a branch in your repository, run the `git checkout -b `
+command. It should be noted that when you first try to push your new branch
+with `git push`, you will be unsuccessful since the remote repository has no
+idea of the branch. Therefore you must run
+`git push --set-upstream origin `. Do not worry if you cannot
+remember this command! Git will give you a reminder of the command to run to
+push a new branch.
+
+To switch branches, run the `git checkout command`.
+To merge the changes of one branch to your current branch, run the
+`git merge ` command.
+
+There is an option to review merges on GitHub and GitLab. On GitHub, you would
+open a pull request. On GitLab, you would open a merge request. Both of these
+options are not part of Git itself, but offered by Git services so that code
+development can be more collaborative. A pull request or merge request, allows
+you to preview the merged changes, comment on specific lines of the changes,
+and fix any conflicts before merging.
+
+Often, you can use Git-based workflows to make changes to your Shiny application
+codebase. The GitHub workflow is a lightweight workflow that leverages pull
+requests for collaborative development. Developers create feature branches from
+the main branch, make changes, and then open pull requests to propose these
+changes for review. Once reviewed and approved, the changes are merged into the
+main branch.
+
+## Servers
+
+Your Shiny App deployment is likely going to live on a remote computer that is
+always running, aptly named a "server". In this section, we will be explaining
+how to connect remotely to servers via SSH from the command line.
+
+Servers running remotely often do not contain a graphical user interface, and
+therefore it is recommended to become familiar with the command line.
+Once connected, you can run the command line commands explained in the previous
+sections of this chapter.
+
+The commands in the following subsections require the setup of a server.
+We outline how to do so in Part IV. However, you can also just read along to
+familiarize yourself with the basic concepts of using a remote server.
+
+### Secure Shell (SSH)
+
+The secure shell protocol (SSH) is used to securely connect with a remote computer.
+The connection works via public key cryptography. This means that a public key
+lives on the server and you have a private key on your local machine.
+The private key on your local machine matches with the public key on the remote
+machine to verify your identity.
+
+There are two common ways to authenticate with SSH: using a password, or using a
+private/public key pair. It is recommended to use the private/public key pair
+method as it is much harder to guess a private key compared to a password.
+
+#### Password Authentication
+
+To authenticate with a password, you can run this command:
+`ssh @`.
+
+When you authenticate with a password, a private/public key pair is automatically
+generated for you. However, this method is less secure because a password is
+easier to guess compared to a randomly generated key file.
+
+#### Key-based Authentication
+
+To authenticate with a key file, you can run this command:
+`ssh -i @`.
+
+By running this command, the remote computer can verify that the user logging
+in with the public key that is stored on the remote computer.
+
+We outline how to generate a key in Part IV.
+
+## Readability on the Command Line
+
+Sometimes commands are too long or it would help reading them if we could
+put the parts on multiple lines without "hitting Enter" (executing) too early.
+This is where the backslash (`\`) comes in. We can use the `\` to write
+multi-line commands. For example these two dummy examples are identical:
+
+```bash
+ssh -i ~/.ssh/id_rsa root@123.456.78.90
+
+ssh -i ~/.ssh/id_rsa \
+ root@123.456.78.90
+```
+
+## Summary
+
+You have all the knowledge and tools installed on your local computer to
+effectively develop your Shiny app and to interact with remote servers and
+online services.
+
+Before we review the different ways of developing and hosting your Shiny app,
+we quickly introduce a few example apps. We will use these apps to demonstrate
+the steps for the hosting options.
diff --git a/book-source/01-04-examples.Rmd b/book-source/01-04-examples.Rmd
new file mode 100755
index 0000000..8acff9f
--- /dev/null
+++ b/book-source/01-04-examples.Rmd
@@ -0,0 +1,362 @@
+# Examples
+
+Shiny apps come in many different shapes and form. We will not be able to
+represent this vast diversity, but instead we wanted some apps that can be
+used to showcase common patterns, and that can also fit onto the pages of a
+printed book reasonably well.
+
+We will use 3 Shiny apps as examples, all 3 are implemented in both R and Python:
+
+- `faithful`: a "Hello Shiny!" app displaying the Old Faithful geyser
+ waiting times data as a histogram with a slider that allows to adjust
+ the number of bins used in the histogram --- this app demonstrates the
+ very basics of of interactivity, and it is very short.
+- `bananas`: an app that classifies the ripeness of banana fruits based on
+ the color composition (green, yellow, brown) --- this app demonstrates
+ a more complex use case with dependencies, and the app also relies on a
+ machine learning model, thus it better reflects real world use cases.
+- `lbtest`: an app to test load balancing when scaling Shiny apps to
+ multiple instances.
+
+Let's learn about the example apps.
+
+## Old Faithful
+
+This is the classic "Hello Shiny!" app that you can see in R by trying
+`shiny::runExample("01_hello")`. The app displays the Old Faithful geyser
+waiting times data as a histogram with a slider that allows to adjust
+the number of bins used in the histogram (Fig. \@ref(fig:part1-examples-faithful)).
+The R version of the app was originally written by the Shiny package authors
+[@R-shiny].
+
+The "Hello Shiny!" in R has no dependencies other than `shiny`.
+The Old Faithful app in Python has more requirements besides `shiny`,
+because the Python standard library does not have the geyser data readily
+available, and you need e.g. `matplotlib` [@Hunter2007] for the histogram.
+We wrote the Python version as a mirror translation of the R version,
+so that you can see the similarities and the differences.
+
+```{r part1-examples-faithful, eval=TRUE, echo=FALSE, fig.cap="The `faithful` example Shiny app."}
+# use PNG in both the html and latex versions
+include_graphics("images/01/example-faithful.png")
+```
+
+In R, the data set `datasets::faithful` [@R-base] contains waiting time between eruptions and
+the duration of the eruption for the Old Faithful geyser in Yellowstone National
+Park, Wyoming, USA. We got the Python data set from the Seaborn library
+`seaborn.load_dataset("geyser")` [@Waskom2021].
+
+The source code for the different builds of the Old Faithful Shiny app is at
+. You can download the GitHub repository
+az a zip file from GitHub, or clone the repository with
+`git clone https://github.com/h10y/faithful.git`.
+
+## Bananas
+
+The `bananas` app was born out of a "stuck-in-the-house" COVID-19 project when
+one of the authors bought some green bananas at the store and took daily
+photographs of each fruit.
+Later, the data set was used as part of workshops. The motivation for the app
+is that it follows workflow that is fairly common in all kinds of data science
+projects:
+
+1. Have a question to answer: _Is my banana ripe?_
+2. Collect data: _Go to the store, buy bananas, set up a ring light and take pictures every day over 3 weeks._
+3. Compile the training data: _Classify colour pixels and calculate the relative proportions, score pictures according to ripeness status._
+4. Run exploratory data analysis: _Let's explore and visualize the data set._
+5. Train a classification model: _Estimate the probability that the banana ripeness given colour composition._
+6. Build a "scoring engine": _Given some colour inputs for a new fruit, tell me the probability that the banana is ripe._
+6. Build a user interface: _Let a non technical user to do the data exploration and classification as part of a web application._
+
+### The Bananas Data Set
+
+The data set tracks the ripening colour composition of banana fruits daily over a
+3-week period. The full data set can be found in the [GitHub repository](https://github.com/psolymos/bananas)
+and R package `bananas` (`install.packages("bananas", repos = "https://psolymos.r-universe.dev")`).
+The subset used in the book and the Shiny app constitutes the 6 fruits that were kept at room temperature.
+
+```{r setup_appA, include=FALSE}
+options(scipen = 99, digits = 3)
+```
+
+The table has the following fields:
+
+- `fruit`: the identifier of the fruit,
+- `day`: number between 0 and 20, the number of days since the first set of photographs,
+- `ripeness`: the ripeness class of the fruit based in Peter's personal judgement (Under, Ripe, Very, Over),
+- `green`, `yellow`, `brown`: colour composition, these 3 values add up to 1 (100%).
+
+The colour composition was determined based on colour mapping the pixel values of
+the banana fruits and converting the pixel based 2-dimensional area to proportions.
+
+
+```{r eval=FALSE,echo=FALSE}
+bananas <- read.csv("data/bananas.csv")
+str(bananas)
+```
+
+```{r eval=TRUE,echo=FALSE, results='asis'}
+if(is_latex_output()){
+txt <- '
+The colour composition was determined based on colour mapping the pixel values of
+the banana fruits and converting the pixel based 2-dimensional area to proportions
+(Table \\@ref(tab:part1-bananas-table)).
+'
+} else {
+txt <- '
+The colour composition was determined based on colour mapping the pixel values of
+the banana fruits and converting the pixel based 2-dimensional area to proportions.
+
+The following summary presents the ripeness (U = Under, R = Ripe, V = Very, O = Over)
+and the percentage values of green, yellow, brown colours.
+'
+}
+cat(txt)
+```
+
+```{r part1-bananas-table, eval=TRUE, echo=FALSE}
+x <- read.csv("data/bananas.csv")
+if (!is_latex_output()) {
+ reactable(x,
+ defaultPageSize = 10,
+ showPageSizeOptions = TRUE,
+ pageSizeOptions = c(10, 30, 60, 120),
+ highlight = TRUE,
+ searchable = TRUE,
+ striped = TRUE)
+} else {
+ capt <- "A summary of the bananas data set presented as fruits (columns) over the days (rows), each cell showing the ripeness (U = Under, R = Ripe, V = Very, O = Over) and the percentage values of green, yellow, brown colours, respectively."
+ z <- matrix("", 20, 6)
+ rownames(z) <- sort(unique(x$day))
+ colnames(z) <- sort(unique(x$fruit))
+ for (i in sort(unique(x$day))) {
+ for (j in sort(unique(x$fruit))) {
+ ij <- x$day == i & x$fruit == j
+ # v <- paste0(x$ripeness[ij], " [G=", round(100*x$green[ij]),
+ # "|Y=", round(100*x$yellow[ij]), "|B=", round(100*x$brown[ij]), "]")
+ v <- paste0(substr(x$ripeness[ij], 1, 1), " (", round(100*x$green[ij]),
+ ", ", round(100*x$yellow[ij]), ", ", round(100*x$brown[ij]), ")")
+ z[as.character(i),j] <- v
+ }
+ }
+ rownames(z) <- paste0("Day ", sort(unique(x$day)))
+ # https://haozhu233.github.io/kableExtra/awesome_table_in_pdf.pdf
+ # x[x$fruit == "fruit_14",] |>
+ z |>
+ kbl(booktabs = TRUE,
+ longtable = TRUE,
+ caption = capt) |>
+ kable_styling(
+ font_size = 8,
+ latex_options = c("repeat_header"))
+}
+```
+
+Figure \@ref(fig:part1-bananas-time) shows the change in colour composition over
+the 3 weeks of the experiment. You can see that the proportion of green colour
+went down, parallel to that the yellow colour proportion peaked around day 5.
+Yellow started decreasing after that while the proportion of brown started
+increasing.
+
+We can also present the same information according to the ripeness classes
+(Fig. \@ref(fig:part1-bananas-classes)). You can see that the under-ripe
+class is characterized by high green proportion and the absence of brown.
+The ripe class is characterized by the highest proportion of yellow.
+Very ripe bananas have higher proportion of brown while yellow colour is still
+the most common. Over ripe bananas are mostly brown.
+
+```{r part1-bananas-time, eval=TRUE, echo=FALSE, fig.cap="Colour composition of the bananas over time."}
+x <- read.csv("data/bananas.csv")
+x$ripeness <- factor(x$ripeness, c("Under", "Ripe", "Very", "Over"))
+
+ggplot(
+ data = data.frame(
+ prop = c(x$green, x$yellow, x$brown),
+ Colours = rep(c("green", "yellow", "brown"), each = nrow(x)),
+ day = x$day),
+ mapping = aes(
+ x = jitter(day, 0.5),
+ y = prop,
+ group = Colours,
+ color = Colours)) +
+ geom_point(alpha = 0.75) +
+ geom_smooth(
+ method = 'loess',
+ formula = 'y ~ x',
+ se = FALSE) +
+ scale_color_manual(
+ values = c(
+ green = "#576a26",
+ yellow = "#eece5a",
+ brown = "#5c361f")) +
+ ylim(0, 1) +
+ xlab("Days") +
+ ylab("Proportion") +
+ # theme_minimal()
+ # theme_classic()
+ theme_linedraw()
+```
+
+```{r part1-bananas-classes, eval=TRUE, echo=FALSE, fig.cap="Colour composition of the bananas by ripeness class."}
+ggplot(
+ data = data.frame(
+ prop = c(x$green, x$yellow, x$brown),
+ Colours = factor(
+ rep(c("green", "yellow", "brown"), each = nrow(x)),
+ c("green", "yellow", "brown")),
+ ripeness = x$ripeness),
+ mapping = aes(
+ x = ripeness,
+ y = prop,
+ # group = Colours,
+ fill = Colours)) +
+ geom_boxplot() +
+ # coord_flip() +
+ # facet_wrap(vars(Colours)) +
+ scale_fill_manual(
+ values = c(
+ green = "#576a26",
+ yellow = "#eece5a",
+ brown = "#5c361f")) +
+ xlab("Ripeness") +
+ ylim(0, 1) +
+ ylab("Proportion") +
+ # theme_minimal()
+ # theme_classic()
+ theme_linedraw()
+```
+
+### Model Training
+
+We chose Support Vector Machines (SVM) to model a multi-level response variable
+(under, ripe, very, over) as a function of the green, yellow, and brown colours.
+
+We used the `e1071` package [@R-e107] in R, and the SVM model's prediction accuracy was 90.8%.
+We saved the trained model object as an R binary `.rds` file:
+
+```R
+library(e1071)
+
+# Read the bananas data
+x <- read.csv("bananas.csv")
+x$ripeness <- factor(x$ripeness, c("Under", "Ripe", "Very", "Over"))
+
+# Multinomial classification with Support Vector Machines
+m <- svm(ripeness ~ green + yellow + brown,
+ data = x,
+ probability = TRUE
+)
+
+# Two-way table to test prediction accuracy
+table(x$ripeness, predict(m))
+sum(diag(table(x$ripeness, predict(m)))) / nrow(x)
+
+# Predict ripeness class
+predict(m, data.frame(green = 1, yellow = 0, brown = 0),
+ probability = TRUE)
+predict(m, data.frame(green = 0, yellow = 1, brown = 0),
+ probability = TRUE)
+predict(m, data.frame(green = 0, yellow = 0, brown = 1),
+ probability = TRUE)
+predict(m, data.frame(green = 0.1, yellow = 0.2, brown = 0.7),
+ probability = TRUE)
+
+# Save the model object
+saveRDS(m, "bananas-svm.rds")
+```
+
+We can fit a similar SVM model in Python using scikit-learn (`sklearn`) [@scikit-learn]:
+
+```python
+import pandas as pd
+from joblib import dump
+from sklearn import svm
+
+# Global
+x = pd.read_csv('bananas.csv')
+
+# Train SVM
+x.loc[x.ripeness == 'Under', 'target'] = 0
+x.loc[x.ripeness == 'Ripe', 'target'] = 1
+x.loc[x.ripeness == 'Very', 'target'] = 2
+x.loc[x.ripeness == 'Over', 'target'] = 3
+data_X = x[['green', 'yellow', 'brown']].to_numpy()
+data_y = x.target.values
+svm_model = svm.SVC(probability = True)
+svm_model.fit(data_X, data_y)
+
+#' Predict ripeness class
+svm_model.predict_proba([[1, 0, 0]])
+svm_model.predict_proba([[0, 1, 0]])
+svm_model.predict_proba([[0, 0, 1]])
+svm_model.predict_proba([[0.1, 0.2, 0.7]])
+
+# Write model object to file
+dump(svm_model, 'bananas-svm.joblib')
+```
+
+### The Shiny App
+
+The Shiny app consists of a ternary plot showing the daily colour composition
+of each banana fruit, alongside the new point to be classified (in red),
+as shown in Figure \@ref(fig:part1-examples-bananas). The three numeric inputs
+on the left hand side of the plot control the position of the red dot.
+The classification results based on these inputs are shown on the right hand side
+of the ternary plot. You can see probabilities of under-ripe, ripe, very ripe, and
+over-ripe classes, and the class with highest probability is assigned as a label.
+
+```{r part1-examples-bananas, eval=TRUE, echo=FALSE, fig.cap="The `bananas` example Shiny app."}
+include_graphics("images/01/example-bananas.png")
+```
+
+The source code for the different builds of the Bananas Shiny app is at
+. You can download the GitHub repository
+az a zip file from GitHub, or clone the repository with
+`git clone https://github.com/h10y/bananas.git`.
+
+## Load Balancing Test
+
+Shiny apps can run multiple sessions in the same app instance.
+A common problem when scaling the number of replicas for shiny apps is that
+traffic might not be sent to the same session and thus the app might randomly fail.
+This app is used to determine if the HTTP requests made by the client are correctly
+routed back to the same R or Python process for the session.
+
+Both the Python and the R version of the app registers a [dynamic route](https://shiny.posit.co/r/reference/shiny/latest/session.html)
+for the client to try to connect to. The JavaScript code on the client side will repeatedly hit the
+dynamic route. The server will send a 200 OK status code only if the client reached
+the correct Shiny session, where it originally came from (Fig. \@ref(fig:part1-examples-lbtest)).
+
+```{r part1-examples-lbtest, eval=TRUE, echo=FALSE, fig.cap="The `lbtest` example Shiny app."}
+include_graphics("images/01/example-lbtest.png")
+```
+
+The original Python app was written by Joe Cheng and is from the `rstudio/py-shiny`
+[GitHub repository](https://github.com/rstudio/py-shiny/blob/7ba8f90a44ee25f41aa8c258eceeba6807e0017a/examples/load_balance/app.py). We wrote the R version to mirror the Python
+version.
+
+This app will be useful when the deployment includes load balancing between
+multiple replicas. For such deployments, session affinity (or sticky sessions) needs to
+be available. This app can be used to test such setups.
+If the test fails, it will stop before the counter reaches 100 and will say _Failure!_
+If the app succeeds 100 times, you'll see _Test complete_.
+The app is not useful for testing a single instance deployment, or with Shinylive,
+because these setups won't fail, but you can still try it.
+
+The source code for the different builds of the load balancing test Shiny app is at
+. You can download the GitHub repository
+az a zip file from GitHub, or clone the repository with
+`git clone https://github.com/h10y/lbtest.git`.
+
+## Summary
+
+This is the end of Part I. We covered all the fundamentals that the rest of the
+book builds upon. In the next part, we'll cover all the technical details of
+Shiny hosting that happens on your local machine.
+
+We recommend getting the example repositories mentioned in this chapter
+available on your computer. This way you will be able to follow all the examples
+from the following chapters and won't have to copy paste the text from the
+book to files. Visit the GitHub organization `h10y` which stands for
+_hostingshiny_ (there are 10 letters between the first _h_ and the last _y_):
+.
diff --git a/book-source/02-01-a-developing-shiny.Rmd b/book-source/02-01-a-developing-shiny.Rmd
new file mode 100755
index 0000000..4eb55c2
--- /dev/null
+++ b/book-source/02-01-a-developing-shiny.Rmd
@@ -0,0 +1,954 @@
+# (PART) Shiny Apps {-}
+
+# Developing Shiny Apps
+
+Shiny apps can be written in two languages, R and Python. These 2 programming
+languages are commonly used for data analysis.
+R is an interpreted computer language for statistical computing and graphics.
+Likewise, Python is an interpreted general programming language that is often
+used for data science. Both R and Python run on a wide variety of operating
+systems including Windows, MacOS, and Linux.
+
+In this first chapter, we will cover getting started with developing in Shiny
+using R and Python environments. We discuss the tools commonly used for these
+programming languages and provide instructions on how to run our example Shiny
+application projects in an integrated development environment (IDE). Then we'll
+review some of the easiest ways of sharing and deploying Shiny apps.
+
+## Creating a Shiny App
+
+A Shiny app is made up of the user interface (UI) and the
+server function. The UI and the server can be written in pure R or Python,
+but it can also incorporate JavaScript, CSS, or HTML code.
+
+The app is served to the client (app user) through a host (Internet
+Protocol or IP address) and port number. The server then keeps a
+websocket connection open to receive requests. The Shiny session behind the app
+will make sure this request translates into the desired interactivity and sends
+back the response, usually an updated object, like a plot or a table
+(Fig. \@ref(fig:part2-shiny-app-architecture)).
+
+```{r part2-shiny-app-architecture, eval=TRUE, echo=FALSE, fig.cap="Simplified Shiny app architecture."}
+if (is_latex_output()) {
+ include_graphics("images/02/shiny-app-architecture.pdf")
+} else {
+ include_graphics("images/02/shiny-app-architecture.png")
+}
+
+```
+
+The Old Faithful (`faithful`) app is a relatively simple example that is concise enough to
+demonstrate the structure of Shiny apps with the basics of reactivity.
+It draws a histogram based on the Old Faithful geyser waiting times.
+The number of bins in the histogram can be changed by the user with a slider.
+
+The source code for the different builds of the Old Faithful Shiny app is at
+. You can download the GitHub repository
+az a zip file from GitHub, or clone the repository with
+`git clone https://github.com/h10y/faithful.git`.
+
+To run this app in R, create a folder called `r-shiny` with a new file called
+`app.R` inside the folder. Put this inside the file:
+
+```R
+# r-shiny/app.R
+library(shiny)
+
+x <- faithful$waiting
+
+app_ui <- fixedPage(
+ title = "Old Faithful",
+ h2("Old Faithful"),
+ plotOutput(outputId = "histogram"),
+ sliderInput(
+ inputId = "n",
+ label = "Number of bins:",
+ min = 1,
+ max = 50,
+ value = 25,
+ ticks = TRUE
+ )
+)
+
+server <- function(input, output, session) {
+ output$histogram <- renderPlot(
+ alt = "Histogram of waiting times",
+ {
+ hist(
+ x,
+ breaks = seq(min(x), max(x),
+ length.out = input$n + 1
+ ),
+ freq = TRUE,
+ col = "#BB74DB",
+ border = "white",
+ main = "Histogram of waiting times",
+ xlab = "Waiting time to next eruption [mins]",
+ ylab = "Frequency"
+ )
+ box()
+ }
+ )
+}
+
+shinyApp(ui = app_ui, server = server)
+```
+
+For the Python version, create a new file called `app.py` inside the `py-shiny`
+folder and put this inside the file:
+
+```python
+# py-shiny/app.py
+import seaborn as sns
+import matplotlib.pyplot as plt
+from shiny import App, render, ui
+
+faithful = sns.load_dataset("geyser")
+x = faithful.waiting
+
+app_ui = ui.page_fixed(
+ ui.panel_title("Old Faithful"),
+ ui.output_plot(id = "histogram"),
+ ui.input_slider(
+ id="n",
+ label="Number of bins:",
+ min=1,
+ max=50,
+ value=25,
+ ticks=True
+ ),
+)
+
+def server(input, output, session):
+ @output
+ @render.plot(alt="Histogram of waiting times")
+ def histogram():
+ plt.hist(
+ x,
+ bins = input.n(),
+ density=False,
+ color="#BB74DB",
+ edgecolor="white")
+ plt.title("Histogram of waiting times")
+ plt.xlabel("Waiting time to next eruption [mins]")
+ plt.ylabel("Frequency")
+
+app = App(ui = app_ui, server = server)
+```
+
+Besides Shiny, you'll need to have the `seaborn` and `matplotlib` libraries
+installed for the geyser data set and the histogram. R has these functions as
+part of the base distribution, so no additional installation is needed.
+Install Python dependencies from the `requirements.txt` file with
+`pip install -r py-shiny/requirements.txt`. Here are the contents of
+the `requirements.txt` file:
+
+```text
+# py-shiny/requirements.txt
+shiny>=0.10.2
+matplotlib
+seaborn
+```
+
+You have probably noticed the similarities between the R and Python versions.
+Both started with loading/importing libraries and defining a globally available
+that contained the Old Faithful geyser waiting times. The files then defined
+the user interface (`app_ui`) and the `server` function. At the end,
+we defined the Shiny app as `shinyApp(ui = app_ui, server = server)` and
+`App(ui = app_ui, server = server)`.
+Now let us explore the user interface and the server function.
+
+### The User Interface
+
+The user interface (UI) object controls the layout and appearance of the
+Shiny app. The UI in R is defined as and object called `app_ui`:
+
+```R
+app_ui <- fixedPage(
+ title = "Old Faithful",
+ h2("Old Faithful"),
+ plotOutput(outputId = "histogram"),
+ sliderInput(
+ inputId = "n",
+ label = "Number of bins:",
+ min = 1,
+ max = 50,
+ value = 25,
+ ticks = TRUE
+ )
+)
+```
+
+The `fixedPage()` function renders the main Shiny interface, a
+plot output is nested inside of it alongside the range slider input. The
+slider with the ID `"n"` controls the number of bins in the histogram
+(ranging between 1 and 50, initial value set to 25). The plot with ID
+`"histogram"` will show the distribution of the waiting times.
+
+If we print the `app_ui` object, we get the following (slightly edited) HTML
+output where you can see how the attributes from the R code translate to
+arguments in the HTML version:
+
+```html
+
+
+ Old Faithful
+
+
+
+
+
+
+
+
+```
+
+You can see the container, and the plot and the slider nested inside.
+This is going to be added to the body of the HTML page rendered by
+Shiny. The final HTML page will also contain all the JavaScript and CSS
+dependencies required to make the app interactive and styled properly.
+
+The Python UI uses the `ui` object imported from `shiny`. The setup is very
+similar to the R setup, but naming conventions are slightly different
+(`fixedPage` vs. `page_fixed`, `plotOutput` vs. `output_plot` and
+`sliderInput` vs. `input_slider`).
+
+```python
+app_ui = ui.page_fixed(
+ ui.panel_title("Old Faithful"),
+ ui.output_plot(id = "histogram"),
+ ui.input_slider(
+ id="n",
+ label="Number of bins:",
+ min=1,
+ max=50,
+ value=25,
+ ticks=True
+ ),
+)
+```
+
+Printing the `app_ui` in Python gives the following (slightly edited) HTML output:
+
+```html
+
+
+
+
+
+
+ Old Faithful
+
+
+
+
+
+
+
+
+
+
+```
+
+The only difference relative to the R output is that you can see the ``,
+`` and `` tags. Shiny will inject elements into the HTML head later.
+
+### The Server Function
+
+The server function contains the instructions for the reactivity needed
+for the Shiny app. The server function takes two arguments: `input` and
+`output` (sometimes also `session`). These reactive objects are created
+by Shiny and passed to the server function.
+
+`input` is used to pass the control values, in this case, `input$n`,
+the number of histogram bins:
+
+```R
+server <- function(input, output, session) {
+ output$histogram <- renderPlot(
+ alt = "Histogram of waiting times",
+ {
+ hist(
+ x,
+ breaks = seq(min(x), max(x),
+ length.out = input$n + 1
+ ),
+ freq = TRUE,
+ col = "#BB74DB",
+ border = "white",
+ main = "Histogram of waiting times",
+ xlab = "Waiting time to next eruption [mins]",
+ ylab = "Frequency"
+ )
+ box()
+ }
+ )
+}
+```
+
+The `output` object contains the reactive output objects, in our case the
+rendered plot. `input` and `output` together describe the state of the
+app. Changes in `input` (`input$n` here) will invalidate reactive objects that
+reference these reactive dependencies
+and cause the relevant render functions (`renderPlot()` here) to re-execute.
+
+We can see some differences in the Python version.
+Shiny for Python uses decorators (e.g. `@output`) instead of
+render functions and inputs are invoked as `input.n()`.
+(See the [Framework Comparisons](https://shiny.posit.co/py/docs/comp-r-shiny.html)
+section of the Shiny for Python documentation for a detailed overview of
+R vs. Python similarities and differences.)
+
+FIXME: KALVIN please explain here the `@output` part etc.
+
+```python
+def server(input, output, session):
+ @output
+ @render.plot(alt="Histogram of waiting times")
+ def histogram():
+ plt.hist(
+ x,
+ bins = input.n(),
+ density=False,
+ color="#BB74DB",
+ edgecolor="white")
+ plt.title("Histogram of waiting times")
+ plt.xlabel("Waiting time to next eruption [mins]")
+ plt.ylabel("Frequency")
+```
+
+The server function is called once for each Shiny session.
+A new session with a new websocket connection is created every time a web
+browser connects to the Shiny application.
+
+### Shiny Express
+
+Python for Shiny has two different syntax options, Shiny Core that you saw
+in the previous sections, and [Shiny Express](https://shiny.posit.co/py/docs/express-vs-core.html).
+Shiny Core drew inspiration from the original Shiny for R framework,
+but is _not_ a literal port of Shiny for R.
+Shiny Expressed was introduced quite recently, and is focused on making it
+easier for beginners to use Shiny, and might feel more natural to Python users.
+
+Shiny Core offers the separation between the UI and the server components,
+making it easier to organize code for larger Shiny apps.
+The server function declaration also helps separating code that should only
+run at startup vs. for the sessions.
+In Shiny Express, all of the code in the app file is executed for each session.
+
+There is only one Shiny syntax option in R.
+
+## Organizing Shiny Apps
+
+The previously presented `faithful` app is organized as a single file.
+The file contained all the globally scoped declarations at the top,
+the definition of the UI object and the server function, and ended
+with the Shiny app object. As Shiny apps grow from demo examples to full on
+data science projects, the increased complexity will necessitate the
+organization of the code. You can organize the code into multiple files,
+or even as a package. Let's see the most common patterns.
+
+### Single file
+
+When Shiny is organized in a single file, the convention is to name it `app.R`.
+This way your IDE (RStudio or VS Code) will recognize that it is a Shiny app.
+Apart from this convenience, the file can be named anything, e.g. `faithful_app.R`.
+The single file follows the following structure:
+
+```R
+# Load libraries
+library(shiny)
+
+# Define global variables
+x <- [...]
+
+# Define the UI
+app_ui <- [...]
+
+# Define the server
+server <- function(input, output, session) {
+ [...]
+}
+
+# Assemble the Shiny app
+shinyApp(ui = app_ui, server = server)
+```
+
+At the end of the file, we define the Shiny app using `shinyApp()`.
+To run the app in R, we either have to source the `app.R` or provide the
+file name as an argument to the `runApp()` function, e.g.
+`runApp("/app.R")`.
+
+The Python version takes a very similar form as a single file, usually named
+as `app.py`.
+
+```python
+# Load libraries
+from shiny import App, render, ui
+[...]
+
+# Define global variables
+x = [...]
+
+# Define the UI
+app_ui = [...]
+
+# Define the server
+def server(input, output, session):
+ [...]
+
+# Assemble the Shiny app
+app = App(ui = app_ui, server = server)
+```
+
+You can run the Python Shiny app in your IDE or by using the `shiny run` command
+in the terminal, `shiny run --reload --launch-browser /app.py`.
+This will launch the app in the browser and the server will watch for changes
+in the app source code and rerender.
+
+The libraries and global variables will be accessible for all the Shiny sessions
+by sourcing the app file when you start the Shiny app.
+Variables defined inside the server functions will be defined for each
+session. This way, one user's changes of the slider won't affect the other
+user's experience. However, if one user changes the globally defined
+variables (i.e. using the `<<-` assignment operator), those changes will be
+visible in every user's session.
+
+### Multiple Files
+
+If your app is a bit more complex, you might have multiple files in
+the same directory. By convention, the directory contains at least
+a `server.R` file and `ui.R` file.
+
+Sometimes, there is a third file called
+`global.R`. The `global.R` file is used to load packages, data sets,
+set variables, or define functions that are available globally.
+
+The directory can also have a `www` folder inside that can store
+assets (files, images, icons). Another folder is called `R` that can
+hold R scripts that are sourced before the app starts up.
+This is usually the place to put helper functions and Shiny modules,
+which are also functions.
+If you prefer, you can use the `source()` function to explicitly source
+files as part of the `global.R` script. Just don't put these files in the
+`R` folder to avoid sourcing them twice.
+
+The `bananas` app is organized into multiple files. Here is how the folder
+structure looks like for the R version of the app:
+
+```bash
+bananas/r-shiny
+├── R
+│ └── functions.R
+├── bananas-svm.rds
+├── bananas.csv
+├── dependencies.json
+├── global.R
+├── server.R
+└── ui.R
+```
+
+The `global.R` file looks like this:
+
+```R
+# bananas/r-shiny/global.R
+library(shiny)
+library(plotly)
+library(e1071)
+
+x <- read.csv("bananas.csv")
+x$ripeness <- factor(x$ripeness, c("Under", "Ripe", "Very", "Over"))
+
+m <- readRDS("bananas-svm.rds")
+```
+
+Apart from loading libraries, we read in a CSV file, set factor levels so that
+those print in a meaningful order instead of alphabetical. Finally, we load
+the model we trained earlier. There is also the file `functions.R` in the `R`
+folder that gets source automatically.
+
+It is important to note, that functions defined inside the files of the `R`
+folder, or anything the you `source()` (e.g. `source("R/functions.R")`) will
+be added to the global environment. If you want a sourced file to have
+local scope, you can include that for example inside your server function as
+`source("functions.R", local = TRUE)`.
+
+To run this app, you can click the Run App button the the IDE
+or use `runApp("")`. As long as the directory contains
+the `server.R` and the `ui.R` files.
+
+The choice between single vs. multiple files comes down to personal
+preference and the complexity of the Shiny app. You might start
+with a single file, but as the file gets larger, you might decide to
+save the pieces into their own files.
+However, keeping Shiny apps in their own folder is generally a good idea
+irrespective of having single or multiple files in the folder. This way,
+changing your mind later won't affect how you run the app. You can just
+use the same `runApp("")` command, if you follow these
+basic naming conventions.
+
+We can also split the Python version of the `bananas` app into multiple files.
+
+```bash
+bananas/py-shiny
+├── app.py
+├── bananas-svm.joblib
+├── bananas.csv
+├── functions.py
+└── requirements.txt
+```
+
+Sourcing files in Python works slight differently in Python. It is not like
+adding scripts inline as you saw for R. But rather it follows the same
+namespace pattern as importing from libraries.
+
+These are the first few lines of the `app.py` file:
+
+```python
+# bananas/py-shiny/app.py
+from shiny import App, render, reactive, ui
+from functions import *
+[...]
+```
+
+We import objects from `shiny`, then import everything from the `functions.py`
+file. We define plotting helper functions in this file. But we also have to
+import libraries used inside `functions.py` within the scope of that file:
+
+FIXME: KALVIN please clarify this to be clear for python users
+
+```python
+# bananas/py-shiny/app.py
+import plotly.graph_objects as go
+[...]
+```
+
+To run a Python Shiny app that is in multiple files, you still need to
+specify the file that has the Shiny app object defined that you want to run,
+`shiny run /app.py`. Or if the file is called `app.py` and the
+app object is called `app`, you can use `shiny run` from the current
+working directory.
+
+As Shiny for Python apps become more widespread in the future, we will see
+many different patterns emerge with best practices for organizing files.
+
+### Shiny App with Nested Files Structure
+
+Your app can grow more complex over time, and you might find that the
+of the multiple-file structure described above to be limiting.
+You might have Shiny modules inside Shiny modules. Such a setup might lend itself
+to a hierarchical file structure.
+
+If this is the case, you can use the [Rhino Shiny framework](https://github.com/Appsilon/rhino)
+and the `rhino` R package [@R-rhino]. This Shiny framework was inspired by
+importing and scoping conventions of the Python and JavaScript languages.
+Rhino enforces strong conventions using a nested file structure and modularized
+R code. Rhino also uses the `box` package [@R-box] that defines a hierarchical
+and composable module system for R.
+
+Here is the directory structure for the Rhino version of the `faithful` app:
+
+```bash
+faithful/r-rhino
+├── app
+│ ├── main.R
+│ └── static
+│ └── favicon.ico
+├── app.R
+├── config.yml
+├── dependencies.R
+└── rhino.yml
+```
+
+The `app/static` folder serves a similar purpose to the `www` folder.
+The R code itself is ins the `app.R` folder, specifically the `app/main.R` file.
+You can see how the import statement is structured at the beginning, and how a
+Shiny module is used for the `ui` and `server`:
+
+```R
+box::use(
+ shiny[fixedPage, moduleServer, NS, plotOutput, sliderInput,
+ renderPlot],
+ graphics[hist, box],
+ datasets[faithful],
+)
+
+x <- faithful$waiting
+
+#' @export
+ui <- function(id) {
+ ns <- NS(id)
+ fixedPage(
+ [...]
+ )
+}
+
+#' @export
+server <- function(id) {
+ moduleServer(id, function(input, output, session) {
+ output$histogram <- renderPlot(
+ [...]
+ )
+ })
+}
+```
+
+To run this app, you can call `shiny::runApp()`, the `app.R` file contains a
+single line calling `rhino::app()` which creates the Shiny app object.
+
+The developers of the framework also released a very similar
+Python implementation called [Tapyr](https://github.com/Appsilon/tapyr-template).
+
+### Programmatic Cases
+
+In R, if you want to run the Shiny app as part of another function, you
+can supply a _list_ with `ui` and `server` components (i.e.
+`runApp(list(ui = ui, server = server))`) or a Shiny _app object_
+created by the `shinyApp()` function (i.e.
+`runApp(shinyApp(ui, server))`).
+
+Note that when `shinyApp()` is used at the R console, the Shiny app object
+is automatically passed to the `print()` function, or more specifically, to the
+`shiny:::print.shiny.appobj` function, which runs the app with `runApp()`.
+If `shinyApp()` is called in the middle of a function, the value will not
+be passed to the print method and the app will not be run. That is why you have
+to run the app using `runApp()`. For example, we can write the following
+function where `app_ui` and `server` are defined above as part of the
+single-file `faithful` Shiny app. The `...` passes possible other arguments
+to `runApp` such as the `host` or `port`.
+
+```R
+run_app <- function(...) {
+ runApp(
+ shinyApp(
+ ui = app_ui,
+ server = server
+ ),
+ ...
+ )
+}
+```
+
+Start the app by typing `run_app()` into the console.
+
+### Shiny App as an R Package
+
+Extension packages are the fundamental building blocks of the R ecosystem.
+Apps can be hosted on the Comprehensive R Archive Network (CRAN), on GitHub, etc.
+The tooling around R packages makes checking and testing these packages easy.
+If you have R installed, you can run `R CMD check ` to test your
+package that might include a `tests` folder with unit tests.
+
+Including Shiny apps in R packages is quite commonplace nowadays. These
+apps might aid data visualization, or simplify calculations for not-so-technical
+users. Sometimes the Shiny app is not the main feature of a package, but rather it is
+more like an extension or a demo. In such cases, you might decide
+to put the Shiny app into the `inst` folder of the package. This will make
+the app available after installation, but the app's code will skip any checks.
+
+A consequence is that some dependencies of the app might not be available,
+because that is not verified during standard checks. At the time of installation,
+the contents of the `inst` folder will be copied to the package's root folder.
+Therefore, such an app can be started as e.g.
+`shiny::runApp(system.file("app", package = "faithful"))`.
+This means that there is a package called `faithful`, and in the `inst/app` folder
+you can find the Shiny app.
+FIXME: add here link to the r-package version of the faithful example
+
+```bash
+# faithful
+├── DESCRIPTION
+├── LICENSE
+├── NAMESPACE
+├── R
+│ └── run_app.R
+├── inst
+│ └── app
+│ ├── global.R
+│ ├── server.R
+│ ├── ui.R
+│ └── www
+│ └── favicon.ico
+└── man
+ └── run_app.Rd
+```
+
+We will not teach you how to write an R package. For that, see R's official
+documentation about [_Writing R Extensions_](https://cran.r-project.org/doc/FAQ/R-exts.html),
+or @rpkg-book. The most important parts of the R package are the functions
+inside the `R` folder and the `DESCRIPTION` file, that describes the dependencies
+of the package:
+
+```text
+Package: faithful
+Version: 0.0.1
+Title: Old Faithful Shiny App
+Author: Peter Solymos
+Maintainer: Peter Solymos <[...]>
+Description: Old Faithful Shiny app.
+Imports: shiny
+License: MIT + file LICENSE
+Encoding: UTF-8
+RoxygenNote: 7.3.1
+```
+
+The `inst` folder contains the Shiny app, the `man` folder
+has the help page for our `run_app` function. The `run_app.R` file has the
+following content:
+
+```R
+#' Run the Shiny App
+#'
+#' @param ... Arguments passed to `shiny::runApp()`.
+#' @export
+run_app <- function(...) {
+ shiny::runApp(system.file("app", package = "faithful"), ...)
+}
+```
+
+The `#'` style comments are used to add the documentation next to the function
+definition, which describes how other parameters can be passed to the
+`shiny::runApp` function. The `@export` tag signifies that the `run_app` function
+should be added to the `NAMESPACE` file by the `roxygen2` package [@R-roxygen2].
+
+Calling `R CMD build faithful` will build the `faithful_0.0.1.tar.gz` source file.
+You can install this package using `install.packages("faithful_0.0.1.tar.gz", repos = NULL)` from R
+or you can use the R command line utility: `R CMD INSTALL faithful_0.0.1.tar.gz`.
+Once the package is installed, you can call `faithful::run_app()` to start the
+Old Faithful example.
+
+If you want to include the app as part of the package's functions, place it in the
+package's `R` folder. In this case, `shiny` and all other packages will have to
+be mentioned in the package's `DESCRIPTION` file, that describes the dependencies,
+as packages that the package imports from.
+
+Best practices can be found out there, both about [writing R packages](https://r-pkgs.org/)
+[@rpkg-book], and about engineering Shiny apps using [@Fay2021].
+You can not only test the underlying functions as part of the package, but you
+can apply Shiny specific testing tools, like `shinytest2` [@R-shinytest2].
+
+An R package provides a structure to follow, and everything becomes
+a function. Including Shiny apps in R packages this way is much safer, and this
+is the approach that some of the most widely used Shiny development frameworks
+took. These are the `golem` [@R-golem], and the `leprechaun` [@R-leprechaun]
+packages.
+
+#### Golem
+
+The use and benefits of the Golem framework are described in the book
+_Engineering Production-Grade Shiny Apps_ by @Fay2021.
+Golem is an opinionated framework for building a production-ready Shiny
+apps by providing a series of tools doe developing you app, with an emphasis on
+writing Shiny modules.
+
+A Golem app is contained inside an R package.
+You'll have to know how to build a package, but this is the price to pay for
+having a mature and trusted tools for testing your package from every aspect.
+Let's review how the Golem structure compares to the previous setup.
+We will call this app `faithfulGolem`:
+
+```bash
+# faithfulGolem
+├── DESCRIPTION
+├── LICENSE
+├── NAMESPACE
+├── R
+│ ├── app_config.R
+│ ├── app_server.R
+│ ├── app_ui.R
+│ ├── mod_histogram.R
+│ └── run_app.R
+├── dev
+│ ├── 01_start.R
+│ ├── 02_dev.R
+│ ├── 03_deploy.R
+│ └── run_dev.R
+├── inst
+│ ├── app
+│ │ └── www
+│ │ └── favicon.ico
+│ └── golem-config.yml
+└── man
+ └── run_app.Rd
+```
+
+The most important difference is that we see the UI and server added to the
+`R` folder as functions, instead of plain script files in the `inst` folder.
+The `dev` folder contains development related boilerplate code and
+functions to use when testing the package without the need to reinstall after
+every tiny change you make to the Shiny app or the R package in general.
+The `inst` folder has the static content for the app with the `www` folder
+inside.
+
+The `DESCRIPTION` file looks like this:
+
+```text
+Package: faithfulGolem
+Title: Old Faithful Shiny App
+Version: 0.0.1
+Author: Peter Solymos
+Maintainer: Peter Solymos <[...]>
+Description: Old Faithful Shiny app.
+License: MIT + file LICENSE
+Imports:
+ config (>= 0.3.2),
+ golem (>= 0.4.1),
+ shiny (>= 1.8.1.1)
+Encoding: UTF-8
+RoxygenNote: 7.3.1
+```
+
+Notice that the `config` and `golem` packages are now part of the list of
+dependencies with the package versions explicitly mentioned to avoid possible
+backwards compatibility issues.
+
+Let's take a look at the UI and server functions. The `app_ui` function
+returns the UI as a tags list object. You might notice that we use a module UI
+function here:
+
+```R
+app_ui <- function(request) {
+ tagList(
+ # Leave this function for adding external resources
+ golem_add_external_resources(),
+ # Your application UI logic
+ fixedPage(
+ title = "Old Faithful",
+ h2("Old Faithful"),
+ mod_histogram_ui("histogram_1")
+ )
+ )
+}
+```
+
+The `app_server` function loads the Old Faithful data set and calls the
+histogram module's server function that uses the same `"histogram_1"`
+identified as the module UI function, plus it also takes the data set as
+an argument too:
+
+```R
+app_server <- function(input, output, session) {
+ x <- datasets::faithful$waiting
+ mod_histogram_server("histogram_1", x)
+}
+```
+
+So what does this module look like? That is what you can find in the
+`R/mod_histogram.R` file that defines the `mod_histogram_ui` and
+`mod_histogram_server` functions:
+
+```R
+mod_histogram_ui <- function(id) {
+ ns <- NS(id)
+ tagList(
+ plotOutput(outputId = ns("histogram")),
+ sliderInput(
+ inputId = ns("n"),
+ label = "Number of bins:",
+ min = 1,
+ max = 50,
+ value = 25,
+ ticks = TRUE
+ )
+ )
+}
+
+mod_histogram_server <- function(id, x) {
+ moduleServer(id, function(input, output, session) {
+ ns <- session$ns
+ output$histogram <- renderPlot(
+ alt = "Histogram of waiting times",
+ {
+ graphics::hist(
+ x,
+ breaks = seq(min(x), max(x),
+ length.out = input$n + 1
+ ),
+ freq = TRUE,
+ col = "#BB74DB",
+ border = "white",
+ main = "Histogram of waiting times",
+ xlab = "Waiting time to next eruption [mins]",
+ ylab = "Frequency"
+ )
+ graphics::box()
+ }
+ )
+ })
+}
+```
+
+After building, checking, and installing the `faithfulGolem` R package,
+you'll be able to start the Shiny app by calling `faithfulGolem::run_app()` from R.
+
+FIXME: mention where to find the code for faithfulGolem.
+
+#### Leprechaun
+
+The `leprechaun` R package [@R-leprechaun] uses a similar philosophy to
+creating Shiny applications as packages. It comes with a full set of
+functions that help you with modules, custom CSS, and JavaScript files.
+When using this package, you will notice that `leprechaun` does not become
+a dependency in the `DESCRIPTION` file, unlike in the case of `golem`.
+Apart from this and some organization choices, the two packages and
+the workflow provided by them are very similar. Choose that helps you more
+in terms of your app's specific needs.
+
+Say we name the the R package containing the Old Faithful example as
+`faithfulLeprechaun`. The main functions that are defined should be already
+familiar:
+
+```R
+# R/ui.R
+ui <- function(req) {
+ fixedPage(
+ [...]
+ )
+}
+
+# R/server.R
+server <- function(input, output, session) {
+ x <- datasets::faithful$waiting
+ output$histogram <- renderPlot(
+ [...]
+ )
+}
+
+# R/run.R
+run <- function(...) {
+ shinyApp(
+ ui = ui,
+ server = server,
+ ...
+ )
+}
+```
+
+After the package is installed, the way to run the app is to call
+the `faithfulLeprechaun::run()` function.
+
+FIXME: mention where to find the code for faithfulLeprechaun.
+
diff --git a/book-source/02-01-b-developing-shiny.Rmd b/book-source/02-01-b-developing-shiny.Rmd
new file mode 100755
index 0000000..1a25072
--- /dev/null
+++ b/book-source/02-01-b-developing-shiny.Rmd
@@ -0,0 +1,515 @@
+### Dynamic Documents
+
+Dynamic documents stem from the _literate programming_ paradigm [@Knuth1992],
+where natural language (like English) is interspersed with computer code snippets.
+Nowadays, dynamic documents are used to create technical reports, slide decks
+for presentations, and books, like the one you are reading.
+
+Markdown is a common plain-text format for such dynamic documents, because it
+can be compiled into many different formats using [Pandoc](https://pandoc.org/).
+R Markdown builds upon previous literate programming examples, e.g. Sweave
+that mixes R and \LaTeX [@Leisch2002], and the flexibility provided by
+Pandoc and the markdown format.
+
+R Markdown contains chunks of embedded R (or other) code between opening
+and closing triple backticks. Underneath, you can find the
+`rmarkdown` [@R-rmarkdown] and `knitr` [@R-knitr] R packages at work.
+A more recent iteration of this idea is [Quarto](https://quarto.org/).
+Quarto is an open-source scientific and technical publishing system
+that can include code chunks in many different formats frequently used by
+data scientists (e.g. R, Python, Julia, Observable).
+
+Both R Markdown and Quarto let you to use Shiny inside the documents
+to build lightweight apps without worrying too much about a user interface.
+Such interactive HTML documents cannot provide the same flexibility for
+designing your apps as a standard Shiny app would, but it works wonders for
+simpler use cases. Let's review how you can use Shiny in R Markdown (`.Rmd`)
+and Quarto (`.qmd`) documents.
+
+#### R Markdown
+
+Markdown files usually begin with a _header_ that defines metadata for the
+document, like the title, the author, etc. The header is between
+tripple dashes (`---`) and is written in YAML format (YAML stands for
+YAML Ain't Markup Language).
+
+We'd like to include the Old Faithful example in an R Markdown document.
+So we create a file called `index.Rmd`.
+In the YAML header we need to specify an output format that produces
+HTML, e.g. `html_document`, and the runtime to be set to `shiny`:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ---',
+'title: "Old Faithful"',
+'output: html_document',
+'runtime: shiny',
+'---'), sep="\n ")
+```
+
+The first code chunk would contain the data set definition and a `knitr` option
+to set the echo to false so we don't have to set it for every chunk:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ```{r include=FALSE}',
+'knitr::opts_chunk$set(echo = FALSE)',
+'x <- faithful$waiting',
+'```'), sep="\n ")
+```
+
+Next comes a code chunk with the slider widget:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ```{r}',
+'sliderInput(',
+' inputId = "n",',
+' label = "Number of bins:",',
+' min = 1,',
+' max = 50,',
+' value = 25,',
+' ticks = TRUE',
+')',
+'```'), sep="\n ")
+```
+
+Finally, we render the plot output:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ```{r}',
+'renderPlot(',
+' alt = "Histogram of waiting times",',
+' {',
+' hist(',
+' x,',
+' breaks = seq(min(x), max(x), length.out = input$n + 1),',
+' freq = TRUE,',
+' col = "#BB74DB",',
+' border = "white",',
+' main = "Histogram of waiting times",',
+' xlab = "Waiting time to next eruption [mins]",',
+' ylab = "Frequency"',
+' )',
+' box()',
+' }',
+')',
+'```'), sep="\n ")
+```
+
+FIXME: where can you find the full document.
+
+The output format can be any format tha creates an HTML file. So for example,
+you can use `ioslides_presentation` to create a slideshow with Shiny widgets
+and interactivity. But because Shiny is involved, you need a server to run the
+document.
+
+To render and run the document and the app inside it you can use
+`rmarkdown::run("index.Rmd")`. As a result, the `rmarkdown` package will
+extract the code chunks to create a server definition and uses the
+`index.html` output file to stich in the reactive elements.
+
+When you start the document, you will notice that it always renders the document
+at startup. Not only that, but it also requires a full document render for each
+end user browser session when deployed.
+This startup time for the users can be reduced if we render the HTML only once.
+Running expensive data import and manipulation tasks only once would also greatly
+help the startup times. The runtime for this is called `shinyrmd`
+(or its alias, `shiny_prerendered`):
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ---',
+'title: "Old Faithful"',
+'output: flexdashboard::flex_dashboard',
+'runtime: shinyrmd',
+'---'), sep="\n ")
+```
+
+We'll use the `flexdashboard` [@R-flexdashboard] package to give the document
+more of a dashboard look and feel.
+
+The execution of pre-rendered Shiny documents is divided into two
+execution contexts, the _rendering_ of the user interface and data,
+and the _serving_ of the document to the users.
+
+To indicate the rendering context, you can use `context="render"` chunk option,
+but this can be omitted because this is the default context for all R code chunks.
+The `"render"` is analogous of the `ui.R` file.
+For the first chunk, we define `context="setup"` to mark code that
+is shared between the UI and the server. This is analogous to the `global.R`
+file.
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ```{r context="setup",include=FALSE}',
+'knitr::opts_chunk$set(echo = FALSE)',
+'x <- faithful$waiting',
+'```'), sep="\n ")
+```
+
+We put the slider widget in the sidebar using the `"render"` context:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' Column {.sidebar}',
+'-------------------------------------------------------------',
+'',
+'```{r context="render"}',
+'sliderInput(',
+' inputId = "n",',
+' label = "Number of bins:",',
+' min = 1,',
+' max = 50,',
+' value = 25,',
+' ticks = TRUE',
+')',
+'```'), sep="\n ")
+```
+
+The plot output element goes into the main panel, still as part of the `"render"`
+context:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' Column',
+'-------------------------------------------------------------',
+'',
+'```{r context="render"}',
+'plotOutput("histogram")',
+'```'), sep="\n ")
+```
+
+Finally, we define the `"server"` context for the reactive output.
+This code is run when the interactive document is served and this is the
+same code that we would put into the `server.R` file:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ```{r context="server"}',
+'output$histogram <- renderPlot(',
+' alt = "Histogram of waiting times",',
+' {',
+' hist(',
+' x,',
+' breaks = seq(min(x), max(x), length.out = input$n + 1),',
+' freq = TRUE,',
+' col = "#BB74DB",',
+' border = "white",',
+' main = "Histogram of waiting times",',
+' xlab = "Waiting time to next eruption [mins]",',
+' ylab = "Frequency"',
+' )',
+' box()',
+' }',
+')',
+'```'), sep="\n ")
+```
+
+The `"render"` and `"server"` contexts are run in separate R sessions.
+The first one is run when rendering happens, the second one is run many times,
+once for each user. A consequence of this _context separation_ is that you cannot
+access variables created in "render" chunks within "server" chunks, and the
+other way around.
+
+To render the document, we use `rmarkdown::render("index.Rmd")`,
+then use `rmarkdown::run("index.Rmd")` to run the dashboard.
+You can also set the `RMARKDOWN_RUN_PRERENDER` environment variable to `0`
+to prevent any pre-rendering from happening, e.g. with
+`Sys.setenv(RMARKDOWN_RUN_PRERENDER=0)`.
+
+You can include Python code chunks in your R Markdown documents. Python code is
+evaluated using the `reticulate` package [@R-reticulate].
+But you cannot include Shiny for Python in R Markdown. For that, you have Quarto.
+
+#### Quarto with R
+
+Quarto is very similar to R Markdown in many respects. You can think of it as a
+generalized version of R Markdown that natively supports different programming
+languages to run code chunks. You will find the YAML header familiar.
+To use the Shiny runtime, we define `server: shiny`. The `format: html` is
+means to produce HTML output. The `execute` part refers to global options,
+so for `echo: false` means that example we don't want to code to be echoed into
+the document. Let's start with the following header information in a file
+called `index.qmd`:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ---',
+'title: "Old Faithful"',
+'execute:',
+' echo: false',
+'format: html',
+'server: shiny',
+'---'), sep="\n ")
+```
+
+The language is specified after the triple backticks, here `{r}` means R.
+What is different from R Markdown is that chunk options are defined as
+special comments prefaced with `#|` at the top of the code block
+instead of following the language declaration inside the curly brackets.
+
+UI elements belong to the render context, which is something we do not have
+to specify:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ```{r}',
+'plotOutput("histogram")',
+'sliderInput(',
+' inputId = "n",',
+' label = "Number of bins:",',
+' min = 1,',
+' max = 50,',
+' value = 25,',
+' ticks = TRUE',
+')',
+'```'), sep="\n ")
+```
+
+The `server-start` context will share code and data across multiple user sessions.
+It will execute when the document is first run and will not re-execute for
+every new user. This is like our `global.R` file.
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ```{r}',
+'#| context: server-start',
+'x <- faithful$waiting',
+'```'), sep="\n ")
+```
+
+We can set the context to `server` for the next chunk:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ```{r}',
+'#| context: server',
+'output$histogram <- renderPlot(',
+' alt = "Histogram of waiting times",',
+' {',
+' hist(',
+' x,',
+' breaks = seq(min(x), max(x), length.out = input$n + 1),',
+' freq = TRUE,',
+' col = "#BB74DB",',
+' border = "white",',
+' main = "Histogram of waiting times",',
+' xlab = "Waiting time to next eruption [mins]",',
+' ylab = "Frequency"',
+' )',
+' box()',
+' }',
+')',
+'```'), sep="\n ")
+```
+
+To render and serve this document we have to use the command
+`quarto serve index.qmd`. Pre-rendering our document to speed up startup
+times for the users is really straightforward. We do not have to change
+the server type. We only have to first render the document with
+`quarto render index.qmd`. Then we can serve it with a flag that will tell
+quarto not to render it again: `quarto serve index.qmd --no-render`.
+
+If you wanted to split the `.qmd` file into multiple files corresponding to
+the evaluation contexts, you can have the header and the UI definition in
+the `index.qmd` file. Put the code for the `server-start` context into the
+`global.R` file. The `server.R` file should return the server function:
+
+```R
+# server.R
+function(input, output, session) {
+ output$histogram <- renderPlot(
+ alt = "Histogram of waiting times",
+ {
+ hist(
+ x,
+ breaks = seq(min(x), max(x), length.out = input$n + 1),
+ freq = TRUE,
+ col = "#BB74DB",
+ border = "white",
+ main = "Histogram of waiting times",
+ xlab = "Waiting time to next eruption [mins]",
+ ylab = "Frequency"
+ )
+ box()
+ }
+ )
+}
+```
+
+#### Quarto with Python
+
+The Python version or our Quarto-based Shiny app is very similar to the R version.
+No change is headed in the header. The code chunks will have `{python}`
+defined instead of `{r}`, and of course you have to copy the Python code
+inside the chunks.
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ---',
+'title: "Old Faithful"',
+'execute:',
+' echo: false',
+'format: html',
+'server: shiny',
+'---'), sep="\n ")
+```
+
+The next chunk contains the setup and loads libraries:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ```{python}',
+'import seaborn as sns',
+'import matplotlib.pyplot as plt',
+'from shiny import App, render, ui',
+'',
+'faithful = sns.load_dataset("geyser")',
+'x = faithful.waiting',
+'```'), sep="\n ")
+```
+
+The UI elements, like the slider input control, come next:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ```{python}',
+'ui.input_slider(',
+' id="n",',
+' label="Number of bins:",',
+' min=1,',
+' max=50,',
+' value=25,',
+' ticks=True)',
+'```'), sep="\n ")
+```
+
+Finally, the rendered plot:
+
+```{r eval=TRUE,echo=FALSE,results="asis"}
+cat(c(' ```{python}',
+'@render.plot(alt="Histogram of waiting times")',
+'def histogram():',
+' plt.hist(',
+' x,',
+' bins = input.n(),',
+' density=False, ',
+' color="#BB74DB",',
+' edgecolor="white")',
+' plt.title("Histogram of waiting times")',
+' plt.xlabel("Waiting time to next eruption [mins]")',
+' plt.ylabel("Frequency")',
+'```'), sep="\n ")
+```
+
+Rendering and serving the Python document is the same you used for the R version:
+`quarto serve index.qmd` will do both, so for a pre rendered version, use
+`quarto render index.qmd` first and the serve with the `--no-render` flag.
+
+### Shinylive
+
+Using [Shinylive](https://shiny.posit.co/py/docs/shinylive.html), you can run
+Shiny applications entirely in a web browser, i.e. on the client side, without
+the need for a separate server running in R or Python. This is achieved by
+R or Python running in the browser. The Python implementation of Shinylive uses
+[WebAssembly (WASM)](https://webassembly.org/) and [Pyodide](https://pyodide.org/).
+WASM is a binary format for compiled programs that can run in a web browser,
+whereas Pyodide is a port of Python and many Python packages compiled to WASM.
+
+#### Python Shinylive
+
+We will create Python Shinylive version of the _Hello Shiny_ app following
+the [`posit-dev/py-shinylive`](https://github.com/posit-dev/py-shinylive) GitHub repository.
+First, export the app inside the `py-shiny` folder with the single-file app
+and put the Shinylive version in the `py-shinylive` folder:
+
+```bash
+shinylive export python py-shinylive
+```
+
+The Shiny live version will consist of _static_ files, which means that we can
+copy these files to any static hosting site (like Netlify or GitHub Pages),
+and a browser will be able to display the contents irrespective of the
+underlying operating system, and without the need to have a Python available.
+
+Can we view the output locally? If you click on the `index.html` sitting
+in the `py-shinylive` folder, you will most likely get an error in the browser.
+To read the error you have to find the _developer tools_ and check the
+error messages. You will see something like this:
+
+```text
+Cross-Origin Request Blocked: The Same Origin Policy disallows reading
+the remote resource at file:///[...]/shinylive/shinylive.js.
+(Reason: CORS request not http).
+```
+
+For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts.
+But what is this cross-origin resource sharing ([CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS))?
+If you load the HTML file (like our `index.html`) from a source, the default CORS
+behaviour will expect any other files, like images, or JavaScript/CSS scripts
+to have the same _origin_. By origin, we mean the domain and subdomain.
+
+Now the problem here is that you are viewing a local version of the files,
+which will set the protocol part of the URIs to be `file://` instead of
+`http://` as indicated in the message. Although the folder following the
+file protocol is the same, but this is considered as having opaque origins
+by most browsers and therefore will be disallowed.
+
+If you want to view the files locally, you do it through a local server.
+That way, all files are served from the same scheme and domain (`localhost`)
+and have the same origin.
+Start the server as:
+
+```bash
+python3 -m http.server --directory py-shinylive
+```
+
+Now visit `http://localhost:8000/` in your browser, and the CORS error should
+be gone (port 8000 is the default port for `http.server`).
+
+#### R Shinylive
+
+Shinylive for R uses similar technology built on WebAssembly using the
+[WebR](https://docs.r-wasm.org/webr/latest/) R package.
+Here is how to create an R Shinylive version of the _Hello Shiny_ app
+following the [`posit-dev/r-shinylive`](https://github.com/posit-dev/r-shinylive/)
+GitHub repository using the interactive R console:
+
+```R
+shinylive::export("r", "r-shinylive")
+httpuv::runStaticServer("r-shinylive")
+```
+
+The first line compiles the Shiny app that is inside the `r-shiny` folder into
+the Shinylive version in the `r-shinylive` folder.
+The second command will start the server and open the page in the browser.
+You will see a random port used on localhost, e.g. `http://127.0.0.1:7446`.
+
+#### Shinylive in Quarto
+
+We can include Shinylive (R or Python) apps in Quarto. For that, we need to
+install the [Shinylive quarto extension](https://github.com/quarto-ext/shinylive)
+inside the Quarto project folder with `quarto add quarto-ext/shinylive`.
+
+The header of the Quarto document should list the `shinylive` extension under
+`filter`:
+
+ ---
+ title: "Old Faithful"
+ format: html
+ filters:
+ - shinylive
+ ---
+
+You can include the Shiny application code into a code chunk marked with
+`{shinylive-r}` or `{shinylive-python}`. Use the comment `standalone: true`
+which tells Quarto that the block contains a complete Shiny app, and not
+only parts of it, as we saw before.
+For R, we include the full single-file app. The `viewerHeight: 500` comment is
+needed to have enough space for the UI:
+
+ ```{shinylive-r}
+ #| standalone: true
+ #| viewerHeight: 500
+
+ [...]
+ ```
+
+The Python version uses `{shinylive-python}` and the Python version of the
+single-file Old Faithful Shiny app:
+
+ ```{shinylive-python}
+ #| standalone: true
+ #| viewerHeight: 500
+
+ [...]
+ ```
+
+To view the Quarto document in your browser, use `quarto preview index.qmd`.
+This will work with the R and the Python versions alike.
diff --git a/book-source/02-01-c-developing-shiny.Rmd b/book-source/02-01-c-developing-shiny.Rmd
new file mode 100755
index 0000000..d9b34c7
--- /dev/null
+++ b/book-source/02-01-c-developing-shiny.Rmd
@@ -0,0 +1,276 @@
+## Running Shiny Apps Locally
+
+When you are developing your app locally, you likely want to rin the app to
+check the look and see the changes that you've made.
+In the previous chapter we already used some of the commands needed to
+run the code. Let's review the different ways of running Shiny for R and Python
+apps locally.
+
+Running your app locally is necessary for testing. Of course testing goes way
+beyond just opening up the app in the browser. We will not cover best practices
+for testing your app. If you are interested, you can read about R package based
+development in @Fay2021 or check out the documentation for the
+`shinytest2` R package [@R-shinytest2]. For testing related the Python version,
+see the [Tapyr](https://github.com/Appsilon/tapyr-template) project
+that used [`pytest`](https://docs.pytest.org/) and
+[`playwright`](https://playwright.dev/python/docs/running-tests)
+for validation and testing.
+
+### R
+When the app is in a single R file, you should name it `app.R`
+just like we did previously for the `faithful` example.
+If you have multiple files, make sure that you have the `server.R` and `ui.R`
+files in the same directory. If you are using other frameworks, an `app.R`
+file usually serves as an _entrypoint_ that your IDE will recognize.
+This way, you can run it easily inside of the RStudio IDE
+(Fig. \@ref(fig:part2-rstudio-r)) or VS Code with the Shiny extension
+(Fig. \@ref(fig:part2-vscode-r)) by pushing the "▷ Run App" button.
+Clicking on button would run the app in either a simple browser window
+tab inside your IDE, or in a separate browser window, depending on your settings.
+
+Besides the app showing up in the browser, you can also see some messages
+appearing in your R console or terminal for Python.
+If you inspect the console output, you should see something like this:
+
+```text
+Running Shiny app
+-----------------
+shiny::runApp("r-shiny/app.R", port = 52938)
+
+Loading required package: shiny
+shiny devmode - Turning on shiny autoreload. To disable,
+call `options(shiny.autoreload = FALSE)`
+This message is displayed once every 8 hours.
+
+Listening on http://127.0.0.1:52938
+```
+
+What does this mean? Pushing the Run App button led to running the
+`runApp()` command. This started a web server on localhost (`127.0.0.1`)
+listening on port `52938` (your port number might be different).
+If you visit the `http://127.0.0.1:52938` address in your
+browser, you should see the Shiny app with the slider and the histogram.
+(Stop the app by closing the app window in RStudio or using CTRL+C).
+
+Running the app this way will allow you to keep the server running while
+making changes to the app source code. Your changes will trigger a reload
+so you can immediately see the results.
+You can disable this behavior by turning off the auto-reload option
+with `options(shiny.autoreload = FALSE)`.
+
+```{r part2-rstudio-r, eval=TRUE, echo=FALSE, fig.pos = "bt", fig.cap="Running an R Shiny app in the RStudio IDE."}
+# use PNG for both
+include_graphics("images/02/run-faithful-rstudio-r.png")
+```
+
+```{r part2-vscode-r, eval=TRUE, echo=FALSE, fig.pos = "bt", fig.cap="Running an R Shiny app in the VS Code IDE with the Shiny extension."}
+# use PNG for both
+include_graphics("images/02/run-faithful-code-r.png")
+```
+
+The `runApp()` function can take different types of arguments to run the
+same app. What you saw above was serving the app from the single file.
+If you name the single file something else, e.g.
+`my-app.R`, you can provide the path to a single file as
+`runApp("/my-app.R")`.
+
+You can start the Shiny app from the terminal using the command
+`R -q -e 'runApp("/my-app.R")'` where the `-q` flag means to
+suppress the usual opening message, and `-e` instructs R to execute the
+expression following it. You can also specify the
+port number as argument, e.g. `R -q -e 'runApp(..., path = 8080)'` will
+start the web server on port `8080`.
+
+Running these lines will start the Shiny server locally that you can visit in the browser.
+To be precise, the `shinyApp()` R function returns the app object which is
+run either by implicitly calling the `print()` method on it when running
+in the R console. You can also pass the app object to the `runApp()`
+function (you can stop the server by CTRL+C).
+
+```text
+R -q -e 'shiny::runApp("r", port = 8080)'
+
+> shiny::runApp("r", port = 8080)
+Loading required package: shiny
+
+Listening on http://127.0.0.1:8080
+```
+
+This pattern might be unusual for you if you are using R mostly in interactive
+mode through an IDE. You will see this pattern in the next chapters when we call
+R from the terminal shell. This is how we can start the web server process in
+non-interactive mode.
+
+### Python
+
+You can run the Python app from the RStudio IDE
+(Fig. \@ref(fig:part2-rstudio-py)) or VS Code
+(Fig. \@ref(fig:part2-vscode-py)) by pushing the same "▷ Run App" button.
+You'll see something like this in your console with localhost and
+a randomly picked and available port number (`52938`).
+
+```text
+python -m shiny run --port 52938 --reload [...] py-shiny/app.py
+
+INFO: Will watch for changes in these directories: ['py-shiny']
+INFO: Uvicorn running on http://127.0.0.1:52938 (Press CTRL+C to quit)
+INFO: Started reloader process [85924] using WatchFiles
+INFO: Started server process [85926]
+INFO: Waiting for application startup.
+INFO: Application startup complete.
+INFO: 127.0.0.1:56050 - "GET [...] HTTP/1.1" 200 OK
+INFO: ('127.0.0.1', 56053) - "WebSocket /websocket/" [accepted]
+INFO: connection open
+```
+
+Running the Shiny app in Python relies on the [Uvicorn](https://www.uvicorn.org/)
+web server library that can handle websocket connections.
+
+```{r part2-rstudio-py, eval=TRUE, echo=FALSE, fig.pos = "bt", fig.cap="Running a Python Shiny app in the RSudio IDE."}
+# use PNG for both
+include_graphics("images/02/run-faithful-rstudio-py.png")
+```
+
+```{r part2-vscode-py, eval=TRUE, echo=FALSE, fig.pos = "bt", fig.cap="Running a Python Shiny app in the VS Code IDE with the Shiny extension."}
+# use PNG for both
+include_graphics("images/02/run-faithful-code-py.png")
+```
+
+The other port number (`56053`) is for the websocket connection
+created for the session. If you open another browser window
+pointing to `http://127.0.0.1:52938`, you'll see another websocket
+connection opening for the new session:
+
+```text
+# Opening another browser tab
+INFO: 127.0.0.1:56194 - "GET / HTTP/1.1" 200 OK
+INFO: ('127.0.0.1', 56196) - "WebSocket /websocket/" [accepted]
+```
+
+Use `App(app_ui, server, debug=False)` to suppress the messages.
+
+From the terminal, you can run the single app file from the terminal with
+`shiny run --port 8080 /app.py` on port `8080`.
+If you change the output of the `App()` statement from the default
+`app = App(...)` to `faithful_app = App(...)`,
+you have to define the app as well not just the file:
+`shiny run /app.py:faithful_app`. If the file is called `app.py`
+and the app object is called `app`, you can omit the file name and use
+`shiny run`, in this case `app.py:app` is assumed in the current working directory.
+
+Trying to run both the R and Python version on the same port at the same time
+will not be possible. If you want to run both, use different port numbers, e.g.
+`8080` and `8081`.
+
+The `shiny run` command starts the app. You can use the `--launch-browser` flag
+to automatically launch the app in a web browser.
+The `--reload` flag means that the Python process restarts and the browser
+reloads when you make and save changes to the `python/app.py` file
+(use CTRL+C to stop the process).
+
+### The Shiny App Lifecycle
+
+The traditional way of serving Shiny apps involves a server that runs an
+R or Python process, and each client connects to this server and keeps an open
+websocket connection as long as they are using the application.
+Let's take a closer look at this to better understand what is happening under
+the hood.
+
+Shiny for R relies on the `httpuv` [@R-httpuv] package to handle connections.
+Whenever a new user connects to the Shiny app a new session is started
+and communication between the client and the user session will be happening
+through the websocket connection. The websocket allows two-way communication
+which is the basis of Shiny's reactivity. The JavaScript code on the client
+side can communicate the the R process via this connection.
+
+In Python, the connections are handled by [Uvicorn](https://www.uvicorn.org/),
+and the messages -- as we saw before -- reveal the port numbers used for
+the different user sessions.
+
+Why is this important? Because user sessions having their own ports is the
+basis for isolating these sessions from one another. Users will not be able
+so access data from another session, unless data is leaked through the global
+environment (which should be avoided).
+
+The Shiny app life cycle can be described as follows
+(Fig. \@ref(fig:part2-shiny-lifecycle)):
+
+- Server start: after calling `runApp()` in R or `shiny run` for Python, the
+ `httpuv` or Uvicorn server is started and is now listening on a random of
+ a pre-defined port (e.g. `8080`).
+- Server ready: the application code is sourced including loading the required
+ libraries, data sets, everything from the global scope; if users try to connect
+ to the app before it is ready they will see an error message.
+- Client connects to the app via the port over HTTP protocol.
+- New session created: the backend server (`httpuv` or Uvicorn) starts a user
+ session and runs the server function inside that session; a websocket
+ connection is created for two-way communication.
+- Client-server communication happening while the user is using the app:
+ the server sends the rendered HTML content to the client, including the
+ JavaScript code that will communicate with the server to send and receive
+ data through the websocket connection.
+- When the client detects that the websocket connection is lost,
+ it will try to reconnect to the server.
+- After a certain amount if inactivity, or in the case of disconnected client,
+ the websocket connection and the user session will get terminated
+ and the client browser will "gray out".
+
+You can find more information about the Shiny app life cycle in @Granjon2022
+and @coene2021javascript.
+
+```{r part2-shiny-lifecycle, eval=TRUE, echo=FALSE,out.width="80%", fig.cap="The Shiny app life cycle with websocket connection."}
+if (is_latex_output()) {
+ include_graphics("images/02/shiny-app-websocket.pdf")
+} else {
+ include_graphics("images/02/shiny-app-websocket.png")
+}
+```
+
+For Shinylive applications, the lifecycle does not include a websocket
+connection, and relies purely on HTTP(S) between the client and the server.
+The server will only send the requested resources to the client, and it will not
+do any other work. It will just "serve" these static files.
+The client browser will do the heavy lifting by rendering the HTML and
+running the Web Assembly binary that will take care of the reactivity.
+Such an application will not time out until the browser tab is closed.
+
+## Sharing the Shiny App Code
+
+The source code for the app can be shared with collaborators,
+clients, and users. They can run the app themselves if they are savvy
+enough. When the audiences of a Shiny app are R or Python users,
+it makes sense to share the app as a Gist, a GitHub repository, or a zip file.
+However, sharing Shiny apps, this leaves installing dependencies up to the user.
+You can email the files or the link to your users, you can even send a
+USB drive or a CD ROM in the mail.
+
+The `shiny` R package comes with a few useful functions that makes sharing
+your apps with technical users a bit easier. You can use the `runUrl()`,
+`runGitHub()`, and `runGist()` functions to run app from specific URLs,
+GitHub repositories, and Gists, respectively. `runUrl()` can be pointed at
+R files or compressed files, such as `.zip`.
+
+Another option is distributing your R Shiny app as an R package.
+This option takes care of dependency management. You can install the R
+package from different sources, like GitHub, and you can also have it
+hosted on CRAN. However, the recipients will have to be able to install the
+package from source, which implies familiarity with R and package management.
+Sharing the app source code is a low effort option on your part,
+but might be a high effort option for the ones you are sharing your app with.
+
+## Summary
+
+We reviewed all the different ways of how Shiny apps can be organized during
+development, from standalone R and Python applications to being
+part of dynamic documents. We also reviewed options for sharing your app's code
+with others. Sharing the app source code has several issues when you are sharing
+it with non-technical users. First off, they will have to have an R or Python
+runtime environment. Second, they will have to have all the right
+dependencies installed, sometimes with specific versions of the libraries.
+
+As you will see in the next chapter, sharing the app as a Docker image
+is also an option. This might help with having a runtime environment and
+managing dependencies, but again, your users will need to understand and use
+Docker that can be often too much to ask for.
+So the real reason we are talking about Docker is that it can help you host the
+app.
diff --git a/book-source/02-02-a-containerizing-shiny.Rmd b/book-source/02-02-a-containerizing-shiny.Rmd
new file mode 100755
index 0000000..dc2cc81
--- /dev/null
+++ b/book-source/02-02-a-containerizing-shiny.Rmd
@@ -0,0 +1,927 @@
+# Containerizing Shiny Apps {#containerizing-shiny-apps}
+
+Containerization is a topic that is of increasing interest to data
+scientists and is a key feature for being able to cover the R and Python
+aspects of Shiny hosting in parallel. Once the container image is built,
+deployment and hosting become independent of the language that the app was
+written in. Tooling around this task has made huge advances over the past two
+years, and thanks to this, the topic is now accessible to a wider audience.
+
+Containerization enables you to separate your applications from
+your infrastructure so you can deliver software faster.
+Containerization help makes an application platform independent by creating
+virtual environments in a container. To run a specific application, you will
+need to develop an _image_ for that platform which is then published on a
+_container registry_. The developed Image can be pulled from the registry to
+run a virtualized container environment for the application.
+
+Docker is one of the most popular tools for creating and managing containers
+for Shiny apps. This chapter will outline the needed concepts for containerizing
+your shiny application.
+
+
+Learning Docker seems daunting at first, but it is an incredibly
+powerful piece of technology once you get the hang of it. It is also the
+building block of the modern web.
+
+Docker is not the only tooling for containerizing applications.
+Docker's licensing model has recently changed and can require a paid license for
+commercial use. Therefore, there are alternatives of the Docker engine such as
+using [Podman](https://podman.io/).
+However, Podman is much more technical to use than Docker.
+
+All the general advantages of containerized applications apply to Shiny
+apps. Docker provides isolation to applications. Images are immutable:
+once build they cannot be changed, and if the app is working, it will
+work the same in the future. Another important consideration is scaling.
+Shiny apps are single-threaded, but running multiple instances of the
+same image can serve many users at the same time. Let's dive into the
+details of how to achieve this.
+
+## Docker Concepts
+
+Containers provide portability, consistency, and are used for packaging,
+deploying, and running cloud-native data science applications.
+[Docker](https://www.docker.com/) is the most
+popular virtualization environment to deliver software in containers.
+DOcker is also well supported for Python and R.
+Among the many use cases, Docker is most commonly used to deploy
+reproducible workflows and to provide isolation for Shiny apps.
+
+Containers bundle their own software, libraries and configuration
+files and are isolated from one another. Containers are the
+run-time environments or instances defined by container
+images. Let's review the most important concepts.
+Figure \@ref(fig:part2-docker-architecture) illustrates
+how all the Docker-related concepts all fit together.
+
+```{r part2-docker-architecture, eval=TRUE, echo=FALSE,out.width="80%", fig.cap="The Shiny app life cycle with websocket connection."}
+if (is_latex_output()) {
+ include_graphics("images/02/docker-architecture.pdf")
+} else {
+ include_graphics("images/02/docker-architecture.png")
+}
+```
+
+You as the user, will use the command line as the _client_ to the Docker Engine
+which exists on a _host_ machine. The Docker Engine interfaces with the
+_container registry_ to pull the necessary _images_ for building a local copy of
+an image on the host for running an instance of a container.
+
+### Docker Engine
+
+The Docker Engine is a client-server application that includes a
+server (a long-running daemon process called `dockerd` that listens to API
+requests), an _application programming interface_
+(REST API) that specifies the interface that programs can use to talk to the
+daemon process, and a _command-line interface_ (CLI) that is the client-side
+of Docker.
+
+The CLI uses the REST API to control or interact with the Docker daemon.
+The daemon creates and manages Docker objects, such as images,
+containers, volumes, networks, etc.
+
+### Container Registries
+
+A Docker registry stores Docker images. [Docker Hub](https://hub.docker.com/)
+is a public registry and Docker is configured to look for images on Docker Hub by
+default. There are many other registries, or users can have their own
+private registries. You will see some examples later.
+Strictly speaking, container registries are for images and not containers.
+
+### Images
+
+An image is a read-only template with instructions for creating a
+Docker container. You can view an image as a set of compressed files and
+metadata describing how these files -- also called image layers -- fit together.
+
+An image can be based on another image with additional customization on top of
+this so-called _base_ or a _parent_ image. A base images is an images created
+from scratch, whereas a parent image is just another image that serves as the
+foundation for a new image. You might see base image used for both situations
+when reading tutorials. Don't get confused, the Docker lingo has a few
+inconsistencies that we just have to accept and move on.
+
+### The Dockerfile
+
+Docker builds images by reading the instructions from a file called
+`Dockerfile`. A Dockerfile is a text document that contains all the
+commands to assemble an image using the `docker build` CLI command. You
+will learn more about the `Dockerfile` as part of the worked Shiny
+examples later.
+
+### Containers
+
+A container is a runnable instance of an image. Users can create,
+start, stop a container using the Docker API or CLI. It is also possible
+to connect a container to networks or attach storage to it.
+
+By default, a container is isolated from other containers and the
+host machine. The degree of isolation can be controlled by the user and
+depends on whether it is connected to networks, storage, other
+containers, or the host machine.
+
+### The Docker Command Line
+
+The most common Docker CLI commands are:
+
+- `docker login`: log into a Docker registry,
+- `docker pull`: pull an image from a registry,
+- `docker build`: build a Docker image based on a `Dockerfile`,
+- `docker push`: push a locally built image to a Docker registry,
+- `docker run`: run a command in a new container based on an image.
+
+You will learn more about these commands in the subsequent sections.
+
+## Working with Existing Images
+
+Let's learn how to work with an existing image. Such an image is stored
+in a container registry where we can pull it from if we know its name.
+
+### Image Names and Tags
+
+Image names follow the pattern `/:`.
+The optional `` name specifies where the image is located.
+If you don't specify a host name, the commands will use Docker's public registry
+`docker.io`, aka the Docker Hub.
+
+The `` can be an "official" image, like `ubuntu`.
+The `` is a human-readable identifier that is often a specific version of
+an image. If not specified, the `latest` tag will be used.
+So `ubuntu` as an image name will be identical to `docker.io/ubuntu:latest`.
+
+It is important to note that the `latest` tag only means "latest" in the sense
+of the last image that was tagged as `latest` or was untagged. If you use
+a different tag, like `v1`, the image with the `latest` tag will not get
+updated as well. So don't let this tag fool you. It is strongly recommended
+in production to always explicitly use a tag that is _not_ `latest` but
+a specific version.
+
+The path is usually more structured and consists of slash-separated components.
+It often looks like `/` where the namespace
+specifies the user account or organization to which the image belongs to.
+The base R image maintained by the [Rocker](https://rocker-project.org/) project
+is names as `rocker/r-base` where `rocker` is the organization namespace,
+`r-base` is the repository.
+
+Another example is the R version of the Old Faithful
+example has the image name `ghcr.io/h10y/faithful/r-shiny:latest` which means:
+
+- `ghcr.io` is the GitHub Container Registry host name,
+- `h10y` is the GitHub organization,
+- `faithful` is the GitHub repository,
+- `r-shiny` is the Shiny app build,
+- `latest` is the version tag.
+
+### Pulling an Image
+
+You can use the `docker pull ` command to pull an image from
+a public registry. For example `docker pull ubuntu:24.04` will pull the
+24.04 version of the "official" Ubuntu image from the Docker Hub.
+`docker pull rocker/r-base:4.4.1` will pull the image with R version 4.1.1.
+Pull the R Shiny version of the Old Faithful as:
+
+```bash
+docker pull ghcr.io/h10y/faithful/r-shiny
+
+# Using default tag: latest
+# latest: Pulling from h10y/faithful/r-shiny
+# Digest: sha256:12e[...]4ea
+```
+
+You can see from the messages that the `latest` tag was applied because
+we did not specify the tag. We can also see the SHA256 digest, that is
+a unique and immutable identifier. The name can change, or multiple names can
+refer to the same image (i.e. a set of layers and their manifest). But the
+image digest will be the same. To "pin" the exact version, you can use the
+`@sha256:12e[...]4ea` pattern:
+
+```bash
+docker pull ghcr.io/h10y/faithful/r-shiny@sha256:12e[...]4ea
+```
+
+To pull all images from a repository, you can use the `--all-tags` flag:
+
+```bash
+docker image pull --all-tags ghcr.io/h10y/faithful/r-shiny
+```
+
+This will pull not only the `latest`, but also the image tagged as `main`
+named after the Git branch. Use the `docker images` command to list the
+images.
+
+### Docker Login
+
+You don't need to authenticate for public images, but in
+case you are trying to pull a private image from a private repository, you
+need to log into the container registry. Suck private repositories are common
+and are available on Docker Hub, the GitHub or GitLab container registries.
+More on the different container registries later.
+
+To log in to the GitHub container registry, use:
+
+```bash
+docker login ghcr.io
+```
+
+This command will ask for our credentials interactively. If you want,
+you can provide your username and password. But it is usually
+recommended to use a personal access token (PAT) instead of your
+password because PAT can have more restricted scopes, i.e. only used to
+(read) access the container registry which is a lot more secure.
+You can also set expiry dates and can revoke these tokens any time.
+
+Let's say that you saved your PAT in a file `~/my_password.txt` in the
+root of your home folder (`~`). You can pass the PAT value to the
+`docker login` command via the standard input as:
+
+```bash
+cat ~/my_password.txt | docker login \
+ --username \
+ --password-stdin
+```
+
+where `` is your GitHub username.
+
+### Running a Container
+
+The next command is `docker run` which runs a command in a new container.
+It pulls the image if needed before starting the container.
+
+Try the following command. It will pull the latest image for the Python build
+of the Old Faithful example app, then it will start a new container:
+
+```bash
+docker run -p 8080:3838 ghcr.io/h10y/faithful/py-shiny
+```
+
+The `-p` is a shorthand for `--publish`, that instructs Docker to
+publish a container's port to the host port. In our example, 3838 is the
+container's port which is mapped to port 8080 of the host machine. As a
+result, you can visit `http://127.0.0.1:8080` in your browser to see the
+Python Shiny app. Hit CTRL+C to stop the container. We will learn about
+container ports in a bit, but in essence just a channel that the
+information is sent back and forth.
+
+## Building a New Image
+
+So far you saw how to use the basic Docker commands to
+pull and run images. Now you'll build a Docker image by recreating the
+Old Faithful Shiny app that we worked with before.
+
+In our examples, we will use the following setup: a file named `Dockerfile`
+sits next to a folder names `app`, and the Shiny app files like
+`app.R` or `app.py` are in this folder. This setup is convenient
+because we can copy all the files from teh `app` folder without
+having to worry about copying files that should not be there.
+
+```bash
+├── Dockerfile
+└── app
+ └── ...
+```
+
+### R for Shiny
+
+FIXME: Link here the repo and folder.
+
+For our R Shiny example this is what is inside the `Dockerfile`:
+
+```dockerfile
+FROM rocker/r2u:24.04
+RUN R -q -e "install.packages('shiny')"
+RUN groupadd app && useradd -g app app
+WORKDIR /home/app
+COPY app .
+RUN chown app:app -R /home/app
+USER app
+EXPOSE 3838
+CMD ["R", "-e", "shiny::runApp(host='0.0.0.0', port=3838)"]
+```
+
+We will explain the `Dockerfile` commands in the next section.
+For now, you can use the `docker build` command to build the image from the
+`Dockerfile`. You will have to be in the same directory as the `Dockerfile`,
+this place is what we'll call as the build context. This is what the `.` at the
+end of the command stands for:
+
+```bash
+docker build -t r-shiny:v1 .
+```
+
+The context here specifies the current directory (`.`), but it can be
+any relative or absolute filepath. Files and directories inside the
+context directory are available to the builder, so it can load them when needed.
+You can use a `.dockerignore` file to list files and directories that should
+be ignored within the build context. It is similar to the `.gitignore` file.
+
+The instructions are taken from the `Dockerfile` at the root of the build
+context. If you want to specify a different file, do so by providing the
+path to the file using the `-f` (or `--file`) option as `docker build -f Dockerfile2 .`.
+
+The `-t` argument (same as `--tag`) is followed by the image name
+(`r-shiny-test`) and the tag (`v1`). If you do not specify the image
+name/tag at image build (i.e. `docker build .`), Docker will not tag the image
+but it will have an image ID that you can use later to tag the image with
+`docker tag r-shiny-test:v1`.
+
+You can apply multiple tags as:
+
+```bash
+docker build -t r-shiny:v1 -t r-shiny:latest .
+```
+
+### Buildx and BuildKit
+
+While the builder is running, you'll see lots of messages printed
+as Docker goes through the instructions from the `Dockerfile`.
+Newer Docker Desktop version use `buildx`, which brings extended build
+capabilities with BuildKit, such as
+
+As of Docker Engine 23.0 and Docker Desktop 4.19, Buildx is the default build
+client and user interface. Buildx brings extended build capabilities with
+BuildKit. BuildKit is the server that handles the build execution, e.g.
+it communicates with registries, instructs the Docker Engine and accesses
+the local file system.
+
+The Buildx output is nicer and it provides you with timings for every step
+of your `Dockerfile`:
+
+```{r eval=TRUE,echo=FALSE,size='footnotesize'}
+cat(c("[+] Building 32.4s (12/12) FINISHED",
+" => [internal] load build definition from Dockerfile 0.0s",
+" => => transferring dockerfile: 282B 0.0s",
+" => [internal] load metadata for docker.io/rocker/r2u:24.04 1.2s",
+" => [auth] rocker/r2u:pull token for registry-1.docker.io 0.0s",
+" => [internal] load .dockerignore 0.0s",
+" => => transferring context: 2B 0.0s",
+" => [1/6] FROM docker.io/rocker/r2u:24.04@sha256:f327[...]dd73 9.2s",
+" => => resolve docker.io/rocker/r2u:24.04@sha256:f327[...]dd73 0.0s",
+"[...]",
+" => [internal] load build context 0.0s",
+" => => transferring context: 845B 0.0s",
+" => [2/6] RUN groupadd app && useradd -g app app 0.7s",
+" => [3/6] RUN R -q -e \"install.packages('shiny')\" 20.9s",
+" => [4/6] WORKDIR /home/app 0.0s",
+" => [5/6] COPY app . 0.0s",
+" => [6/6] RUN chown app:app -R /home/app 0.1s",
+" => exporting to image 0.3s",
+" => => exporting layers 0.3s",
+" => => writing image sha256:4d10[...]bab7 0.0s",
+" => => naming to docker.io/library/r-shiny:v1 0.0s"), sep="\n")
+```
+
+Sometimes you want to inspect the output and do not only want the collapsed
+output. Add the `--progress=plain` to the build command to see all the
+output. This comes handy when you want to troubleshoot.
+
+BuildKit also offers other nice features, for example setting the target
+platform(s) for the build via the `--platform` option.
+The default value is the platform of the BuildKit daemon where the build runs,
+i.e. your laptop of a server. This can be important for Mac OS X users
+on Apple Silicone (M1 and above), because the default ARM64 build will
+have poor performance or might fail on other platforms on AMD64 machines.
+Use the `--platform=linux/arm64` to build the image for AMD64 architecture.
+You can also build for multiple architectures at once with
+`docker build --platform linux/amd64,linux/arm64 .`.
+
+### Inspecting the Image
+
+The output of the build is an image that has a SHA256 hash that can be used
+as a unique identifier. The image is made up of image layers. These layers are
+created by the instructions from the `Dockerfile`. If you run the build command
+again you will notice that instead of 32 seconds, it will take almost no time
+to build the image. This is because the layers are cached by default and Docker
+smartly evaluates which instructions and files have changed since the last
+build. Sometimes the cache gets tangled, or you just want to make sure that
+the error is not a caching issue. In this case use the `--no-cache` flag
+with `docker build`.
+
+You can use the `docker history r-shiny:v1` command to see how the image was
+built and you can see the sizes for every layer. Intermediate layers
+have a size of 0B and these do not contribute to the overall image size.
+The layers created 2 hours ago are the layers we created, the layers
+created 2 weeks ago are the layers from the parent image `rocker/r2u:24.04`,
+whereas the layers created 2 months ago are the official `ubuntu:24.04` image
+layers that form the parent image of the `rocker/r2u:24.04` one:
+
+```{r eval=TRUE,echo=FALSE,size='footnotesize'}
+cat(c("IMAGE CREATED CREATED BY SIZE",
+"4d[...]52 2 hours ago CMD [\"R\" \"-e\" \"shiny::runApp(host='0.0.0.0', 0B",
+" 2 hours ago EXPOSE map[3838/tcp:{}] 0B",
+" 2 hours ago USER app 0B",
+" 2 hours ago RUN /bin/sh -c chown app:app -R /home/app # 780B",
+" 2 hours ago COPY app . # buildkit 780B",
+" 2 hours ago WORKDIR /home/app 0B",
+" 2 hours ago RUN /bin/sh -c R -q -e \"install.packages('sh 109MB",
+" 2 hours ago RUN /bin/sh -c groupadd app && useradd -g ap 5.14kB",
+" 2 weeks ago RUN /bin/sh -c apt-get update && apt 642MB",
+" 2 weeks ago ENV TZ=UTC 0B",
+" 2 weeks ago ENV DEBIAN_FRONTEND=noninteractive 0B",
+" 2 weeks ago ENV LANG=en_US.UTF-8 0B",
+" 2 weeks ago ENV LC_ALL=en_US.UTF-8 0B",
+" 2 weeks ago RUN /bin/sh -c useradd -s /bin/bash -m docke 81.6MB",
+" 2 weeks ago LABEL org.label-schema.license=GPL-2.0 org.l 0B",
+" 2 months ago /bin/sh -c #(nop) CMD [\"/bin/bash\"] 0B",
+" 2 months ago /bin/sh -c #(nop) ADD file:ac9d5a9d5b9b1217 76.2MB",
+" 2 months ago /bin/sh -c #(nop) LABEL org.opencontainers. 0B",
+" 2 months ago /bin/sh -c #(nop) LABEL org.opencontainers. 0B",
+" 2 months ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B",
+" 2 months ago /bin/sh -c #(nop) ARG RELEASE 0B"), sep="\n")
+```
+
+The `docker inspect r-shiny:v1` returns a long JSON output that is the
+metadata of the image. It also has the SHA256 hash of the image.
+Here is the greatly stripped output:
+
+```{r eval=TRUE,echo=FALSE,size='footnotesize'}
+cat(c("[",
+" {",
+" \"Id\": \"sha256:4d10[...]bab7\",",
+" \"RepoTags\": [\"r-shiny:v1\"],",
+" \"Created\": \"2024-07-05T04:59:01.123398172Z\",",
+" \"Config\": {",
+" \"User\": \"app\",",
+" \"ExposedPorts\": {\"3838/tcp\": {}},",
+" \"Cmd\": [\"R\",\"-e\",",
+" \"shiny::runApp(host='0.0.0.0', port=3838)\"],",
+" \"Volumes\": null,",
+" \"WorkingDir\": \"/home/app\",",
+" \"Entrypoint\": null,",
+" },",
+" \"Architecture\": \"amd64\",",
+" \"Os\": \"linux\",",
+" \"Size\": 909132976,",
+" \"Metadata\": {",
+" \"LastTagTime\": \"2024-07-05T06:20:22.2764725Z\"",
+" }",
+" }",
+"]"), sep="\n")
+```
+
+Once the docker image is built, you can run the container to
+make sure the app is working as expected:
+
+```bash
+docker run -p 8080:3838 r-shiny:v1
+```
+
+### R for Python
+
+FIXME: provide link to this example.
+The `Dockerfile` for the Python version looks like this:
+
+```Dockerfile
+FROM python:3.9
+COPY app/requirements.txt .
+RUN pip install --no-cache-dir --upgrade -r requirements.txt
+RUN groupadd app && useradd -g app app
+WORKDIR /home/app
+COPY app .
+RUN chown app:app -R /home/app
+USER app
+RUN mkdir .config
+ENV MPLCONFIGDIR=/home/app/.config
+ENV HOME=/home/app
+EXPOSE 3838
+CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "3838"]
+```
+
+Again, we'll explain each line shortly. To build anc check the Docker image,
+use the following commands:
+
+```bash
+docker build -t py-shiny:v1 .
+
+docker run -p 8080:3838 py-shiny:v1
+```
+
+## Managing Images and Containers
+
+To list the Docker images, use the `docker images` command. It will give you
+a quick summary of the images:
+
+```bash
+REPOSITORY TAG IMAGE ID CREATED SIZE
+py-shiny v1 ed11a2980c07 5 seconds ago 1.24GB
+r-shiny v1 4d10f42d6a52 About an hour ago 909MB
+```
+
+Try the `docker ps` command which lists the containers. If you have a container
+running, you will see it listed with status `Up`. If you stopped all the containers
+by exiting with CTRL+C, you will not see any running containers listed.
+To see the stopped but not removed containers, use the `docker ps -a` command:
+
+```bash
+CONTAINER ID IMAGE COMMAND CREATED [...]
+3d91a5a2a47f r-shiny:v1 "R -e 'shiny::runApp…" 3 minutes ago [...]
+3d91a5a2a47f r-shiny:v1 "R -e 'shiny::runApp…" 53 minutes ago [...]
+```
+
+Sometimes you need to be able to manage containers because the kill signal
+is not properly relayed to the container when using CTRL+C. This happens when
+the `CMD` instruction is provided in shell form
+(i.e. `CMD R -e "shiny::runApp()"` instead of `CMD ["R", "-e", "shiny::runApp()"]`).
+The shell form runs as a child process of `/bin/sh -c` (default `ENTRYPOINT`),
+and the executable does not receive Unix signals.
+
+If this happens, you need to find a way to stop the container.
+The following commands help you manage the containers:
+
+- `docker container stop `: gracefully stop a running container
+ (wait for the process to stop),
+- `docker container start `: start a stopped container,
+- `docker container restart `: restart a container,
+- `docker container rm `; remove a container,
+- `docker container kill `: kill a container (abruptly terminate
+ the entry point process).
+
+`docker container rm --force ` will remove running containers too.
+You can make sure the container is removed after CTRL+C if you add the `--rm`
+option to automatically remove the container when it exits.
+
+If you build images during development while keeping the image name and tag the
+same you will end up with "dangling" images that are untagged and are not used
+any longer. Dangling images can accumulate over time and fill up the
+available space that Docker Desktop is allocating for images.
+Use `docker system prune` to clean up these dangling images.
+The command `docker system prune -a` will remove all unused images and containers.
+
+Dangling images `docker system prune` or all image layers with
+`docker system prune -a` to save up space (sometimes the available space
+for your docker desktop (64GB?) fills up -- see the bottom of you desktop UI).
+
+## Sharing Images
+
+As we saw, Docker images are just compressed files linked by metadata.
+You should be able to copy these files and move them around.
+The `docker save` command lets you save an image to a compressed tar file:
+
+```bash
+docker pull r-shiny:v1
+
+docker save -o r-shiny-v1.tar r-shiny:v1
+```
+
+Next, you take this tar file, copy it to another server and load it with:
+
+```bash
+docker load --input r-shiny-v1.tar
+```
+
+Now imagine that you are managing more than two machines, or you want to share
+the Docker image with others so that they can use it or to serve as a parent image.
+The save/copy/load workflow becomes cumbersome quickly.
+In this case, using a registry might be a much better idea. There are
+many options to choose from, and you can even host your own registry.
+
+### Pushing Images
+
+Let's tag the `r-shiny` image so that it has a host defined:
+
+```bash
+docker tag r-shiny:v1 ghcr.io/h10y/faithful/r-shiny:latest
+```
+
+Now we can push the locally built Docker image to a container registry:
+
+```bash
+docker push ghcr.io/h10y/faithful/r-shiny:latest
+```
+
+Note that this command will not work on your machine because you do not
+have write access to the `ghcr.io/h10y/faithful` repository.
+You need to create an image tag that would let you push for example
+to your own personal Docker Hub account.
+
+The image tag should start with the registry name unless you are pushing
+to Docker Hub. When the image tag is not specified, Docker will treat
+the new image as `:latest` automatically.
+
+### Docker Registries
+
+A Docker registry stores Docker images. This is where we push images to and pull
+images from. [Docker Hub](https://hub.docker.com/)
+is a public registry and Docker is configured to look for images on Docker Hub by
+default. Docker Hub is a service provided by Docker for finding and sharing
+container images. The canonical host name for Docker Hub is `docker.io`. This
+is the default registry when you don't specify a registry host as part of the
+image name.
+
+There are many other registries out there besides Docker Hub. Here is a
+non-exhaustive list of options.
+
+The GitHub Container Registry (GHCR) is available as part of GitHub Packages
+for free and paid plans, even for private repositories under the free
+plan. This registry requires no authentication for public images, otherwise
+you have to authenticate using your GitHub token. The visibility of the
+images inherits the repository visibility but can be change by the owner.
+The host name for GHCR is `ghcr.io`.
+
+An alternative to GitHub is GitLab (host name `registry.gitlab.com`),
+that has provided registry support for its free (public and private) repositories
+long before GitHub. The registry is tightly integrated with GitLab's CICD
+pipelines. This registry also needs login with a token ofr private images.
+
+Heroku is a platform provider and it also comes with a Docker
+registry (host name is `registry.heroku.com`) where the Docker-based deployments
+push the images to.
+
+Of course, every major cloud provider offers a Docker container registry
+that is often integrated with their other offerings. Latency should be
+minimal due to network proximity to the servers:
+
+- Amazon Elastic Container Registry
+- Azure Container Registry
+- Google Container Registry
+- DigitalOcean Container Registry
+
+Other common alternatives for container registries include the JFrog Container
+Registry, Harbor, and Scaleway.
+
+Although these services are called "container registry", but strictly
+speaking they store container images.
+
+### Log In to a Registry
+
+When you work with private registries or private images, you need to log
+in with the `docker login` command. For Docker Hub, just type
+`docker login`. For all other registries, type in the registry URL as
+well, e.g. `docker login ghcr.io`.
+
+The Docker CLI then will prompt you for your username and password (or
+access token).
+
+You can log in programmatically by providing your username and the
+password through standard input from a file:
+
+```bash
+cat ~/my_password.txt | docker login -u USER --password-stdin
+```
+
+You can also use an environment variable to store your token value
+that you can pass to the login command as:
+
+```bash
+ export TOKEN=
+echo $TOKEN | docker login ghcr.io -u USER --password-stdin
+```
+
+Notice the white space before the `export` statement, use double spaces so that
+the command after the spaces will not be saved in your shell history.
+The history allows you to recall previous commands by pushing the up arrow
+key. The shell history is really just a text file, so copy pasting secrets
+into the terminal will leave a trace.
+
+Use one of these approaches to log into any public or private repository
+for which you have credentials. The credentials will be stored
+locally in `$HOME/.docker/config.json` on Linux and Mac or in
+`%USERPROFILE%/.docker/config.json` on Windows. After login, there is no
+need to re-authenticate until you log out with `docker logout`.
+
+Note that `docker login` requires users to use `sudo` or be `root` in
+most cases.
+
+It is always a good idea to use a token instead of your password. Tokens
+can have limited scope (i.e. only for pulling images), and can be
+revoked at any time without it impacting other areas of your life.
+
+### Local Registry
+
+You might not want the Docker images to leave your computer because you need an
+air gapped environment, or you are setting up a registry within your
+virtual private network (VPN). In these situations, you can host your own
+container registry.
+
+
+If you want a registry hosted on your machine, just pull the registry image.
+The next command will pull the registry image, and run the similarly named
+container in the background on port 5000:
+
+```bash
+docker run -d \ # daemonixed background process
+ -p 5000:5000 \ # expose port
+ --restart=always \ # restart after errors
+ --name registry \ # give it a name
+ registry:2 # image name and version tag
+```
+
+Tag an image with the host name of your local registry, `localhost:5000`, and
+push the image:
+
+```bash
+docker tag r-shiny:v1 localhost:5000/r-shiny:v1
+
+docker push localhost:5000/r-shiny:v1
+```
+
+To test if it worked, remove the images from your local Docker system.
+If you use the `-f` flag and specify the image ID then the `docker rmi`
+command untags and removes all images that match that ID (get the image ID
+from `docker images`):
+
+```bash
+docker rmi -f
+```
+
+Now you can pull the image from your local registry:
+
+```bash
+docker pull localhost:5000/r-shiny:v1
+```
+
+The next command stops and removes the registry container. It is a daemonized
+(background) process, so CTRL+C won't work. The `-v` option makes sure to remove
+anonymous volumes associated with the container which is often used to mount a
+volumes from your hard drive into the container where the images are stored:
+
+```bash
+docker container stop registry && \
+ docker container rm -v registry
+```
+
+If you want your registry to be accessed over a public network, then you need to
+think about security and access control. You'll have to set up transport
+layer security (TLS) for HTTPS and user authentication, which are advanced topics
+and we recommend using a commercial container registry that we listed above
+and use private repositories to control access to your images.
+
+## The Dockerfile
+
+Let's review the Dockerfiles line by line. The full Dockerfile
+reference can be found
+[here](https://docs.docker.com/engine/reference/builder/).
+
+The [`FROM`
+instruction](https://docs.docker.com/engine/reference/builder/#from)
+initializes a new build stage and sets the *base image*.
+Take the latest `r-base` image from the `rocker` project (see on [Docker
+Hub](https://hub.docker.com/r/rocker/r-base)):
+
+```dockerfile
+FROM rocker/r-base:latest
+```
+
+The `LABEL` instruction is optional, it adds metadata to an image, e.g.
+who to contact in case of issues or questions:
+
+```dockerfile
+LABEL maintainer="USER "
+```
+
+The
+[`RUN`](https://docs.docker.com/engine/reference/builder/#run)
+[instruction](https://docs.docker.com/engine/reference/builder/#run)
+executes the command in a new *layer* (a
+[layer](https://docs.docker.com/glossary/#layer)
+is a modification to the image) on top of the current image. The
+following command updates the base image with a couple of libraries that
+are required by Shiny and related R packages (system dependencies):
+
+```dockerfile
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ sudo \
+ libcurl4-gnutls-dev \
+ libcairo2-dev \
+ libxt-dev \
+ libssl-dev \
+ libssh2-1-dev \
+ && rm -rf /var/lib/apt/lists/*
+```
+
+The following `RUN` command uses the
+[littler](https://github.com/eddelbuettel/littler)
+command-line interface shipped with the `r-base` image to install the
+Shiny package and its dependencies:
+
+```dockerfile
+RUN install.r shiny
+```
+
+The next command sets the options in the `Rprofile.site` file which are
+going to be loaded by the R session. These options specify the Shiny
+host and port that `runApp` will use.
+
+Do not run containers as root in production. Running the container with
+root privileges allows unrestricted use which is to be avoided in
+production. Although you can find lots of examples on the Internet where
+the container is run as root, this is generally considered bad practice.
+
+> Switching to the root `USER` opens up certain security risks if an
+> attacker gets access to the container. In order to mitigate this,
+> switch back to a non privileged user after running the commands you
+> need as root. – [Hadolint rule
+> DL3002](https://github.com/hadolint/hadolint#rules)
+
+The following command creates a Linux group and user, both called `app`.
+This user will have access to the app instead of the default root user:
+
+```dockerfile
+RUN addgroup --system app \
+ && adduser --system --ingroup app app
+```
+
+You can read more about security considerations
+[here](https://engineering.bitnami.com/articles/why-non-root-containers-are-important-for-security.html)
+and Dockerfile related code smells
+[here](https://arxiv.org/abs/2103.12298). Read
+about [best
+practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
+and [linting
+rules](https://github.com/hadolint/hadolint#readme)
+in general that can be helpful in reducing vulnerabilities of your
+containerized application.
+
+The [`WORKDIR`
+instruction](https://docs.docker.com/engine/reference/builder/#workdir)
+sets the working directory for subsequent instructions. Change this to
+the home folder of the `app` user which is `/home/app`:
+
+```dockerfile
+WORKDIR /home/app
+```
+
+The [`COPY`
+instruction](https://docs.docker.com/engine/reference/builder/#copy)
+copies new files or directories from the source (our `app` folder
+containing the R script files for our Shiny app) and adds them to the
+file system of the container at the destination path (`.` refers to the
+current work directory defined at the previous step):
+
+```dockerfile
+COPY app .
+```
+
+The next command sets permissions for the `app` user:
+
+```dockerfile
+RUN chown app:app -R /home/app
+```
+
+In general, use the shell form for RUN, and the exec form for CMD and ENTRYPOINT.
+https://emmer.dev/blog/docker-shell-vs.-exec-form/
+
+The [`USER`
+instruction](https://docs.docker.com/engine/reference/builder/#user)
+sets the user name (or UID) and optionally the user group (or GID) to
+use when running the image:
+
+```dockerfile
+USER app
+```
+
+The [`EXPOSE`
+instruction](https://docs.docker.com/engine/reference/builder/#expose)
+tells Docker which ports the container listens on at runtime. Set this
+to the Shiny port defined in the `Rprofile.site` file:
+
+```dockerfile
+EXPOSE 3838
+```
+
+Finally, the [`CMD`
+instruction](https://docs.docker.com/engine/reference/builder/#cmd)
+closes off our `Dockerfile`. The `CMD` instruction provides the defaults
+for an executing container. There can only be one `CMD` instruction in a
+`Dockerfile` (only the last `CMD` will take effect). Our `CMD` specifies
+the executable (`"R"`) and parameters for the executable in an array.
+The `-e` option means you are running an expression that is
+`shiny::runApp('/home/app')`. The expression will run the Shiny app that
+we copied into the `/home/app` folder:
+
+```dockerfile
+CMD ["R", "-e", "shiny::runApp('/home/app')"]
+```
+
+In general, use the shell form for RUN, and the exec form for CMD and ENTRYPOINT.
+https://emmer.dev/blog/docker-shell-vs.-exec-form/
+
+
+Build the image using `docker build` by specifying the tag (`-t`) and
+the context (`.` indicates the current directory):
+
+```bash
+docker build -t registry.gitlab.com/analythium/shinyproxy-hello/hello .
+```
+
+You can test and push the locally build Docker image to the container as
+before.
+
+#### Shiny host and port
+
+When we discussed [local hosting of Shiny
+apps](https://hosting.analythium.io/run-shiny-apps-locally/) and
+`runApp`, we did not review all the possible arguments for this R
+function. Besides the app location (app object, list, file, or
+directory) there are two other important arguments:
+
+- `host`: this defines the IP address (defaults to 'localhost':
+ 127.0.0.1),
+- `port`: TCP port that the application should listen on; a random
+ port when no value provided.
+
+When you run the shiny app locally, you see a message
+`Listening on http://127.0.0.1:7800` or similar, which is the protocol
+(HTTP), the host address, and the port number. The Shiny app is running
+in a web server that listens to client requests and provides a response.
+
diff --git a/book-source/02-02-b-containerizing-shiny.Rmd b/book-source/02-02-b-containerizing-shiny.Rmd
new file mode 100755
index 0000000..e4fab48
--- /dev/null
+++ b/book-source/02-02-b-containerizing-shiny.Rmd
@@ -0,0 +1,828 @@
+## Shiny Apps With Dependencies (use bananas)
+
+What makes programming languages like R and Python great for making data
+applications is the wealth of contributed extension packages that
+supercharge app development. You can turn your code into an interactive
+web app with not much extra code once you have a workflow and an
+interesting question.
+
+We have reviewed [Docker
+basics](https://hosting.analythium.io/docker-basics-for-data-apps/) and
+how to [dockerize a very simple Shiny
+app](https://hosting.analythium.io/dockerizing-shiny-applications/). For
+anything that is a little bit more complex, you will have to manage
+dependencies. Dependency management is one of the most important aspects
+of app development with Docker. In this post, you will learn about
+different options.
+
+### Parent Images
+
+In the previous post, we explored [dependency management for Shiny
+apps](https://hosting.analythium.io/dockerized-shiny-apps-with-dependencies/)
+using the `rocker/r-ubuntu:20.04` as the parent image. A parent image is
+an image that you define in the `FROM` directive of the `Dockerfile`. A
+[base
+image](https://docs.docker.com/develop/develop-images/baseimages/)
+has `FROM scratch` as the first line. The R base images start with
+parent images. For example, the R Ubuntu image starts with
+`FROM ubuntu:focal`.
+
+Here are the four commonly used parent images for R:
+
+```bash
+docker pull rhub/r-minimal:4.0.5
+docker pull rocker/r-base:4.0.4
+docker pull rocker/r-ubuntu:20.04
+docker pull rstudio/r-base:4.0.4-focal
+```
+
+The image sizes vary quite a bit with the Alpine Linux base
+`rhub/r-minimal` being smallest and the Ubuntu-based `rstudio/r-base`
+25x the size of the smallest image:
+
+```bash
+$ docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}'
+
+REPOSITORY TAG SIZE
+rhub/r-minimal 4.0.5 35.3MB
+rocker/r-base 4.0.4 761MB
+rocker/r-ubuntu 20.04 673MB
+rstudio/r-base 4.0.4-focal 894MB
+```
+
+The Debian Linux based `rocker/r-base` Docker image from the
+[Rocker](https://github.com/rocker-org/rocker/tree/master/r-base)
+project is considered bleeding edge when it comes to system
+dependencies, i.e. latest development versions are usually available
+sooner than on other Linux distributions.
+
+The two Ubuntu Linux based images, `rocker/r-ubuntu` and
+`rstudio/r-base` from the
+[Rocker](https://github.com/rocker-org/rocker/tree/master/r-ubuntu)
+project and from
+[RStudio](https://github.com/rstudio/r-docker)
+are for long-term support Ubuntu versions and use the RSPM CRAN binaries.
+
+The Alpine Linux based `rhub/r-minimal` Docker image from the
+[r-hub](https://github.com/r-hub/r-minimal)
+project is preferred for its small image sizes.
+
+FIXME: add FROM Scratch, ubuntu, alpine
+
+#### Rocker, r-lib, rstudio (posit???)
+
+We built the same Shiny app in three different ways. The sizes of the
+three images differ quite a bit, with the `:renv` image being 40% bigger
+than the other two images:
+
+```bash
+$ docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}'
+
+REPOSITORY TAG SIZE
+analythium/covidapp-shiny renv 1.7GB
+analythium/covidapp-shiny deps 1.18GB
+analythium/covidapp-shiny basic 1.24GB
+```
+
+The `:basic` image has 105 packages installed (try
+`docker run analythium/covidapp-shiny:basic R -q -e 'nrow(installed.packages())'`).
+The `:deps` image has remotes added on top of these, the `:renv` image
+has remotes, renv and BH as extras. BH seems to be responsible for the
+size difference, this package provides [Boost C++ header
+files](https://cran.r-project.org/package=BH).
+The COVID-19 app works perfectly fine without BH. In this particular
+case, this is a price to pay for the convenience of automatic dependency
+discovery provided by renv.
+
+The renv package has a few different [snapshot
+modes](https://rstudio.github.io/renv/reference/snapshot.html#snapshot-type).
+The default is called "implicit". This mode adds the intersection of all
+your installed packages and those used in your project as inferred by
+`renv::dependencies()` to the lockfile. Another mode, called "explicit",
+only captures packages that are listed in the project `DESCRIPTION`
+file. For the COVID-19 app, both these resulted in identical lockfiles.
+You can use `renv::remove("BH")` to remove BH from the project or use
+the "custom" model and list all the packages to be added to the
+lockfile.
+
+If you go with the other two approaches, explicitly stating dependencies
+in the `Dockerfile` or in the `DESCRIPTION` file, you might end up
+missing some packages at first. These approaches might need a few
+iterations before getting the package list just right.
+
+Another important difference between these approaches is that renv pins
+the exact package versions in the lockfile. If you want to install
+versioned packages, use the `remotes::install_version()` function in the
+`Dockerfile`. The [version-tagged Rocker
+images](https://hub.docker.com/r/rocker/r-ver)
+will by default use the [MRAN snapshot
+mirror](https://mran.microsoft.com/documents/rro/reproducibility)
+associated with the most recent date for which that image was current.
+
+FIXME: add Shiny Server inside a container as a way to host multiple apps
+
+FIXME: layers & caching vs size, why using alpine is not always needed
+
+
+### system libraries
+
+First of all, each package lists its system requirements. These are
+usually run-time dependencies that the package needs to properly
+function. So check that first.
+
+There are at least two databases listing package requirements: one
+maintained by
+[RStudio](https://github.com/rstudio/r-system-requirements)
+(this supports RSPM, you can browse the database
+[here](https://packagemanager.rstudio.com/client/#/repos/2/packages)),
+another one by
+[R-hub](https://github.com/r-hub/sysreqsdb).
+Both of these list system packages for various Linux distributions,
+macOS, and Windows. But even with these databases, the build- vs.
+run-time dependencies can be sometimes hard to distinguish. Build-time
+system libraries are always named with a `-dev` or `-devel` postfix.
+Read [the vignette of the
+maketools](https://cran.r-project.org/web/packages/maketools/vignettes/sysdeps.html)
+R package by Jeroen Ooms for a nice explanation and a suggested workflow
+for determining run-time dependencies of packages.
+
+#### RSPM, BSPM, r2u, python?
+
+### R dependencies
+
+The wealth of contributed R packages can supercharge Shiny app
+development. This also means that you have to manage these dependencies.
+Learn about dependency management when working with R and Docker.
+
+When building Docker images for your R-based applications, the biggest
+hurdle is knowing exactly which packages and system libraries your
+package depends on. Luckily, the tools have evolved quite a bit over the
+past few years. In this post, I show you where the deps package fits in
+and how this can be a great choice for dependency management for
+Docker-based workflows.
+
+Tools like [packrat](https://cran.r-project.org/package=packrat),
+[renv](https://cran.r-project.org/package=renv), and [capsule](https://github.com/MilesMcBain/capsule)
+let you go to great lengths to make your R projects perfectly
+reproducible. This requires knowing the exact package versions and the
+source where it was installed from (CRAN, remotes, local files). This
+information is registered in a lock file, which serves as the manifest
+for recreating the exact replica of the environment.
+
+Full reproducibility is often required for reports, markdown-based
+documents, and scripts. A loosely defined project that is combined with
+strict versioning requirements, often erring on the side of “more
+dependencies are safer”.
+
+On the other end of the spectrum, we have package-based development.
+This is the main use case for dependency management-oriented packages,
+such as [remotes](https://cran.r-project.org/package=remotes) and
+[pak](https://cran.r-project.org/package=pak). Note that pak is
+to replace remotes at some point in the future.
+
+In this case, exact versions are managed only to the extent of avoiding
+breaking changes (given that testing can surface these). So what we have
+is a package-based workflow combined with a “no breaking changes”
+philosophy to version requirements. This approach often leads to leaner
+installation.
+
+
+#### explicit dependencies
+
+The first approach is to use `RUN` statements in the `Dockerfile` to
+install the required packages. Check the `Dockerfile` in the
+`03-docker-basic` folder. The structure of the `Dockerfile` follows the
+general pattern outlined in
+[this](https://hosting.analythium.io/dockerizing-shiny-applications/)
+post. We use the `rocker/r-ubuntu:20.04` parent image and specify the
+[RStudio Package
+Manager](https://packagemanager.rstudio.com/)
+(RSPM) CRAN repository in `Rprofile.site` so that we can install binary
+packages for speedy Docker builds. Here are the relevant lines:
+
+```dockerfile
+FROM rocker/r-ubuntu:20.04
+...
+COPY Rprofile.site /etc/R
+...
+RUN install.r shiny forecast jsonlite ggplot2 htmltools
+RUN Rscript -e "install.packages('plotly')"
+...
+```
+
+Required packages are installed with the [littler
+utility](https://cran.r-project.org/web/packages/littler/vignettes/littler-examples.html#install.r-direct-cran-installation)
+`install.r` (littler is installed on all Rocker images). You can also
+use `Rscript` to call `install.packages()`. There are other options too,
+like `install2.r` from littler, or using `R -q -e install.packages()` –
+`-q` suppresses the startup message, `-e` executes an expression then
+quits.
+
+Build and test the image locally, use any image name you like (in
+`export IMAGE=""`), then visit `http://localhost:8080` to see the app:
+
+```bash
+# name of the image
+export IMAGE="analythium/covidapp-shiny:basic"
+
+# build image
+docker build -t $IMAGE .
+
+# run and test locally
+docker run -p 8080:3838 $IMAGE
+```
+
+#### DESCRIPTION file, remotes & pak
+
+The second approach is to record the dependencies in the `DESCRIPTION`
+file. You can find the example in the `04-docker-deps` folder. The
+[`DESCRIPTION`](https://cran.r-project.org/doc/manuals/r-release/R-exts.html#The-DESCRIPTION-file)
+[file](https://cran.r-project.org/doc/manuals/r-release/R-exts.html#The-DESCRIPTION-file)
+contains basic information about an R package. The file states package
+dependencies and is used when installing the packages and their
+dependencies. The `install_deps()` function from the
+[remotes](https://cran.r-project.org/package=remotes)
+package can install dependencies stated in a `DESCRIPTION` file. The
+`DESCRIPTION` file used here is quite rudimentary but it states the
+dependencies to be installed nonetheless:
+
+```text
+Imports:
+ shiny,
+ forecast,
+ jsonlite,
+ ggplot2,
+ htmltools,
+ plotly
+```
+
+Use the same Ubuntu-based R image and the RSPM CRAN repository. Install
+the remotes package, copy the `DESCRIPTION` file into the image. Call
+`remotes::install_deps()` which will find the `DESCRIPTION` file in the
+current directory. Here are the relevant lines from the `Dockerfile`:
+
+```dockerfile
+FROM rocker/r-ubuntu:20.04
+...
+COPY Rprofile.site /etc/R
+...
+RUN install.r remotes
+COPY DESCRIPTION .
+RUN Rscript -e "remotes::install_deps()"
+...
+```
+
+Build and test the image as before, but use a different tag:
+
+```bash
+# name of the image
+export IMAGE="analythium/covidapp-shiny:deps"
+
+# build image
+docker build -t $IMAGE .
+
+# run and test locally
+docker run -p 8080:3838 $IMAGE
+```
+
+#### renv
+
+The [renv](https://cran.r-project.org/package=renv)
+package is a versatile dependency management toolkit for R. You can
+discover dependencies with `renv::init()` and occasionally save the
+state of these libraries to a lockfile with `renv::snapshot()`. The nice
+thing about this approach is that the exact version of each package is
+recorded that makes Docker builds reproducible.
+
+Switch to the `05-docker-renv` directory and inspect the `Dockerfile`.
+Here are the most important lines (Focal Fossa is the code name for
+Ubuntu Linux version 20.04 LTS that matches our parent image):
+
+```dockerfile
+FROM rocker/r-ubuntu:20.04
+...
+RUN install.r remotes renv
+...
+COPY ./renv.lock .
+RUN Rscript -e "options(renv.consent = TRUE); \
+ renv::restore(lockfile = '/home/app/renv.lock', repos = \
+ c(CRAN='https://packagemanager.rstudio.com/all/__linux__/focal/latest'))"
+...
+```
+
+We need the remotes and renv packages. Then copy the `renv.lock` file,
+call `renv::restore()` by specifying the lockfile and the RSPM CRAN
+repository. The `renv.consent = TRUE` option is needed because this is a
+fresh setup (i.e. not copying the whole renv project).
+
+Tag the Docker image with `:renv` and build:
+
+```bash
+# name of the image
+export IMAGE="analythium/covidapp-shiny:renv"
+
+# build image
+docker build -t $IMAGE .
+
+# run and test locally
+docker run -p 8080:3838 $IMAGE
+```
+
+#### deps
+
+What if we are not writing an R package and wanted to combine the best
+of both approaches? – A loosely defined project with just strict-enough
+versioning requirements. All this without having to write a
+`DESCRIPTION` file by hand. Because why would you need a `DESCRIPTION`
+file when you have no package? Also, a `DESCRIPTION` file won’t let you
+pin an exact package version or specify alternative CRAN-like
+repositories.
+
+What if you could manage dependencies by decorating your existing R code
+with special, roxygen-style comments? Just like this:
+
+```R
+#' @remote analythium/rconfig@CRAN-v0.1.3
+rconfig::config()
+
+#' @repo sf https://r-spatial.r-universe.dev
+library(sf)
+
+#' @ver rgl 0.108.3
+library(rgl)
+```
+
+This is exactly what deps does:
+
+- helps to find all dependencies from our files,
+- writes these into a `dependencies.json` file,
+- performs package installs according to the decorators.
+
+The decorators make our intent explicit, just like if we were writing an
+R package. But we do not need to manually write these into a file and
+keep it up-to-date. We can just rerun `create` to update the JSON
+manifest file.
+
+There are many different tags that you can use as part of your
+roxygen-style comments.
+
+These tags are listed and explained in the package's GitHub repository.
+
+The deps package has 2 main functions:
+
+- `create()` crawls the project directory for package dependencies. It
+ will amend the dependency list and package sources based on the
+ comments and query system requirements for the packages where those
+ requirements are known for a particular platform; the summary is
+ written into the `dependencies.json` file.
+- `install()` looks for the `dependencies.json` file in the root of
+ the project directory (or runs `create()` when the JSON file is not
+ found) and performs dependency installation according to the
+ instructions in the JSON file.
+
+In the simplest case, one might have a project folder with some R code
+inside. Running `deps::install()` will perform the package installation
+in one go. Additional arguments can be passed to `install()` so that
+local libraries etc. can be specified.
+
+These arguments are passed to `install.packages()`. This is a really
+important consideration when it comes to utilizing RSPM or BSPM
+repositories on Linux systems.
+[RSPM](https://cran.r-project.org/package=rspm?ref=hosting.analythium.io)
+(RStudio Package Manager) provides rebuild binaries,
+[BSPM](https://cran.r-project.org/package=bspm?ref=hosting.analythium.io)
+(Bridge to System Package Manager) provides full system dependency
+resolution and integration with `apt` on top of binary packages.
+
+The deps package helps users be more intentional about the R package
+source and version requirements using text decorators in comments. This
+is similar to a package-based workflow without actually writing a
+package. But deps also lends itself to Dockerized development. It
+identifies system requirements for the R packages, which is a welcome
+addition to making the Docker experience for R as user-friendly and
+hands-off as possible.
+
+#### Using the deps CLI
+
+FIXME: explain the deps CLI, note that it also works with pak, etc.
+
+### Python requirements
+
+FIXME: KALVIN to add here
+
+#### pip
+
+FIXME: KALVIN to add here
+
+#### Other options for python
+
+??? Conda, venv???
+
+FIXME: KALVIN to add here
+
+## Best Practices
+
+The use of Docker with R has been [transformative in many
+ways](https://journal.r-project.org/archive/2020/RJ-2020-007/RJ-2020-007.pdf)
+over the past 5 years. What is common in this diversity of use cases is
+that the Docker images almost always start with a parent image. What
+parent image you use? How do you add new layers to it? These questions
+will determine how quickly you can iterate while in development, and the
+size of the final image you send to production. In this post, I will
+compare using different parent images and outline best practices. I
+focus on Shiny apps but most of these ideas apply generally to any
+dockerized R application, like images for compute jobs or interfaces.
+
+Based on these results and the list of [Dockerfile best
+practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#dont-install-unnecessary-packages),
+here are a few suggestions to improve the developer experience and the
+quality of the final Docker images.
+
+### Minimize dependencies
+
+
+#### Minimize dependencies
+
+Avoid installing "nice to have" packages and do not start from
+general-purpose parent images aimed at interactive use. Images for Shiny
+apps and other web services benefit from keeping the images as lean as
+possible by adding those R packages and system requirements that are
+absolutely necessary. [Multi-stage
+builds](https://docs.docker.com/develop/develop-images/multistage-build/)
+can be helpful to only include artifacts that are needed.
+
+### Use caching
+
+When building an image, Docker executes each instruction in the order
+specified in the `Dockerfile`. Docker looks for an existing image in its
+cache that it can reuse, rather than creating a new (duplicate) image.
+Only the instructions `RUN`, `COPY`, `ADD` create layers:
+
+- for the `RUN` instructions, just the command string from the
+ `Dockerfile` is used to find a match from an existing image;
+- for the `ADD` and `COPY` instructions, the contents of the file(s)
+ in the image are examined and a checksum is calculated for each
+ file;
+- the last-modified and last-accessed times of the file(s) are not
+ considered in these checksums for the `ADD` and `COPY` instructions.
+
+### Order layers
+
+Caching can be useful is when installing R package dependencies. In a
+previous post, we looked at [how to use the renv
+package](https://hosting.analythium.io/dockerized-shiny-apps-with-dependencies/#use-the-renv-r-package)
+to install dependencies. Here is a simplified snippet from
+[that](https://github.com/analythium/covidapp-shiny/blob/main/05-docker-renv/Dockerfile)
+`Dockerfile`:
+
+```dockerfile
+## install dependencies
+COPY ./renv.lock .
+RUN Rscript -e "renv::restore()"
+
+## copy the app
+COPY app .
+```
+
+What would happen if we switched the two blocks?
+
+```dockerfile
+## copy the app
+COPY app .
+
+## install dependencies
+COPY ./renv.lock .
+RUN Rscript -e "renv::restore()"
+```
+
+You would have to wait for the build to reinstall all the R packages
+whenever the app files have changed. This is because once the cache is
+invalidated all subsequent `Dockerfile` commands generate new images
+instead of using the cache.
+
+It is best to order the instructions in your `Dockerfile` from the less
+frequently changed to the more frequently changed. This ensures that the
+build cache is reusable.
+
+```{r part2-docker-layers, eval=TRUE, echo=FALSE,out.width="80%", fig.cap="Docker layers for single (left) and multi-stage builds (right). Dashed lines are temporary layers."}
+if (is_latex_output()) {
+ include_graphics("images/02/docker-layers.pdf")
+} else {
+ include_graphics("images/02/docker-layers.png")
+}
+```
+
+### Switch user
+
+
+Running the container with root privileges allows unrestricted use which
+is to be avoided in production. Although you can find lots of examples
+on the Internet where the container is run as root, this is generally
+considered bad practice. Use something like this:
+
+```dockerfile
+RUN addgroup --system app \
+ && adduser --system --ingroup app app
+WORKDIR /home/app
+COPY app .
+RUN chown app:app -R /home/app
+USER app
+```
+
+### Multi-stage Builds
+
+### Other considerations
+
+#### Use a linter
+
+Best practices for writing Dockerfiles are being followed more and more
+often according to [this
+paper](https://arxiv.org/pdf/2103.12298.pdf)
+after mining more than 10 million Dockerfiles on Docker Hub and GitHub.
+However, there is still room for improvement. This is where
+[linters](https://en.wikipedia.org/wiki/Lint_(software))
+come in as useful tools for static code analysis.
+[Hadolint](https://github.com/hadolint/hadolint#rules)
+lists lots of rules for Dockerfiles and is available as a [VS Code
+extension](https://marketplace.visualstudio.com/items?itemName=exiasr.hadolint).
+
+- [Dockerfile best
+ practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache)
+- [Hadolint
+ rules](https://github.com/hadolint/hadolint#rules)
+- [Tips to Speed up Your Docker Image
+ Build](https://vsupalov.com/5-tips-to-speed-up-docker-build/)-->
+
+#### Use labels
+
+#### Docker Security Scanning
+
+This brings up an important consideration when it comes to production
+code and environments. Knowing the exact versions of your packages is
+not only good for reproducibility but is also the foundation for
+vulnerability scanning.
+
+R users are familiar with the renv package and how it registers the
+exact version of the R packages being used. However, it does not
+register anything about the underlying system libraries, the version of
+curl or openssl or GDAL libraries being used. This is what a Software
+Bill Of Materials (SBOM) is used for. For this reason, Docker Desktop
+4.7.0 introduced the experimental
+[`docker sbom`](https://docs.docker.com/engine/sbom/) CLI command that
+is based on a collaboration with the
+[Syft](https://github.com/anchore/syft) project.
+
+Reproducibility is concerned with keeping the versions immutable. It is
+like a vintage car that is used once a year to derive to the car show.
+As opposed to this, in production, we are more concerned with acting on
+the information and upgrade packages when necessary while also making
+sure that our app is running flawlessly. It is like how people drive
+their kids to dance and soccer practice 7 days a week in a minivan. The
+car is maintained continuously because there is no room for error.
+
+The [`docker scan`](https://docs.docker.com/engine/scan/) CLI command
+was introduced to quickly detect and learn how to remediate
+vulnerabilities in your images.
+
+> Vulnerability scanning for Docker local images allows developers and
+> development teams to review the security state of the container images
+> and take actions to fix issues identified during the scan, resulting
+> in more secure deployments. Docker Scan runs on Snyk engine, providing
+> users with visibility into the security posture of their local
+> Dockerfiles and local images.
+>
+> Users trigger vulnerability scans through the CLI, and use the CLI to
+> view the scan results. The scan results contain a list of Common
+> Vulnerabilities and Exposures (CVEs), the sources, such as OS packages
+> and libraries, versions in which they were introduced, and a
+> recommended fixed version (if available) to remediate the CVEs
+> discovered.
+
+Another popular vulnerability scanner for container images and
+filesystems is called [Grype](https://github.com/anchore/grype).
+You will find links to some tutorials below walking through the use of
+Syft and Grype.
+
+SBOM was announced in 2022.
+Here is the original announcement about SBOM from Docker in April 2022:
+
+> making what is inside your container images more visible so that you
+> can better secure your software supply chain
+
+Announcing Docker SBOM: Increased Docker Image Visibility
+
+Image visibility and transparency are key to securing your software
+supply chain. Learn how our Docker SBOM feature highlights core image
+components.
+
+Here is a short intro to how `docker sbom` command and the Syft project
+are related (Syft supports the OCI, Docker, and Singularity image
+formats):
+[How to Improve Docker Security with `docker sbom` and Syft](https://thenewstack.io/how-to-improve-docker-security-with-docker-sbom-and-syft/)
+
+Grype can use the SBOM output or scan the Docker image or local file
+system directly. Then it performs a vulnerability scan by comparing the
+package versions to information found in vulnerability databases. This
+tutorial outlines the whole process:
+[Container vulnerability scan with Syft and Grype](https://medium.com/rahasak/container-vulnerability-scan-with-syft-and-grype-f4ec9cd4d7f1)
+
+[How to add a Software Bill of Materials (SBOM) to your containers with GitHub Actions](https://actuated.dev/blog/sbom-in-github-actions):
+
+You might use GitHub actions to build a new image every time you merge
+changes to your production branch. It can be important to make sure
+there are no vulnerabilities. This post outlines how to do that:
+
+## WHAT ELSE
+
+### Layers and Caching
+
+### Hardware Architectures
+
+### Multi-stage builds
+
+### Mounting volumes
+
+### Ports
+
+### UID
+
+### Troubleshooting
+
+FIXME: Accessing the logs:
+FIXME: how to enter a running container to find runtime info
+
+
+### Shinylive
+
+Shinylive: R & Python using Nginx and of-watchdog
+
+### Rmd
+
+Rmd: this is for R
+
+### Quarto
+
+Quarto: R & Python
+
+### Using BuildKit
+
+Docker versions 18.09 or higher come with a new opt-in builder backend
+called
+BuildKit. BuildKit prints out a nice
+summary of each layer including timing for the layers and the overall
+build. This is the general build command that I used to compare the four
+parent images:
+
+```bash
+DOCKER_BUILDKIT=1 docker build --no-cache -f $FILE -t $IMAGE .
+```
+
+BuildKit backend is enabled by turning on the `DOCKER_BUILDKIT=1`
+environment variable. I use the `--no-cache` option to avoid using
+cached layers, thus having a fair assessment of build times (you usually
+only build 1 and not 4). The `-f $FILE` flag allows building from
+different files kept in the same folder.
+
+All the code used here can be found in our Covid-19 Shiny app [GitHub repository](https://github.com/analythium/covidapp-shiny), look in
+the folder `99-images`.
+
+#### Image build times
+
+This is the script I used to build the four images with BuildKit:
+
+```bash
+# rhub/r-minimal
+export IMAGE="analythium/covidapp-shiny:minimal"
+export FILE="Dockerfile.minimal"
+DOCKER_BUILDKIT=1 docker build --no-cache -f $FILE -t $IMAGE .
+
+# rocker/r-base
+export IMAGE="analythium/covidapp-shiny:base"
+export FILE="Dockerfile.base"
+DOCKER_BUILDKIT=1 docker build --no-cache -f $FILE -t $IMAGE .
+
+# rocker/r-ubuntu
+export IMAGE="analythium/covidapp-shiny:ubuntu"
+export FILE="Dockerfile.ubuntu"
+DOCKER_BUILDKIT=1 docker build --no-cache -f $FILE -t $IMAGE .
+
+# rstudio/r-base
+export IMAGE="analythium/covidapp-shiny:focal"
+export FILE="Dockerfile.focal"
+DOCKER_BUILDKIT=1 docker build --no-cache -f $FILE -t $IMAGE .
+```
+
+I changed the CRAN repository for the Debian and Ubuntu Rocker images to
+see timing differences between installing packages as binary or from
+source. Total build times (on a 6-year old MacBook Pro) were the
+following:
+
+- `rhub/r-minimal`: 27 minutes with building packages from source
+- `rocker/r-base`: 12 minutes when building from source, 2.9 minutes
+ when installing binary packages
+- `rocker/r-ubuntu`: 12 minutes when building from source, 3.2 minutes
+ when installing binary packages
+- `rstudio/r-base`: 3.1 minutes with installing binary packages
+
+The difference between the binary vs. source package installs is
+expected. What is interesting is the 12 vs. 27 minutes between the
+Debian/Ubuntu images and the minimal Alpine image. Is it worth waiting
+for?
+
+#### Image sizes
+
+I got the image sizes from `docker images` and made a small data frame
+in R to calculate the size difference between the final and parent
+images:
+
+```R
+x = data.frame(TAG=c("minimal", "base", "ubuntu", "focal"),
+ PARENT_SIZE=c(35, 761, 673, 894) / 1000, # base image
+ FINAL_SIZE=c(222 / 1000, 1.05, 1.22, 1.38)) # final image
+
+x$DIFF = x$FINAL_SIZE - x$PARENT_SIZE
+
+# TAG PARENT_SIZE FINAL_SIZE DIFF
+# 1 minimal 0.035 0.222 0.187
+# 2 base 0.761 1.050 0.289
+# 3 ubuntu 0.673 1.220 0.547
+# 4 focal 0.894 1.380 0.486
+```
+
+The image sizes themselves differed quite a bit, the RStudio Ubuntu
+image was 6.2x larger than the minimal R image. Size differences were
+similarly different.
+
+Image sizes can be deceiving. It might not matter much if images are
+large if for example, we have multiple images [sharing some of the
+layers](https://semaphoreci.com/blog/2018/03/14/docker-image-size.html)
+(i.e. ones from the parent image). The CPU and RAM footprint of the
+containers might also be unrelated to the image sizes. But it might
+impact "cold start" performance when images are pulled to an empty
+server.
+
+#### Alpine Linux based image
+
+The Dockerfiles and the build experience for the Ubuntu and Debian
+images were very similar. Build times and image sizes were also
+comparable. The Alpine Linux-based minimal image took the longest time
+to build but resulted in the smallest image size. The Dockerfile for
+this setup also looks quite different from the Debian/Ubuntu setup:
+
+```dockerfile
+FROM rhub/r-minimal:4.0.5
+
+RUN apk update
+RUN apk add --no-cache --update-cache \
+ --repository http://nl.alpinelinux.org/alpine/v3.11/main \
+ autoconf=2.69-r2 \
+ automake=1.16.1-r0 \
+ bash tzdata
+RUN echo "America/Edmonton" > /etc/timezone
+
+RUN installr -d \
+ -t "R-dev file linux-headers libxml2-dev gnutls-dev openssl-dev libx11-dev cairo-dev libxt-dev" \
+ -a "libxml2 cairo libx11 font-xfree86-type1" \
+ remotes shiny forecast jsonlite ggplot2 htmltools plotly Cairo
+
+RUN rm -rf /var/cache/apk/*
+
+RUN addgroup --system app && adduser --system --ingroup app app
+WORKDIR /home/app
+COPY app .
+RUN chown app:app -R /home/app
+USER app
+
+EXPOSE 3838
+
+CMD ["R", "-e", "options(tz='America/Edmonton');shiny::runApp('/home/app', port = 3838, host = '0.0.0.0')"]
+```
+
+The base image is so bare-bones that it needs to install time zones,
+fonts and the Cairo device for ggplot2 to work (read the limitations
+[here](https://github.com/r-hub/r-minimal#limitations)).
+Instead of `apt` you have `apk` and might have to work a bit harder to
+find all the Alpine-specific dependencies.
+
+One interesting aspect of this image is that instead of the littler
+utilities familiar from the Rocker images, we have the very similar
+`installr` script that installs R packages and system requirements:
+
+- the `-d` flag installs then removes compilers ( `gcc`, `musl-dev`,
+ `g++`), as these are typically not needed on the final image;
+- system packages listed after the `-t` flag are removed after the R
+ packages have been installed;
+- system packages listed after the `-a` flag are run-time dependencies
+ that are needed for the packages to function properly and are not
+ removed from the image.
+
+The installation of the system packages – that are all available on the
+other parent images – contributes to the longer build times. The
+BuildKit output gives you a clue about where exactly the time was spent.
+
+The rest of the `Dockerfile` is very similar to the other distributions:
+add Linux user, copy files, expose port, define the entrypoint command.
+But how do you figure out what system packages you need?
diff --git a/book-source/02-02-c-containerizing-shiny.Rmd b/book-source/02-02-c-containerizing-shiny.Rmd
new file mode 100755
index 0000000..999a628
--- /dev/null
+++ b/book-source/02-02-c-containerizing-shiny.Rmd
@@ -0,0 +1,49 @@
+## Container Orchestration
+
+### Docker Compose
+
+## Troubleshooting
+
+
+FIXME: Enter the containers etc. to see variables etc.
+
+
+## Summary
+
+With the newfound ability to wrap any Shiny app in a Docker container,
+you'll be able to deploy these images to many different hosting
+platforms. Of course, there is a lot more to learn, e.g. about handling
+dependencies, persisting data across sessions and containers, and so on.
+We'll cover these use cases in due time. Until then, celebrate this
+milestone, check out further readings, and try to containerize some of
+your own Shiny apps.
+
+You can also share Docker images with others. This, however, will require the
+recipient of your app to have Docker installed and be able to run it locally.
+
+In the next Part, we'll cover options for hosting your app, so that others
+will only need a browser to be able to access it. No R, Python, or Docker
+runtime environment is needed on the user's part. Hosting the app for your
+users will also be the preferred option in case you do not want to share
+the source code or the Docker image with the users.
+
+Further reading:
+
+- [Get started with
+ Docker](https://docs.docker.com/get-started/)
+- [Docker
+ simplified](https://www.freecodecamp.org/news/docker-simplified-96639a35ff36/)
+- [Docker for
+ beginners](https://docker-curriculum.com/)
+- [Docker
+ glossary](https://docs.docker.com/glossary/)
+- [The Docker
+ Handbook](https://docker.farhan.info/)
+- [R Docker
+ tutorial](https://jsta.github.io/r-docker-tutorial/)
+- [Docker for R
+ users](https://colinfay.me/docker-r-reproducibility/)
+- [An Introduction to
+ Rocker](https://journal.r-project.org/archive/2017/RJ-2017-065/RJ-2017-065.pdf)
+- [The
+ Rockerverse](https://journal.r-project.org/archive/2020/RJ-2020-007/RJ-2020-007.pdf)
diff --git a/book-source/03-01-hosting-shiny.Rmd b/book-source/03-01-hosting-shiny.Rmd
new file mode 100755
index 0000000..55430c1
--- /dev/null
+++ b/book-source/03-01-hosting-shiny.Rmd
@@ -0,0 +1,490 @@
+# (PART) Hosting Shiny Apps {-}
+
+# A Review of Shiny Hosting Options
+
+The Hosting Data Apps website recently celebrated its [6-months
+anniversary](https://hosting.analythium.io/hosting-data-apps-6-months-and-40-posts-later/).
+During this time I have written 40 posts, almost all about Shiny hosting
+options. Some of these posts reviewed particular hosting options, such
+as Shinyapps.io, Shiny Server, Heroku, and ShinyProxy.
+
+A lot has been said about the hosting options themselves, but what about
+the needs of the developers and the users?
+
+The most important question to ask is: "*which option is better *for
+what**"? This is where developers and the users of the app come into
+the picture with their *specific needs and constraints*.
+
+You could say, for example, that "*I want to host my portfolio for free
+and I don't care about a custom domain name*". Or a nonprofit might say,
+"*we want to host our apps at low cost, we want custom domains, and we
+want to be able to handle surge traffic, but we don't want to maintain
+any servers*".
+
+These are really specific criteria, and if you ask me, I might say
+Shinyapps.io is best for you and Heroku with a Docker-based deployment
+is best for this organization. But how would you or I make such a
+decision?
+
+If you have been developing Shiny apps, you might already have your
+preferred way of deployment. But as your needs evolve, you will identify
+additional requirements and might find that your go-to option is not the
+best anymore. Then you'll do some research and find the next option.
+
+If you are not yet familiar with Shiny hosting options, you still need
+to make an informed choice at some point, so that you are not wasting
+your time and effort on something that will not serve you well over the
+long run. Here is the 3 step process that you can follow to help you
+with this decision.
+
+Before you begin take a piece of paper.
+
+## Start with the why
+
+*Why do you want the app or apps deployed?* Are you building a
+portfolio to boost your career? Are you deploying useful apps for
+stakeholders or clients of your organization? Are you trying to sell an
+app as a software-as-a-service (SaaS) offering?
+
+Write down your answer.
+
+Getting clear on the why is the most important question. You might even
+realize that *you don't need to host* your Shiny app. For example,
+your app might be used on a laptop as a GUI to analyze data by
+non-specialists in the field without internet or cell coverage. In this
+case, no need to move on to step 2, because all you need is to [run
+Shiny locally](https://hosting.analythium.io/run-shiny-apps-locally/).
+
+However, if your answer makes it clear to you that your users will be
+accessing the app over the Internet, move on to step 2.
+
+## List your requirements
+
+Answering the Why question will probably reveal important details about
+your motivations, your audience, the number of apps you are going to
+host, etc. The answer will also bring you closer to identifying the
+*requirements* that you'll need.
+
+For example, do you need a custom domain, how many users are you
+expecting, do you need authentication or app-level authorization? Do you
+want to host a single app or do you need to host many apps? Will you
+host non-Shiny apps?
+
+Write these down too.
+
+The following table lists the important features for many different
+*Shiny hosting options*. The table lists tiers offered by the same
+company as a separate option. Use this table to find the options that
+meet your requirements. For now, just ignore the columns inside the blue
+rectangle.
+
+![](images/part-xx/feature-matrix-comparing-shiny-hosting-options.png)
+
+If you crave a more interactive experience, I made a filterable version:
+
+View the video of it here: [https://www.youtube.com/watch?v=iGKQMKuz-ww](https://www.youtube.com/watch?v=iGKQMKuz-ww).
+
+Visit [https://hosting.analythium.io/assets/files/shiny-hosting-options.html](https://hosting.analythium.io/assets/files/shiny-hosting-options.html) to filter the options and pick the right hosting option for your Shiny app.
+
+Once you filter the table according to your requirements, you'll see a
+list of your *ideal hosting options*. Put these in the file or onto
+the paper too.
+
+## Identify your constraints
+
+The last step involves identifying your *constraints*:
+
+- What is your *budget*?
+- What is your current *skill level*?
+- How much time do you have *time*?
+
+Recognizing these constraints will guide you toward an *optimal
+solution*. This is the point where the columns inside the blue
+rectangle come in.
+
+The Total Cost of Ownership (TCO) (USD/year) covers licensing fees and
+operating costs for the "Number of Apps" listed in the table. Prices
+range quite a bit from free to the tens of thousands. Price increases
+with performance and with the availability of enterprise features, such
+as custom domains and authentication.
+
+*PaaS* means platform-as-a-service, i.e. it is a fully managed system
+without you having to worry about the underlying infrastructure. This
+also means less control over the infrastructure, i.e. when it comes to
+choosing the *data region* where your app is served from.
+
+*Unlimited app hours* are more common for self-hosted options or paid
+PaaS plans including a single app. The need to host *multiple apps*
+will involve some compromises. The ability to host *non-Shiny apps*
+(Dahs, Streamlit, etc.) is a feature for RStudio Connect and the
+Docker-based options ([Shinyapps and Shiny Server can host Shiny for
+Python](https://shiny.rstudio.com/py/docs/deploy.html)).
+
+*Time* as a constraint will depend on how far your current skill level
+is from the level needed for a specific hosting option. You also have to
+consider that some options are fully managed PaaS offerings, others you
+have to manage, or learn how to use Docker.
+
+If you have to develop *new skills*, it might take longer. If you have
+to manage your servers, it will take more time to get started and then
+you are on the hook for maintaining your setup.
+
+Make your selections inside the columns within the blue area.
+
+## Options at a glance
+
+After the 3-step process, you should see only a few or a single option
+left. Click on the name of the hosting option and the link will take you
+to the relevant tag page on the Hosting Data Apps website:
+
+- Follow the instructions in the *tutorials* to get started
+- At the end of each post, you'll find a *Further reading* section
+ listing *additional resources*
+
+If there is no option left in the table, then you might need to be more
+realistic about your expectations or relax some of your constraints. For
+example, to keep costs low, you could spend more time and invest in
+skill development. But if you have more room in your budget, you might
+choose a different path. You can also revise your requirements until you
+find an acceptable solution.
+
+
+The following diagram gives an intuitive overview of the different
+options. The vertical axis represents the *total cost* from the table
+above: free, low, and high cost. The horizontal axis shows a *range of
+skills* you need to [set up and manage your hosting
+solution](https://hosting.analythium.io/the-taxonomy-of-shiny-hosting-options/).
+It can be as simple as pushing a button, or as complex as managing
+servers or cloud clusters.
+
+![](images/part-xx/shiny-hosting-comparison-costs-complexity.png)
+
+The hosting options in this diagram are not separated by tiers but
+rather shown as spanning over a range. The fill colours identify Docker
+and non-Docker-based options, the stroke styling indicates the PaaS
+solutions.
+
+Starting November 28, 2022, free Heroku Dynos, free Heroku Postgres, and
+free Heroku Data for Redis will no longer be available – see [this
+FAQ](https://hello.heroku.com/e/36622/-heroku-free-product-plans-faq/m49jd4/1138553151?h=KeNZWXMYkD7V9c11YSkM3-wKpW2ysvxg12dL3_hgXRk)
+for details.
+
+If you followed the 3-step process to collect all the information you
+need, it is likely that you have found an option that is best for your
+needs. Now go ahead and learn more about that option, deploy, and start
+hosting your app.
+
+Shiny is a very popular interactive data application framework. As a
+result, *new hosting options* are popping up every time. As the number
+of these hosting options grows in the future, I might update this post
+by adding the new contenders to the table.
+
+If you know a Shiny hosting option that is not listed in the table, add
+that to this form so that I can include it next time!
+
+## Hosting Patterns
+
+> Include here: Hosting Patterns for Shiny Apps
+
+Web applications can run statically or dynamically. If running statically, then a backend server is not required as all the necessary content is pre-generated.
+Dynamic web applications require a backend server to render pages.
+
+The pros of hosting an app statically include a small app size and fast loading speed, the ability to be hosted for free, improved SEO, and content decoupling. However, the cons are that the app is the same for all users and not reactive.
+
+One benefit of a dynamically hosted app is the provision of user-specific content along with full reactivity facilitated by websockets. However, the drawbacks include the tendency to be resource-hungry, slow initial loading times, challenges with SEO optimization, more complex hosting requirements, and the high performance costs.
+
+Both static and dynamic apps can be hosted on platforms, or servers that you manage. In this chapter, we want to build the intuition behind the different kinds of hosting options and choosing the option that best suits your needs.
+
+To connect to a hosted application, a reverse proxy is required. A reverse proxy
+sits in front of your web application and forwards browser requests to your web application.
+It is used to create a secure connection to your web application and to distribute
+the load of user requests to your web application. A reverse proxy also helps to
+route requests to a domain sent by a client to the server to the appropriate web application.
+
+In this chapter we dive into reverse proxies, static Shiny apps, dynamic Shiny apps,
+and custom domain names.
+
+## The Cloud
+The cloud refers to remote servers that are available for rent at a cost. Often the costs of these remote servers are charged by the hour. The cloud is part of IaaS (Infrastructure as a Service) where server infrastructure can be provided on demand. Some of the post popular cloud services include:
+
+* DigitalOcean
+* AWS EC2
+* Akami (Linode)
+* Google Cloud Platform
+* Vultr
+
+These service can spin up a server for you to remotely connect to in minutes. This remote connection can be done via the command line as we have described in Part 3.
+
+### Servers
+You might be wondering, what plan do I choose or how do I get started? In this section, we want to highlight the general intuition behind getting started with a cloud provider. We will outline the concepts of the different options when choosing a server plan for a cloud provider.
+
+For our examples, we will be using DigitalOcean. However, the same principles apply to different cloud providers.
+
+- CPU
+- RAM
+- OS
+- IP Address (IPV4 vs IPV6)
+- SSH Key
+
+Later we will talk about securing your server instance.
+
+## Custom Domain Names
+
+Custom domain names are important for giving an easy to access url for your Shiny application.
+It is important for branding memorability.
+
+To obtain a custom domain, you must register a domain with a registrar.
+The registrar leases you a domain name from the domain registry annually for a nominal fee.
+You must renew your domain name every year.
+Each domain extension (e.g.\ .com) has its own registry.
+
+Some common domain name registrars are NameCheap, NameSilo, and Porkbun.
+
+Domain names are linked to a name server that helps resolve to an IP address of
+a server that returns content to a client.
+
+On the domain name server (DNS), the `A` record entry helps resolve a name to an IPV4 address.
+The `AAAA` record entry helps resolve a name to an IPV6 address.
+Lots of websites have shifted to using IPV4 in addition to IPV6.
+It is recommended that you at least have an IPV4 record as not everybody has an IPV6 connection.
+
+A fully registered domain name is one of the requirements for an HTTPS certificate.
+
+## Reverse Proxies
+
+There are different options of reverse proxies such as: Nginx, Apache, and Caddy.
+
+One of the easiest to use and configure is Caddy.
+Caddy uses HTTPS automatically and by default. It obtains
+and renews TLS certificates for your sites automatically, and comes with
+lots of handy features.
+
+HTTPS is important because it encrypts the traffic between the client and server.
+It ensures that the data sent to the client from the server is not tampered with and vice-versa.
+
+To use this section, we assume that you have setup a linux server.
+
+Start a Ubuntu 20.04 virtual machine and follow instructions from the
+[introductory ShinyProxy
+post](https://hosting.analythium.io/how-to-set-up-shinyproxy-to-host-shiny-apps/)
+to have the server available on `http://$HOST:8080` with the two demo
+applications.
+
+For a [Let's Encrypt
+certificate](https://letsencrypt.org/), you
+need a fully registered domain name and an email address. I use the
+`example.com` domain here, you have to substitute your domain name. Add
+an `A` record with `example.com` pointing to your server's public IP
+address.
+
+### Install Caddy
+
+Log in to the server (`ssh root@$HOST`) and follow the
+[instructions](https://caddyserver.com/docs/install):
+
+ sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
+ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo apt-key add -
+ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
+
+ sudo apt update
+ sudo apt install caddy
+
+After installing Caddy with `apt` it will already be running as a
+service. It is also enabled (see output from
+`systemctl is-enabled caddy`), which means that it will restart with the
+system. Visit `http://$HOST` to see the Caddy welcome page:
+
+![](images/part-05/caddy-welcome-page.png)
+
+Make a text file called `Caddyfile`: `touch Caddyfile` then
+`nano Caddyfile`, and add the following content. Make sure you add your
+domain name to the 1st line, or just use `:80` if you want to serve
+Shiny apps without custom domain and over HTTP, replace
+your email in the global configuration block (some challenges and Let's
+Encrypt notifications require the email, but this block is optional):
+
+ {
+ email your.name@example.com
+ }
+
+ yourdomain.com {
+ reverse_proxy 0.0.0.0:3838
+ }
+
+Save (CTRL+O) and exit (CTRL+X) `nano`. Now copy the file to the
+`/etc/caddy/Caddyfile` location. With this you make sure that
+`systemctl` will restart the Caddy server according to the new
+specifications:
+
+ sudo cp Caddyfile /etc/caddy/Caddyfile
+
+Use `systemctl reload caddy` to apply the changes. Now Caddy will be
+busy in the background setting up transport layer security (TLS)
+certificates for HTTPS. Use `journalctl -u caddy --no-pager | less` to
+view the logs of what is happening behind the scenes.
+
+Visit `https://yourdomain.com` to see the Shiny Server welcome page. The
+two demo apps are located at `https://yourdomain.com/sample-apps/hello/`
+and `https://yourdomain.com/sample-apps/rmd/`.
+
+That's it! You can now disable port 3838 with
+`sudo ufw delete allow 3838`. HTTP requests will redirect to HTTPS.
+Finally, don't forget to destroy the server if you don't need it
+anymore. This is the time to save a machine image for future use, such
+images cost very little.
+
+Caddy server makes it super easy to obtain TLS certificates for your
+custom domain and to serve Shiny apps securely over HTTPS on top of the
+open-source Shiny Server.
+
+
+## Monitoring Processes
+Often times, you might need to check if a specific program is running on your remote computer, or you would like to know the memory or CPU that is currently being used.
+
+By typing the `top` command you can get a live view of the processes currently running on your computer.
+
+At the top you can see the total time your computer has been running for.
+
+The `%cpu` row shows how much of the CPU is being used. The `us` stands for user time of the cpu, while the `sy` stands for the system of the CPU.
+
+Below the `%cpu` row is the `MiB Mem` row which shows the amount of memory that is consumed in megabytes. And the `MiB Swap` which is the hard disk space used for RAM.
+
+Finally, there are the live processes in a table below the top heading. You can see the command that has been executed and the amount of CPU and memory it has been using.
+
+To quit `top`, simply press the `q` character on your keyboard.
+
+FIXME: add used/free disk space commands.
+
+## Systemctl
+
+Enable, start, stop, log, restart/reload system processes.
+
+FIXME: SHOULD THIS GO INTO THE HOSTING CHAPTER WHEN DEALING WITH VM'S?
+I think we should mention it here...
+
+## Shutdown and Reboot
+You may need to reboot or shutoff your server at times. These commands will require super user access as you may be interrupting the operations of other users on the server.
+
+Therefore to reboot the server, you would run: `sudo reboot`.
+
+To shut off the server, you would run: `sudo poweroff`.
+
+If you do shut off the server, you will have to turn it back on in your cloud hosting provider panel. Or if it is a physical server, you can also physically press the power button.
+
+## Networking/Firewalls
+
+`ufw` commands.
+
+
+
+Talk about securing the app with firewall.
+
+### systemd
+
+## Static Shiny Apps
+
+### Compiling without containers (R vs Python)
+
+### Compiling with containers
+
+### Static Hosting Options
+
+GitHub Pages, netlify, surge.sh
+File Server
+
+## Dynamic Shiny Apps
+
+### Hosting platforms (PaaS)
+
+- Heroku
+- DO App Platform
+- Fly etc
+- Cloud Services (AWS, AZURE)
+
+We do not want to focus too much on hosting platforms. But give the intuition to use any hosting platform.
+
+### Reverse Proxy Required
+
+- Non-container hosting (R vs Python):
+
+Posit Connect
+
+ShinyServer
+
+~~systemd or pm2~~
+
+- ShinyProxy
+
+
+## Deploying Static Files with Shinylive
+
+How to deploy to GH pages etc.
+
+Share Shinylive: https://shiny.posit.co/py/docs/shinylive.html
+
+
+FIXME: this needs a visuals. Include relevant links:
+
+> Code for deploying Shiny applications that will run completely in the browser, using Pyodide and webR (Python and R compiled to WebAssembly).
+
+-
+-
+- and
+
+> Exporting 'shiny' applications with 'shinylive' allows you to run them entirely in a web browser, without the need for a separate R server. The traditional way of deploying 'shiny' applications involves in a separate server and client: the server runs R and 'shiny', and clients connect via the web browser. When an application is deployed with 'shinylive', R and 'shiny' run in the web browser (via 'webR'): the browser is effectively both the client and server for the application. This allows for your 'shiny' application exported by 'shinylive' to be hosted by a static web server. --
+
+## Deploying Shiny Apps to Shinyapps.io
+
+- Share your Shiny app as a web page
+ - synamic Shiny deployment (Shinyapps)
+ - static Shiny deployment (Shinylive + GH pages)
+
+IDE has button to deploy to Shinyapps, also show the rsconnect ways fr R & Python
+
+The developers of Shiny have developed one-click solutions for deploying to [ShinyApps.io](http://ShinyApps.io). The caveat is that hosting costs can become quite costly, and there are limitations to the hours that an app can be run for in the free tier.
+
+To begin, sign up for [ShinyApps.io](http://ShinyApps.io).
+
+### R
+
+To deploy in RStudio, install the rsconnect package, load the rsconnect package.
+
+**Shinyapps.io**: free or paid cloud hosting with push-button publishing.
+Push-button publishing is available for Shinyapps.io
+from the RStudio IDE (desktop or server edition) or from any R console
+using the [rsconnect](https://cran.r-project.org/package=rsconnect) R extension package.
+
+FIXME: add more here.
+
+### Python
+
+To deploy with Python, install the `rsconnect-python` pacakge, run the rsconnect commands in the command line.
+
+FIXME: add more here.
+
+
+
diff --git a/book-source/03-02-shiny-in-production.Rmd b/book-source/03-02-shiny-in-production.Rmd
new file mode 100755
index 0000000..a0593b8
--- /dev/null
+++ b/book-source/03-02-shiny-in-production.Rmd
@@ -0,0 +1,56 @@
+# Considerations for Hosting Production Shiny Apps {#considerations-hosting}
+
+## Security
+
+### Certificates
+### Storing Secrets
+
+* Environment variables
+* Secrets service
+
+### Shiny App Authentication
+
+* [ShinyApps.io](http://ShinyApps.io)
+* PositConnect
+* ShinyProxy
+* HTTP Basic Auth Reverse Proxy
+* Built-in App
+
+## Performance, Availability, and Monitoring
+
+### Scaling
+
+#### Benchmarking
+#### What to Monitor
+
+* CPU
+* Memory
+* Disk I/O
+* Network traffic
+* Uptime
+
+## Operations and Maintenance
+
+### CI/CD
+
+* Alerts
+* Updating Apps
+
+## Embedding Onto Your Website
+
+You may find yourself wanting to embed your app onto your existing website.
+Your existing website may be hosted elsewhere or run code that is independent of your Shiny app.
+In such cases, you may find using iframes useful.
+
+Iframe is an HTML tag that allows the embedding of websites within a website.
+
+Beware that iframes can be restricted by configuring your reverse proxy to send a header that
+most clients respect.
+```
+X-Frame-Options: DENY
+```
+
+## OTHER STUFF
+
+-
+-
diff --git a/book-source/04-01-what-is-next.Rmd b/book-source/04-01-what-is-next.Rmd
new file mode 100644
index 0000000..6f44fdb
--- /dev/null
+++ b/book-source/04-01-what-is-next.Rmd
@@ -0,0 +1,68 @@
+# (PART) What Is Next? {-}
+
+# Information about hosting
+
+In most data-intensive situations, the capabilities of the backend system
+will determine your costs and the user experience. That is why it is important
+to make informed choices.
+
+data apps is scattered around the Internet and often incomplete.
+Recommendations are usually narrowly focused and never really ask the
+important questions. Such as: *Why* do you want this app deployed?
+*Who* is it for? *What* is your budget? The goal of this book is to help you learn
+about your hosting options. Depending on how you answer the questions
+you will be able to pick the option that suits your needs best.
+
+We have worked with clients from a variety of sectors, large and small, and
+advised about hosting data applications. Over the years, we have seen
+some patterns emerge, and we decided that it is time to share our
+experiences in the form of this website.
+
+Data applications are becoming more common. We have seen a shift from
+proof-of-concept and demo applications to business-critical dashboards
+and real-world decision support tools. Along with this trend, we also
+saw the diversification of client and user needs that requires the
+authors and developers of these data apps to constantly evaluate their
+*hosting strategy*.
+
+We have also noticed that clients want more control over their
+technology stack (branding, security, data regions) and costs. They are
+ready to invest in skill development to address their unique needs
+and to reduce the dependency on proprietary software/platform
+solutions.
+
+We will keep the Dev side of the cycle to a minimum on this website.
+There are truly great resources dedicated to general programming, data
+science, or app development, that we are not intending to repeat.
+Instead, we will provide relevant pointers for you to quickly find
+reliable information on these topics.
+
+This website focuses on the Ops side. We publish *reviews* to help in
+your decision-making process when it comes to hosting data apps. We also
+share step-by-step *tutorials* describing the various options from
+1-click deployments to self-hosted clusters.
+
+
+This website is about hosting data apps. But what is a data app and why
+do you need hosting for it?
+
+
+# Conclusions
+
+Conclusions & next steps
+
+This is what you've learnt, and this is what you haven't. Go here to find out more.
+
+
+
+\cleardoublepage
+
+```{r eval=TRUE,echo=FALSE, results='asis'}
+# this marks the beginning of the Appendix, no content here
+if(!is_latex_output()){
+ cat("# (APPENDIX) Appendices {-}")
+} else {
+ # cat("\\appendix")
+ cat("# (PART) Appendices {-}")
+}
+```
diff --git a/book-source/05-01-appendix-bananas.Rmd b/book-source/05-01-appendix-bananas.Rmd
new file mode 100755
index 0000000..ea55bf1
--- /dev/null
+++ b/book-source/05-01-appendix-bananas.Rmd
@@ -0,0 +1,75 @@
+# The Bananas Data Set {-#appendix-bananas}
+
+The bananas data set is the product of a home experiment that one of the authors made during the first months of the COVID-19 pandemic.
+The data set tracks the ripening colour composition of banana fruits daily over 3 weeks period.
+The full data set can be found in the [GitHub repository](https://github.com/psolymos/bananas) and R package `bananas` (`install.packages("bananas", repos = "https://psolymos.r-universe.dev")`). The subset used in the book and the Shiny app constitutes the 6 fruits that were kept at room temperature.
+
+```{r include=FALSE}
+options(scipen = 99, digits = 3)
+```
+
+The table has the following fields:
+
+- `fruit`: the identifier of the fruit,
+- `day`: number between 0 and 20, the number of days since the first set of photographs,
+- `ripeness`: the ripeness class of the fruit based in Peter's personal judgement (Under, Ripe, Very, Over),
+- `green`, `yellow`, `brown`: colour composition, these 3 values add up to 1 (100%).
+
+The colour composition was determined based on colour mapping the pixel values of the banana fruits and converting the pixel based 2-dimensional area to proportions.
+
+```{r eval=FALSE,echo=FALSE}
+bananas <- read.csv("data/bananas.csv")
+str(bananas)
+```
+
+
+
+```{r eval=TRUE,echo=FALSE, results='asis'}
+# this marks the beginning of the Appendix, no content here
+if(!is_latex_output()){
+txt <- ''
+} else {
+txt <- '
+The following summary presents the ripeness (U = Under, R = Ripe, V = Very, O = Over)
+and the percentage values of green, yellow, brown colours.
+'
+}
+cat(txt)
+```
+
+
+```{r eval=TRUE, echo=FALSE}
+x <- read.csv("data/bananas.csv")
+if (!is_latex_output()) {
+ reactable(x,
+ defaultPageSize = 10,
+ showPageSizeOptions = TRUE,
+ pageSizeOptions = c(10, 30, 60, 120),
+ highlight = TRUE,
+ searchable = TRUE,
+ striped = TRUE)
+} else {
+ z <- matrix("", 20, 6)
+ rownames(z) <- sort(unique(x$day))
+ colnames(z) <- sort(unique(x$fruit))
+ for (i in sort(unique(x$day))) {
+ for (j in sort(unique(x$fruit))) {
+ ij <- x$day == i & x$fruit == j
+ # v <- paste0(x$ripeness[ij], " [G=", round(100*x$green[ij]),
+ # "|Y=", round(100*x$yellow[ij]), "|B=", round(100*x$brown[ij]), "]")
+ v <- paste0(substr(x$ripeness[ij], 1, 1), " (", round(100*x$green[ij]),
+ ", ", round(100*x$yellow[ij]), ", ", round(100*x$brown[ij]), ")")
+ z[as.character(i),j] <- v
+ }
+ }
+ rownames(z) <- paste0("Day ", sort(unique(x$day)))
+ # https://haozhu233.github.io/kableExtra/awesome_table_in_pdf.pdf
+ # x[x$fruit == "fruit_14",] |>
+ z |>
+ kbl(booktabs = TRUE,
+ longtable = TRUE) |>
+ kable_styling(
+ font_size = 8,
+ latex_options = c("striped", "repeat_header"))
+}
+```
diff --git a/book-source/05-02-appendix-lbtest.Rmd b/book-source/05-02-appendix-lbtest.Rmd
new file mode 100755
index 0000000..cb49a34
--- /dev/null
+++ b/book-source/05-02-appendix-lbtest.Rmd
@@ -0,0 +1,241 @@
+# Shiny App for Testing Load Balancing {-#appendix-lb-app}
+
+Shiny apps can run multiple sessions in the same app instance.
+A common problem when scaling the number of replicas for shiny apps is that
+traffic might not be sent to the same session and thus the app might randomly fail.
+
+This app is used to determine if the HTTP requests made by the client are correctly
+routed back to the same R or Python process for the session.
+
+Both the Python and the R version of the app registers a dynamic route for the client
+to try to connect to. The JavaScript code on the client side will repeatedly hit the
+dynamic route. The server will send a 200 OK status code only if the client reached
+the correct Shiny session, where it originally came from.
+
+The original Python app was written by Joe Cheng and is from the `rstudio/py-shiny` GitHub repository[^Appendix-lb-app-01]
+
+[^Appendix-lb-app-01]:
+
+FIXME: Where you can find it on the Book website.
+
+### The Python Version {-}
+
+Create a `python` folder.
+Put `shiny` in the `requirements.txt` file:
+
+```txt
+shiny>=0.2.7
+```
+
+Copy the app into the `app.py` file:
+
+```python
+from shiny import *
+import starlette.responses
+
+app_ui = ui.page_fluid(
+ ui.markdown(
+ """
+ ## Sticky load balancing test - Shiny for Python
+
+ The purpose of this app is to determine if HTTP
+ requests made by the client are correctly routed
+ back to the same Python process where the session
+ resides. It is only useful for testing deployments
+ that load balance traffic across more than one
+ Python process.
+
+ If this test fails, it means that sticky load
+ balancing is not working, and certain Shiny
+ functionality (like file upload/download or
+ server-side selectize) are likely to randomly fail.
+ """
+ ),
+ ui.tags.div(
+ {"class": "card"},
+ ui.tags.div(
+ {"class": "card-body font-monospace"},
+ ui.tags.div("Attempts: ", ui.tags.span("0", id="count")),
+ ui.tags.div("Status: ", ui.tags.span(id="status")),
+ ui.output_ui("out"),
+ ),
+ ),
+)
+
+def server(input: Inputs, output: Outputs, session: Session):
+ @output
+ @render.ui
+ def out():
+ url = session.dynamic_route(
+ "test",
+ lambda req: starlette.responses.PlainTextResponse(
+ "OK", headers={"Cache-Control": "no-cache"}
+ ),
+ )
+ return ui.tags.script(
+ f"""
+ const url = "{url}";
+ const count_el = document.getElementById("count");
+ const status_el = document.getElementById("status");
+ let count = 0;
+ async function check_url() {{
+ count_el.innerHTML = ++count;
+ try {{
+ const resp = await fetch(url);
+ if (!resp.ok) {{
+ status_el.innerHTML = "Failure!";
+ return;
+ }} else {{
+ status_el.innerHTML = "In progress";
+ }}
+ }} catch(e) {{
+ status_el.innerHTML = "Failure!";
+ return;
+ }}
+ if (count === 100) {{
+ status_el.innerHTML = "Test complete";
+ return;
+ }}
+ setTimeout(check_url, 10);
+ }}
+ check_url();
+ """
+ )
+
+app = App(app_ui, server)
+```
+
+Install the libraries with `pip install -r requirements.txt`.
+Run the app with `shiny run --reload --port 8080 python/app.py`,
+then visit `http://127.0.0.1:8080` in your browser.
+You'll see a counter running up to 100 and reporting if the test failed or succeeded.
+
+### The R Version {-}
+
+Create an `r` folder.
+Put the app code in the `app.R` file:
+
+```R
+library(shiny)
+library(bslib)
+
+ui <- fixedPage(
+ theme = bs_theme(version = 5), # force BS v5
+ markdown("
+## Sticky load balancing test - Shiny for Python
+
+The purpose of this app is to determine if HTTP
+requests made by the client are correctly routed
+back to the same R process where the session
+resides. It is only useful for testing deployments
+that load balance traffic across more than one
+R process.
+
+If this test fails, it means that sticky load
+balancing is not working, and certain Shiny
+functionality (like file upload/download or
+server-side selectize) are likely to randomly fail.
+ "),
+ tags$div(
+ class = "card",
+ tags$div(
+ class = "card-body font-monospace",
+ tags$div("Attempts: ", tags$span(id="count", "0")),
+ tags$div("Status: ", tags$span(id="status")),
+ uiOutput("out")
+ )
+ )
+)
+
+server <- function(input, output, session) {
+
+ url <- session$registerDataObj(
+ name = "test",
+ data = list(),
+ filter = function(data, req) {
+ message("INFO: ",
+ req$REMOTE_ADDR, ":",
+ req$REMOTE_PORT,
+ " - ",
+ req$REQUEST_METHOD,
+ " /session/",
+ session$token,
+ req$PATH_INFO,
+ req$QUERY_STRING)
+ shiny:::httpResponse(
+ status = 200L,
+ content_type = "text/html; charset=UTF-8",
+ content = "OK",
+ headers = list("Cache-Control" = "no-cache"))
+ }
+ )
+ output$out <- renderUI({
+ message("Incoming connection")
+ tags$script(
+ sprintf('
+ const url = "%s";
+ const count_el = document.getElementById("count");
+ const status_el = document.getElementById("status");
+ let count = 0;
+ async function check_url() {
+ count_el.innerHTML = ++count;
+ try {
+ const resp = await fetch(url);
+ if (!resp.ok) {
+ status_el.innerHTML = "Failure!";
+ return;
+ } else {
+ status_el.innerHTML = "In progress";
+ }
+ } catch(e) {
+ status_el.innerHTML = "Failure!";
+ return;
+ }
+ if (count === 100) {
+ status_el.innerHTML = "Test complete";
+ return;
+ }
+ setTimeout(check_url, 10);
+ }
+ check_url();
+ ', url)
+ )
+ })
+
+}
+
+app <- shinyApp(ui, server)
+```
+
+Install packages in R with `install.packages(c("shiny", "bslib"))`.
+Run the app in R with `shiny::runApp("r", port = 8080)`
+then visit `http://127.0.0.1:8080` in your browser.
+You'll see a counter running up to 100 and reporting the success of the test.
+
+### Shinylive {-}
+
+Create Python shinylive version following
+(you will need to have `shinylive` installed):
+
+```bash
+shinylive export python py-shinylive
+python3 -m http.server --directory py-shinylive 8008
+```
+
+Create R shinylive version following :
+
+```bash
+R -q -e 'shinylive::export("r", "r-shinylive")'
+R -q -e 'httpuv::runStaticServer("r-shinylive", port=8008)'
+```
+
+### Test Load Balancing {-}
+
+The app is useful when the deployment includes load balancing between
+multiple replicas. For sush deployments, session affinity (or sticky sessions) needs to
+be available. This app can be used to test such setups.
+
+If the test fails, it will stop before the counter reaches 100 and will say _Failure!_.
+If the app succeeds 100 times, you'll see _Test complete_.
+The app is not useful for testing a single instance deployment, or with Shinylive,
+because these setups won't fail, try it!
diff --git a/book-source/05-03-appendix-cheatsheet.Rmd b/book-source/05-03-appendix-cheatsheet.Rmd
new file mode 100755
index 0000000..65b3bb9
--- /dev/null
+++ b/book-source/05-03-appendix-cheatsheet.Rmd
@@ -0,0 +1,168 @@
+# How to Pick the Right Hosting Option {-#appendix-how-to-pick}
+
+The Hosting Data Apps website recently celebrated its [6-months
+anniversary](https://hosting.analythium.io/hosting-data-apps-6-months-and-40-posts-later/).
+During this time I have written 40 posts, almost all about Shiny hosting
+options. Some of these posts reviewed particular hosting options, such
+as Shinyapps.io, Shiny Server, Heroku, and ShinyProxy.
+
+A lot has been said about the hosting options themselves, but what about
+the needs of the developers and the users?
+
+The most important question to ask is: "*which option is better *for
+what**"? This is where developers and the users of the app come into
+the picture with their *specific needs and constraints*.
+
+You could say, for example, that "*I want to host my portfolio for free
+and I don't care about a custom domain name*". Or a nonprofit might say,
+"*we want to host our apps at low cost, we want custom domains, and we
+want to be able to handle surge traffic, but we don't want to maintain
+any servers*".
+
+These are really specific criteria, and if you ask me, I might say
+Shinyapps.io is best for you and Heroku with a Docker-based deployment
+is best for this organization. But how would you or I make such a
+decision?
+
+If you have been developing Shiny apps, you might already have your
+preferred way of deployment. But as your needs evolve, you will identify
+additional requirements and might find that your go-to option is not the
+best anymore. Then you'll do some research and find the next option.
+
+If you are not yet familiar with Shiny hosting options, you still need
+to make an informed choice at some point, so that you are not wasting
+your time and effort on something that will not serve you well over the
+long run. Here is the 3 step process that you can follow to help you
+with this decision.
+
+Before you begin take a piece of paper.
+
+## Start with the Why {-}
+
+*Why do you want the app or apps deployed?* Are you building a
+portfolio to boost your career? Are you deploying useful apps for
+stakeholders or clients of your organization? Are you trying to sell an
+app as a software-as-a-service (SaaS) offering?
+
+Write down your answer.
+
+Getting clear on the why is the most important question. You might even
+realize that *you don't need to host* your Shiny app. For example,
+your app might be used on a laptop as a GUI to analyze data by
+non-specialists in the field without internet or cell coverage. In this
+case, no need to move on to step 2, because all you need is to [run
+Shiny locally](https://hosting.analythium.io/run-shiny-apps-locally/).
+
+However, if your answer makes it clear to you that your users will be
+accessing the app over the Internet, move on to step 2.
+
+## List Your Requirements {-}
+
+Answering the Why question will probably reveal important details about
+your motivations, your audience, the number of apps you are going to
+host, etc. The answer will also bring you closer to identifying the
+*requirements* that you'll need.
+
+For example, do you need a custom domain, how many users are you
+expecting, do you need authentication or app-level authorization? Do you
+want to host a single app or do you need to host many apps? Will you
+host non-Shiny apps?
+
+Write these down too.
+
+The following table lists the important features for many different
+*Shiny hosting options*. The table lists tiers offered by the same
+company as a separate option. Use this table to find the options that
+meet your requirements. For now, just ignore the columns inside the blue
+rectangle.
+
+![](images/part-xx/feature-matrix-comparing-shiny-hosting-options.png)
+
+If you crave a more interactive experience, I made a filterable version:
+
+View the video of it here: [https://www.youtube.com/watch?v=iGKQMKuz-ww](https://www.youtube.com/watch?v=iGKQMKuz-ww).
+
+Visit [https://hosting.analythium.io/assets/files/shiny-hosting-options.html](https://hosting.analythium.io/assets/files/shiny-hosting-options.html) to filter the options and pick the right hosting option for your Shiny app.
+
+Once you filter the table according to your requirements, you'll see a
+list of your *ideal hosting options*. Put these in the file or onto
+the paper too.
+
+## Identify Your Constraints {-}
+
+The last step involves identifying your *constraints*:
+
+- What is your *budget*?
+- What is your current *skill level*?
+- How much time do you have *time*?
+
+Recognizing these constraints will guide you toward an *optimal
+solution*. This is the point where the columns inside the blue
+rectangle come in.
+
+The Total Cost of Ownership (TCO) (USD/year) covers licensing fees and
+operating costs for the "Number of Apps" listed in the table. Prices
+range quite a bit from free to the tens of thousands. Price increases
+with performance and with the availability of enterprise features, such
+as custom domains and authentication.
+
+*PaaS* means platform-as-a-service, i.e. it is a fully managed system
+without you having to worry about the underlying infrastructure. This
+also means less control over the infrastructure, i.e. when it comes to
+choosing the *data region* where your app is served from.
+
+*Unlimited app hours* are more common for self-hosted options or paid
+PaaS plans including a single app. The need to host *multiple apps*
+will involve some compromises. The ability to host *non-Shiny apps*
+(Dahs, Streamlit, etc.) is a feature for RStudio Connect and the
+Docker-based options ([Shinyapps and Shiny Server can host Shiny for
+Python](https://shiny.rstudio.com/py/docs/deploy.html)).
+
+*Time* as a constraint will depend on how far your current skill level
+is from the level needed for a specific hosting option. You also have to
+consider that some options are fully managed PaaS offerings, others you
+have to manage, or learn how to use Docker.
+
+If you have to develop *new skills*, it might take longer. If you have
+to manage your servers, it will take more time to get started and then
+you are on the hook for maintaining your setup.
+
+Make your selections inside the columns within the blue area.
+
+## Options at a glance {-}
+
+After the 3-step process, you should see only a few or a single option
+left. Click on the name of the hosting option and the link will take you
+to the relevant tag page on the Hosting Data Apps website:
+
+- Follow the instructions in the *tutorials* to get started
+- At the end of each post, you'll find a *Further reading* section
+ listing *additional resources*
+
+If there is no option left in the table, then you might need to be more
+realistic about your expectations or relax some of your constraints. For
+example, to keep costs low, you could spend more time and invest in
+skill development. But if you have more room in your budget, you might
+choose a different path. You can also revise your requirements until you
+find an acceptable solution.
+
+
+The following diagram gives an intuitive overview of the different
+options. The vertical axis represents the *total cost* from the table
+above: free, low, and high cost. The horizontal axis shows a *range of
+skills* you need to [set up and manage your hosting
+solution](https://hosting.analythium.io/the-taxonomy-of-shiny-hosting-options/).
+It can be as simple as pushing a button, or as complex as managing
+servers or cloud clusters.
+
+![](images/part-xx/shiny-hosting-comparison-costs-complexity.png)
+
+The hosting options in this diagram are not separated by tiers but
+rather shown as spanning over a range. The fill colours identify Docker
+and non-Docker-based options, the stroke styling indicates the PaaS
+solutions.
+
+Starting November 28, 2022, free Heroku Dynos, free Heroku Postgres, and
+free Heroku Data for Redis will no longer be available -- see [this
+FAQ](https://hello.heroku.com/e/36622/-heroku-free-product-plans-faq/m49jd4/1138553151?h=KeNZWXMYkD7V9c11YSkM3-wKpW2ysvxg12dL3_hgXRk)
+for details.
diff --git a/book-source/05-04-appendix-setup.Rmd b/book-source/05-04-appendix-setup.Rmd
new file mode 100755
index 0000000..5664a6c
--- /dev/null
+++ b/book-source/05-04-appendix-setup.Rmd
@@ -0,0 +1,27 @@
+# Setting Up Your Development Environment {-#appendix-setup}
+
+Install R & Python.
+
+Install Shiny.
+Install Shiny in R with `install.packages("shiny")`, in Python with `pip install shiny`.
+
+RStudio
+
+VS Code
+
+-
+-
+
+R language support for VS Code, Python Shiny extension.
+
+Install Docker & compose
+
+Get a shell
+
+- VS Code
+- RStudio terminal
+- WSL
+- term2
+
+Other stuff we need?
+
diff --git a/book-source/05-99-references.Rmd b/book-source/05-99-references.Rmd
new file mode 100755
index 0000000..fed5f18
--- /dev/null
+++ b/book-source/05-99-references.Rmd
@@ -0,0 +1,4 @@
+\backmatter
+
+`r if(is_html_output()) '# References {-}' else '# References {-}'`
+
diff --git a/book-source/DESCRIPTION b/book-source/DESCRIPTION
new file mode 100644
index 0000000..f03180c
--- /dev/null
+++ b/book-source/DESCRIPTION
@@ -0,0 +1,8 @@
+Package: HostingShiny
+Title: This file exists for continuous integration to work for R-based repos
+Version: 1.2.0
+Imports:
+ bookdown,
+ rmarkdown,
+ knitr,
+ devtools
diff --git a/book-source/LICENSE.md b/book-source/LICENSE.md
new file mode 100755
index 0000000..ab9f160
--- /dev/null
+++ b/book-source/LICENSE.md
@@ -0,0 +1,161 @@
+# Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
+
+## Section 1 – Definitions.
+
+a. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
+
+b. __Adapter's License__ means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
+
+c. __BY-NC-SA Compatible License__ means a license listed at [creativecommons.org/compatiblelicenses](http://creativecommons.org/compatiblelicenses), approved by Creative Commons as essentially the equivalent of this Public License.
+
+d. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
+
+e. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
+
+f. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
+
+g. __License Elements__ means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike.
+
+h. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
+
+i. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
+
+j. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License.
+
+k. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.
+
+l. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
+
+m. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
+
+n. __You__ means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
+
+## Section 2 – Scope.
+
+a. ___License grant.___
+
+ 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
+
+ A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and
+
+ B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only.
+
+ 2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
+
+ 3. __Term.__ The term of this Public License is specified in Section 6(a).
+
+ 4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
+
+ 5. __Downstream recipients.__
+
+ A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
+
+ B. __Additional offer from the Licensor – Adapted Material.__ Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply.
+
+ C. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
+
+ 6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
+
+b. ___Other rights.___
+
+ 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
+
+ 2. Patent and trademark rights are not licensed under this Public License.
+
+ 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.
+
+## Section 3 – License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the following conditions.
+
+a. ___Attribution.___
+
+ 1. If You Share the Licensed Material (including in modified form), You must:
+
+ A. retain the following if it is supplied by the Licensor with the Licensed Material:
+
+ i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
+
+ ii. a copyright notice;
+
+ iii. a notice that refers to this Public License;
+
+ iv. a notice that refers to the disclaimer of warranties;
+
+ v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
+
+ B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
+
+ C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
+
+ 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
+
+ 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
+
+b. ___ShareAlike.___
+
+In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply.
+
+1. The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License.
+
+2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material.
+
+3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply.
+
+## Section 4 – Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
+
+a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;
+
+b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and
+
+c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
+
+## Section 5 – Disclaimer of Warranties and Limitation of Liability.
+
+a. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__
+
+b. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__
+
+c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
+
+## Section 6 – Term and Termination.
+
+a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
+
+b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
+
+ 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
+
+ 2. upon express reinstatement by the Licensor.
+
+ For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
+
+c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
+
+d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
+
+## Section 7 – Other Terms and Conditions.
+
+a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
+
+b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
+
+## Section 8 – Interpretation.
+
+a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
+
+b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
+
+c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
+
+d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
+
+> Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at [creativecommons.org/policies](http://creativecommons.org/policies), Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
+>
+> Creative Commons may be contacted at creativecommons.org
diff --git a/book-source/README.md b/book-source/README.md
new file mode 100755
index 0000000..fc7c738
--- /dev/null
+++ b/book-source/README.md
@@ -0,0 +1,285 @@
+# Bookdown folder
+> Hosting Shiny Book
+
+Setup (you need to have R and LaTeX/pdflatex installed), see
+
+```bash
+cd bookdown
+R -e "install.packages('deps');deps::install(ask=FALSE)"
+```
+
+How to build the book:
+
+```bash
+# HTML output into the ./docs folder (uses settings in _output.yml)
+R -q -e "bookdown::clean_book(TRUE); bookdown::render_book('index.Rmd')"
+# open the HTML output
+R -q -e "browseURL('docs/index.html')"
+
+# create PDF of the book and all TeX files for CRC Press
+Rscript pdf_build_from_tex.R
+```
+
+See
+
+Using Docker:
+
+```bash
+docker run -it --rm --platform linux/amd64 \
+ -v $PWD/bookdown:/home/root/bookdown \
+ -w /home/root ghcr.io/analythium/hosting-shiny-book-dev:v1 \
+ /bin/bash /home/root/bookdown/_build.sh
+```
+
+## Overview of Parts & Chapters
+
+- Part I: Getting Started
+ - Background (DEvOps, Shiny hosting cycle)
+ - Hosting Concepts
+ - The Tools of the Trade (local setup: why?)
+- Part II: Shiny Apps
+ - Developing Shiny Apps
+ - ...
+ - Containerizing Shiny Apps
+ - Docker Concepts
+ - ...
+ - Alternatives to Docker
+ - Working with Existing Images
+ - Docker Login
+ - Docker Images
+ - Pulling Images
+ - Running Images
+ - Building a New Image
+ - The Dockerfile
+ - Base and Parent Images
+ - Rocker, r-lib, rstudio (posit???)
+ - layers & caching vs size
+ - image is zip layers + metadata with hashes
+ - Architecture (AMD/ARM), buildkit
+ - Sharing Images
+ - Pushing Images
+ - Docker Registries
+ - Shiny Apps With Dependencies (use bananas)
+ - system libraries
+ - RSPM, BSPM, r2u, python?
+ - R dependencies
+ - explicit dependencies
+ - DESCRIPTION file, remotes & pak
+ - renv
+ - deps
+ - Python requirements
+ - pip
+ - pipreqs
+ - poetry
+ - Conda?
+ - ??? for python
+ - Best Practices
+ - Minimize dependencies
+ - Use caching
+ - Order layers
+ - Switch user
+ - Other considerations
+ - Use a linter
+ - Use labels
+ - Docker Security Scanning https://github.com/analythium/hosting-shiny-book-dev/tree/main/blog-posts/2023-02-04_insiders-digest-53
+ - Summary
+- Part III: Hosting Shiny Apps
+ - A Review of Shiny Hosting Options
+ - Cost & complexity
+ - 2x2 setup options
+ - Deployment considerations
+ - PaaS
+ - Shinyapps (not container based)
+ - DOAP
+ - Static content
+ - Containerized apps
+ - Heroku
+ - Fly.io (& other firecracker based ones)
+ - Fargate-like offerings
+ - Multiple apps using containerized Shiny Server
+ - ...???
+ - Virtual Private Servers (https://cloud.google.com/learn/what-is-a-virtual-private-server)
+ - Setup
+ - DO droplet & ssh login
+ - Navigation & commands
+ - Reverse proxy / Caddy (mention Apache & Nginx)
+ - Custom domain & TLS
+ - Firewall
+ - Static hosting on file server
+ - Reverse proxy setup with Caddy
+ - systemd
+ - Posit Connect
+ - Shiny Server
+ - multiple apps / linux users
+ - Containerized setups
+ - ShinyProxy
+ - Setup
+ - Docker compose
+ - Basic setup
+ - File server & dynamic apps example
+ - Containerized ShinyProxy
+ - Hybrid setups
+ - Embedding onto your website (Iframes)
+ - iframe + Shinyapps ...
+ - Considerations for Production
+ - Security
+ - security groups
+ - secret management
+ - Snapshots & backups
+ - Reserved IP
+ - Configuration
+ - Access control
+ - In-app auth
+ - Password protected server (basic auth with Caddy per route)
+ - OIDC OAuth
+ - CICD:
+ - Docker GitHub actions (steps, auto tagging)
+ - cron job polling
+ - webhooks
+ - Scaling
+ - Shinyapps & Connect
+ - Heroku
+ - Load balancing with Docker compose
+- Part IV: What is next?
+ - Reevaluating your hosting needs
+ - Licensing considerations
+ - Advanced topics
+ - Kubernetes
+ - Shiny apps vanilla
+ - ShinyProxy operator
+ - OIDC/SSO
+ - IaC: terraform, packer
+- Part V: Appendices
+
+Notes on system package management and R base images:
+
+- https://hub.docker.com/_/r-base
+- https://github.com/r-lib/rig
+- focus on pak instead of remotes? https://pak.r-lib.org/reference/features.html
+- https://github.com/r-hub/r-minimal
+- https://solutions.posit.co/envs-pkgs/environments/docker/
+- https://hub.docker.com/r/rstudio/r-base
+- https://github.com/rstudio/r-docker
+- https://rocker-project.org/images/
+- https://github.com/rocker-org/rocker
+- https://eddelbuettel.github.io/r2u/
+
+Python:
+
+- https://jfrog.com/devops-tools/article/how-to-choose-a-docker-base-image-for-python/
+- https://pythonspeed.com/articles/base-image-python-docker-images/
+- https://hub.docker.com/_/python
+- https://snyk.io/blog/best-practices-containerizing-python-docker/
+
+Useful for both:
+
+- https://docs.docker.com/build/building/best-practices/
+- https://stackoverflow.com/questions/21553353/what-is-the-difference-between-cmd-and-entrypoint-in-a-dockerfile
+- https://docs.docker.com/glossary/
+- valid image tags https://docs.docker.com/reference/cli/docker/image/tag/
+- https://github.com/opencontainers/distribution-spec
+- CLI reference: https://docs.docker.com/reference/cli/docker/
+
+Need to talk about
+
+- the `:latest` tag and catches
+- image name vs tag (tagging an image, applies `name:tag`)
+- base vs parent image
+- `docker manifest inspect --verbose golang:1.17.1` etc
+- name vs hash https://hackernoon.com/docker-images-name-vs-tag-vs-digest
+- https://medium.com/@mccode/the-misunderstood-docker-tag-latest-af3babfd6375
+
+
+This repository is used in shinyapps.io to install those dependencies:
+- https://github.com/rstudio/shinyapps-package-dependencies
+
+A curated list of awesome R and Python packages:
+- https://github.com/nanxstats/awesome-shiny-extensions
+
+Code formatting: https://github.com/r-lib/styler
+
+```R
+library(styler)
+# styler::style_dir("book-files/01-shiny-apps")
+styler::style_dir("book-files/01-shiny-apps/bananas")
+```
+
+## Links
+
+For links, we can abbreviate them and set up `s3y.ca` as URL shortener. When we get there:
+
+- set up gh pages
+- scan draft for links
+- include csv with original and shortened url's
+- write script to replace the urls (if not already the short domain)
+- write script that adds the redirect pages to the shortener repo
+
+## References
+
+Shiny in general
+
+- https://unleash-shiny.rinterface.com/
+- https://book.javascript-for-r.com/
+- https://mastering-shiny.org/
+
+Deployment (not really hosting)
+
+- https://mastering-shiny.org/
+- https://engineering-shiny.org/
+
+Devops, docker, etc.
+
+- https://do4ds.com/
+- https://raps-with-r.dev/
+
+## For each chapter
+
+- start with an overview of what the chapter is about and how it is structured at a high level
+- end with a Summary section and list a few key references where readers can go to learn more
+
+## Using knitr for floating environments
+
+Reference it in text like:
+
+Now consider Fig. \@ref(fig:part1-hosting-cycle), which shows the cycle.
+
+Use the R code chunk like this:
+
+ ```{r part1-hosting-cycle, eval=TRUE, echo=FALSE, fig.cap="Shiny hosting cycle."}
+ include_graphics("images/part-01/hosting-cycle.png")
+ ```
+
+Important: do not use _ in ref names because it is spacial in latex. See .
+
+When adding code chunks, use tripletick and the language name, otherwise there will be no code chunk background in the PDF.
+
+## Index
+
+
+
+This \index{Preface}Preface is indexed.
+
+## Cross references
+
+
+
+You can also reference sections using the same syntax `\@ref(label)` where label is the sligified title used as the anchor, where label is the section ID or use `[Section header text]`.
+
+Need to test this in HTML & PDF.
+
+## Calling for help
+
+Use PETER and KALVIN as a placeholder if you want something to be filled in by the other co-author.
+
+FIXME: use it to flag parts that we have to revisit (update a link etc.)
+
+## Line breaks
+
+Use 70 char width in latex code (we can fit 80 chars but then inline code bacomes too small height)
+
+## Figure placements
+
+
+
+It is set to `H` which right where it is mentioned.
+We can probably use `fig.pos = "btp"` to override this as needed.
diff --git a/book-source/WORDLIST b/book-source/WORDLIST
new file mode 100644
index 0000000..42a3f6d
--- /dev/null
+++ b/book-source/WORDLIST
@@ -0,0 +1,14 @@
+Sólymos
+Eng
+Posit
+RStudio
+Docker
+Rocker
+Python
+seaborn
+matplotlib
+Shiny
+shiny
+plotly
+e1071
+scikit_learn
diff --git a/book-source/_bookdown.yml b/book-source/_bookdown.yml
new file mode 100755
index 0000000..028e3fe
--- /dev/null
+++ b/book-source/_bookdown.yml
@@ -0,0 +1,59 @@
+book_filename: "HostingShiny"
+output_dir: "docs"
+delete_merged_file: true
+# To build only specific chapters of the book, comment out the .Rmd files you
+# want to omit but index.Rmd must not be commented out
+rmd_files:
+ html: [
+ "index.Rmd",
+ "00-01-foreword.Rmd",
+ "00-02-preface.Rmd",
+ "01-01-background.Rmd",
+ "01-02-hosting-concepts.Rmd",
+ "01-03-tools.Rmd",
+ "01-04-examples.Rmd",
+ "02-01-a-developing-shiny.Rmd",
+ "02-01-b-developing-shiny.Rmd",
+ "02-01-c-developing-shiny.Rmd",
+ "02-02-a-containerizing-shiny.Rmd",
+ "02-02-b-containerizing-shiny.Rmd",
+ "02-02-c-containerizing-shiny.Rmd",
+ "03-01-hosting-shiny.Rmd",
+ "03-02-shiny-in-production.Rmd",
+ "04-01-what-is-next.Rmd",
+ "05-01-appendix-bananas.Rmd",
+ "05-02-appendix-lbtest.Rmd",
+ "05-03-appendix-cheatsheet.Rmd",
+ "05-04-appendix-setup.Rmd",
+ "05-99-references.Rmd"
+ ]
+ latex: [
+ "index.Rmd",
+ "00-01-foreword.Rmd",
+ "00-02-preface.Rmd",
+ "01-01-background.Rmd",
+ "01-02-hosting-concepts.Rmd",
+ "01-03-tools.Rmd",
+ "01-04-examples.Rmd",
+ "02-01-a-developing-shiny.Rmd",
+ "02-01-b-developing-shiny.Rmd",
+ "02-01-c-developing-shiny.Rmd",
+ "02-02-a-containerizing-shiny.Rmd",
+ "02-02-b-containerizing-shiny.Rmd",
+ "02-02-c-containerizing-shiny.Rmd",
+ "03-01-hosting-shiny.Rmd",
+ "03-02-shiny-in-production.Rmd",
+ "04-01-what-is-next.Rmd",
+ "05-01-appendix-bananas.Rmd",
+ "05-02-appendix-lbtest.Rmd",
+ "05-03-appendix-cheatsheet.Rmd",
+ "05-04-appendix-setup.Rmd",
+ "05-99-references.Rmd"
+ ]
+language:
+ label:
+ fig: "Figure "
+ tab: "Table "
+ ui:
+ edit: "Edit"
+ chapter_name: "Chapter "
diff --git a/book-source/_build_pdf.R b/book-source/_build_pdf.R
new file mode 100644
index 0000000..5a12c66
--- /dev/null
+++ b/book-source/_build_pdf.R
@@ -0,0 +1,55 @@
+# This script is used to create PDF of the book and all TeX files for CRC Press.
+
+# Keep all the intermediary files so they can be copied below. This step also
+# generates the .tex file in the `docs` folder
+options(tinytex.clean = FALSE)
+bookdown::render_book("index.Rmd",
+ bookdown::pdf_book(
+ pandoc_args="--top-level-division=chapter",
+ latex_engine = "xelatex",
+ keep_tex = TRUE,
+ toc_depth = 3,
+ toc_unnumbered = FALSE,
+ toc_appendix = TRUE,
+ quote_footer = c("\\VA{", "}{}"),
+ highlight_bw = FALSE,
+ includes = list(
+ in_header="latex/preamble.tex",
+ before_body="latex/before_body.tex",
+ after_body="latex/after_body.tex")
+ ),
+ clean = FALSE,
+ encoding = "UTF-8"
+)
+
+# Copy R generated figures
+if (!dir.exists(file.path("docs", "figures"))) {
+ tmp <- dir.create(file.path("docs", "figures"),
+ recursive = TRUE
+ )
+}
+tmp <- file.copy(from = "figures", to = "docs", recursive = TRUE)
+
+# Copy references over
+if (!dir.exists(file.path("docs", "bib"))) {
+ tmp <- dir.create(file.path("docs", "bib"))
+}
+tmp <- file.copy(from = "bib", to = "docs", recursive = TRUE)
+
+# For printing the PDF from the .tex file
+if (!dir.exists("docs")) {
+ tmp <- dir.create("docs")
+}
+tmp <- file.copy("krantz.cls",
+ file.path("docs", "krantz.cls"),
+ overwrite = TRUE
+)
+
+# Clean up extra files
+extra_extensions <- c(
+ ".bbl", ".blg", ".idx", ".ilg", ".ind",
+ ".log", ".synctex.gz", ".toc", ".aux"
+)
+# extra_files <- file.path("docs", paste0("HostingShiny", extra_extensions))
+# suppressWarnings({tmp <- file.remove(file.path(paste0("HostingShiny", extra_extensions)))})
+suppressWarnings({tmp <- file.remove(file.path("docs", paste0("HostingShiny", extra_extensions)))})
diff --git a/book-source/_check.R b/book-source/_check.R
new file mode 100644
index 0000000..07c8fa4
--- /dev/null
+++ b/book-source/_check.R
@@ -0,0 +1,32 @@
+library(urlchecker)
+check_urls <- function (dir=".") {
+ urlchecker:::with_pandoc_available(0)
+ urls <- path <- character()
+ rfiles <- list.files(dir, pattern = "\\.Rmd$", full.names = TRUE)
+ for (rfile in rfiles) {
+ if (!is.na(rfile) && nzchar(Sys.which("pandoc"))) {
+ rpath <- asNamespace("tools")$.file_path_relative_to_dir(rfile, dir)
+ tfile <- tempfile(fileext = ".html")
+ on.exit(unlink(tfile), add = TRUE)
+ out <- urlchecker:::.pandoc_md_for_CRAN2(rfile, tfile)
+ if (!out$status) {
+ rurls <- urlchecker:::tools$.get_urls_from_HTML_file(tfile)
+ urls <- c(urls, rurls)
+ path <- c(path, rep.int(rpath, length(rurls)))
+ }
+ }
+ }
+ # probably need to remove localhost from the list
+# keep <- !grepl("127\\.0\\.0\\.1", urls)
+# urls <- urls[keep]
+# path <- path[keep]
+ db <- urlchecker:::tools$url_db(urls, path)
+ res <- urlchecker:::tools$check_url_db(db, verbose = FALSE, parallel = TRUE, pool = curl::new_pool())
+ if (NROW(res) > 0) {
+ res$root <- normalizePath(dir)
+ }
+ class(res) <- c("urlchecker_db", class(res))
+ res
+}
+# list.files("book-source", pattern = "\\.Rmd$") |> sort() |> cat(sep="\",\n\"")
+check_urls()
diff --git a/book-source/_includes/analytics.html b/book-source/_includes/analytics.html
new file mode 100644
index 0000000..1c4fe59
--- /dev/null
+++ b/book-source/_includes/analytics.html
@@ -0,0 +1,3 @@
+
diff --git a/book-source/_includes/footer.html b/book-source/_includes/footer.html
new file mode 100644
index 0000000..7770780
--- /dev/null
+++ b/book-source/_includes/footer.html
@@ -0,0 +1,15 @@
+
+
diff --git a/book-source/_includes/logo.html b/book-source/_includes/logo.html
new file mode 100644
index 0000000..ef82ca6
--- /dev/null
+++ b/book-source/_includes/logo.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/book-source/_lint.R b/book-source/_lint.R
new file mode 100644
index 0000000..dae749a
--- /dev/null
+++ b/book-source/_lint.R
@@ -0,0 +1,20 @@
+lintr::lint_dir()
+library(lintr)
+dr <- "."
+rmd_files <- list.files(dir, pattern = "\\.Rmd$", full.names = TRUE)
+
+lint(
+ filename,
+ linters = NULL,
+ ...,
+ cache = FALSE,
+ parse_settings = TRUE,
+ text = NULL
+ )
+
+i <- 2
+lint(rmd_files[i], lintr::linters_with_defaults())
+lint(rmd_files[i], pkgpurl::default_linters)
+
+lint_dir(dir, pattern = "(?i)[.](rmd)$")
+
diff --git a/book-source/_output.yml b/book-source/_output.yml
new file mode 100755
index 0000000..ffd6257
--- /dev/null
+++ b/book-source/_output.yml
@@ -0,0 +1,43 @@
+# Modified from https://github.com/rstudio/bookdown/blob/master/inst/examples/_output.yml
+# To uncomment these lines to create a PDF version, we recommend activating
+# multiple cursors in RStudio using (on a Mac) Control + Option + A:
+# bookdown::pdf_book:
+# includes:
+# in_header: latex/preamble.tex
+# before_body: latex/before_body.tex
+# after_body: latex/after_body.tex
+# keep_tex: true
+# dev: "cairo_pdf"
+# latex_engine: xelatex
+# citation_package: natbib
+# template: null
+# pandoc_args: --top-level-division=chapter
+# toc_depth: 3
+# toc_unnumbered: false
+# toc_appendix: true
+# quote_footer: ["\\VA{", "}{}"]
+# highlight_bw: true
+bookdown::gitbook:
+ df_print: default
+ css: style.css
+ split_by: "chapter+number"
+ config:
+ toc:
+ collapse: section
+ scroll_highlight: true
+ edit:
+ link: https://github.com/analythium/hosting-shiny-book/edit/main/%s
+ includes:
+ in_header: _includes/analytics.html
+ before_body: _includes/logo.html
+# bookdown::bs4_book:
+# css: [css/style.css, css/style_gitbook.css]
+# repo: https://github.com/analythium/hosting-shiny-book
+# includes:
+# in_header: _includes/analytics.html
+# before_body: _includes/logo.html
+# after_body: _includes/footer.html
+# theme:
+# primary: "#DE633C"
+# fg: "#2b2121"
+# bg: "#ffffff"
diff --git a/book-source/bib/articles.bib b/book-source/bib/articles.bib
new file mode 100755
index 0000000..edeb9d9
--- /dev/null
+++ b/book-source/bib/articles.bib
@@ -0,0 +1,68 @@
+@article{Leisch2002,
+ author = {Leisch, Friedrich},
+ title = {Sweave, part I: Mixing R and LaTeX},
+ journal = {R News},
+ year = {2002},
+ note = {https://journal.r-project.org/articles/RN-2002-025/},
+ volume = {2},
+ issue = {3},
+ issn = {1609-3631},
+ pages = {28-31}
+}
+
+@article{rocker,
+ author = {Carl Boettiger and Dirk Eddelbuettel},
+ title = {{An Introduction to Rocker: Docker Containers for R}},
+ year = {2017},
+ journal = {{The R Journal}},
+ doi = {10.32614/RJ-2017-065},
+ url = {https://doi.org/10.32614/RJ-2017-065},
+ pages = {527--536},
+ volume = {9},
+ number = {2}
+}
+
+@article{Rockerverse,
+ author = {Daniel Nüst and Dirk Eddelbuettel and Dom Bennett and
+ Robrecht Cannoodt and Dav Clark and Gergely Daróczi and
+ Mark Edmondson and Colin Fay and Ellis Hughes and Lars
+ Kjeldgaard and Sean Lopp and Ben Marwick and Heather Nolis
+ and Jacqueline Nolis and Hong Ooi and Karthik Ram and Noam
+ Ross and Lori Shepherd and Péter Sólymos and Tyson Lee
+ Swetnam and Nitesh Turaga and Charlotte Van Petegem and
+ Jason Williams and Craig Willis and Nan Xiao},
+ title = {{The Rockerverse: Packages and Applications for
+ Containerisation with R}},
+ year = {2020},
+ journal = {{The R Journal}},
+ doi = {10.32614/RJ-2020-007},
+ url = {https://doi.org/10.32614/RJ-2020-007},
+ pages = {437--461},
+ volume = {12},
+ number = {1}
+}
+
+@INPROCEEDINGS{Eng_2021,
+ author={Eng, Kalvin and Hindle, Abram},
+ booktitle={2021 IEEE/ACM 18th International Conference on Mining Software Repositories (MSR)},
+ title={Revisiting Dockerfiles in Open Source Software Over Time},
+ year={2021},
+ volume={},
+ number={},
+ pages={449-459},
+ keywords={Education;Containers;Tools;Market research;Cognition;History;Data mining;Git;GitHub;Docker},
+ doi={10.1109/MSR52588.2021.00057}
+}
+
+@article{Eng_2024,
+ title={Patterns of multi-container composition for service orchestration with Docker Compose},
+ volume={29},
+ ISSN={1573-7616},
+ url={http://dx.doi.org/10.1007/s10664-024-10462-8},
+ DOI={10.1007/s10664-024-10462-8},
+ number={3},
+ journal={Empirical Software Engineering},
+ publisher={Springer Science and Business Media LLC},
+ author={Eng, Kalvin and Hindle, Abram and Stroulia, Eleni},
+ year={2024}
+}
diff --git a/book-source/bib/books.bib b/book-source/bib/books.bib
new file mode 100755
index 0000000..e3c4d92
--- /dev/null
+++ b/book-source/bib/books.bib
@@ -0,0 +1,124 @@
+@book{Rodrigues2023,
+ title = {Building reproducible analytical pipelines with {R}},
+ author = {Bruno Rodrigues},
+ year = {2023},
+ url = {https://raps-with-r.dev/}
+}
+
+@book{Wickham2021,
+ title = {Mastering {Shiny}},
+ author = {Hadley Wickham},
+ year = {2021},
+ publisher = {O'Reilly Media, Inc.},
+ isbn = {9781492047339},
+ url = {https://mastering-shiny.org/}
+}
+
+@book{Fay2021,
+ title = {Engineering Production-Grade {Shiny} Apps},
+ author = {Colin Fay and Sébastien Rochette and Vincent Guyader and Cervan Girard},
+ year = {2021},
+ publisher = {Chapman & Hall},
+ isbn = {9780367466022 },
+ url = {https://engineering-shiny.org/}
+}
+
+@book{coene2021javascript,
+ title={Javascript for R},
+ author={Coene, J.},
+ isbn={9781000408171},
+ lccn={2021008502},
+ series={Chapman \& Hall/CRC The R Series},
+ url={https://books.google.ch/books?id=ntUxEAAAQBAJ},
+ year={2021},
+ publisher={CRC Press}
+}
+
+@book{Granjon2022,
+ title = {Outstanding User Interfaces with Shiny},
+ author = {David Granjon},
+ year = {2022},
+ publisher = {Chapman & Hall},
+ isbn = {9780367643652},
+ url = {https://unleash-shiny.rinterface.com/}
+}
+
+@book{Gold2024,
+ title = {DevOps for Data Science},
+ author = {Alex Gold},
+ year = {2024},
+ publisher = {Chapman & Hall},
+ isbn = {9781032100340},
+ url = {https://do4ds.com/}
+}
+
+
+@book{usedtor2016,
+ title = {Getting Used to R, {RStudio}, and R Markdown},
+ author = {Chester Ismay and Patrick C. Kennedy},
+ year = {2016},
+ url = {https://rbasics.netlify.com}
+}
+
+@book{wilkinson2005,
+ title = {The Grammar of Graphics (Statistics and Computing)},
+ author = {Leland Wilkinson},
+ year = {2005},
+ edition = {First},
+ publisher = {Springer-Verlag},
+ address = {Secaucus, NJ},
+ isbn = {978-0387245447}
+}
+
+@Book{xie2015,
+ author = {Yihui Xie},
+ title = {Dynamic Documents with {R} and knitr},
+ publisher = {Chapman and Hall/CRC},
+ address = {Boca Raton, Florida},
+ year = {2015},
+ edition = {2nd},
+ note = {ISBN 978-1498716963},
+ url = {http://yihui.name/knitr/}
+}
+
+@Book{AdvancedR,
+ author = {Wickham Hadley},
+ title = {Advanced R},
+ publisher = {Chapman and Hall/CRC},
+ address = {Boca Raton, Florida},
+ year = {2019},
+ edition = {2nd},
+ note = {ISBN 978-0815384571},
+ url = {https://adv-r.hadley.nz/}
+}
+
+@book{R4DS,
+author = {Wickham, Hadley and Grolemund, Garrett},
+title = {R for Data Science: Import, Tidy, Transform, Visualize, and Model Data},
+year = {2017},
+isbn = {1491910399},
+publisher = {O'Reilly Media, Inc.},
+adress = {Farnham, UK},
+edition = {1st}
+}
+
+@Book{rpkg-book,
+ author = {Wickham Hadley and Jennifer Bryan},
+ title = {R Packages},
+ publisher = {O'Reilly Media, Inc.},
+ adress = {Farnham, UK},
+ year = {2023},
+ edition = {2nd},
+ note = {ISBN 978-1098134945},
+ url = {https://r-pkgs.org/}
+}
+
+@Book{Knuth1992,
+ author = {Donald Knuth},
+ title = {Literate Programming},
+ publisher = {Center for the Study of Language and Information---CSLI},
+ adress = {Stanford, CA},
+ year = {1992},
+ note = {ISBN 978-0937073803}
+}
+
diff --git a/book-source/bib/libraries.bib b/book-source/bib/libraries.bib
new file mode 100755
index 0000000..31bb8cf
--- /dev/null
+++ b/book-source/bib/libraries.bib
@@ -0,0 +1,39 @@
+@article{Waskom2021,
+ doi = {10.21105/joss.03021},
+ url = {https://doi.org/10.21105/joss.03021},
+ year = {2021},
+ publisher = {The Open Journal},
+ volume = {6},
+ number = {60},
+ pages = {3021},
+ author = {Michael L. Waskom},
+ title = {seaborn: statistical data visualization},
+ journal = {Journal of Open Source Software}
+}
+
+@Article{Hunter2007,
+ Author = {Hunter, J. D.},
+ Title = {Matplotlib: A 2D graphics environment},
+ Journal = {Computing in Science \& Engineering},
+ Volume = {9},
+ Number = {3},
+ Pages = {90--95},
+ abstract = {Matplotlib is a 2D graphics package used for Python for
+ application development, interactive scripting, and publication-quality
+ image generation across user interfaces and operating systems.},
+ publisher = {IEEE COMPUTER SOC},
+ doi = {10.1109/MCSE.2007.55},
+ year = 2007
+}
+
+@article{scikit-learn,
+ title={Scikit-learn: Machine Learning in {P}ython},
+ author={Pedregosa, F. and Varoquaux, G. and Gramfort, A. and Michel, V.
+ and Thirion, B. and Grisel, O. and Blondel, M. and Prettenhofer, P.
+ and Weiss, R. and Dubourg, V. and Vanderplas, J. and Passos, A. and
+ Cournapeau, D. and Brucher, M. and Perrot, M. and Duchesnay, E.},
+ journal={Journal of Machine Learning Research},
+ volume={12},
+ pages={2825--2830},
+ year={2011}
+}
diff --git a/book-source/bib/packages.bib b/book-source/bib/packages.bib
new file mode 100644
index 0000000..ee61a7d
--- /dev/null
+++ b/book-source/bib/packages.bib
@@ -0,0 +1,208 @@
+@Manual{R-base,
+ title = {R: A Language and Environment for Statistical Computing},
+ author = {{R Core Team}},
+ organization = {R Foundation for Statistical Computing},
+ address = {Vienna, Austria},
+ year = {2024},
+ url = {https://www.R-project.org/},
+}
+
+@Manual{R-shiny,
+ title = {shiny: Web Application Framework for R},
+ author = {Winston Chang and Joe Cheng and JJ Allaire and Carson Sievert and Barret Schloerke and Yihui Xie and Jeff Allen and Jonathan McPherson and Alan Dipert and Barbara Borges},
+ year = {2024},
+ note = {R package version 1.8.1.1},
+ url = {https://CRAN.R-project.org/package=shiny},
+}
+
+@Manual{R-bookdown,
+ title = {bookdown: Authoring Books and Technical Documents with R Markdown},
+ author = {Yihui Xie},
+ year = {2024},
+ note = {R package version 0.39,
+https://pkgs.rstudio.com/bookdown/},
+ url = {https://github.com/rstudio/bookdown},
+}
+
+@Manual{R-ggplot2,
+ title = {ggplot2: Create Elegant Data Visualisations Using the Grammar of Graphics},
+ author = {Hadley Wickham and Winston Chang and Lionel Henry and Thomas Lin Pedersen and Kohske Takahashi and Claus Wilke and Kara Woo and Hiroaki Yutani and Dewey Dunnington and Teun {van den Brand}},
+ year = {2024},
+ note = {R package version 3.5.1,
+https://github.com/tidyverse/ggplot2},
+ url = {https://ggplot2.tidyverse.org},
+}
+
+@Manual{R-kableExtra,
+ title = {kableExtra: Construct Complex Table with kable and Pipe Syntax},
+ author = {Hao Zhu},
+ year = {2024},
+ note = {R package version 1.4.0,
+https://github.com/haozhu233/kableExtra},
+ url = {http://haozhu233.github.io/kableExtra/},
+}
+
+@Manual{R-knitr,
+ title = {knitr: A General-Purpose Package for Dynamic Report Generation in R},
+ author = {Yihui Xie},
+ year = {2024},
+ note = {R package version 1.46},
+ url = {https://yihui.org/knitr/},
+}
+
+@Manual{R-rmarkdown,
+ title = {rmarkdown: Dynamic Documents for R},
+ author = {JJ Allaire and Yihui Xie and Christophe Dervieux and Jonathan McPherson and Javier Luraschi and Kevin Ushey and Aron Atkins and Hadley Wickham and Joe Cheng and Winston Chang and Richard Iannone},
+ year = {2024},
+ note = {R package version 2.27, https://pkgs.rstudio.com/rmarkdown/},
+ url = {https://github.com/rstudio/rmarkdown},
+}
+
+@Book{bookdown2016,
+ title = {bookdown: Authoring Books and Technical Documents with {R} Markdown},
+ author = {Yihui Xie},
+ publisher = {Chapman and Hall/CRC},
+ address = {Boca Raton, Florida},
+ year = {2016},
+ isbn = {978-1138700109},
+ url = {https://bookdown.org/yihui/bookdown},
+}
+
+@Book{ggplot22016,
+ author = {Hadley Wickham},
+ title = {ggplot2: Elegant Graphics for Data Analysis},
+ publisher = {Springer-Verlag New York},
+ year = {2016},
+ isbn = {978-3-319-24277-4},
+ url = {https://ggplot2.tidyverse.org},
+}
+
+@Book{knitr2015,
+ title = {Dynamic Documents with {R} and knitr},
+ author = {Yihui Xie},
+ publisher = {Chapman and Hall/CRC},
+ address = {Boca Raton, Florida},
+ year = {2015},
+ edition = {2nd},
+ note = {ISBN 978-1498716963},
+ url = {https://yihui.org/knitr/},
+}
+
+@InCollection{knitr2014,
+ booktitle = {Implementing Reproducible Computational Research},
+ editor = {Victoria Stodden and Friedrich Leisch and Roger D. Peng},
+ title = {knitr: A Comprehensive Tool for Reproducible Research in {R}},
+ author = {Yihui Xie},
+ publisher = {Chapman and Hall/CRC},
+ year = {2014},
+ note = {ISBN 978-1466561595},
+}
+
+@Book{rmarkdown2018,
+ title = {R Markdown: The Definitive Guide},
+ author = {Yihui Xie and J.J. Allaire and Garrett Grolemund},
+ publisher = {Chapman and Hall/CRC},
+ address = {Boca Raton, Florida},
+ year = {2018},
+ isbn = {9781138359338},
+ url = {https://bookdown.org/yihui/rmarkdown},
+}
+
+@Book{rmarkdown2020,
+ title = {R Markdown Cookbook},
+ author = {Yihui Xie and Christophe Dervieux and Emily Riederer},
+ publisher = {Chapman and Hall/CRC},
+ address = {Boca Raton, Florida},
+ year = {2020},
+ isbn = {9780367563837},
+ url = {https://bookdown.org/yihui/rmarkdown-cookbook},
+}
+
+@Manual{R-e107,
+ title = {e1071: Misc Functions of the Department of Statistics, Probability
+Theory Group (Formerly: E1071), TU Wien},
+ author = {David Meyer and Evgenia Dimitriadou and Kurt Hornik and Andreas Weingessel and Friedrich Leisch},
+ year = {2023},
+ note = {R package version 1.7-14},
+ url = {https://CRAN.R-project.org/package=e1071},
+}
+
+@Manual{R-httpuv,
+ title = {httpuv: HTTP and WebSocket Server Library},
+ author = {Joe Cheng and Winston Chang and Steve Reid and James Brown and Bob Trower and Alexander Peslyak},
+ year = {2024},
+ note = {R package version 1.6.15},
+ url = {https://CRAN.R-project.org/package=httpuv},
+}
+
+@Manual{R-rhino,
+ title = {rhino: A Framework for Enterprise Shiny Applications},
+ author = {Kamil Żyła and Jakub Nowicki and Leszek Siemiński and Marek Rogala and Recle Vibal and Tymoteusz Makowski and Rodrigo Basa},
+ year = {2024},
+ note = {R package version 1.7.0},
+ url = {https://CRAN.R-project.org/package=rhino},
+}
+
+@Manual{R-box,
+ title = {box: Write Reusable, Composable and Modular R Code},
+ author = {Konrad Rudolph},
+ year = {2024},
+ note = {R package version 1.2.0},
+ url = {https://CRAN.R-project.org/package=box},
+}
+
+@Manual{R-golem,
+ title = {golem: A Framework for Robust Shiny Applications},
+ author = {Colin Fay and Vincent Guyader and Sébastien Rochette and Cervan Girard},
+ year = {2023},
+ note = {R package version 0.4.1},
+ url = {https://CRAN.R-project.org/package=golem},
+}
+
+@Manual{R-leprechaun,
+ title = {leprechaun: Create Simple 'Shiny' Applications as Packages},
+ author = {John Coene},
+ year = {2022},
+ note = {R package version 1.0.0},
+ url = {https://CRAN.R-project.org/package=leprechaun},
+}
+
+@Manual{R-roxygen2,
+ title = {roxygen2: In-Line Documentation for R},
+ author = {Hadley Wickham and Peter Danenberg and Gábor Csárdi and Manuel Eugster},
+ year = {2024},
+ note = {R package version 7.3.1},
+ url = {https://CRAN.R-project.org/package=roxygen2},
+}
+
+@Manual{R-shinytest2,
+ title = {shinytest2: Testing for Shiny Applications},
+ author = {Barret Schloerke},
+ year = {2024},
+ note = {R package version 0.3.2},
+ url = {https://CRAN.R-project.org/package=shinytest2},
+}
+
+@Manual{R-flexdashboard,
+ title = {flexdashboard: R Markdown Format for Flexible Dashboards},
+ author = {Garrick Aden-Buie and Carson Sievert and Richard Iannone and JJ Allaire and Barbara Borges},
+ year = {2023},
+ note = {R package version 0.6.2},
+ url = {https://CRAN.R-project.org/package=flexdashboard},
+}
+
+@Manual{R-reticulate,
+ title = {reticulate: Interface to 'Python'},
+ author = {Kevin Ushey and JJ Allaire and Yuan Tang},
+ year = {2024},
+ note = {R package version 1.37.0},
+ url = {https://CRAN.R-project.org/package=reticulate},
+}
+
+@Manual{R-languageserver,
+ title = {languageserver: Language Server Protocol},
+ author = {Randy Lai},
+ year = {2023},
+ note = {R package version 0.3.16},
+ url = {https://CRAN.R-project.org/package=languageserver},
+}
diff --git a/book-source/css/style.css b/book-source/css/style.css
new file mode 100755
index 0000000..fa6f438
--- /dev/null
+++ b/book-source/css/style.css
@@ -0,0 +1,42 @@
+.learncheck {
+ padding: 1em 1em 1em 1em;
+ margin-bottom: 10px;
+ background: #9ED3AD 5px center/3em no-repeat;
+}
+
+.announcement {
+ padding: 1em 1em 1em 1em;
+ margin-bottom: 10px;
+ background: #F6D328 5px center/3em no-repeat;
+}
+
+.review {
+ padding: 1em 1em 1em 1em;
+ margin-bottom: 10px;
+ background: #9ED3AD 1px center/1em no-repeat;
+}
+
+
+p.caption {
+ color: #777;
+ margin-top: 10px;
+}
+p code {
+ white-space: inherit;
+}
+pre {
+ word-break: normal;
+ word-wrap: normal;
+}
+pre code {
+ white-space: inherit;
+}
+p.flushright {
+ text-align: right;
+}
+blockquote > p:last-child {
+ text-align: right;
+}
+blockquote > p:first-child {
+ text-align: inherit;
+}
diff --git a/book-source/css/style_gitbook.css b/book-source/css/style_gitbook.css
new file mode 100644
index 0000000..cbb47de
--- /dev/null
+++ b/book-source/css/style_gitbook.css
@@ -0,0 +1,128 @@
+/* Special text */
+.advert {
+ color: #FF8929;
+ font-style: italic;
+}
+.codecommand, .codebox code {
+ background-color: #E0E0E0;
+ font-family: monospace;
+}
+.Large {
+ font-size: 2rem;
+}
+.exercise, .exo {
+ color: #00BA10;
+ font-size: 1.1rem;
+}
+.math.inline {
+ background-color: aliceblue;
+}
+
+/* Special backgrounds*/
+.blueShaded {
+ background-color: #D6E8F5;
+ font-family: monospace;
+}
+.blueShaded pre:not([class]) {
+ background-color: #D6E8F5;
+}
+.redbox {
+ background-color: #FF7F7F;
+ padding: 2px 5px;
+}
+
+/* Document formatting*/
+body {
+ font-family: "Noto Sans", sans-serif;
+ /* font-size: 1.5rem; */
+ text-align: justify;
+}
+
+h3 {
+ font-style: italic;
+}
+
+/* Figures */
+.figure {
+ margin-bottom: 1.5em;
+}
+
+/* Custom specific to my bookdown template */
+.body-inner {
+/* background-color: #1e73be;*/
+ background-color: #f6f6f6;
+}
+.book .book-body .page-wrapper .page-inner {
+ max-width: 1025px;
+}
+.book .book-body .page-wrapper .page-inner section {
+ padding: 5px 3em;
+}
+.book .book-body .page-wrapper .page-inner section.normal p.caption {
+ margin: 0 10%;
+ text-align: center;
+ font-size: 1.4rem;
+}
+
+.page-inner {
+ max-width: 1025px;
+ margin-left: auto;
+ margin-right: auto;
+ background-color: white;
+ /* padding: 1em 30px 15px; */
+ }
+
+.book .book-body .navigation {
+ font-size: 70px;
+ color: #ccc;
+ text-align: center;
+}
+
+#content a, .book .book-body .page-wrapper .page-inner section.normal a {
+ color: #DE633C;
+}
+
+
+/* thinkr.css
+.container-fluid.main-container {
+ margin-top: 80px;
+}
+h1.title {
+ margin-top: 80px;
+}
+.container-fluid.main-container h1.title {
+ margin-top: 0px;
+}
+*/
+.logos {
+ position: fixed;
+ top: 50px;
+ width: 100px;
+ left: 330px;
+ z-index: 1;
+}
+.logos img {
+ margin: 5px auto; /* Modified for gitbook */
+ display: block;
+ height: 60px;
+}
+a {
+ color: #15b7d6;
+}
+h1 {
+ color: #DE633C;
+}
+h2 {
+ color: #15b7d6;
+}
+.sidebar h2 {
+ color: black;
+}
+
+/* Footer with logo */
+footer {
+ text-align: center;
+}
+footer img {
+ max-height: 40px;
+}
diff --git a/book-source/data/bananas.csv b/book-source/data/bananas.csv
new file mode 100644
index 0000000..9167f24
--- /dev/null
+++ b/book-source/data/bananas.csv
@@ -0,0 +1,121 @@
+"fruit","day","ripeness","green","yellow","brown"
+"fruit_14",0,"Under",0.387,0.592,0.021
+"fruit_14",1,"Ripe",0.149,0.844,0.007
+"fruit_14",2,"Ripe",0.117,0.801,0.082
+"fruit_14",4,"Ripe",0.09,0.771,0.139
+"fruit_14",5,"Ripe",0.113,0.774,0.113
+"fruit_14",6,"Ripe",0.165,0.767,0.068
+"fruit_14",7,"Very",0.144,0.646,0.21
+"fruit_14",8,"Very",0.183,0.55,0.267
+"fruit_14",9,"Very",0.21,0.429,0.361
+"fruit_14",10,"Very",0.108,0.319,0.573
+"fruit_14",11,"Very",0.099,0.296,0.605
+"fruit_14",12,"Over",0.081,0.208,0.711
+"fruit_14",13,"Over",0.097,0.168,0.735
+"fruit_14",14,"Over",0.066,0.121,0.813
+"fruit_14",15,"Over",0.062,0.075,0.863
+"fruit_14",16,"Over",0.056,0.044,0.9
+"fruit_14",17,"Over",0.051,0.041,0.908
+"fruit_14",18,"Over",0.044,0.034,0.922
+"fruit_14",19,"Over",0.043,0.033,0.924
+"fruit_14",20,"Over",0.044,0.024,0.932
+"fruit_15",0,"Under",0.568,0.425,0.007
+"fruit_15",1,"Under",0.295,0.691,0.014
+"fruit_15",2,"Ripe",0.27,0.693,0.037
+"fruit_15",4,"Ripe",0.168,0.687,0.145
+"fruit_15",5,"Ripe",0.157,0.704,0.139
+"fruit_15",6,"Ripe",0.186,0.724,0.09
+"fruit_15",7,"Ripe",0.158,0.657,0.185
+"fruit_15",8,"Very",0.176,0.626,0.198
+"fruit_15",9,"Very",0.208,0.528,0.264
+"fruit_15",10,"Very",0.168,0.422,0.41
+"fruit_15",11,"Very",0.135,0.451,0.414
+"fruit_15",12,"Very",0.108,0.431,0.461
+"fruit_15",13,"Very",0.153,0.362,0.485
+"fruit_15",14,"Over",0.104,0.373,0.523
+"fruit_15",15,"Over",0.11,0.242,0.648
+"fruit_15",16,"Over",0.101,0.206,0.693
+"fruit_15",17,"Over",0.083,0.172,0.745
+"fruit_15",18,"Over",0.076,0.137,0.787
+"fruit_15",19,"Over",0.074,0.097,0.829
+"fruit_15",20,"Over",0.072,0.061,0.867
+"fruit_21",0,"Under",0.402,0.566,0.032
+"fruit_21",1,"Ripe",0.149,0.82,0.031
+"fruit_21",2,"Ripe",0.095,0.887,0.018
+"fruit_21",4,"Ripe",0.076,0.885,0.039
+"fruit_21",5,"Ripe",0.054,0.901,0.045
+"fruit_21",6,"Ripe",0.064,0.886,0.05
+"fruit_21",7,"Very",0.074,0.81,0.116
+"fruit_21",8,"Very",0.111,0.689,0.2
+"fruit_21",9,"Very",0.154,0.598,0.248
+"fruit_21",10,"Very",0.104,0.398,0.498
+"fruit_21",11,"Over",0.099,0.352,0.549
+"fruit_21",12,"Over",0.087,0.269,0.644
+"fruit_21",13,"Over",0.081,0.188,0.731
+"fruit_21",14,"Over",0.054,0.145,0.801
+"fruit_21",15,"Over",0.052,0.107,0.841
+"fruit_21",16,"Over",0.045,0.056,0.899
+"fruit_21",17,"Over",0.042,0.051,0.907
+"fruit_21",18,"Over",0.038,0.038,0.924
+"fruit_21",19,"Over",0.043,0.032,0.925
+"fruit_21",20,"Over",0.039,0.018,0.943
+"fruit_22",0,"Under",0.837,0.145,0.018
+"fruit_22",1,"Under",0.197,0.797,0.006
+"fruit_22",2,"Ripe",0.129,0.828,0.043
+"fruit_22",4,"Ripe",0.108,0.863,0.029
+"fruit_22",5,"Ripe",0.049,0.919,0.032
+"fruit_22",6,"Ripe",0.048,0.925,0.027
+"fruit_22",7,"Ripe",0.054,0.894,0.052
+"fruit_22",8,"Ripe",0.069,0.84,0.091
+"fruit_22",9,"Very",0.09,0.778,0.132
+"fruit_22",10,"Very",0.063,0.666,0.271
+"fruit_22",11,"Very",0.072,0.622,0.306
+"fruit_22",12,"Very",0.065,0.542,0.393
+"fruit_22",13,"Very",0.078,0.488,0.434
+"fruit_22",14,"Over",0.068,0.396,0.536
+"fruit_22",15,"Over",0.077,0.339,0.584
+"fruit_22",16,"Over",0.073,0.26,0.667
+"fruit_22",17,"Over",0.074,0.205,0.721
+"fruit_22",18,"Over",0.07,0.153,0.777
+"fruit_22",19,"Over",0.081,0.115,0.804
+"fruit_22",20,"Over",0.09,0.071,0.839
+"fruit_23",0,"Under",0.339,0.641,0.02
+"fruit_23",1,"Ripe",0.156,0.836,0.008
+"fruit_23",2,"Ripe",0.142,0.832,0.026
+"fruit_23",4,"Ripe",0.106,0.861,0.033
+"fruit_23",5,"Ripe",0.061,0.908,0.031
+"fruit_23",6,"Ripe",0.055,0.915,0.03
+"fruit_23",7,"Ripe",0.098,0.83,0.072
+"fruit_23",8,"Very",0.111,0.783,0.106
+"fruit_23",9,"Very",0.16,0.683,0.157
+"fruit_23",10,"Very",0.1,0.499,0.401
+"fruit_23",11,"Very",0.119,0.385,0.496
+"fruit_23",12,"Very",0.099,0.33,0.571
+"fruit_23",13,"Over",0.142,0.262,0.596
+"fruit_23",14,"Over",0.107,0.209,0.684
+"fruit_23",15,"Over",0.104,0.159,0.737
+"fruit_23",16,"Over",0.099,0.113,0.788
+"fruit_23",17,"Over",0.087,0.083,0.83
+"fruit_23",18,"Over",0.066,0.075,0.859
+"fruit_23",19,"Over",0.074,0.068,0.858
+"fruit_23",20,"Over",0.066,0.037,0.897
+"fruit_26",0,"Under",0.178,0.813,0.009
+"fruit_26",1,"Ripe",0.083,0.909,0.008
+"fruit_26",2,"Ripe",0.05,0.924,0.026
+"fruit_26",4,"Ripe",0.046,0.9,0.054
+"fruit_26",5,"Ripe",0.061,0.885,0.054
+"fruit_26",6,"Ripe",0.095,0.845,0.06
+"fruit_26",7,"Very",0.124,0.695,0.181
+"fruit_26",8,"Very",0.169,0.577,0.254
+"fruit_26",9,"Very",0.216,0.463,0.321
+"fruit_26",10,"Over",0.086,0.345,0.569
+"fruit_26",11,"Over",0.095,0.317,0.588
+"fruit_26",12,"Over",0.069,0.26,0.671
+"fruit_26",13,"Over",0.092,0.199,0.709
+"fruit_26",14,"Over",0.06,0.182,0.758
+"fruit_26",15,"Over",0.052,0.143,0.805
+"fruit_26",16,"Over",0.056,0.084,0.86
+"fruit_26",17,"Over",0.059,0.071,0.87
+"fruit_26",18,"Over",0.048,0.051,0.901
+"fruit_26",19,"Over",0.058,0.037,0.905
+"fruit_26",20,"Over",0.054,0.023,0.923
diff --git a/book-source/data/test.csv b/book-source/data/test.csv
new file mode 100644
index 0000000..898fd68
--- /dev/null
+++ b/book-source/data/test.csv
@@ -0,0 +1,2 @@
+x,y
+"a",1
diff --git a/book-source/hostingshiny.Rproj b/book-source/hostingshiny.Rproj
new file mode 100644
index 0000000..fd31270
--- /dev/null
+++ b/book-source/hostingshiny.Rproj
@@ -0,0 +1,18 @@
+Version: 1.0
+
+RestoreWorkspace: No
+SaveWorkspace: No
+AlwaysSaveHistory: No
+
+EnableCodeIndexing: Yes
+UseSpacesForTab: Yes
+NumSpacesForTab: 2
+Encoding: UTF-8
+
+RnwWeave: knitr
+LaTeX: XeLaTeX
+
+AutoAppendNewline: Yes
+StripTrailingWhitespace: Yes
+
+BuildType: Website
diff --git a/book-source/images/01/devops-cycle.drawio b/book-source/images/01/devops-cycle.drawio
new file mode 100644
index 0000000..8c9c770
--- /dev/null
+++ b/book-source/images/01/devops-cycle.drawio
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/book-source/images/01/devops-cycle.pdf b/book-source/images/01/devops-cycle.pdf
new file mode 100644
index 0000000..ee127b1
Binary files /dev/null and b/book-source/images/01/devops-cycle.pdf differ
diff --git a/book-source/images/01/devops-cycle.png b/book-source/images/01/devops-cycle.png
new file mode 100644
index 0000000..5b247c0
Binary files /dev/null and b/book-source/images/01/devops-cycle.png differ
diff --git a/book-source/images/01/example-bananas.png b/book-source/images/01/example-bananas.png
new file mode 100644
index 0000000..923c626
Binary files /dev/null and b/book-source/images/01/example-bananas.png differ
diff --git a/book-source/images/01/example-faithful.png b/book-source/images/01/example-faithful.png
new file mode 100644
index 0000000..090811f
Binary files /dev/null and b/book-source/images/01/example-faithful.png differ
diff --git a/book-source/images/01/example-lbtest.png b/book-source/images/01/example-lbtest.png
new file mode 100644
index 0000000..3da44f4
Binary files /dev/null and b/book-source/images/01/example-lbtest.png differ
diff --git a/book-source/images/01/hosting-cycle.drawio b/book-source/images/01/hosting-cycle.drawio
new file mode 100644
index 0000000..4872106
--- /dev/null
+++ b/book-source/images/01/hosting-cycle.drawio
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/book-source/images/01/hosting-cycle.pdf b/book-source/images/01/hosting-cycle.pdf
new file mode 100644
index 0000000..abf70db
Binary files /dev/null and b/book-source/images/01/hosting-cycle.pdf differ
diff --git a/book-source/images/01/hosting-cycle.png b/book-source/images/01/hosting-cycle.png
new file mode 100644
index 0000000..9a662e0
Binary files /dev/null and b/book-source/images/01/hosting-cycle.png differ
diff --git a/book-source/images/02/docker-architecture.drawio b/book-source/images/02/docker-architecture.drawio
new file mode 100644
index 0000000..89b48e9
--- /dev/null
+++ b/book-source/images/02/docker-architecture.drawio
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/book-source/images/02/docker-architecture.pdf b/book-source/images/02/docker-architecture.pdf
new file mode 100644
index 0000000..8ee951e
Binary files /dev/null and b/book-source/images/02/docker-architecture.pdf differ
diff --git a/book-source/images/02/docker-architecture.png b/book-source/images/02/docker-architecture.png
new file mode 100644
index 0000000..dae8828
Binary files /dev/null and b/book-source/images/02/docker-architecture.png differ
diff --git a/book-source/images/02/docker-layers.drawio b/book-source/images/02/docker-layers.drawio
new file mode 100644
index 0000000..12c0249
--- /dev/null
+++ b/book-source/images/02/docker-layers.drawio
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/book-source/images/02/docker-layers.pdf b/book-source/images/02/docker-layers.pdf
new file mode 100644
index 0000000..182a093
Binary files /dev/null and b/book-source/images/02/docker-layers.pdf differ
diff --git a/book-source/images/02/docker-layers.png b/book-source/images/02/docker-layers.png
new file mode 100644
index 0000000..d59e849
Binary files /dev/null and b/book-source/images/02/docker-layers.png differ
diff --git a/book-source/images/02/run-faithful-code-py-dark.png b/book-source/images/02/run-faithful-code-py-dark.png
new file mode 100644
index 0000000..691ae1b
Binary files /dev/null and b/book-source/images/02/run-faithful-code-py-dark.png differ
diff --git a/book-source/images/02/run-faithful-code-py.png b/book-source/images/02/run-faithful-code-py.png
new file mode 100644
index 0000000..1581ae3
Binary files /dev/null and b/book-source/images/02/run-faithful-code-py.png differ
diff --git a/book-source/images/02/run-faithful-code-r-dark.png b/book-source/images/02/run-faithful-code-r-dark.png
new file mode 100644
index 0000000..48e8df0
Binary files /dev/null and b/book-source/images/02/run-faithful-code-r-dark.png differ
diff --git a/book-source/images/02/run-faithful-code-r.png b/book-source/images/02/run-faithful-code-r.png
new file mode 100644
index 0000000..593f1da
Binary files /dev/null and b/book-source/images/02/run-faithful-code-r.png differ
diff --git a/book-source/images/02/run-faithful-rstudio-py.png b/book-source/images/02/run-faithful-rstudio-py.png
new file mode 100644
index 0000000..f1ca53b
Binary files /dev/null and b/book-source/images/02/run-faithful-rstudio-py.png differ
diff --git a/book-source/images/02/run-faithful-rstudio-r.png b/book-source/images/02/run-faithful-rstudio-r.png
new file mode 100644
index 0000000..495265d
Binary files /dev/null and b/book-source/images/02/run-faithful-rstudio-r.png differ
diff --git a/book-source/images/02/shiny-app-architecture.drawio b/book-source/images/02/shiny-app-architecture.drawio
new file mode 100644
index 0000000..f7f7626
--- /dev/null
+++ b/book-source/images/02/shiny-app-architecture.drawio
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/book-source/images/02/shiny-app-architecture.pdf b/book-source/images/02/shiny-app-architecture.pdf
new file mode 100644
index 0000000..baa476b
Binary files /dev/null and b/book-source/images/02/shiny-app-architecture.pdf differ
diff --git a/book-source/images/02/shiny-app-architecture.png b/book-source/images/02/shiny-app-architecture.png
new file mode 100644
index 0000000..d3e2193
Binary files /dev/null and b/book-source/images/02/shiny-app-architecture.png differ
diff --git a/book-source/images/02/shiny-app-websocket.drawio b/book-source/images/02/shiny-app-websocket.drawio
new file mode 100644
index 0000000..4295fff
--- /dev/null
+++ b/book-source/images/02/shiny-app-websocket.drawio
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/book-source/images/02/shiny-app-websocket.pdf b/book-source/images/02/shiny-app-websocket.pdf
new file mode 100644
index 0000000..ca8b350
Binary files /dev/null and b/book-source/images/02/shiny-app-websocket.pdf differ
diff --git a/book-source/images/02/shiny-app-websocket.png b/book-source/images/02/shiny-app-websocket.png
new file mode 100644
index 0000000..7577db7
Binary files /dev/null and b/book-source/images/02/shiny-app-websocket.png differ
diff --git a/book-source/images/02/vm-vs-container.drawio b/book-source/images/02/vm-vs-container.drawio
new file mode 100644
index 0000000..a720f19
--- /dev/null
+++ b/book-source/images/02/vm-vs-container.drawio
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/book-source/images/02/vm-vs-container.pdf b/book-source/images/02/vm-vs-container.pdf
new file mode 100644
index 0000000..d945df6
Binary files /dev/null and b/book-source/images/02/vm-vs-container.pdf differ
diff --git a/book-source/images/02/vm-vs-container.png b/book-source/images/02/vm-vs-container.png
new file mode 100644
index 0000000..958a0b3
Binary files /dev/null and b/book-source/images/02/vm-vs-container.png differ
diff --git a/book-source/images/logos/book_cover.jpg b/book-source/images/logos/book_cover.jpg
new file mode 100755
index 0000000..a68c993
Binary files /dev/null and b/book-source/images/logos/book_cover.jpg differ
diff --git a/book-source/images/logos/favicons/apple-touch-icon.ico b/book-source/images/logos/favicons/apple-touch-icon.ico
new file mode 100644
index 0000000..0d5e757
Binary files /dev/null and b/book-source/images/logos/favicons/apple-touch-icon.ico differ
diff --git a/book-source/images/logos/favicons/favicon.ico b/book-source/images/logos/favicons/favicon.ico
new file mode 100644
index 0000000..0d5e757
Binary files /dev/null and b/book-source/images/logos/favicons/favicon.ico differ
diff --git a/book-source/images/part-02/hello-shiny-in-rstudio.png b/book-source/images/part-02/hello-shiny-in-rstudio.png
new file mode 100644
index 0000000..71a0434
Binary files /dev/null and b/book-source/images/part-02/hello-shiny-in-rstudio.png differ
diff --git a/book-source/images/part-02/hello-shiny-in-vscode.png b/book-source/images/part-02/hello-shiny-in-vscode.png
new file mode 100644
index 0000000..3126563
Binary files /dev/null and b/book-source/images/part-02/hello-shiny-in-vscode.png differ
diff --git a/book-source/images/part-02/official-shiny-deployment-options.png b/book-source/images/part-02/official-shiny-deployment-options.png
new file mode 100644
index 0000000..2d7bbaa
Binary files /dev/null and b/book-source/images/part-02/official-shiny-deployment-options.png differ
diff --git a/book-source/images/part-02/published-shinyapps-io-app.png b/book-source/images/part-02/published-shinyapps-io-app.png
new file mode 100644
index 0000000..2041e81
Binary files /dev/null and b/book-source/images/part-02/published-shinyapps-io-app.png differ
diff --git a/book-source/images/part-02/rstudio-deployment-logs.png b/book-source/images/part-02/rstudio-deployment-logs.png
new file mode 100644
index 0000000..2e73f1b
Binary files /dev/null and b/book-source/images/part-02/rstudio-deployment-logs.png differ
diff --git a/book-source/images/part-02/rstudio-publish-application-button.png b/book-source/images/part-02/rstudio-publish-application-button.png
new file mode 100644
index 0000000..8cd4fc6
Binary files /dev/null and b/book-source/images/part-02/rstudio-publish-application-button.png differ
diff --git a/book-source/images/part-02/rstudio-publish-to-server-prompt.png b/book-source/images/part-02/rstudio-publish-to-server-prompt.png
new file mode 100644
index 0000000..d17cca3
Binary files /dev/null and b/book-source/images/part-02/rstudio-publish-to-server-prompt.png differ
diff --git a/book-source/images/part-02/rstudio-runapp-button.png b/book-source/images/part-02/rstudio-runapp-button.png
new file mode 100644
index 0000000..8d7ef51
Binary files /dev/null and b/book-source/images/part-02/rstudio-runapp-button.png differ
diff --git a/book-source/images/part-02/run-faithful-code-py-dark.png b/book-source/images/part-02/run-faithful-code-py-dark.png
new file mode 100644
index 0000000..691ae1b
Binary files /dev/null and b/book-source/images/part-02/run-faithful-code-py-dark.png differ
diff --git a/book-source/images/part-02/run-faithful-code-py.png b/book-source/images/part-02/run-faithful-code-py.png
new file mode 100644
index 0000000..1581ae3
Binary files /dev/null and b/book-source/images/part-02/run-faithful-code-py.png differ
diff --git a/book-source/images/part-02/run-faithful-code-r-dark.png b/book-source/images/part-02/run-faithful-code-r-dark.png
new file mode 100644
index 0000000..48e8df0
Binary files /dev/null and b/book-source/images/part-02/run-faithful-code-r-dark.png differ
diff --git a/book-source/images/part-02/run-faithful-code-r.png b/book-source/images/part-02/run-faithful-code-r.png
new file mode 100644
index 0000000..593f1da
Binary files /dev/null and b/book-source/images/part-02/run-faithful-code-r.png differ
diff --git a/book-source/images/part-02/run-faithful-rstudio-py.png b/book-source/images/part-02/run-faithful-rstudio-py.png
new file mode 100644
index 0000000..f1ca53b
Binary files /dev/null and b/book-source/images/part-02/run-faithful-rstudio-py.png differ
diff --git a/book-source/images/part-02/run-faithful-rstudio-r.png b/book-source/images/part-02/run-faithful-rstudio-r.png
new file mode 100644
index 0000000..495265d
Binary files /dev/null and b/book-source/images/part-02/run-faithful-rstudio-r.png differ
diff --git a/book-source/images/part-02/shiny-app-architecture.png b/book-source/images/part-02/shiny-app-architecture.png
new file mode 100644
index 0000000..91bd90c
Binary files /dev/null and b/book-source/images/part-02/shiny-app-architecture.png differ
diff --git a/book-source/images/part-02/shiny-hello.gif b/book-source/images/part-02/shiny-hello.gif
new file mode 100644
index 0000000..80268b9
Binary files /dev/null and b/book-source/images/part-02/shiny-hello.gif differ
diff --git a/book-source/images/part-02/shiny-hello.png b/book-source/images/part-02/shiny-hello.png
new file mode 100644
index 0000000..b7a8c0f
Binary files /dev/null and b/book-source/images/part-02/shiny-hello.png differ
diff --git a/book-source/images/part-02/shiny-rstudio-website.png b/book-source/images/part-02/shiny-rstudio-website.png
new file mode 100644
index 0000000..70e6b89
Binary files /dev/null and b/book-source/images/part-02/shiny-rstudio-website.png differ
diff --git a/book-source/images/part-02/shinyapps-io-costs.png b/book-source/images/part-02/shinyapps-io-costs.png
new file mode 100644
index 0000000..14b84dd
Binary files /dev/null and b/book-source/images/part-02/shinyapps-io-costs.png differ
diff --git a/book-source/images/part-02/shinyapps-io-dashboard-application.png b/book-source/images/part-02/shinyapps-io-dashboard-application.png
new file mode 100644
index 0000000..ea63f5e
Binary files /dev/null and b/book-source/images/part-02/shinyapps-io-dashboard-application.png differ
diff --git a/book-source/images/part-02/shinyapps-io-dashboard-main.png b/book-source/images/part-02/shinyapps-io-dashboard-main.png
new file mode 100644
index 0000000..4f4fb4b
Binary files /dev/null and b/book-source/images/part-02/shinyapps-io-dashboard-main.png differ
diff --git a/book-source/images/part-02/shinyapps-io-website.png b/book-source/images/part-02/shinyapps-io-website.png
new file mode 100644
index 0000000..e74c114
Binary files /dev/null and b/book-source/images/part-02/shinyapps-io-website.png differ
diff --git a/book-source/images/part-03/.gitkeep b/book-source/images/part-03/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/book-source/images/part-04/.gitkeep b/book-source/images/part-04/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/book-source/images/part-04/covid-19-app-plotly.png b/book-source/images/part-04/covid-19-app-plotly.png
new file mode 100644
index 0000000..2319717
Binary files /dev/null and b/book-source/images/part-04/covid-19-app-plotly.png differ
diff --git a/book-source/images/part-04/covid-19-app-prediction.png b/book-source/images/part-04/covid-19-app-prediction.png
new file mode 100644
index 0000000..46de63c
Binary files /dev/null and b/book-source/images/part-04/covid-19-app-prediction.png differ
diff --git a/book-source/images/part-04/vm-vs-container.png b/book-source/images/part-04/vm-vs-container.png
new file mode 100644
index 0000000..c904885
Binary files /dev/null and b/book-source/images/part-04/vm-vs-container.png differ
diff --git a/book-source/images/part-05/.gitkeep b/book-source/images/part-05/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/book-source/images/part-05/caddy-welcome-page.png b/book-source/images/part-05/caddy-welcome-page.png
new file mode 100644
index 0000000..456986d
Binary files /dev/null and b/book-source/images/part-05/caddy-welcome-page.png differ
diff --git a/book-source/images/part-xx/feature-matrix-comparing-shiny-hosting-options.png b/book-source/images/part-xx/feature-matrix-comparing-shiny-hosting-options.png
new file mode 100644
index 0000000..550e022
Binary files /dev/null and b/book-source/images/part-xx/feature-matrix-comparing-shiny-hosting-options.png differ
diff --git a/book-source/images/part-xx/shiny-hosting-comparison-costs-complexity.png b/book-source/images/part-xx/shiny-hosting-comparison-costs-complexity.png
new file mode 100644
index 0000000..2c98451
Binary files /dev/null and b/book-source/images/part-xx/shiny-hosting-comparison-costs-complexity.png differ
diff --git a/book-source/images/part-xx/shiny-hosting-flywheel.png b/book-source/images/part-xx/shiny-hosting-flywheel.png
new file mode 100644
index 0000000..64f4464
Binary files /dev/null and b/book-source/images/part-xx/shiny-hosting-flywheel.png differ
diff --git a/book-source/index.Rmd b/book-source/index.Rmd
new file mode 100755
index 0000000..ed632c4
--- /dev/null
+++ b/book-source/index.Rmd
@@ -0,0 +1,102 @@
+---
+title: "`r ifelse(knitr::is_latex_output(), 'Hosting Shiny Applications for R and Python', 'Hosting Shiny Applications for R and Python')`"
+subtitle: "Everything you wanted to know about hosting Shiny apps"
+author: "`r ifelse(knitr::is_latex_output(), 'Péter Sólymos and Kalvin Eng', 'Péter Sólymos and Kalvin Eng')`"
+date: "`r format(Sys.time(), '%B %d, %Y')`"
+site: bookdown::bookdown_site
+documentclass: krantz
+bibliography: [bib/books.bib, bib/packages.bib, bib/articles.bib, bib/libraries.bib]
+biblio-style: apalike
+fontsize: '12pt, krantz2'
+monofont: "Source Code Pro" #"Inconsolata"
+monofontoptions: "Scale=0.7"
+link-citations: yes
+colorlinks: yes
+lot: false
+lof: false
+always_allow_html: yes
+github-repo: analythium/hosting-shiny-book
+twitter-handle: analythium
+graphics: yes
+description: "Everything you wanted to know about hosting Shiny apps."
+cover-image: "images/logos/book_cover.jpg"
+url: 'https\://hostingshiny.com/'
+apple-touch-icon: "images/logos/favicons/apple-touch-icon.png"
+favicon: "images/logos/favicons/favicon.ico"
+---
+
+
+```{r set-options, eval=TRUE, include=FALSE, purl=FALSE}
+library(knitr)
+library(kableExtra)
+library(reactable)
+library(ggplot2)
+
+# Set output options
+if (is_html_output()) {
+ options(width = 80)
+ theme_set(theme_grey())
+}
+if (is_latex_output()) {
+ options(width = 75)
+ theme_set(theme_light())
+}
+options(digits = 7, bookdown.clean_book = TRUE, knitr.kable.NA = "NA")
+opts_chunk$set(
+ tidy = FALSE,
+ out.width = "\\textwidth",
+ fig.align = "center",
+ comment = NA,
+ eval = FALSE,
+ fig.path = "figures/"
+)
+# this allow to override the font size per chunk
+# use size="tiny"
+# Huge > huge > LARGE > Large > large > normalsize > small > footnotesize > scriptsize > tiny
+# see https://stackoverflow.com/questions/25646333/code-chunk-font-size-in-rmarkdown-with-knitr-and-latex
+def.chunk.hook <- knitr::knit_hooks$get("chunk")
+knitr::knit_hooks$set(chunk = function(x, options) {
+ x <- def.chunk.hook(x, options)
+ ifelse(options$size != "normalsize", paste0("\n \\", options$size,"\n\n", x, "\n\n \\normalsize"), x)
+})
+
+# Create empty docs folder which will ultimately contain output
+if (!dir.exists("docs")) {
+ dir.create("docs")
+}
+
+# Make sure all images copy to docs folder
+if (!dir.exists(file.path("docs", "images"))) {
+ dir.create(file.path("docs", "images"))
+}
+file.copy(from = "images", to = "docs", recursive = TRUE)
+
+# To get kable tables to print nicely in .tex file
+if (is_latex_output()) {
+ options(kableExtra.auto_format = FALSE, knitr.table.format = "latex")
+}
+
+# variables
+book_website_link <- "https://hostingshiny.dev"
+book_code_link <- "https://github.com/analythium/hosting-shiny-book-dev"
+```
+
+`r if (knitr::is_latex_output()) ''`
+
+# Foreword {-}
diff --git a/book-source/krantz.cls b/book-source/krantz.cls
new file mode 100644
index 0000000..bbb2212
--- /dev/null
+++ b/book-source/krantz.cls
@@ -0,0 +1,1952 @@
+%% This is file `Krantz.cls'
+%%% Created by Shashi Kumar / ITC [August 2008]
+
+
+\NeedsTeXFormat{LaTeX2e}[1995/12/01]
+\ProvidesClass{krantz}
+ [2005/09/16 v1.4f
+ Standard LaTeX document class]
+\newcommand\@ptsize{}
+\newif\if@restonecol
+\newif\if@titlepage
+\@titlepagetrue
+\newif\if@openright
+\newif\if@mainmatter \@mainmattertrue
+\if@compatibility\else
+\DeclareOption{a4paper}
+ {\setlength\paperheight {297mm}%
+ \setlength\paperwidth {210mm}}
+\DeclareOption{a5paper}
+ {\setlength\paperheight {210mm}%
+ \setlength\paperwidth {148mm}}
+\DeclareOption{b5paper}
+ {\setlength\paperheight {250mm}%
+ \setlength\paperwidth {176mm}}
+\DeclareOption{letterpaper}
+ {\setlength\paperheight {11in}%
+ \setlength\paperwidth {8.5in}}
+\DeclareOption{legalpaper}
+ {\setlength\paperheight {14in}%
+ \setlength\paperwidth {8.5in}}
+\DeclareOption{executivepaper}
+ {\setlength\paperheight {10.5in}%
+ \setlength\paperwidth {7.25in}}
+\DeclareOption{landscape}
+ {\setlength\@tempdima {\paperheight}%
+ \setlength\paperheight {\paperwidth}%
+ \setlength\paperwidth {\@tempdima}}
+\fi
+\if@compatibility
+ \renewcommand\@ptsize{0}
+\else
+\DeclareOption{10pt}{\renewcommand\@ptsize{0}}
+\fi
+\DeclareOption{11pt}{\renewcommand\@ptsize{1}}
+\DeclareOption{12pt}{\renewcommand\@ptsize{2}}
+\if@compatibility\else
+\DeclareOption{oneside}{\@twosidefalse \@mparswitchfalse}
+\fi
+\DeclareOption{twoside}{\@twosidetrue \@mparswitchtrue}
+\DeclareOption{draft}{\setlength\overfullrule{5pt}}
+\if@compatibility\else
+\DeclareOption{final}{\setlength\overfullrule{0pt}}
+\fi
+\DeclareOption{titlepage}{\@titlepagetrue}
+\if@compatibility\else
+\DeclareOption{notitlepage}{\@titlepagefalse}
+\fi
+\if@compatibility
+\@openrighttrue
+\else
+\DeclareOption{openright}{\@openrighttrue}
+\DeclareOption{openany}{\@openrightfalse}
+\fi
+\if@compatibility\else
+\DeclareOption{onecolumn}{\@twocolumnfalse}
+\fi
+\DeclareOption{twocolumn}{\@twocolumntrue}
+\DeclareOption{leqno}{\input{leqno.clo}}
+\DeclareOption{fleqn}{\input{fleqn.clo}}
+\DeclareOption{openbib}{%
+ \AtEndOfPackage{%
+ \renewcommand\@openbib@code{%
+ \advance\leftmargin\bibindent
+ \itemindent -\bibindent
+ \listparindent \itemindent
+ \parsep \z@
+ }%
+ \renewcommand\newblock{\par}}%
+}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\newif\if@numbysec
+\DeclareOption{numbysec}{\@numbysectrue}
+\newif\if@numberinsequence
+\DeclareOption{numberinsequence}{\@numberinsequencetrue}
+\newif\if@nocaptionbreak
+\DeclareOption{NoCaptionBreak}{\@nocaptionbreaktrue}
+\newif\if@sevenbyten
+\DeclareOption{sevenbyten}{\@sevenbytentrue}
+\newif\if@cip
+\DeclareOption{cip}{\@ciptrue}
+\newif\if@times
+\DeclareOption{times}{\@timestrue}
+\newif\if@chapnumonly
+\DeclareOption{chapnumonly}{\@chapnumonlytrue}
+\newif\if@ChapterResetsPage
+\DeclareOption{ChapterResetsPage}{\@ChapterResetsPagetrue}
+\newif\if@ChapterTOCs
+\DeclareOption{ChapterTOCs}{\@ChapterTOCstrue}
+\newif\if@EOCRefs
+\DeclareOption{EOCRefs}{\@EOCRefstrue}%
+\newif\if@SuperscriptCites
+\DeclareOption{SuperscriptCites}{\@SuperscriptCitestrue}%
+\newif\if@UnnumberedReferences
+\DeclareOption{UnnumberedReferences}{\@UnnumberedReferencestrue}%
+\newif\if@pdf
+\DeclareOption{pdf}{\@pdftrue}
+\DeclareOption{krantz1}{\@krantzatrue}
+\newif\if@krantza
+\DeclareOption{krantz2}{\@krantzbtrue}
+\newif\if@krantzb
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+\ExecuteOptions{letterpaper,10pt,twoside,onecolumn,final,openright}
+\ProcessOptions
+
+%%%%%%%%%%%%%%%%%%%
+
+\def\helv@scale{.82}
+%
+\DeclareFontFamily{T1}{helvetica}{}%
+\DeclareFontShape{T1}{helvetica}{m}{n}{<->s*[\helv@scale]phvr8t}{}%
+\DeclareFontShape{T1}{helvetica}{m}{it}{<->s*[\helv@scale]phvro8t}{}%
+\DeclareFontShape{T1}{helvetica}{m}{sc}{<->s*[\helv@scale]phvrc8t}{}%
+\DeclareFontShape{T1}{helvetica}{b}{n}{<->s*[\helv@scale]phvb8t}{}%
+\DeclareFontShape{T1}{helvetica}{b}{it}{<->s*[\helv@scale]phvbo8t}{}%
+\DeclareFontShape{T1}{helvetica}{m}{sl}{<->s*[\helv@scale]phvro8t}{}%
+\DeclareFontShape{T1}{helvetica}{b}{sc}{<->s*[\helv@scale]phvbc8t}{}%
+\DeclareFontShape{T1}{helvetica}{b}{sl}{<->s*[\helv@scale]phvbo8t}{}%
+\DeclareFontShape{T1}{helvetica}{bx}{n}{<->s*[\helv@scale]phvb8t}{}%
+\DeclareFontShape{T1}{helvetica}{bx}{it}{<->s*[\helv@scale]phvbo8t}{}%
+\DeclareFontShape{T1}{helvetica}{bx}{sc}{<->s*[\helv@scale]phvbc8t}{}%
+\DeclareFontShape{T1}{helvetica}{bx}{sl}{<->ssub * helvetica/b/it}{}%
+
+\DeclareFontFamily{OT1}{helvetica}{}%
+\DeclareFontShape{OT1}{helvetica}{m}{n}{<->s*[\helv@scale]phvr7t}{}%
+\DeclareFontShape{OT1}{helvetica}{m}{it}{<->s*[\helv@scale]phvro7t}{}%
+\DeclareFontShape{OT1}{helvetica}{m}{sc}{<->s*[\helv@scale]phvrc7t}{}%
+\DeclareFontShape{OT1}{helvetica}{b}{n}{<->s*[\helv@scale]phvb7t}{}%
+\DeclareFontShape{OT1}{helvetica}{b}{it}{<->s*[\helv@scale]phvbo7t}{}%
+\DeclareFontShape{OT1}{helvetica}{m}{sl}{<->s*[\helv@scale]phvro7t}{}%
+\DeclareFontShape{OT1}{helvetica}{b}{sc}{<->s*[\helv@scale]phvbc8t}{}%
+\DeclareFontShape{OT1}{helvetica}{b}{sl}{<->s*[\helv@scale]phvbo7t}{}%
+\DeclareFontShape{OT1}{helvetica}{bx}{n}{<->s*[\helv@scale]phvb7t}{}%
+\DeclareFontShape{OT1}{helvetica}{bx}{it}{<->s*[\helv@scale]phvbo7t}{}%
+\DeclareFontShape{OT1}{helvetica}{bx}{sc}{<->s*[\helv@scale]phvbc8t}{}%
+\DeclareFontShape{OT1}{helvetica}{bx}{sl}{<->s*[\helv@scale]phvbo7t}{}%
+
+%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%% Font Defined %%%%%%%%%%%%%%%%%
+
+ \def\@xipt{11}
+ \def\@xviiipt{18}
+ \def\@xxivpt{24}
+
+
+\newcommand\ContributorAffiliationFont{\reset@font\fontsize{10}{12}\raggedright\selectfont}
+\newcommand\ContributorNameFont{\reset@font\fontsize{10}{12}\bfseries\raggedright\selectfont}
+
+\newcommand\TitlePageTitleFont{\fontsize{24}{28}\slshape\bfseries\selectfont}
+\newcommand\PageNumFont{\reset@font\fontsize{10}{12}\selectfont}
+\newcommand\ChapNumFont{\reset@font\fontsize{24}{24}\bfseries\selectfont}
+\newcommand\ChapTitleFont{\reset@font\fontsize{18}{20}\slshape\selectfont}
+\newcommand\SectionHeadFont{\fontsize{12}{14}\bfseries\selectfont}
+\newcommand\SubsectionHeadFont{\fontsize{11}{13}\bfseries\selectfont}
+\newcommand\SubsubsectionHeadFont{\fontsize{10}{12}\bfseries\selectfont}
+\newcommand\ParagraphHeadFont{\fontsize{10}{12}\itshape\selectfont}
+\newcommand\SubParagraphHeadFont{\fontsize{10}{12}\itshape\selectfont}
+\newcommand\FMHeadFont{\reset@font\fontsize{18}{20}\slshape\bfseries\selectfont}
+\newcommand\RunningHeadFont{\fontsize{10}{12}\itshape\selectfont}
+\newcommand\NameFont{\fontsize{10}{12}\itshape\selectfont}
+\newcommand\AffiliationFont{\fontsize{8}{10}\selectfont}
+\newcommand\FigCapFont{\fontsize{10}{12}\bfseries\selectfont}
+\newcommand\FigCapBIFont{\fontsize{10}{12}\bfseries\itshape\selectfont}
+\newcommand\TableColHeadFont{\fontsize{10}{12}\bfseries\selectfont}
+\newcommand\TableTitleFont{\fontsize{10}{12}\selectfont}
+\newcommand\TableNumberFont{\fontsize{11}{13}\bfseries\selectfont}
+\newcommand\TableBodyFont{\reset@font\fontsize{9}{11}\selectfont}
+\newcommand\TableSubheadFont{\reset@font\fontsize{9}{11}\selectfont}
+\newcommand\TableFootnoteFont{\reset@font\fontsize{8}{10}\selectfont}
+\newcommand\CAPlusOneFont{\fontsize{10}{12}\bfseries\selectfont}
+\newcommand\CAAPlusOneFont{\fontsize{10}{12}\itshape\selectfont}
+\newcommand\tocfont{\fontsize{10}{12}\selectfont}
+\newcommand\extraFont{\fontsize{24}{28}\selectfont}
+\newcommand\VfFont{\fontsize{10}{12}\selectfont}
+
+%%%%%%%%%%%%%%%%%
+
+\input{bk1\@ptsize.clo}
+\setlength\lineskip{1\p@}
+\setlength\normallineskip{1\p@}
+\renewcommand\baselinestretch{}
+\setlength\parskip{0\p@ \@plus \p@}
+\@lowpenalty 51
+\@medpenalty 151
+\@highpenalty 301
+\@beginparpenalty -\@lowpenalty
+\@endparpenalty -\@lowpenalty
+\@itempenalty -\@lowpenalty
+%
+\clubpenalty=0 % 'Club line' at bottom of page.
+\widowpenalty=10000 % 'Widow line' at top of page.
+\setcounter{topnumber}{2}
+\renewcommand\topfraction{.7}
+\setcounter{bottomnumber}{1}
+\renewcommand\bottomfraction{.3}
+\setcounter{totalnumber}{3}
+\renewcommand\textfraction{.2}
+\renewcommand\floatpagefraction{.5}
+\setcounter{dbltopnumber}{2}
+\renewcommand\dbltopfraction{.7}
+\renewcommand\dblfloatpagefraction{.5}
+
+% ****************************************
+% * PAGE LAYOUT *
+% ****************************************
+%
+% All margin dimensions measured from a point one inch from top and side
+% of page.
+%
+% SIDE MARGINS:
+%
+\oddsidemargin 6pc %5pc
+\evensidemargin 5.7pc %5pc
+\marginparwidth 4pc
+\marginparsep 1pc
+\topmargin 12pt %0pt
+\headheight 12pt
+\headsep 12pt
+\footskip 2pc
+%
+% DIMENSION OF TEXT:
+\newdimen\trimheight
+\newdimen\trimwidth
+\newdimen\normaltextheight
+\newdimen\tempa
+\newdimen\tempdimen
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Parameter Initializaton %%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+\newdimen\htrim
+\newdimen\vtrimtop
+\newdimen\vtrimbot
+
+\setlength\trimheight{9in}
+\setlength\trimwidth{6in}
+%
+%
+\if@krantza
+\textheight = 45pc
+ %\advance\textheight by \topskip
+\addtolength\textheight{3pt}
+ \textwidth 28pc
+\addtolength\textwidth{.5pt}
+ \topmargin0in
+ \oddsidemargin1.1875in
+ \evensidemargin1.1875in
+ \htrim.7365in
+ \vtrimtop1.068in
+ \vtrimbot1.068in
+ \hoffset-15pt
+ \voffset39pt
+\let\normaltextheight\textheight
+\else\if@krantzb
+ \textheight = 51pc
+% \advance\textheight by \topskip
+ \textwidth 33pc
+ \topmargin0in
+ \oddsidemargin.5in
+ \evensidemargin.5in
+ \htrim.75in
+ \vtrimtop.8607in
+ \vtrimbot1.027in
+ \hoffset-.1in
+ \voffset-.15in%.04in
+\let\normaltextheight\textheight
+\else
+%%%Uncomment to get 6x9 trim
+%%%%\textheight = 43pc
+%%%% %\advance\textheight by \topskip
+%%%%\addtolength\textheight{3pt}
+%%%% \textwidth 26pc
+%%%%\addtolength\textwidth{.5pt}
+%%%% \topmargin0in
+%%%% \oddsidemargin1.1875in
+%%%% \evensidemargin1.1875in
+%%%% \htrim5.05pc
+%%%% \vtrimtop7.7pc
+%%%% \vtrimbot5.44pc
+%%%%% \hoffset-5pt
+%%%% \voffset45pt
+%%%%\let\normaltextheight\textheight
+\textheight = 45pc
+ %\advance\textheight by \topskip
+\addtolength\textheight{3pt}
+ \textwidth 28pc
+\addtolength\textwidth{.5pt}
+ \topmargin0in
+ \oddsidemargin1.1875in
+ \evensidemargin1.1875in
+ \htrim.7365in
+ \vtrimtop1.068in
+ \vtrimbot1.068in
+ \hoffset-15pt
+ \voffset39pt
+\let\normaltextheight\textheight
+
+ \fi
+\fi
+%
+\columnsep 1pc
+\columnseprule 0pt
+%
+% FOOTNOTES
+%
+\footnotesep 6.65pt
+\skip\footins 12pt plus 3pt minus 1.5pt
+%
+
+%%%% Trim marks %%%%%%%%%%%
+\newsavebox\ul@box
+\newsavebox\ur@box
+\newsavebox\ll@box
+\newsavebox\lr@box
+\def\top@cornermarks{%
+ \hskip-\htrim
+ \vbox to 0\p@{\vskip-\vtrimtop\llap{\copy\ul@box}\vss}%
+ \vbox to 0\p@{\vskip-\vtrimtop\rlap{\hskip\textwidth\hskip2\htrim\copy\ur@box}\vss}%
+ \vbox to 0\p@{\vskip\textheight\vskip\vtrimbot\llap{\copy\ll@box}\vss}%
+ \vbox to 0\p@{\vskip\textheight\vskip\vtrimbot\rlap{\hskip\textwidth\hskip2\htrim\copy\lr@box}\vss}%
+ \hskip\htrim}
+\def\make@cornermarks{%
+ \sbox\ul@box{\rule{18\p@}{.25\p@}\hskip8\p@\hbox to.25\p@{\vbox to26\p@{\noindent\rule{.25\p@}{18\p@}}}}%
+ \sbox\ur@box{\hbox to.25\p@{\vbox to26\p@{\noindent\rule{.25\p@}{18\p@}}}\hskip8\p@\rule{18\p@}{.25\p@}}%
+ \sbox\ll@box{\rule{18\p@}{.25\p@}\hskip8\p@\lower34\p@\hbox to.25\p@{\vbox to26\p@{\noindent\rule{.25\p@}{18\p@}}}}%
+ \sbox\lr@box{\lower34\p@\hbox to.25\p@{\vbox to26\p@{\noindent\rule{.25\p@}{18\p@}}}\hskip8\p@\rule{18\p@}{.25\p@}}}
+
+%%%%%%%%%%%%%%%%%%%% End Trim Marks %%%%%%%%%%%%
+
+
+\def\ps@plain{\let\@mkboth\@gobbletwo
+ \let\@oddhead\top@cornermarks%\@empty
+ \def\@oddfoot{\reset@font\hfil\thepage
+ \hfil}\let\@evenhead\@empty\let\@evenfoot\@oddfoot}
+
+
+
+\def\even@head{%
+\top@cornermarks
+ {\@the@page\RunningHeadFont
+ \hfill
+\if@mainmatter\ifnum\value{chapter}>0 \thechapter\enspace\fi\fi\leftmark
+ }}
+\def\odd@head{%
+\top@cornermarks
+ \hfil{\RunningHeadFont
+\if@mainmatter\ifnum\value{section}>0 \thesection\enspace\fi\fi\rightmark
+ }
+\hfill
+ \@the@page
+ }
+\def\@the@page{{\PageNumFont\thepage}}
+
+
+\if@twoside
+\def\ps@headings{%
+ \let\@mkboth\@gobbletwo
+ \if@pdf
+ \let\@evenhead\@empty
+ \let\@oddhead\@empty
+ \def\@oddfoot{\@cip\hfil}%
+ \def\@evenfoot{\@cip\hfil}%
+ \else
+\let\@oddfoot\@empty
+\let\@evenfoot\@empty
+ \let\@evenhead\even@head
+ \let\@oddhead\odd@head
+ \fi
+ }
+\else
+ \def\ps@headings{\let\@mkboth\@gobbletwo%
+\if@pdf
+ \let\@evenhead\@empty
+ \let\@oddhead\@empty
+ \def\@oddfoot{\@cip\hfil}%
+ \def\@evenfoot{\@cip\hfil}%
+ \else
+\let\@oddfoot\@empty
+\let\@evenfoot\@empty
+ \let\@evenhead\even@head
+ \let\@oddhead\odd@head
+ \fi
+}
+\fi
+\def\ps@myheadings{%
+ \let\@oddfoot\@empty\let\@evenfoot\@empty
+ \def\@evenhead{\thepage\hfil\slshape\leftmark}%
+ \def\@oddhead{{\slshape\rightmark}\hfil\thepage}%
+ \let\@mkboth\@gobbletwo
+ \let\chaptermark\@gobble
+ \let\sectionmark\@gobble
+ }
+\def\ps@empty{%
+ \let\@mkboth\@gobbletwo
+ \if@pdf
+ \let\@evenhead\@empty
+ \let\@oddhead\@empty
+ \def\@oddfoot{\@cip\hfil}%
+ \def\@evenfoot{\@cip\hfil}%
+ \else
+ \make@cornermarks
+ \let\@oddhead\top@cornermarks
+ \let\@evenhead\top@cornermarks
+ \let\@oddfoot\@empty
+ \let\@evenfoot\@empty
+ \fi
+ }
+\def\ps@folio{%
+ \let\@mkboth\@gobbletwo
+ \if@pdf
+ \let\@evenhead\@empty
+ \let\@oddhead\@empty
+ \def\@oddfoot{\@cip\hfil}%
+ \def\@evenfoot{\@cip\hfil}%
+ \else
+\let\@oddhead\top@cornermarks
+ \def\@oddfoot{%
+ \parindent\z@
+ \baselineskip7\p@
+ \hbox{%
+ \textwidth\@ciprulewidth
+ \vbox{%
+ \if@cip\rule{\@ciprulewidth}{.25pt}\par
+ \hbox{\vbox{\noindent\copy\@cipboxa\par\noindent\copy\@cipboxb}}\fi}}
+ \hfill\@the@page}
+ \let\@evenhead\top@cornermarks%\odd@head
+ \let\@evenfoot\@oddfoot
+ \fi
+ }
+\newcommand\HeadingsBookChapter{%
+ \def\chaptermark##1{%
+ \markboth{\@title}{%
+ ##1}}%
+ \def\sectionmark##1{}}
+\def\HeadingsChapterSection{%
+ \def\chaptermark##1{%
+ \markboth{%
+ ##1}{}}%
+ \def\sectionmark##1{%
+ \markright{%
+ ##1}}}
+\def\pdfon{\@pdftrue}
+\def\pdfoff{\@pdffalse}
+\if@pdf
+ \def\@cip{{\fontsize{6\p@}{8\p@}\selectfont\copyright 2001 by CRC Press LLC}}
+\else
+ \newsavebox\@cipboxa
+ \newsavebox\@cipboxb
+ \newdimen\@ciprulewidth
+ \def\@cip#1#2{%
+ \sbox\@cipboxa{\fontsize{6\p@}{8\p@}\selectfont #1}%
+ \sbox\@cipboxb{\fontsize{6\p@}{8\p@}\selectfont #2}%
+ \@ciprulewidth\wd\@cipboxa
+ \ifnum\@ciprulewidth<\wd\@cipboxb\@ciprulewidth\wd\@cipboxb\fi}%
+\fi
+\if@pdf
+\else
+ \AtBeginDocument{%
+ \@cip{\rule{0pt}{9pt}0-8493-0052-5/00/\$0.00+\$.50}%
+ {\copyright\ \ 2001 by CRC Press LLC}}%
+\fi
+ \if@titlepage
+ \newcommand\maketitle{\begin{titlepage}%
+ \let\footnotesize\small
+ \let\footnoterule\relax
+ \let \footnote \thanks
+{\parindent \z@ \raggedright \baselineskip \z@ \lineskip \z@ \parskip \z@
+ \vbox{
+ \vskip -7bp
+ {\baselineskip 10bp\lineskip 10bp\NameFont\uppercase{\@author}\par}
+ \vskip 6bp
+ \AffiliationFont \@affiliation
+ \vskip -2bp
+ \crcrule
+ \vskip 22bp
+ {\baselineskip 24bp\lineskip 24bp\TitlePageTitleFont\@title\par}}}
+ \@thanks
+ \vfil\null
+ \end{titlepage}%
+ \setcounter{footnote}{0}%
+ \global\let\thanks\relax
+ \global\let\maketitle\relax
+ \global\let\@thanks\@empty
+ \global\let\@author\@empty
+ \global\let\@date\@empty
+% \global\let\@title\@empty
+ \global\let\title\relax
+ \global\let\author\relax
+ \global\let\date\relax
+ \global\let\and\relax
+}
+\else
+\newcommand\maketitle{\par
+ \begingroup
+ \renewcommand\thefootnote{\@fnsymbol\c@footnote}%
+ \def\@makefnmark{\rlap{\@textsuperscript{\normalfont\@thefnmark}}}%
+ \long\def\@makefntext##1{\parindent 1em\noindent
+ \hb@xt@1.8em{%
+ \hss\@textsuperscript{\normalfont\@thefnmark}}##1}%
+ \if@twocolumn
+ \ifnum \col@number=\@ne
+ \@maketitle
+ \else
+ \twocolumn[\@maketitle]%
+ \fi
+ \else
+ \newpage
+ \global\@topnum\z@ % Prevents figures from going at top of page.
+ \@maketitle
+ \fi
+ \thispagestyle{empty}\@thanks
+ \endgroup
+ \setcounter{footnote}{0}%
+ \global\let\thanks\relax
+ \global\let\maketitle\relax
+ \global\let\@maketitle\relax
+ \global\let\@thanks\@empty
+ \global\let\@author\@empty
+ \global\let\@date\@empty
+ \global\let\@title\@empty
+ \global\let\title\relax
+ \global\let\author\relax
+ \global\let\date\relax
+ \global\let\and\relax
+}
+\def\@maketitle{%
+ \newpage
+ \null
+ \vskip 2em%
+{\parindent \z@ \raggedright \baselineskip \z@ \lineskip \z@ \parskip \z@
+ \vbox{
+ \vskip -7bp
+ {\baselineskip 10bp\lineskip 10bp\NameFont\uppercase{\@author}\par}
+ \vskip 6bp
+ \AffiliationFont \@affiliation
+ \vskip 10bp
+ \crcrule
+ \vskip 26bp
+ {\baselineskip 24bp\lineskip 24bp\TitlePageTitleFont\@title\par}}}
+ \par
+ \vskip 1.5em}
+\fi
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+\newcommand*\chaptermark[1]{}
+\setcounter{secnumdepth}{3}
+\newcounter {part}
+\newcounter {chapter}
+\newcounter {section}[chapter]
+\newcounter {subsection}[section]
+\newcounter {subsubsection}[subsection]
+\newcounter {paragraph}[subsubsection]
+\newcounter {subparagraph}[paragraph]
+\renewcommand \thepart {\@Roman\c@part}
+\renewcommand \thechapter {\@arabic\c@chapter}
+\renewcommand \thesection {\thechapter.\@arabic\c@section}
+\renewcommand\thesubsection {\thesection.\@arabic\c@subsection}
+\renewcommand\thesubsubsection{\thesubsection .\@arabic\c@subsubsection}
+\renewcommand\theparagraph {\thesubsubsection.\@arabic\c@paragraph}
+\renewcommand\thesubparagraph {\theparagraph.\@arabic\c@subparagraph}
+\newcommand\@chapapp{\chaptername}
+\newcommand\frontmatter{%
+ \cleardoublepage
+ \@mainmatterfalse
+ \pagenumbering{roman}}
+\newcommand\mainmatter{%
+ \cleardoublepage
+ \@mainmattertrue
+ \pagenumbering{arabic}}
+\newcommand\backmatter{%
+ \if@openright
+ \cleardoublepage
+ \else
+ \clearpage
+ \fi
+ \@mainmatterfalse}
+\newcommand\part{\make@cornermarks%
+ \if@openright
+ \cleardoublepage
+ \else
+ \clearpage
+ \fi
+ \thispagestyle{empty}%
+ \if@twocolumn
+ \onecolumn
+ \@tempswatrue
+ \else
+ \@tempswafalse
+ \fi
+ \null\vfil
+ \secdef\@part\@spart}
+
+\def\@part[#1]#2{%
+ \ifnum \c@secnumdepth >-2\relax
+ \refstepcounter{part}%
+ \addcontentsline{toc}{part}{\thepart\hspace{1em}#1}%
+ \else
+ \addcontentsline{toc}{part}{#1}%
+ \fi
+ \markboth{}{}%
+ {\centering
+ \interlinepenalty \@M
+ \normalfont
+ \ifnum \c@secnumdepth >-2\relax
+ \huge\bfseries \partname\nobreakspace\thepart
+ \par
+ \vskip 20\p@
+ \fi
+ \Huge \bfseries #2\par}%
+ \@endpart}
+\def\@spart#1{%
+ {\centering
+ \interlinepenalty \@M
+ \normalfont
+ \Huge \bfseries #1\par}%
+ \@endpart}
+\def\@endpart{\vfil\newpage
+ \if@twoside
+ \if@openright
+ \null
+ \thispagestyle{empty}%
+ \newpage
+ \fi
+ \fi
+ \if@tempswa
+ \twocolumn
+ \fi}
+
+\if@ChapterTOCs
+ \newwrite\@chaptoc
+ \def\secnumwidth{21pt}\def\subsecnumwidth{30pt}\def\ssubsecnumwidth{36pt}\def\subsubsecnumwidth{66pt}\fi
+\long\def\@trplarg#1{\@ifnextchar[{\@xtrplarg{#1}}{\@ztrplarg{#1}}}
+\long\def\@xtrplarg#1[#2]{\@ifnextchar[{#1[#2]}{\@ytrplarg{#1}[{#2}]}}
+\long\def\@ytrplarg#1[#2]#3{#1[{#2}][{#2}]{#3}}
+\long\def\@ztrplarg#1#2{#1[{#2}][{#2}]{#2}}
+
+
+\newcommand\chapter{\if@openright\cleardoublepage\else\clearpage\fi
+ \make@cornermarks
+ \cleardoublepage
+ \if@ChapterTOCs\if@filesw\immediate\closeout\@chaptoc\fi\fi
+ \pagestyle{headings}%
+ \thispagestyle{folio}%
+\if@ChapterResetsPage\global\c@page\@ne\fi
+ \global\@topnum\z@
+ \gdef\chapterauthor{\@ca}%
+ \gdef\endchapterauthors{\end@cas}%
+ \@afterindentfalse
+ \secdef\@chapter\@schapter
+%%% \@ifstar{\@schapter}{\@trplarg{\@chapter}}
+ }
+
+\newif\ifappendix
+\global\appendixfalse
+
+\def\@chapter[#1]#2{%
+ \ifnum\c@secnumdepth>\m@ne
+ \if@mainmatter
+ \refstepcounter{chapter}%
+ \typeout{\@chapapp\space\thechapter.}%
+\ifappendix
+ \addcontentsline{toc}{chapter}{\protect\noindent{Appendix~\thechapter\hskip14pt}#1}%
+\else
+ \addcontentsline{toc}{chapter}{\protect\numberline{\thechapter}#1}%
+\fi
+ \else
+ \addcontentsline{toc}{chapter}{#1}\fi
+ \else
+ \addcontentsline{toc}{chapter}{#1}\fi
+ \chaptermark{%
+ #2}%
+ \addtocontents{lof}{\protect\addvspace{10\p@}}%
+ \addtocontents{lot}{\protect\addvspace{10\p@}}%
+ \if@twocolumn
+ \@topnewpage[\@makechapterhead{#2}]%
+ \else
+ \@makechapterhead{#2}%
+ \@afterheading\fi
+ \if@ChapterTOCs\if@filesw\immediate\openout\@chaptoc\thechapter.toc\fi\fi
+}
+\def\@makechapterhead#1{%
+ {\parindent \z@ \raggedright \baselineskip \z@ \lineskip \z@ \parskip \z@
+ \vbox{
+ \vskip -2\p@
+ \ChapNumFont
+%Remove comment if "Chapter" word required before Number
+%\if@chapnumonly\else
+% \@chapapp\
+%\fi
+ \thechapter
+ \vskip -15\p@
+ \chap@rule
+ \vskip 6\p@
+ {\baselineskip 20\p@\lineskip 20\p@\ChapTitleFont #1\par\vskip-15pt}%
+ \noindent\hbox{\vrule height.5pt width84pt}
+ \vskip28\p@}
+ \if@ChapterTOCs
+ \make@chaptoc
+ \else
+\fi
+ \vskip 19.3\p@}
+ \def\theequation{\thechapter.\arabic{equation}}}%
+
+
+\def\@schapter#1{\if@twocolumn
+ \@topnewpage[\@makeschapterhead{#1}]%
+ \else
+ \@makeschapterhead{#1}%
+ \addcontentsline{toc}{fm}{#1}
+ \markboth{#1}{#1}
+ \@afterheading
+ \fi}
+
+\def\@makeschapterhead#1{%
+ {\parindent \z@ \raggedright \baselineskip 6\p@ \lineskip \z@ \parskip \z@
+ \vbox{
+ \vskip 22\p@
+ \unnumchap@rule
+ \vskip 5\p@
+ \FMHeadFont #1\par\vskip-12pt
+ \noindent\hbox{\vrule height.5pt width84pt}
+ \vskip 41\p@}}%
+ \def\theequation{\thechapter.\arabic{equation}}}
+
+%%%\def\@startsection#1#2#3#4#5#6{%
+%%% \if@noskipsec\leavevmode\fi
+%%% \par
+%%% \@tempskipa #4\relax
+%%% \@afterindenttrue
+%%% \ifdim \@tempskipa <\z@
+%%% \@tempskipa -\@tempskipa \@afterindentfalse
+%%% \fi
+%%% \if@nobreak
+%%% \everypar{}%
+%%% \else
+%%% \addpenalty\@secpenalty\addvspace\@tempskipa
+%%% \fi
+%%% \@ifstar
+%%% {\@ssect{#1}{#3}{#4}{#5}{#6}}%
+%%% {\@trplarg{\@sect{#1}{#2}{#3}{#4}{#5}{#6}}}}
+%%%\def\@ssect#1#2#3#4#5#6{%
+%%% \@tempskipa #4\relax
+%%% \ifdim \@tempskipa>\z@
+%%% \begingroup
+%%% #5{%
+%%% \@hangfrom{\hskip #2}%
+%%% \interlinepenalty \@M #6\@@par}%
+%%% \endgroup
+%%% \csname #1mark\endcsname{#6}%
+%%% \else
+%%% \def\@svsechd{#5{\hskip #2\relax #6}\csname #1mark\endcsname{#6}}%
+%%% \fi
+%%% \@xsect{#4}}
+%%%\def\@sect#1#2#3#4#5#6[#7][#8]#9{%
+%%% \ifnum #2>\c@secnumdepth
+%%% \let\@svsec\@empty
+%%% \else
+%%% \refstepcounter{#1}%
+%%%\protected@edef\@svsec{\@seccntformat{#1}\relax}%
+%%% \fi
+%%% \@tempskipa #5\relax
+%%% \ifdim \@tempskipa>\z@
+%%% \begingroup
+%%% #6{%
+%%%\@hangfrom{\hskip #3\relax\@svsec}\interlinepenalty \@M %
+%%% #9\@@par}%
+%%% \endgroup
+%%% \csname #1mark\endcsname{%
+%%% #8}%
+%%% \addcontentsline{toc}{#1}{%
+%%% \ifnum #2>\c@secnumdepth \else
+%%% \protect\numberline{\csname the#1\endcsname}%
+%%% \fi
+%%% #7}%
+%%% \else
+%%% \def\@svsechd{%
+%%% #6{\hskip #3\relax
+%%% \@svsec #9}%
+%%% \csname #1mark\endcsname{%
+%%% #8}%
+%%% \addcontentsline{toc}{#1}{%
+%%% \ifnum #2>\c@secnumdepth \else
+%%% \protect\numberline{\csname the#1\endcsname}%
+%%% \fi
+%%% #7}}%
+%%% \fi
+%%% \@xsect{#5}}
+
+%%Change mydotted also
+\newdimen\secwd
+\newdimen\subsecwd
+\newdimen\subsubsecwd
+
+\def\secwd{31pt}
+\def\subsecwd{36pt}
+\def\subsubsecwd{46pt}
+
+
+\def\ssubnumberline#1{\@hangfrom{\hbox to \secwd{#1\hfill}}}
+\def\subnumberline#1{\@hangfrom{\hskip\subsecnumwidth\hbox to \subsecwd{#1\hfill}}}
+\def\subsubnumberline#1{\@hangfrom{\hskip\subsubsecnumwidth\hbox to \subsubsecwd{#1\hfill}}}
+
+
+\newcommand\section{%
+ \gdef\chapterauthor{\@caplusone}%
+ \gdef\endchapterauthors{\end@casplusone}%
+ \@ifstar{\@ssection}{\@trplarg{\@section}}}
+\def\@ssection#1{%
+ \if@ChapterTOCs
+ \myaddcontentsline{\@chaptoc}{chapsection}{\string\makebox[\secnumwidth][l]{}#1}\fi
+ \@startsection{section}{1}{\z@}{-30\p@}{6\p@}{\sec@rule\nopagebreak\vskip9.5\p@\nopagebreak\SectionHeadFont}*{#1}}
+\def\@section[#1][#2]#3{%
+ \if@ChapterTOCs
+ \addtocounter{section}{1}%
+ \myaddcontentsline{\@chaptoc}{chapsection}{\protect\ssubnumberline{\thesection}#1}%
+ \addtocounter{section}{-1}\fi
+ \@startsection{section}{1}{\z@}{-30\p@}{6\p@}{\sec@rule\nopagebreak\vskip9.5\p@\nopagebreak\SectionHeadFont}[#2]{#3}}
+\def\sectionauthor#1{\hfill{\ChapTOCAuthorFont #1}}
+
+\newcommand\subsection{\@ifstar{\@ssubsection}{\@trplarg{\@subsection}}}
+\def\@ssubsection#1{%
+ \if@ChapterTOCs
+ \myaddcontentsline{\@chaptoc}{chapsubsection}{\string\makebox[\subsecnumwidth][l]{}#1}\fi
+ \@startsection{subsection}{2}{\z@}{-18\p@}{6\p@}{%
+ \SubsectionHeadFont}*{#1}}
+\def\@subsection[#1][#2]#3{%
+ \if@ChapterTOCs
+ \addtocounter{subsection}{1}%
+ \myaddcontentsline{\@chaptoc}{chapsubsection}{\protect\subnumberline{\thesubsection}#1}%
+ \addtocounter{subsection}{-1}\fi
+ \@startsection{subsection}{2}{\z@}{-18\p@}{6\p@}{%
+ \SubsectionHeadFont}[#2]{#3}}
+
+\newcommand\subsubsection{\@ifstar{\@ssubsubsection}{\@trplarg{\@subsubsection}}}
+\def\@ssubsubsection#1{%
+ \if@ChapterTOCs
+ \myaddcontentsline{\@chaptoc}{chapsubsubsection}{\string\makebox[\subsecnumwidth][l]{}#1}\fi
+ \@startsection{subsubsection}{3}{\z@}{-12\p@}{6\p@}{%
+ \SubsubsectionHeadFont}*{#1}}
+\def\@subsubsection[#1][#2]#3{%
+ \if@ChapterTOCs
+ \addtocounter{subsubsection}{1}%
+ \myaddcontentsline{\@chaptoc}{chapsubsubsection}{\protect\subsubnumberline{\thesubsubsection}#1}%
+ \addtocounter{subsubsection}{-1}\fi
+ \@startsection{subsubsection}{3}{\z@}{-12\p@}{6\p@}{%
+ \SubsubsectionHeadFont}[#2]{#3}}
+
+\newcommand\paragraph{\@startsection{paragraph}{4}{\z@}%
+{-12\p@}{6\p@}{\ParagraphHeadFont}}
+
+\newcommand\subparagraph{\@startsection{subparagraph}{5}{\parindent}%
+{-12\p@}{6\p@}{\SubParagraphHeadFont}}
+
+
+\if@twocolumn
+ \setlength\leftmargini {2em}
+\else
+ \setlength\leftmargini {2.5em}
+\fi
+\leftmargin \leftmargini
+\setlength\leftmarginii {2.2em}
+\setlength\leftmarginiii {1.87em}
+\setlength\leftmarginiv {1.7em}
+\if@twocolumn
+ \setlength\leftmarginv {.5em}
+ \setlength\leftmarginvi {.5em}
+\else
+ \setlength\leftmarginv {1em}
+ \setlength\leftmarginvi {1em}
+\fi
+\setlength \labelsep {.5em}
+\setlength \labelwidth{\leftmargini}
+\addtolength\labelwidth{-\labelsep}
+\@beginparpenalty -\@lowpenalty
+\@endparpenalty -\@lowpenalty
+\@itempenalty -\@lowpenalty
+\renewcommand\theenumi{\@arabic\c@enumi}
+\renewcommand\theenumii{\@alph\c@enumii}
+\renewcommand\theenumiii{\@roman\c@enumiii}
+\renewcommand\theenumiv{\@Alph\c@enumiv}
+\newcommand\labelenumi{\theenumi.}
+\newcommand\labelenumii{(\theenumii)}
+\newcommand\labelenumiii{\theenumiii.}
+\newcommand\labelenumiv{\theenumiv.}
+\renewcommand\p@enumii{\theenumi}
+\renewcommand\p@enumiii{\theenumi(\theenumii)}
+\renewcommand\p@enumiv{\p@enumiii\theenumiii}
+\newcommand\labelitemi{\textbullet}
+\newcommand\labelitemii{\normalfont\bfseries \textendash}
+\newcommand\labelitemiii{\textasteriskcentered}
+\newcommand\labelitemiv{\textperiodcentered}
+\newenvironment{description}
+ {\list{}{\labelwidth\z@ \itemindent-\leftmargin
+ \let\makelabel\descriptionlabel}}
+ {\endlist}
+\newcommand*\descriptionlabel[1]{\hspace\labelsep
+ \normalfont\bfseries #1}
+\newenvironment{verse}
+ {\let\\\@centercr
+ \list{}{\itemsep \z@
+ \itemindent -1.5em%
+ \listparindent\itemindent
+ \rightmargin \leftmargin
+ \advance\leftmargin 1.5em}%
+ \item\relax}
+ {\endlist}
+\newenvironment{quotation}
+ {\list{}{\listparindent 1.5em%
+ \itemindent \listparindent
+ \rightmargin \leftmargin
+ \parsep \z@ \@plus\p@}%
+ \item\relax}
+ {\endlist}
+\newenvironment{quote}
+ {\list{}{\rightmargin\leftmargin}%
+ \item\relax}
+ {\endlist}
+\if@compatibility
+\newenvironment{titlepage}
+ {%
+ \cleardoublepage
+ \if@twocolumn
+ \@restonecoltrue\onecolumn
+ \else
+ \@restonecolfalse\newpage
+ \fi
+ \thispagestyle{empty}%
+ \setcounter{page}\z@
+ }%
+ {\if@restonecol\twocolumn \else \newpage \fi
+ }
+\else
+\newenvironment{titlepage}
+ {%
+ \cleardoublepage
+ \if@twocolumn
+ \@restonecoltrue\onecolumn
+ \else
+ \@restonecolfalse\newpage
+ \fi
+ \thispagestyle{empty}%
+ \setcounter{page}\@ne
+ }%
+ {\if@restonecol\twocolumn \else \newpage \fi
+ \if@twoside\else
+ \setcounter{page}\@ne
+ \fi
+ }
+\fi
+\newcommand\appendix{\par\appendixtrue
+ \setcounter{chapter}{0}%
+ \setcounter{section}{0}%
+ \gdef\@chapapp{\appendixname}%
+ \gdef\thechapter{\@Alph\c@chapter}}
+\setlength\arraycolsep{5\p@}
+\setlength\tabcolsep{6\p@}
+\setlength\arrayrulewidth{.4\p@}
+\setlength\doublerulesep{2\p@}
+\setlength\tabbingsep{\labelsep}
+\skip\@mpfootins = \skip\footins
+\setlength\fboxsep{3\p@}
+\setlength\fboxrule{.4\p@}
+\@addtoreset {equation}{chapter}
+\renewcommand\theequation
+ {\ifnum \c@chapter>\z@ \thechapter.\fi \@arabic\c@equation}
+\newcounter{figure}[chapter]
+\renewcommand \thefigure
+ {\ifnum \c@chapter>\z@ \thechapter.\fi \@arabic\c@figure}
+\def\fps@figure{tbp}
+\def\ftype@figure{1}
+\def\ext@figure{lof}
+\def\fnum@figure{\figurename\nobreakspace\thefigure}
+\newenvironment{figure}
+ {\@float{figure}}
+ {\end@float}
+\newenvironment{figure*}
+ {\@dblfloat{figure}}
+ {\end@dblfloat}
+\newcounter{table}[chapter]
+\renewcommand \thetable
+ {\ifnum \c@chapter>\z@ \thechapter.\fi \@arabic\c@table}
+\def\fps@table{tbp}
+\def\ftype@table{2}
+\def\ext@table{lot}
+\def\fnum@table{\tablename\nobreakspace\thetable}
+\newenvironment{table}
+ {\@float{table}}
+ {\end@float}
+\newenvironment{table*}
+ {\@dblfloat{table}}
+ {\end@dblfloat}
+\newlength\abovecaptionskip
+\newlength\belowcaptionskip
+\setlength\abovecaptionskip{10\p@}
+\setlength\belowcaptionskip{0\p@}
+\long\def\@makecaption#1#2{%
+ \vskip\abovecaptionskip
+ \sbox\@tempboxa{#1: #2}%
+ \ifdim \wd\@tempboxa >\hsize
+ {\FigCapFont #1} #2\par
+ \else
+ \global \@minipagefalse
+% \hb@xt@\hsize{\hfil\box\@tempboxa\hfil}%
+ {\FigCapFont #1} #2\par
+ \fi
+ \vskip\belowcaptionskip}
+\DeclareOldFontCommand{\rm}{\normalfont\rmfamily}{\mathrm}
+\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf}
+\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt}
+\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf}
+\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit}
+\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl}
+\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc}
+\DeclareRobustCommand*\cal{\@fontswitch\relax\mathcal}
+\DeclareRobustCommand*\mit{\@fontswitch\relax\mathnormal}
+\newcommand\@pnumwidth{1.55em}
+\newcommand\@tocrmarg{2.55em}
+\newcommand\@dotsep{4.5}
+\setcounter{tocdepth}{3}
+
+
+\newcounter{numauthors}
+\newif\if@break
+\newif\if@firstauthor
+
+\newcommand\tableofcontents{\cleardoublepage\markboth{Contents}{Contents}%
+ \make@cornermarks
+ \gdef\chapterauthor{\@caplusone}%
+ \gdef\endchapterauthors{\end@casplusone}%
+ \if@twocolumn
+ \@restonecoltrue\onecolumn
+ \else
+ \@restonecolfalse
+ \fi
+ {\parindent \z@ \raggedright \baselineskip 6\p@ \lineskip \z@ \parskip \z@
+ \vbox{
+ \vskip 22\p@
+ \unnumchap@rule
+ \vskip 5\p@
+ \FMHeadFont \contentsname\par\vskip-12pt
+ \noindent\hbox{\vrule height.5pt width84pt}
+ \vskip 41\p@}}
+%%% \chapter*{\contentsname
+%%% \@mkboth{%
+%%% \MakeUppercase\contentsname}{\MakeUppercase\contentsname}}%
+ \pagestyle{headings}\thispagestyle{folio}
+ {\let\break\space
+ \let\author\toc@author
+ \reset@authors
+ \let\toc@draw\relax
+ \@starttoc{toc}
+%% \toc@draw
+ }
+ \if@restonecol\twocolumn\fi
+ }
+
+
+\def\draw@part#1#2{%
+ \addpenalty{-\@highpenalty}%
+ \vskip1em plus\p@
+ \@tempdima1.5em
+ \begingroup
+ \parindent\z@\rightskip\@pnumwidth
+ \parfillskip-\rightskip
+ \bfseries
+ \leavevmode
+ \advance\leftskip\@tempdima
+ \hskip-\leftskip
+ {#1\hfil}\nobreak
+ \if@pdf
+ \else
+ \hfil\nobreak\hb@xt@\@pnumwidth{\hss #2}%
+\fi
+ \par
+ \penalty\@highpenalty\endgroup}
+
+\let\toc@draw\relax
+%
+\def\l@part#1#2{%
+\toc@draw
+ \gdef\toc@draw{\draw@part{\large #1}{\large #2}}}
+
+\def\l@fm#1#2{%
+ \toc@draw
+ \gdef\toc@draw{\draw@fm{#1}{#2}}}
+\def\@pnumwidth{1.8em}
+\def\draw@fm#1#2{%
+ \addpenalty{-\@highpenalty}%
+ \vskip1em plus\p@
+ \@tempdima1.5em
+ \begingroup
+ \parindent\z@\rightskip\@pnumwidth
+ \parfillskip-\rightskip
+ \bfseries
+ \leavevmode
+ \advance\leftskip\@tempdima
+ \hskip-\leftskip
+ {#1\hfil}\nobreak
+ \if@pdf
+ \else
+ \hfil\nobreak\hb@xt@\@pnumwidth{\hss #2}%
+\fi
+ \par
+ \penalty\@highpenalty\endgroup}
+
+
+
+ \def\l@chapter#1#2{%
+ \toc@draw
+ \gdef\toc@draw{\draw@chapter{#1}{#2}}}
+\def\@pnumwidth{1.8em}
+\def\draw@chapter#1#2{%
+ \addpenalty{-\@highpenalty}%
+ \vskip1em plus\p@
+ \@tempdima1.5em
+ \begingroup
+ \parindent\z@\rightskip\@pnumwidth
+ \parfillskip-\rightskip
+ \bfseries
+ \leavevmode
+ \advance\leftskip\@tempdima
+ \hskip-\leftskip
+ {#1\hfil}\nobreak
+ \if@pdf
+ \else
+ \hfil\nobreak\hb@xt@\@pnumwidth{\hss #2}%
+\fi
+ \par
+ {\it\draw@authors}%
+ \penalty\@highpenalty\endgroup}
+\def\toc@author#1#2{%
+ \if@firstauthor
+ \@firstauthorfalse
+ \else
+ \ifx\@authors\@empty
+ \xdef\@authors{\last@author}%
+ \else
+ \@cons{\@authors}{, \last@author}\fi\fi
+ \stepcounter{numauthors}%
+%%%%%%% commented and deleted below the second part to aviod inaccessible error % shashi % September-2008
+%% \gdef\last@author{#1 {\rm\fontsize{9\p@}{11\p@}\selectfont #2}}
+\gdef\last@author{#1}
+}
+\def\draw@authors{%
+ \let\@t\@authors
+ \ifx\@t\@empty
+ \let\@t\last@author\fi
+ \ifx\@t\@empty\else
+ \hskip\leftskip
+ \ifx\@authors\@empty
+ \else
+ \@authors
+ \ifnum\c@numauthors>2,\fi
+ \if@break\break\fi
+ \ and \fi
+ \last@author\break\fi
+ \reset@authors}
+\def\reset@authors{%
+ \gdef\@authors{}%
+ \gdef\last@author{}%
+ \@firstauthortrue
+ \setcounter{numauthors}{0}}
+\newlength\section@toc@skip
+\section@toc@skip1.5em
+\newlength\SectionTOCWidth
+\SectionTOCWidth2.3em
+\def\l@section#1#2{%
+ \toc@draw
+ \gdef\toc@draw{\draw@section{#1}{#2}}}
+\def\draw@section#1#2{%
+ \@dottedtocline{1}{\section@toc@skip}{\SectionTOCWidth}{#1 }{{
+\tocfont #2}}}
+\newlength\subsection@toc@skip
+\subsection@toc@skip\section@toc@skip
+\advance\subsection@toc@skip\SectionTOCWidth
+\newlength\SubSectionTOCWidth
+\SubSectionTOCWidth3.2em
+\def\l@subsection#1#2{%
+ \toc@draw
+ \gdef\toc@draw{\draw@subsection{#1}{#2}}}
+\def\draw@subsection#1#2{%
+ \@dottedtocline{2}{\subsection@toc@skip}{\SubSectionTOCWidth}{#1}{{
+\tocfont #2}}}
+\newlength\subsubsection@toc@skip
+\subsubsection@toc@skip\subsection@toc@skip
+\advance\subsubsection@toc@skip\SubSectionTOCWidth
+\newlength\SubSubSectionTOCWidth
+\SubSubSectionTOCWidth4.1em
+\def\l@subsubsection#1#2{%
+ \toc@draw
+ \gdef\toc@draw{\draw@subsubsection{#1}{#2}}}
+\def\draw@subsubsection#1#2{%
+ \@dottedtocline{3}{\subsubsection@toc@skip}{\SubSubSectionTOCWidth}{#1}{{
+\tocfont #2}}}
+\newlength\paragraph@toc@skip
+\paragraph@toc@skip\subsubsection@toc@skip
+\advance\paragraph@toc@skip\SubSubSectionTOCWidth
+\newlength\ParagraphTOCWidth
+\ParagraphTOCWidth4.1em
+\def\l@paragraph#1#2{%
+ \toc@draw
+ \gdef\toc@draw{\draw@paragraph{#1}{#2}}}
+\def\draw@paragraph#1#2{%
+ \@dottedtocline{4}{\paragraph@toc@skip}{\ParagraphTOCWidth}{#1}{{
+\tocfont #2}}}
+\newlength\subparagraph@toc@skip
+\subparagraph@toc@skip\paragraph@toc@skip
+\advance\subparagraph@toc@skip\ParagraphTOCWidth
+\def\l@subparagraph#1#2{%
+ \toc@draw
+ \gdef\toc@draw{\draw@subparagraph{#1}{#2}}}
+\def\draw@subparagraph#1#2{%
+ \@dottedtocline{5}{\subparagraph@toc@skip}{6em}{#1}{{
+\tocfont #2}}}
+
+\def\@dottedtocline#1#2#3#4#5{%
+ \ifnum #1>\c@tocdepth
+ \else
+ \vskip \z@ \@plus.2\p@
+ {\leftskip #2\relax\rightskip\@tocrmarg\parfillskip-\rightskip
+ \parindent #2\relax\@afterindenttrue
+ \interlinepenalty\@M
+ \leavevmode
+ \@tempdima #3\relax
+ \advance\leftskip\@tempdima\null\hskip-\leftskip
+ {#4\hfil}\nobreak
+ \if@pdf
+ \else
+ \leaders\hbox{$\m@th\mkern\@dotsep mu\hbox{.}\mkern\@dotsep mu$}\hfill
+ \nobreak
+ \hb@xt@\@pnumwidth{\hfil\normalfont\normalcolor #5}%
+\fi
+ \par}\fi}
+
+\newcommand\chapterauthors{%
+ \def\break{\string\break\ }%
+ \def\protect##1{\string ##1 }}
+\def\end@cas{}
+\def\end@casplusone{\vskip4pt\@doendpe}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\def\make@chaptoc{% chapter author
+ {\parindent\z@
+ \newcommand\FolioBoldFont{}%
+ \let\@b\bullet
+ \def\bullet{\raisebox{2pt}{$\scriptscriptstyle\@b$}}%
+ \let\SubsectionItalicFont\it
+%\ifx\chapter@author\@empty\else
+{\rm\fontsize{10\p@}{10\p@}\bfseries\selectfont
+%\the\c@numauthors
+ \ifnum\c@numauthors=1
+ \chapter@authorone\vskip6\p@
+ {\it\fontsize{10\p@}{10\p@}\selectfont\chapter@affiliationone}\vskip12\p@
+ \fi
+ \ifnum\c@numauthors=2
+ \chapter@authorone\vskip6\p@
+ {\it\fontsize{10\p@}{10\p@}\selectfont\chapter@affiliationone}\vskip12\p@
+ \chapter@authortwo\vskip6\p@
+ {\it\fontsize{10\p@}{10\p@}\selectfont\chapter@affiliationtwo}
+ \fi
+ \ifnum\c@numauthors=3
+ \chapter@authorone\vskip6\p@
+ {\it\fontsize{10\p@}{10\p@}\selectfont\chapter@affiliationone}\vskip12\p@
+ \chapter@authortwo\vskip6\p@
+ {\it\fontsize{10\p@}{10\p@}\selectfont\chapter@affiliationtwo}\vskip12\p@
+ \chapter@authorthree\vskip6\p@
+ {\it\fontsize{10\p@}{10\p@}\selectfont\chapter@affiliationthree}
+ \fi
+ \ifnum\c@numauthors=4
+ \chapter@authorone\vskip6\p@
+ {\it\fontsize{10\p@}{10\p@}\selectfont\chapter@affiliationone}\vskip12\p@
+ \chapter@authortwo\vskip6\p@
+ {\it\fontsize{10\p@}{10\p@}\selectfont\chapter@affiliationtwo}\vskip12\p@
+ \chapter@authorthree\vskip6\p@
+ {\it\fontsize{10\p@}{10\p@}\selectfont\chapter@affiliationthree}\vskip12\p@
+ \chapter@authorfour\vskip6\p@
+ {\it\fontsize{10\p@}{10\p@}\selectfont\chapter@affiliationfour}
+ \fi
+}
+ \gdef\chapter@authorone{}\gdef\chapter@affiliationone{}%
+ \gdef\chapter@authortwo{}\gdef\chapter@affiliationtwo{}%
+ \gdef\chapter@authorthree{}\gdef\chapter@affiliationthree{}%
+ \gdef\chapter@authorfour{}\gdef\chapter@affiliationfour{}%
+ \vskip 14.6\p@
+{\leftskip\secnumwidth\def\author##1##2{}\vskip14pt\hbox{\leftskip0pt\SubsectionHeadFont CONTENTS}\vskip6pt\par\@input{\thechapter.toc}\par}%
+ }
+\reset@authors}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\newif\iffinishedfromone
+\global\finishedfromonefalse
+%
+\newif\iffinishedfromtwo
+\global\finishedfromtwofalse
+%
+\newif\iffinishedfromthree
+\global\finishedfromthreefalse
+%
+\newif\iffinishedfromfour
+\global\finishedfromfourfalse
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+\newcommand\singleauthorchapter{\finishedfromonetrue}
+\newcommand\twoauthorchapter{\finishedfromtwotrue}
+\newcommand\threeauthorchapter{\finishedfromthreetrue}
+\newcommand\fourauthorchapter{\finishedfromfourtrue}
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\newif\iffinish
+\global\finishfalse
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\newsavebox\@AUonebox
+\newsavebox\@AUtwobox
+\newsavebox\@AUthreebox
+\newsavebox\@AUfourbox
+%
+\newsavebox\@AUaffonebox
+\newsavebox\@AUafftwobox
+\newsavebox\@AUaffthreebox
+\newsavebox\@AUafffourbox
+%
+\newsavebox\@finalAUboxfromone
+\newsavebox\@finalAUboxfromtwo
+\newsavebox\@finalAUboxfromthree
+\newsavebox\@finalAUboxfromfour
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\def\@ca#1#2{%
+% \def\chapter@author{#1}%
+% \def\chapter@affiliation{#2}%
+ \if@filesw%
+ \write\@auxout{%
+\string\@writefile{toc}{\string\author{#1}{}}%
+}%
+ \fi
+%%%%%%%%%%%%%%%
+
+\ifnum\c@numauthors>4
+ \resetcounter{numauthors}
+\fi
+\stepcounter{numauthors}
+%%\the\c@numauthors
+\ifnum\c@numauthors=1 %
+ \sbox\@AUonebox{\CAPlusOneFont#1}
+ \sbox\@AUaffonebox{\vbox{\hsize\textwidth\CAAPlusOneFont\noindent #2\par}}
+ \sbox\@finalAUboxfromone{\copy\@AUonebox}
+ \def\chapter@authorone{\copy\@finalAUboxfromone}
+ \def\chapter@affiliationone{\copy\@AUaffonebox}
+\fi \ifnum\c@numauthors=2
+ \sbox\@AUtwobox{\CAPlusOneFont#1}
+ \sbox\@AUafftwobox{\vbox{\hsize\textwidth\CAAPlusOneFont\noindent #2\par}}
+ \sbox\@finalAUboxfromtwo{\copy\@AUtwobox}
+ \def\chapter@authortwo{\copy\@finalAUboxfromtwo}
+ \def\chapter@affiliationtwo{\copy\@AUafftwobox}
+\fi \ifnum\c@numauthors=3
+ \sbox\@AUthreebox{\CAPlusOneFont#1}
+ \sbox\@AUaffthreebox{\vbox{\hsize\textwidth\CAAPlusOneFont\noindent #2\par}}
+ \sbox\@finalAUboxfromthree{\copy\@AUthreebox}
+ \def\chapter@authorthree{\copy\@finalAUboxfromthree}
+ \def\chapter@affiliationthree{\copy\@AUaffthreebox}
+\fi \ifnum\c@numauthors=4
+ \sbox\@AUfourbox{\CAPlusOneFont#1}
+ \sbox\@AUafffourbox{\vbox{\hsize\textwidth\CAAPlusOneFont\noindent #2\par}}
+ \sbox\@finalAUboxfromfour{\copy\@AUfourbox}
+ \def\chapter@authorfour{\copy\@finalAUboxfromfour}
+ \def\chapter@affiliationfour{\copy\@AUafffourbox}
+\fi}
+
+
+\def\@caplusone{\@ifstar{\@scaplusone}{\@ifnextchar[{\@xcaplusone}{\@xcaplusone[]}}}
+\def\@xcaplusone[#1]#2#3{%
+ \def\@@empty{#1}\ifx\@empty\@@empty\@ca{#2}{#3}\else\@ca{#2}{#1}\fi\@scaplusone{#2}{#3}}
+\def\@scaplusone#1#2{%
+ \ifhmode\vskip-12pt\fi
+%%Shashi Commented
+%%% \noindent\hskip3pc{\CAPlusOneFont\baselineskip14pt #1\def\@t{#2}\ifx\@t\@empty\else,\fi}\hskip6pt{\CAAPlusOneFont #2}\par
+}
+
+\def\chapterauthoronly#1#2{\@ca{#1}{}\@scaplusone{#1}{#2}}
+\def\myaddcontentsline#1#2#3{%
+ \if@filesw
+ \begingroup
+ \let\label\@gobble\let\index\@gobble\let\glossary\@gobble
+ \def\break{\ }%
+ \def\protect##1{\string ##1 }%
+ \@temptokena{\thepage}%
+ \edef\@tempa{\write#1{\string\chapcontentsline{#2}{\string\raggedright\space #3}{\the\@temptokena}}}\@tempa
+ \if@nobreak\ifvmode\nobreak\fi\fi
+ \endgroup
+ \fi}
+\def\chapcontentsline#1{\csname l@#1\endcsname}
+\def\l@chapsection{\@mydottedtocline{1}{\z@}{6pt}}
+\def\l@chapsubsection{\@mydottedtocline{2}{\secnumwidth}{6pt}}
+\def\l@chapsubsubsection{\@mydottedtocline{3}{\subsecnumwidth}{36pt}}
+\newcount\c@chaptocdepth
+\setcounter{chaptocdepth}{3}
+\def\@mytocline#1#2#3#4#5{%
+ \ifnum #1>\c@chaptocdepth
+ \else
+ \vskip 2pt plus.2\p@
+ \ifnum #1=1\ifnum\c@chaptocdepth>1\addvspace{12pt}\fi\fi
+ {\leftskip #2\relax% \rightskip \@tocrmarg \parfillskip -\rightskip
+ \interlinepenalty\@M
+ \leavevmode
+ \@tempdima #3\relax
+ \rightskip\z@
+ \vbox{\ChapTOCFont #4\nobreak}%
+ \par}\fi}
+\def\@mydottedtocline#1#2#3#4#5{%
+ \ifnum #1>\c@chaptocdepth
+ \else
+\fontsize{10}{12}\selectfont
+{\leftskip #2\relax \rightskip \@tocrmarg \parfillskip -\rightskip
+ % \parindent #2\relax\@afterindenttrue
+ \interlinepenalty\@M
+ \leavevmode
+ \def\@dotsep{1.2}%
+ \@tempdima #3\relax
+ \rightskip\z@
+% \advance\hsize-\secnumwidth
+% \hskip-\secnumwidth
+\if@sevenbyten
+\hangindent\secnumwidth\hsize372pt\else\hangindent\secnumwidth\hsize312pt\fi
+#4
+ \if@pdf
+ \hfill
+ \else
+ \nobreak\leaders\hbox{$\m@th\mkern\@dotsep mu.\mkern\@dotsep mu$}\hfill\nobreak
+ \hbox to24\p@{\hfil #5}\fi
+ \par}\fi}
+
+\newcommand\listoffigures{%
+ \if@twocolumn
+ \@restonecoltrue\onecolumn
+ \else
+ \@restonecolfalse
+ \fi
+ \chapter*{\listfigurename}%
+ \@mkboth{\MakeUppercase\listfigurename}%
+ {\MakeUppercase\listfigurename}%
+ \@starttoc{lof}%
+ \if@restonecol\twocolumn\fi
+ }
+\newcommand*\l@figure{\@dottedtocline{1}{1.5em}{2.3em}}
+\newcommand\listoftables{%
+ \if@twocolumn
+ \@restonecoltrue\onecolumn
+ \else
+ \@restonecolfalse
+ \fi
+ \chapter*{\listtablename}%
+ \@mkboth{%
+ \MakeUppercase\listtablename}%
+ {\MakeUppercase\listtablename}%
+ \@starttoc{lot}%
+ \if@restonecol\twocolumn\fi
+ }
+\let\l@table\l@figure
+\newdimen\bibindent
+\setlength\bibindent{1.5em}
+\newenvironment{thebibliography}[1]
+ {\chapter*{\bibname}%
+ \@mkboth{\MakeUppercase\bibname}{\MakeUppercase\bibname}%
+% \addcontentsline{toc}{chapter}{\bibname}
+ \list{\@biblabel{\@arabic\c@enumiv}}%
+ {\settowidth\labelwidth{\@biblabel{#1}}%
+ \leftmargin\labelwidth
+ \advance\leftmargin\labelsep
+ \@openbib@code
+ \usecounter{enumiv}%
+ \let\p@enumiv\@empty
+ \renewcommand\theenumiv{\@arabic\c@enumiv}}%
+ \sloppy
+ \clubpenalty4000
+ \@clubpenalty \clubpenalty
+ \widowpenalty4000%
+ \sfcode`\.\@m}
+ {\def\@noitemerr
+ {\@latex@warning{Empty `thebibliography' environment}}%
+ \endlist}
+\newcommand\newblock{\hskip .11em\@plus.33em\@minus.07em}
+\let\@openbib@code\@empty
+\newcommand\indexname{Index}
+\newenvironment{theindex}
+ {\cleardoublepage\if@twocolumn
+ \@restonecolfalse
+ \else
+ \@restonecoltrue
+ \fi
+ \twocolumn[\@makeschapterhead{\indexname}]%
+ \@mkboth{\MakeUppercase\indexname}%
+ {\MakeUppercase\indexname}%
+ \pagestyle{headings}
+ \addcontentsline{toc}{chapter}{\indexname}
+% there seems to be a weird bug in krantz.cls that prevents the very _last_ item
+% of \addcontentsline from being added to TOC, so I have to add an empty entry
+\addcontentsline{toc}{section}{}
+ \thispagestyle{folio}\parindent\z@\markboth{\indexname}{\indexname}
+ \parskip\z@ \@plus .3\p@\relax\raggedright
+ \columnseprule \z@
+ \columnsep 35\p@
+ \let\item\@idxitem}
+ {\if@restonecol\onecolumn\else\clearpage\fi}
+\newcommand\@idxitem{\par\hangindent 40\p@}
+\newcommand\subitem{\@idxitem \hspace*{20\p@}}
+\newcommand\subsubitem{\@idxitem \hspace*{30\p@}}
+\newcommand\indexspace{\par \vskip 10\p@ \@plus5\p@ \@minus3\p@\relax}
+\renewcommand\footnoterule{%
+ \kern-3\p@
+ \hrule\@width.4\columnwidth
+ \kern2.6\p@}
+\@addtoreset{footnote}{chapter}
+\newcommand\@makefntext[1]{%
+ \parindent 1em%
+ \noindent
+ \hb@xt@1.8em{\hss\@makefnmark}#1}
+\newcommand\contentsname{Contents}
+\newcommand\listfigurename{List of Figures}
+\newcommand\listtablename{List of Tables}
+\newcommand\bibname{Bibliography}
+\newcommand\figurename{Figure}
+\newcommand\tablename{Table}
+\newcommand\partname{Part}
+\newcommand\chaptername{Chapter}
+\newcommand\appendixname{Appendix}
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\setlength\columnsep{10\p@}
+\setlength\columnseprule{0\p@}
+\pagestyle{headings}
+\pagenumbering{arabic}
+\if@twoside
+\else
+ \raggedbottom
+\fi
+\if@twocolumn
+ \twocolumn
+ \sloppy
+ \flushbottom
+\else
+ \onecolumn
+\fi
+\newcommand\unnumcrcrule{\hbox to\textwidth{\rlap{\rule[-3.5\p@]{84\p@}{4\p@}}}}
+\newcommand\unnumchap@rule{\unnumcrcrule}
+\newcommand\crcrule{\hbox to\textwidth{\rlap{\rule[-3.5\p@]{84\p@}{4\p@}}\rule{\textwidth}{.5\p@}}}
+\newcommand\chap@rule{\crcrule}
+\newcommand\sec@rule{\crcrule}
+\def\@affiliate[#1]{\gdef\@affiliation{#1}}
+\def\@affiliation{}
+
+\def\def@theequation{%
+ \if@numberinsequence
+ \def\theequation{%
+\if@numbysec\thesection\else\thechapter\fi.%
+\@arabic\c@shared}%
+ \else
+ \def\theequation{%
+\if@numbysec\thesection\else\thechapter\fi.%
+\@arabic\c@equation}\fi}
+
+\def\affiliation#1{{\AffiliationFont\noindent #1\vskip 36bp}}
+\newbox\tempbox
+
+\newdimen\nomenwidth
+
+\newenvironment{symbollist}[1]{%
+\addvspace{12pt}
+ \setbox\tempbox\hbox{#1\hskip1em}%
+ \global\nomenwidth\wd\tempbox
+ %\section*{Sumbol Description}
+\noindent{\SectionHeadFont Symbol Description}\vskip6pt
+\begin{multicols}{2}}{%
+ \end{multicols}\par\addvspace{12pt}}
+\def\symbolentry#1#2{\par\noindent\@hangfrom{\hbox to \nomenwidth{#1\hss}}#2\par}
+
+
+\tabcolsep 5pt
+\arrayrulewidth .5pt
+\doublerulesep 1pt
+%\newcounter{subtable}[table]
+\newif\if@tablerules\@tablerulestrue
+\newif\if@centertable\@centertabletrue
+\newif\if@centertabletitle\@centertabletitletrue
+\newbox\@tablebox
+\newbox\@tabletitlebox
+\newdimen\@tablewidth
+\newdimen\@tabletitlewidth
+\newdimen\max@tablewidth
+\newcommand\automaticrules{\@tablerulestrue}
+\newcommand\noautomaticrules{\@tablerulesfalse}
+\def\thetable{%
+\thechapter.%
+\@arabic\c@table}
+\def\thesubtable{%
+\thechapter.%
+\@arabic\c@table\alph{subtable}}
+\def\resettableletter{\setcounter{subtable}{0}}
+\def\@Tabletitle{}
+\newcommand\tabletitle{\@ifnextchar[{\@xtabletitle}{\@tabletitlewidth\z@\@ytabletitle}}
+\def\@@tabletitle{}
+\newif\ifshorttabletitle
+\global\shorttabletitlefalse
+%\def\@xtabletitle#1{\@tabletitlewidth#1\@ytabletitle}
+%
+\def\@xtabletitle[#1]#2{%
+ \gdef\@@tabletitle{#1}%
+ \gdef\@tabletitle{#2}%
+ \let\@Tabletitle\@TableTitle
+ \refstepcounter{table}%
+ {\let\footnotemark\@empty
+ \let\footnote\@gobble
+ \addcontentsline{\ext@table}{table}{\protect\numberline{\thetable}{\@@tabletitle}}}}
+%%%%
+%\long\def\@xtabletitle[#1]#2{%
+% \setbox\@ttbox\hbox{#1}\global\shorttabletitletrue
+% \def\@@tabletitle{\ifx\@ttbox\@empty\else#1\fi}%
+% \def\@tabletitle{#2}%
+% \let\@Tabletitle\@TableTitle
+% \refstepcounter{table}%
+% {\let\footnotemark\@empty
+% \let\footnote\@gobble
+% \addcontentsline{\ext@table}{table}{\protect\numberline{\thetable}{%
+%\ifshorttabletitle\@@tabletitle\else\@tabletitle\fi}}}}
+
+%%%
+%
+\long\def\@ytabletitle#1{%
+ \def\@tabletitle{#1}%
+ \let\@Tabletitle\@TableTitle
+ \refstepcounter{table}%
+ {\let\footnotemark\@empty
+ \let\footnote\@gobble
+ \addcontentsline{\ext@table}{table}{\protect\numberline{\thetable}{\@tabletitle}}}}
+\def\tabletitlelet{\@ifnextchar[{\@xtabletitlelet}{\@tabletitlewidth\z@\@ytabletitlelet}}
+\def\@xtabletitlelet[#1]{\@tabletitlewidth#1\@ytabletitlelet}
+\long\def\@ytabletitlelet#1{%
+ \def\@tabletitle{#1}%
+ \let\@Tabletitle\@TableTitle
+ \ifnum\c@subtable=0\stepcounter{table}\fi
+ \let\@currentlabel\thesubtable
+ {\let\footnotemark\@empty
+ \let\footnote\@gobble
+ \addcontentsline{\ext@table}{table}{\protect\numberline{\thetable}{\@tabletitle}}}}
+\def\@TableTitle{%
+ \noindent
+ {%
+ \vbox{{\TableNumberFont TABLE\ \thetable}}\par\TableTitleFont\@tabletitle}}
+\def\table{%
+ %\long\def\caption##1{\tabletitle{##1}\@TableTitle\par}%
+ \@float{table}}
+\@namedef{table*}{%
+ \long\def\caption##1{\tabletitle{##1}\@TableTitle\par}%
+ \@dblfloat{table}}
+
+\def\endtabular{\crcr\egroup\egroup $\egroup}
+\expandafter \let \csname endtabular*\endcsname = \endtabular
+\def\tabular{\let\@halignto\@empty\@tabular}
+\@namedef{tabular*}#1{%
+ \setlength\dimen@{#1}%
+ \edef\@halignto{to\the\dimen@}\@tabular}
+
+
+\def\tch#1{\TableColHeadFont #1\llstrut\hfill}
+\def\tsh#1{\TableSubheadFont #1\hfill}
+\newcommand\llstrut{\rule[-6pt]{0pt}{14pt}}
+\newcommand\flstrut{\rule{0pt}{10pt}}
+\newcommand\tabletitlestrut{\rule{0pt}{20pt}}
+
+\def\Boxhead#1{\par\addvspace{3pt plus2pt}\noindent{\centering\bfseries#1\par}\vskip3pt}
+
+
+\newbox\tempbox%
+\newdimen\tempdimen%
+%
+\newenvironment{shortbox}{\par\addvspace{12pt plus2pt}%
+\if@krantza
+\setbox\tempbox\vbox\bgroup\hsize27pc%
+\else\if@krantzb
+\setbox\tempbox\vbox\bgroup\hsize32pc%
+\else
+\setbox\tempbox\vbox\bgroup\hsize25pc%
+\fi\fi
+}{%
+\egroup%
+\noindent\fboxsep6pt\fboxrule.5pt\hspace*{0pt}\fbox{\box\tempbox}
+\par\addvspace{12pt plus2pt}}%
+%
+
+
+\def\grayink{\special{color cmyk 0 0 0 0.2}}
+\def\blackink{\special{color cmyk 0 0 0 1.0}} %
+\def\whiteink{\special{color cmyk 0 0 0 0}} % 0%
+
+\newenvironment{shadebox}{%
+\setbox\tempbox\hbox\bgroup\vbox\bgroup\leftskip12pt\rightskip\leftskip\vspace*{12pt}}{\par\addvspace{-6pt}
+\egroup\egroup\par\addvspace{15pt}
+\tempdimen\ht\tempbox
+\advance\tempdimen by 1pc
+\noindent{\hbox to \wd\tempbox{\vbox to \ht\tempbox{\hsize\textwidth{\special{color push}\grayink\noindent\vrule height\tempdimen width\textwidth
+\special{color pop}\blackink}}}}%
+\llap{\unhbox\tempbox}\par\addvspace{20pt}}
+
+%%%%%%%%%% Note %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\newbox\tempbox
+\newdimen\notewidth
+\newenvironment{notelist}[1]{%
+\addvspace{6pt}
+ \setbox\tempbox\hbox{#1\hskip.57em}%
+ \global\notewidth\wd\tempbox
+}{%
+ \par\addvspace{6pt}}
+
+\def\notes#1#2{\par\noindent\@hangfrom{\hbox to \notewidth{\bf #1\hss}}#2\par}
+%%%%%%%%%%%%%%%% wherelist %%%%%%%%%%%%%%%%
+\newbox\wherebox
+\newdimen\wherewidth
+\newenvironment{wherelist}[1]{\leftskip10pt%
+\addvspace{6pt}
+ \setbox\wherebox\hbox{#1\hskip1em}%
+ \global\wherewidth\wd\wherebox
+\noindent\hspace*{-14pt} where
+}{%
+\par\addvspace{6pt}}
+\def\whereentry#1#2#3{\par\noindent\@hangfrom{\hbox to \wherewidth{#1\hss}#2\hskip6pt}#3\par}
+%%%%%%%%%%%%
+\newenvironment{unnumlist}{%
+ \ifnum \@enumdepth >3 \@toodeep\else
+ \advance\@enumdepth\@ne
+ \list{}{%
+\leftmargini27.5pt \leftmarginii17.5pt\leftmarginiv17.5pt
+% \leftmargin\parindent
+ \advance\leftmargin-.2em
+ \advance\leftmarginii.2em
+ \advance\leftmarginiii.1em
+ \advance\leftmarginiv.2em
+ \def\makelabel##1{\hss\llap{##1}}}
+ \fi%
+}{%
+ \endlist}
+ %
+\newenvironment{extract}{%
+ \par\addvspace{11.5pt minus2pt}%
+ \leftskip2em\rightskip\leftskip
+ \noindent\ignorespaces
+}{%
+ \par\addvspace{11.5pt minus2pt}%
+ \@endparenv}
+%
+%
+\def\VA#1#2{\addvspace{12pt}\raggedleft #1\rightskip3em\par #2\rightskip3em}
+%
+\newenvironment{VF}{\VfFont%
+ \par\addvspace{12pt minus2pt}%
+\noindent{\vrule height2pt width\textwidth}\par\vskip7.3pt
+ \leftskip3em\rightskip\leftskip
+ \noindent\ignorespaces
+}{%
+\par\vskip6pt\leftskip0pt\noindent{{\vrule height2pt width\textwidth}}\par\addvspace{12pt minus2pt}%
+ \@endparenv}
+%
+\def\VTA#1#2{\addvspace{12pt}\raggedleft #1\rightskip3em\par {\it #2}\rightskip3em}
+%
+%
+\def\VT{\par\addvspace{3.5pt}\noindent}
+
+\def\VH#1{{\normalfont\fontsize{12.5}{14.5}\itshape\centering\selectfont #1\par}\addvspace{5.5pt}}
+%
+\newenvironment{VT1}{\VfFont%
+ \par\addvspace{12pt minus2pt}%
+\noindent{\vrule height2pt width\textwidth}\par\vskip7.5pt
+ \leftskip3em\rightskip\leftskip
+%\@afterheading
+\parindent0pt
+ \noindent\ignorespaces
+}{%
+\par\vskip6pt\leftskip0pt\noindent{{\vrule height2pt width\textwidth}}\par\addvspace{10pt minus2pt}%
+ \@endparenv}
+%
+%%%%%%%%%%%% Glossary %%%%%%%%%%%%%%%%%%%%%%%
+\newenvironment{Glossary}
+ {\list{}{\labelwidth\z@\leftmargin18pt \itemindent-18pt
+ \let\makelabel\glosslabel}}
+ {\endlist}
+\newcommand\glosslabel[1]{\hspace\labelsep\normalfont\bfseries #1:}
+
+%%%%%%%%%%%%
+\newif\iffnalpha
+\global\fnalphafalse
+
+\newskip\listtextleftmargin\listtextleftmargin 20pt%24pt
+\newskip\listtextleftmarginii\listtextleftmarginii0pt% 24pt
+\newskip\listtextleftmarginiii\listtextleftmarginiii0pt% 24pt
+
+\newskip\listtextrightmargin\listtextrightmargin12pt%.5pc
+\newskip\listlabelleftskip \listlabelleftskip4pt%3.3pt
+\newskip\listlabelleftskipii \listlabelleftskipii0pt%3.3pt
+\newskip\listlabelleftskipiii \listlabelleftskipiii0pt%3.3pt
+
+\newskip\abovelistskipi\abovelistskipi6pt plus2pt
+\newskip\belowlistskipi\belowlistskipi6pt plus2pt
+\newskip\abovelistskipii\abovelistskipii0pt plus2pt
+\newskip\belowlistskipii\belowlistskipii0pt plus2pt
+\newskip\abovelistskipiii\abovelistskipiii0pt plus2pt
+\newskip\belowlistskipiii\belowlistskipiii0pt plus2pt
+
+\newskip\labelsepi \labelsepi6pt
+\newskip\labelsepii \labelsepii6pt
+\newskip\labelsepiii \labelsepiii6pt%\z@
+
+\newskip\itemsepi \itemsepi0pt%10pt
+\newskip\itemsepii \itemsepii0pt
+\newskip\itemsepiii \itemsepiii0pt
+
+
+\newdimen\enumdimwd
+\newif\iflabelrightalign\labelrightaligntrue
+\newdimen\enumdim%
+%
+\def\enummax#1{%
+ \labelsep\csname labelsep\romannumeral\the\@enumdepth\endcsname
+ \ifdim\listtextleftmargin>\z@\labelsepi0pt\fi
+ \ifdim\listtextleftmarginii>\z@\labelsepii0pt\fi
+ \ifdim\listtextleftmarginiii>\z@\labelsepiii0pt\fi
+ \setbox\tempbox\hbox{\csname listdevicefont\romannumeral\the\@enumdepth\endcsname#1\hskip\labelsep}%
+ \enumdim\wd\tempbox
+ \setbox\tempbox\hbox{\csname listdevicefont\romannumeral\the\@enumdepth\endcsname#1}%
+ \enumdimwd\wd\tempbox
+ \expandafter\global\csname leftmargin\romannumeral\the\@enumdepth\endcsname\enumdim
+ \ifdim\listtextleftmargin>\z@
+ \leftmargini\listtextleftmargin
+ \ifdim\listlabelleftskip>\z@
+ \advance\leftmargini-\listlabelleftskip
+ \fi
+ \fi
+ \ifdim\listtextleftmarginii>\z@
+ \leftmarginii\listtextleftmarginii
+ \ifdim\listlabelleftskipii>\z@
+ \advance\leftmarginii-\listlabelleftskipii
+ \fi
+ \fi
+ \ifdim\listtextleftmarginiii>\z@
+ \leftmarginiii\listtextleftmarginiii
+ \ifdim\listlabelleftskipiii>\z@
+ \advance\leftmarginiii-\listlabelleftskipiii
+ \fi
+ \fi
+}
+%
+\enummax{1.}
+%
+\def\enumerate{\@ifnextchar[{\@enumerate}{\@enumerate[\csname label\@enumctr\endcsname]}}%%
+%
+
+\def\@enumerate[#1]{\par
+ \ifnum \@enumdepth >3 \@toodeep
+ \else
+ \advance\@enumdepth\@ne
+ \edef\@enumctr{enum\romannumeral\the\@enumdepth}%
+ \setcounter{\@enumctr}{1}\enummax{#1}%
+ \list
+ {\csname label\@enumctr\endcsname}{\usecounter{\@enumctr}%
+ \topsep\csname abovelistskip\romannumeral\the\@enumdepth\endcsname
+ \itemsep\csname itemsep\romannumeral\the\@enumdepth\endcsname
+% \listfont %\listparindent18.25pt
+ \ifnum \@enumdepth=1 \leftmargin32.7pt
+ \rightmargin\listtextrightmargin
+ \advance\rightmargin\rightskip
+ \advance\leftmargin\leftskip
+ \tempdimen\leftmargini
+ \advance\tempdimen-\labelsep
+ %%%%%%%%%%%
+ \iffnalpha
+ \def\makelabel##1{{\hskip\listlabelleftskip{\csname listdevicefont\romannumeral\the\@enumdepth\endcsname{\iflabelrightalign\hss\fi\textlistlabel##1}}}}%
+ \global\fnalphafalse
+ \else
+ \def\makelabel##1{\hbox to \tempdimen{\hskip\listlabelleftskip{\csname listdevicefont\romannumeral\the\@enumdepth\endcsname\hbox to \enumdimwd{\iflabelrightalign\hss\fi\textlistlabel##1}}\blackink}}%
+ \fi
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%
+ \else
+ \ifnum \@enumdepth=2
+ \tempdimen\leftmarginii
+ \advance\tempdimen-\labelsep
+ \def\makelabel##1{\hbox to \tempdimen{\hskip\listlabelleftskipii{\csname listdevicefont\romannumeral\the\@enumdepth\endcsname\hbox to \enumdimwd{\iflabelrightalign\hss\fi##1}\blackink}}}%
+ \else
+ \ifnum \@enumdepth=3
+ \tempdimen\leftmarginiii
+ \advance\tempdimen-\labelsep
+ \def\makelabel##1{\hbox to \tempdimen{\hskip\listlabelleftskipiii{\csname listdevicefont\romannumeral\the\@enumdepth\endcsname\hbox to \enumdimwd{\iflabelrightalign\hss\fi##1}\blackink}}}%
+ \else
+ \def\makelabel##1{\hss\llap{\csname listdevicefont\romannumeral\the\@enumdepth\endcsname##1}}%
+ \fi
+ \fi
+ \fi}
+ \fi}
+%
+\def\endenumerate{\@topsepadd\csname belowlistskip\romannumeral\the\@enumdepth\endcsname\endlist}%
+%
+
+\def\textlistlabel{}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\newdimen\concolwidth
+\newbox\stempbox
+\def\contributor#1#2#3{\addvspace{10pt}{%
+\setbox\stempbox\hbox{\ContributorAffiliationFont #2}
+\concolwidth\wd\stempbox
+ \noindent{\ContributorNameFont #1}\par
+ \ifdim\concolwidth>\columnwidth \vspace*{3pt} \else \fi
+ \noindent{\vbox{\hangindent12pt\ContributorAffiliationFont #2}}\vskip-1\p@
+ \noindent{\vbox{\hangindent12pt\ContributorAffiliationFont #3}}}}
+
+%%\def\contributors{%
+%% \twocolumn[\contributorshead]
+%% \pagestyle{empty}
+%% \leftskip1pc
+%% \parindent-1pc}
+%%\def\contributorshead{%
+%% \vbox{}\vskip2pc
+%% {\centering\HeadFont CONTRIBUTORS\vskip2\p@}
+%% \noindent\rule{\textwidth}{1\p@}\vskip25\p@}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\def\cleardoublepage{\clearpage\if@twoside \ifodd\c@page\else
+ \hbox{}\thispagestyle{empty}\newpage\if@twocolumn\hbox{}\newpage\fi\fi\fi}
+
+\frenchspacing
+\tolerance=5000
+\raggedbottom
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\@centertabletitlefalse
+%\HeadingsBookChapter
+\HeadingsChapterSection
+\endinput
+%%
+%% End of file `krantz.cls'.
diff --git a/book-source/latex/after_body.tex b/book-source/latex/after_body.tex
new file mode 100755
index 0000000..c033d64
--- /dev/null
+++ b/book-source/latex/after_body.tex
@@ -0,0 +1,2 @@
+%\backmatter
+\printindex
diff --git a/book-source/latex/before_body.tex b/book-source/latex/before_body.tex
new file mode 100755
index 0000000..482e38d
--- /dev/null
+++ b/book-source/latex/before_body.tex
@@ -0,0 +1,15 @@
+% you may need to leave a few empty pages before the dedication page (CRC guidelines are that the Table of Contents starts on page v. The title page is page i.)
+%\cleardoublepage
+\newpage
+\thispagestyle{empty}
+
+\begin{center}
+Peter: To people in my life.
+
+\vspace{0.3in}
+
+Kalvin: To people in my life.
+\end{center}
+
+\setlength{\abovedisplayskip}{-5pt}
+\setlength{\abovedisplayshortskip}{-5pt}
diff --git a/book-source/latex/preamble.tex b/book-source/latex/preamble.tex
new file mode 100755
index 0000000..94d1b67
--- /dev/null
+++ b/book-source/latex/preamble.tex
@@ -0,0 +1,128 @@
+\usepackage{booktabs}
+\usepackage{longtable}
+\usepackage{bbm}
+\usepackage[bf,singlelinecheck=off]{caption}
+
+\usepackage{framed,color}
+\definecolor{shadecolor}{RGB}{248,248,248}
+
+\usepackage{float}
+\floatplacement{figure}{H} % figures placed after mention
+% \floatplacement{figure}{bth}
+% \floatplacement{table}{H}
+\floatplacement{table}{btp} % only allow tables to be top or bottom
+\setlength{\textfloatsep}{0.1cm}
+\usepackage{array}
+\usepackage{multirow}
+%\usepackage[table]{xcolor}
+\usepackage{wrapfig}
+\usepackage{colortbl}
+\usepackage{pdflscape}
+\usepackage{tabu}
+\usepackage{threeparttable}
+\usepackage{threeparttablex}
+\usepackage[normalem]{ulem}
+\usepackage{makecell}
+\usepackage{kotex}
+
+% Changed via Rob Hyndman's suggestions
+% https://robjhyndman.com/hyndsight/latex-floats/
+\setcounter{topnumber}{2}
+\setcounter{bottomnumber}{2}
+\setcounter{totalnumber}{4}
+\renewcommand{\topfraction}{0.85}
+\renewcommand{\bottomfraction}{0.85}
+\renewcommand{\textfraction}{0.15}
+\renewcommand{\floatpagefraction}{0.7}
+% Force images to appear after their definition
+\usepackage{flafter}
+
+\renewenvironment{quote}{\begin{VF}}{\end{VF}}
+\usepackage{hyperref}
+\let\oldhref\href
+\renewcommand{\href}[2]{#2\footnote{\url{#1}}}
+
+\makeatletter
+\newenvironment{kframe}{%
+\medskip{}
+\setlength{\fboxsep}{.8em}
+ \def\at@end@of@kframe{}%
+ \ifinner\ifhmode%
+ \def\at@end@of@kframe{\end{minipage}}%
+ \begin{minipage}{\columnwidth}%
+ \fi\fi%
+ \def\FrameCommand##1{\hskip\@totalleftmargin \hskip-\fboxsep
+ \colorbox{shadecolor}{##1}\hskip-\fboxsep
+ % There is no \\@totalrightmargin, so:
+ \hskip-\linewidth \hskip-\@totalleftmargin \hskip\columnwidth}%
+ \MakeFramed {\advance\hsize-\width
+ \@totalleftmargin\z@ \linewidth\hsize
+ \@setminipage}}%
+ {\par\unskip\endMakeFramed%
+ \at@end@of@kframe}
+\makeatother
+
+\renewenvironment{Shaded}{\begin{kframe}}{\end{kframe}}
+
+\usepackage{makeidx}
+\makeindex
+
+\urlstyle{tt}
+
+%% Need to clean up
+\newenvironment{rmdblock}[1]
+ {\begin{shaded*}
+ \begin{itemize}
+ \renewcommand{\labelitemi}{
+ \raisebox{-.7\height}[0pt][0pt]{
+ % {\setkeys{Gin}{width=3em,keepaspectratio}\includegraphics{images/#1}}
+ }
+ }
+ \item
+ }
+ {
+ \end{itemize}
+ \end{shaded*}
+ }
+
+\newenvironment{rmdnote}
+ {\begin{rmdblock}{note}}
+ {\end{rmdblock}}
+\newenvironment{rmdcaution}
+ {\begin{rmdblock}{caution}}
+ {\end{rmdblock}}
+\newenvironment{rmdimportant}
+ {\begin{rmdblock}{important}}
+ {\end{rmdblock}}
+\newenvironment{rmdtip}
+ {\begin{rmdblock}{tip}}
+ {\end{rmdblock}}
+\newenvironment{rmdwarning}
+ {\begin{rmdblock}{warning}}
+ {\end{rmdblock}}
+\newenvironment{learncheck}
+ {\begin{rmdblock}{warning}}
+ {\end{rmdblock}}
+\newenvironment{review}
+ {\begin{rmdblock}{warning}}
+ {\end{rmdblock}}
+\newenvironment{announcement}
+ {\begin{rmdblock}{warning}}
+ {\end{rmdblock}}
+
+% No widow lines
+% Copied from https://github.com/hadley/adv-r/blob/master/latex/preamble.tex
+\widowpenalty=10000
+\clubpenalty=10000
+
+\usepackage{amsthm}
+\makeatletter
+\def\thm@space@setup{%
+ \thm@preskip=8pt plus 2pt minus 4pt
+ \thm@postskip=\thm@preskip
+}
+\makeatother
+
+%\listfiles
+
+\frontmatter