# notes

path: React_useState.md
sortable_path: React_useState.md
title: useState
checksum: e3bc20741b5029e4be18e645b02bae622e038b3e40b6f60627620481e4a59686
created: 2024-10-22 16:52:58.467480137+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["javascript","react"]}
lead:
  `useState` is the primary way in which React updates the application state based
  on user interaction.

body:
  `useState` is the primary way in which React updates the application state based
  on user interaction.

  - `useState` **receives**: the initial state as an argument
  - `useState` **returns**: the current state and a function that updates it

  The two items that are returned are returned as an array. We could refer to them
  with array syntax (i.e with `[0]` and `[1]` ) but it is more concise to use the
  destructuring syntax. But all that's happening is that a method belonging to the
  `React` library (i.e. `useState` ) is taking an argument (the initial state) and
  returning two values as an array of two elements. The first of these values is a
  variable, the second is a function.

  ```jsx
  (1) const [count, setCount] = useState(0);
  (2) const handleClick = () => { setCount(count + 1) };

  (3) <span>{count}</span>
  (4) <button onClick={handleClick)>Add one</button>
  ```

  - At line 1 we initialise the `useState` hook.
    - We use destructuring syntax to declare two variables
    - The first variable stores our initial state. The value of the initial state
      is an argument passed to `useState` , i.e. `0`. Do not confuse the variable
      with the value it refers to.
    - The second variable is a method that mutates the state referred to by the
      first variable into some new value, i.e. from `0` to a different integer
  - At line 3, which in reality, will be within your JSX and part of the
    `render()` block of the function, we render the initial state in the React
    DOM. This will change and be re-rendered in accordance with the state logic
  - At line 4 we have a user interface that executes the function on line 2. This
    function applies the state change represented by the second variable in the
    destructuring assignment at line 1 (`setCount` ): it mutates the initial state
    (`0`) by the value of `1'.

  > You can utilise as many `useState` declarations as your component requires.

  ## Passing variables as initial state

  We don't always have to declare the data types within the destructuring
  assignment. We can store them in variables and pass the variables to the
  destructuring assignment on `useState` . Below we do this for a function that
  mutates a string value:

  ```jsx
  const hi = "hello";
  const bye = "goodbye";

  const [initText, changeText] = useState(hi);

  <p>{initText}</p>
  <button onClick={()=>changeText(bye)}>ChangeText</button>
  ```

  ## Lazily setting initial state

  Although you will typically use a literal value or variable as your initial
  state, React provides a way for you to set this value through a function. This
  will only run once at the first render so as not to interfere with the second
  update parameter of `useState`. This is called **lazy initialisation**

  ```jsx
  const getInitialState = () => Number(window.localStorage.getItem("count"));
  const [count, setCount] = React.useState(getInitialState);
  ```

  ## Accessing previous state

  It is also possible to access the previous state via the `useState` hook. We can
  access this as a callback on the updater function.

  Let's say we have a simple counter set up like so:

  ```js
  const [count, setCount] = useState(0);
  ```

  We can access the prior value of `count` with:

  ```js
  const getPrevValue = () => setCount((prev) => console.log(prev));
  ```

  ### Naming best practise

  When destructuring the two variables from `useState`, React follows uses the
  following semantics: `[something, setSomething]` . This isn't required but it
  helps to keep everything clear, particularly the fact that the first thing
  destructured is a variable and the second is a function.

  > https://stackoverflow.com/questions/54807454/what-is-prevstate-in-reactjs

raw_content:
  ---
  tags:
    - javascript
    - react
  ---

  # `useState`

  `useState` is the primary way in which React updates the application state based
  on user interaction.

  - `useState` **receives**: the initial state as an argument
  - `useState` **returns**: the current state and a function that updates it

  The two items that are returned are returned as an array. We could refer to them
  with array syntax (i.e with `[0]` and `[1]` ) but it is more concise to use the
  destructuring syntax. But all that's happening is that a method belonging to the
  `React` library (i.e. `useState` ) is taking an argument (the initial state) and
  returning two values as an array of two elements. The first of these values is a
  variable, the second is a function.

  ```jsx
  (1) const [count, setCount] = useState(0);
  (2) const handleClick = () => { setCount(count + 1) };

  (3) <span>{count}</span>
  (4) <button onClick={handleClick)>Add one</button>
  ```

  - At line 1 we initialise the `useState` hook.
    - We use destructuring syntax to declare two variables
    - The first variable stores our initial state. The value of the initial state
      is an argument passed to `useState` , i.e. `0`. Do not confuse the variable
      with the value it refers to.
    - The second variable is a method that mutates the state referred to by the
      first variable into some new value, i.e. from `0` to a different integer
  - At line 3, which in reality, will be within your JSX and part of the
    `render()` block of the function, we render the initial state in the React
    DOM. This will change and be re-rendered in accordance with the state logic
  - At line 4 we have a user interface that executes the function on line 2. This
    function applies the state change represented by the second variable in the
    destructuring assignment at line 1 (`setCount` ): it mutates the initial state
    (`0`) by the value of `1'.

  > You can utilise as many `useState` declarations as your component requires.

  ## Passing variables as initial state

  We don't always have to declare the data types within the destructuring
  assignment. We can store them in variables and pass the variables to the
  destructuring assignment on `useState` . Below we do this for a function that
  mutates a string value:

  ```jsx
  const hi = "hello";
  const bye = "goodbye";

  const [initText, changeText] = useState(hi);

  <p>{initText}</p>
  <button onClick={()=>changeText(bye)}>ChangeText</button>
  ```

  ## Lazily setting initial state

  Although you will typically use a literal value or variable as your initial
  state, React provides a way for you to set this value through a function. This
  will only run once at the first render so as not to interfere with the second
  update parameter of `useState`. This is called **lazy initialisation**

  ```jsx
  const getInitialState = () => Number(window.localStorage.getItem("count"));
  const [count, setCount] = React.useState(getInitialState);
  ```

  ## Accessing previous state

  It is also possible to access the previous state via the `useState` hook. We can
  access this as a callback on the updater function.

  Let's say we have a simple counter set up like so:

  ```js
  const [count, setCount] = useState(0);
  ```

  We can access the prior value of `count` with:

  ```js
  const getPrevValue = () => setCount((prev) => console.log(prev));
  ```

  ### Naming best practise

  When destructuring the two variables from `useState`, React follows uses the
  following semantics: `[something, setSomething]` . This isn't required but it
  helps to keep everything clear, particularly the fact that the first thing
  destructured is a variable and the second is a function.

  > https://stackoverflow.com/questions/54807454/what-is-prevstate-in-reactjs


path: Read_command__in_Bash.md
sortable_path: Read_command__in_Bash.md
title: read
checksum: c9fb9f3512d6c4406ed8ab423f11a31c5c9e23de462b89c7dd18af357cccc1d0
created: 2024-10-22 16:52:58.473233251+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["shell"]}
lead:
  The primary use of `read` is to capture user input from `stdin`. It can also be
  used to parse strings or files that are redirected to it (with `<` and `<<`) or
  piped to it. In each case, what is read is stored as a variable.

body:
  The primary use of `read` is to capture user input from `stdin`. It can also be
  used to parse strings or files that are redirected to it (with `<` and `<<`) or
  piped to it. In each case, what is read is stored as a variable.

  `read` will parse line by line using a space (`\n`) as the default delimiter.
  You can use IFS to parse by other characters and/or
  [split the contents into an array](Split_into_array_in_Bash.md).

  ## Example of capturing user input

  When we use `read` to gather user input, the script will pause until the input
  is provided.

  ```bash
  echo "What is your name?"
  read name

  echo "What is your password?"
  read -s pass

  read

  ```

  > If you don't specify variables, `read` will automatically parse using
  > whitespace

  ## Example of piping to read

  Here we use [find](Find_Bash_command.md) to collate the files in
  the current directory and then pipe them to read.

  ```bash
  find -type -f  -not -path "./.git/" | read $fname
  ```

  ## Example of parsing a file

  We will typically read from a source and then do something with each variable
  that `read` returns, e.g:

  ```bash
  while read line; do
    if [ var == 'something' ]; then
      # do something
  done < './input-file.txt
  ```

  ## $REPLY

  If you do not assign a variable name to store the value that `read` reads a
  default (`$REPLY`) is applied. You can reference this value in your code.

  For example the following loop does something if `$REPLY` is equal to an empty
  string:

  ```bash
  while read;
  do
      ((count++))
      if [[ -z "$REPLY" ]]; then
          echo "$count"
      fi
  done < "$input
  ```

raw_content:
  ---
  tags:
    - shell
  ---

  # read

  The primary use of `read` is to capture user input from `stdin`. It can also be
  used to parse strings or files that are redirected to it (with `<` and `<<`) or
  piped to it. In each case, what is read is stored as a variable.

  `read` will parse line by line using a space (`\n`) as the default delimiter.
  You can use IFS to parse by other characters and/or
  [split the contents into an array](Split_into_array_in_Bash.md).

  ## Example of capturing user input

  When we use `read` to gather user input, the script will pause until the input
  is provided.

  ```bash
  echo "What is your name?"
  read name

  echo "What is your password?"
  read -s pass

  read

  ```

  > If you don't specify variables, `read` will automatically parse using
  > whitespace

  ## Example of piping to read

  Here we use [find](Find_Bash_command.md) to collate the files in
  the current directory and then pipe them to read.

  ```bash
  find -type -f  -not -path "./.git/" | read $fname
  ```

  ## Example of parsing a file

  We will typically read from a source and then do something with each variable
  that `read` returns, e.g:

  ```bash
  while read line; do
    if [ var == 'something' ]; then
      # do something
  done < './input-file.txt
  ```

  ## $REPLY

  If you do not assign a variable name to store the value that `read` reads a
  default (`$REPLY`) is applied. You can reference this value in your code.

  For example the following loop does something if `$REPLY` is equal to an empty
  string:

  ```bash
  while read;
  do
      ((count++))
      if [[ -z "$REPLY" ]]; then
          echo "$count"
      fi
  done < "$input
  ```


path: Reciprocals.md
sortable_path: Reciprocals.md
title: Recipricols
checksum: ed560badcaa7ccb1a422db87818532d451e6e49d4b8d43d356089e8f9edf1af6
created: 2024-10-22 16:52:58.489801931+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["prealgebra","fractions","theorems"]}
lead:
  The [Property of Multiplicative Identity](Multiplicative%20identity.md) applies
  to fractions as well as to whole numbers:

body:
  The [Property of Multiplicative Identity](Multiplicative%20identity.md) applies
  to fractions as well as to whole numbers:

  $$
  \frac{a}{b} \cdot 1 = \frac{a}{b}
  $$

  With fractions there is a related property: the **Multiplicative Inverse**.

  > If $\frac{a}{b}$ is any fraction, the fraction $\frac{b}{a}$ is called the
  > _multiplicative inverse_ or _reciprocol_ of $\frac{a}{b}$. The product of a
  > fraction multiplied by its reciprocol will always be 1. $$ \frac{a}{b} \cdot
  > \frac{b}{a} = 1$$

  For example:

  $$
  \frac{3}{4} \cdot \frac{4}{3} = \frac{12}{12} = 1
  $$

  In this case $\frac{4}{3}$ is the reciprocol or multiplicative inverse of
  $\frac{3}{4}$.

  This accords with what we know a fraction to be: a representation of an amount
  that is less than one whole. When we multiply a fraction by its reciprocol, we
  demonstrate that it makes up one whole.

  This also means that whenever we have a whole number $n$, we can represent it
  fractionally by expressing it as $\frac{n}{1}$

raw_content:
  ---
  tags:
    - prealgebra
    - fractions
    - theorems
  ---

  # Recipricols

  The [Property of Multiplicative Identity](Multiplicative%20identity.md) applies
  to fractions as well as to whole numbers:

  $$
  \frac{a}{b} \cdot 1 = \frac{a}{b}
  $$

  With fractions there is a related property: the **Multiplicative Inverse**.

  > If $\frac{a}{b}$ is any fraction, the fraction $\frac{b}{a}$ is called the
  > _multiplicative inverse_ or _reciprocol_ of $\frac{a}{b}$. The product of a
  > fraction multiplied by its reciprocol will always be 1. $$ \frac{a}{b} \cdot
  > \frac{b}{a} = 1$$

  For example:

  $$
  \frac{3}{4} \cdot \frac{4}{3} = \frac{12}{12} = 1
  $$

  In this case $\frac{4}{3}$ is the reciprocol or multiplicative inverse of
  $\frac{3}{4}$.

  This accords with what we know a fraction to be: a representation of an amount
  that is less than one whole. When we multiply a fraction by its reciprocol, we
  demonstrate that it makes up one whole.

  This also means that whenever we have a whole number $n$, we can represent it
  fractionally by expressing it as $\frac{n}{1}$


path: Recursion.md
sortable_path: Recursion.md
title: Schema
checksum: 4a8a39f5c37cf98784efe3d990418f7685f37a2348e7edcdb0b155b05a8e4b80
created: 2024-10-22 16:52:58.504501301+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["algorithms","data-structures","recursion"]}
lead:
  The general structure of a recursive function is as follows:
  ![javascript-recursion.png](javascript-recursion.png)

body:
  The general structure of a recursive function is as follows:
  ![javascript-recursion.png](javascript-recursion.png)

  ## Why use recursive functions?

  Recursion is made for solving problems that can be broken down into smaller,
  repetitive problems. It is especially good for working on things that have many
  possible branches but are too complex for an iterative approach or too costly in
  terms of memory and time complexity.

  Recursive programming differs from **iterative** programming but both have
  similar use cases. Looping is a canonical example of working iteratively.

  ## Base condition

  > Once a condition is met, the function stops calling itself. This is called a
  > base condition.

  Because recursion has the potential for infinity, to use it, we must specify a
  point at which it ends. However as we are not using an iterative approach we
  cannot rely on `while` or `foreach` to specify the boundaries of its operation.
  We call this the **base condition** and this will typically be specified using
  conditional logic.

  The schema for a recursive function with a base condition is:

  ```jsx
  function recurse() {
    if (condition) {
      recurse();
    } else {
      // stop calling recurse()
    }
  }

  recurse();
  ```

  ## Demonstrations

  ### Countdown

  ```jsx
  // program to count down numbers to 1
  function countDown(number) {
    // display the number
    console.log(number);

    // decrease the number value
    let newNumber = number - 1;

    // base case
    if (newNumber > 0) {
      countDown(newNumber);
    }
  }

  countDown(4);
  ```

  - This code takes `4` as it's input and then outputs a countdown returning:
    `4, 3, 2, 1`
  - In each iteration, the number value is decreased by 1
  - The base condition is `newNumber > 0` . This breaks the recursive loop once
    the output reaches `1` and stops it continuing into negative integers.

  Each stage in the process is noted below:

  ```
  countDown(4) prints 4 and calls countDown(3)
  countDown(3) prints 3 and calls countDown(2)
  countDown(2) prints 2 and calls countDown(1)
  countDown(1) prints 1 and calls countDown(0)
  ```

  ### Finding factorials

  > The factorial of a positive integer **n** is the product of all the positive
  > integers less than or equal to **n**.

  To arrive at the factorial of **n** you subtract 1 from **n** and multiply the
  result of the subtraction by **n**. You repeat until the subtractive process
  runs out of positive integers. For example, if 4 is **n,** the factorial of
  **n** is **24:**

  $$ 4 _ 3 _ 2 \*1 $$

  4 multiplied by 3 gives you 12, 12 multiplied by 2 gives you 24. 24 multiplied
  by 1 is 24.

  This is clearly a process that could be implemented with a recursive function:

  ```js
  // program to find the factorial of a number
  function factorial(x) {
    // if number is 0
    if (x === 0) {
      return 1;
    }

    // if number is positive
    else {
      return x * factorial(x - 1);
    }
  }

  let num = 3;

  // calling factorial() if num is non-negative
  if (num > 0) {
    let result = factorial(num);
    console.log(`The factorial of ${num} is ${result}`);
  }
  ```

  ![javascript-factorial 1.png](javascript-factorial%201.png)

raw_content:
  ---
  tags:
    - algorithms
    - data-structures
    - recursion
  ---

  > A recursive function is a function that calls itself in its definition.

  More generally recursion means when a thing is defined in terms of itself. There
  are visual analogues that help to represent the idea such as the Droste effect
  where an image contains a version of itself within itself. The ouroboros is
  another example. Also fractals display recursive properties.

  ## Schema

  The general structure of a recursive function is as follows:
  ![javascript-recursion.png](javascript-recursion.png)

  ## Why use recursive functions?

  Recursion is made for solving problems that can be broken down into smaller,
  repetitive problems. It is especially good for working on things that have many
  possible branches but are too complex for an iterative approach or too costly in
  terms of memory and time complexity.

  Recursive programming differs from **iterative** programming but both have
  similar use cases. Looping is a canonical example of working iteratively.

  ## Base condition

  > Once a condition is met, the function stops calling itself. This is called a
  > base condition.

  Because recursion has the potential for infinity, to use it, we must specify a
  point at which it ends. However as we are not using an iterative approach we
  cannot rely on `while` or `foreach` to specify the boundaries of its operation.
  We call this the **base condition** and this will typically be specified using
  conditional logic.

  The schema for a recursive function with a base condition is:

  ```jsx
  function recurse() {
    if (condition) {
      recurse();
    } else {
      // stop calling recurse()
    }
  }

  recurse();
  ```

  ## Demonstrations

  ### Countdown

  ```jsx
  // program to count down numbers to 1
  function countDown(number) {
    // display the number
    console.log(number);

    // decrease the number value
    let newNumber = number - 1;

    // base case
    if (newNumber > 0) {
      countDown(newNumber);
    }
  }

  countDown(4);
  ```

  - This code takes `4` as it's input and then outputs a countdown returning:
    `4, 3, 2, 1`
  - In each iteration, the number value is decreased by 1
  - The base condition is `newNumber > 0` . This breaks the recursive loop once
    the output reaches `1` and stops it continuing into negative integers.

  Each stage in the process is noted below:

  ```
  countDown(4) prints 4 and calls countDown(3)
  countDown(3) prints 3 and calls countDown(2)
  countDown(2) prints 2 and calls countDown(1)
  countDown(1) prints 1 and calls countDown(0)
  ```

  ### Finding factorials

  > The factorial of a positive integer **n** is the product of all the positive
  > integers less than or equal to **n**.

  To arrive at the factorial of **n** you subtract 1 from **n** and multiply the
  result of the subtraction by **n**. You repeat until the subtractive process
  runs out of positive integers. For example, if 4 is **n,** the factorial of
  **n** is **24:**

  $$ 4 _ 3 _ 2 \*1 $$

  4 multiplied by 3 gives you 12, 12 multiplied by 2 gives you 24. 24 multiplied
  by 1 is 24.

  This is clearly a process that could be implemented with a recursive function:

  ```js
  // program to find the factorial of a number
  function factorial(x) {
    // if number is 0
    if (x === 0) {
      return 1;
    }

    // if number is positive
    else {
      return x * factorial(x - 1);
    }
  }

  let num = 3;

  // calling factorial() if num is non-negative
  if (num > 0) {
    let result = factorial(num);
    console.log(`The factorial of ${num} is ${result}`);
  }
  ```

  ![javascript-factorial 1.png](javascript-factorial%201.png)


path: Redirect_to_dev_null.md
sortable_path: Redirect_to_dev_null.md
title: Redirecting to /dev/null
lead: You'll see the following a lot when reading shell scripts:
checksum: 1864a7e2ce0dd078e01afeec825f780ffbceb38790e093fb936a971e522965c6
created: 2024-10-22 16:52:58.510652658+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["shell"]}
body:
  You'll see the following a lot when reading shell scripts:

  ```bash
  [some_command] > /dev/null 2>&1
  ```

  This is a redirection statement. It is redirecting data to the `null` device on
  Unix systems. Basically to a black hole or shredder where you can't access it
  because you don't want it to be output to `stdout`.

  The `2>&1` argument is the content: any errors that the program may generate and
  try to show in stout. Notice we are using the
  [file descriptors](File_descriptors_and_redirection.md)
  `1` and `2`.

  If you just want the errors regardless of whether they appear in `stdout` you
  do:

  ```bash
  [some_command] > /dev/null 2>
  ```

  ## Example

  I have used this in my Mongo start-up script:

  ```
  mongodb-compass > /dev/null 2>&1
  ```

  Here, I just want the MongoDB Compass application to start, I don't care if it
  wants to output complaints to the stout.

raw_content:
  ---
  tags:
    - shell
  ---

  # Redirecting to `/dev/null`

  You'll see the following a lot when reading shell scripts:

  ```bash
  [some_command] > /dev/null 2>&1
  ```

  This is a redirection statement. It is redirecting data to the `null` device on
  Unix systems. Basically to a black hole or shredder where you can't access it
  because you don't want it to be output to `stdout`.

  The `2>&1` argument is the content: any errors that the program may generate and
  try to show in stout. Notice we are using the
  [file descriptors](File_descriptors_and_redirection.md)
  `1` and `2`.

  If you just want the errors regardless of whether they appear in `stdout` you
  do:

  ```bash
  [some_command] > /dev/null 2>
  ```

  ## Example

  I have used this in my Mongo start-up script:

  ```
  mongodb-compass > /dev/null 2>&1
  ```

  Here, I just want the MongoDB Compass application to start, I don't care if it
  wants to output complaints to the stout.


path: Redirection_in_Bash.md
sortable_path: Redirection_in_Bash.md
title: Redirection
lead: ## Redirecting outputs
checksum: e64c3fd247bb692dbe02ecb5f5d645298857b8d8675be4a8a9e3532475b2fc00
created: 2024-10-22 16:52:58.526275098+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["shell"]}
body:
  ## Redirecting outputs

  The symbol `>` is called the **redirection operator** because it redirects
  `stdout` to another program or file. You most frequently use this when you want
  to save contents to a file rather than standard output.

  ```bash
  ls | grep d* > result.txt
  ```

  ### Combining redirection with file escriptors

  It is common practice to combine redirection with the
  [file descriptors](File_descriptors.md) to redirect
  the output of `stdout` and `stderr`. A common case is to
  [redirect error output to `/dev/null`](Redirect_to_dev_null.md).

  Redirection defaults to interpreting `>` as the redirection of `stdout` (`1`);

  ## Redirecting inputs

  We can also switch the direction of the redirection symbol and pass in a file to
  a command rather than command ouput to a file:

  ```bash
  sql-processing-program < data.sql
  ```

  We can redirect a string with three carets:

  ```bash
  program <<< "this is a string input"
  ```

  ## Appending

  We use `>>` to append contents on the next available line of a pre-existing
  file. Continuing on from the example above:

  ```bash
  echo 'These are the files I just grepped' >> result.txt
  ```

raw_content:
  ---
  tags:
    - shell
  ---

  # Redirection

  ## Redirecting outputs

  The symbol `>` is called the **redirection operator** because it redirects
  `stdout` to another program or file. You most frequently use this when you want
  to save contents to a file rather than standard output.

  ```bash
  ls | grep d* > result.txt
  ```

  ### Combining redirection with file escriptors

  It is common practice to combine redirection with the
  [file descriptors](File_descriptors.md) to redirect
  the output of `stdout` and `stderr`. A common case is to
  [redirect error output to `/dev/null`](Redirect_to_dev_null.md).

  Redirection defaults to interpreting `>` as the redirection of `stdout` (`1`);

  ## Redirecting inputs

  We can also switch the direction of the redirection symbol and pass in a file to
  a command rather than command ouput to a file:

  ```bash
  sql-processing-program < data.sql
  ```

  We can redirect a string with three carets:

  ```bash
  program <<< "this is a string input"
  ```

  ## Appending

  We use `>>` to append contents on the next available line of a pre-existing
  file. Continuing on from the example above:

  ```bash
  echo 'These are the files I just grepped' >> result.txt
  ```


path: Reducing_fractions.md
sortable_path: Reducing_fractions.md
title: Reducing fractions to their lowest terms
checksum: 68cf5bc8ce92610196555183479c98c61eca2b63e644783aada411a0031c2667
created: 2024-10-22 16:52:58.539505327+00:00
modified: 2024-10-18 18:20:03.709922699+00:00
metadata: {"tags":["prealgebra","fractions"]}
lead:
  > A fraction is said to be _reduced to its lowest terms_ if the
  > [greatest common divisor](Factors%20and%20divisors.md#greatest-common-divisor)
  > of the numerator and the denominator is $1$.

body:
  > A fraction is said to be _reduced to its lowest terms_ if the
  > [greatest common divisor](Factors%20and%20divisors.md#greatest-common-divisor)
  > of the numerator and the denominator is $1$.

  > Whenever we reduce a fraction, the resultant fraction will always be
  > [equivalent](Equivalent%20fractions.md) to the fraction we started with.

  Thus the fraction $\frac{2}{3}$ is reduced to its lowest terms because the
  greatest common divisor is 1. Neither the numerator or the denominator can be
  reduced to any lower terms. In contrast, the fraction $\frac{4}{6}$ is not
  reduced to its lowest terms because the greatest common divisor of both 4 and 6
  is 2, not 1.

  ### 1. Reducing with repeated application of divisors

  The following demonstrates the process of reducing a fraction to its lowest
  terms in a series of steps:

  $$
  \\frac{18}{24} = \frac{18/2}{24/2} = \frac{9}{12} = \frac{9/3}{12/3} = \frac{3}{4}
  $$

  \_Once we get to $\frac{3}{4}$ the greatest common divisor is 1, therefore
  $\frac{18}{24}$ has been reduced to its lowest terms \_.

  ### 2. Reducing in one step with the highest common divisor

  In the previous example the reduction took two steps: first we divided by two
  and then we divided by three. There is a more efficient way: find the
  [highest common divisor](Factors%20and%20divisors.md#greatest-common-divisor) of
  the numerator and denominator and then use this as the basis for the reduction.
  With this method, the reduction can be completed in a single step.

  The greatest common divisor of 18 and 24 is 6, thus:

  $$
  \frac{18}{24} = \frac{18/6}{24/6} = \frac{3}{4}
  $$

  Note how our earlier two divisors 2 and 3 are
  [factors](Factors%20and%20divisors.md#factors) of 6, showing the consistency
  between the two methods.

  ### 3. Reducing with factors and cancellation

  The two methods above are not very systematic and are quite heuristic. The third
  approach is more systematic and relies on the
  [interchangeability of factors and divisors](Factors%20and%20divisors.md).

  Instead of thinking asking what is the greatest common divisor of 18 and 24 we
  could ask: which single number can we multiply by to get 18 and 24? Obviously
  both numbers are in the six times table. This is therefore to say that 6 is a
  [factor](Factors%20and%20divisors.md#factors) of both: we can multiply some
  number by 6 to arrive at both 18 and 24. The numbers are 3 and 4 respectively:

  $$
  \\begin{split}
  3 \cdot 6 = 18 \\
  4 \cdot 6  = 24
  \\end{split}
  $$

  Here, 3 and 4 are the multiplicands of the factor 6. As $\frac{3}{4}$ doesn't
  have a lower common factor, it is therefore defined in its lowest terms.

  Once we have reached this point, we no longer need the common factor 6, we can
  therefore cancel it out, leaving the multiplicands as the reduced fraction:

  $$
  \\begin{split}
  3  \cancel{\cdot6= 18}\\
  4  \cancel{\cdot6= 24}
  \\end{split}
  $$

  ### 4. Reducing with prime factorisation

  This is still a bit long-winded however particularly when finding the factors of
  larger numbers because we have to go through the factors of both numbers to find
  the largest held in common.

  A better method is to utilise [prime factorization](Prime%20factorization.md)
  combined with the canceling technique.

  First we find the prime factors of both the numerator and denominator:
  ![drawio-Page-7.drawio.png](drawio-Page-7.drawio.png)

  This gives us:

  $$
  \\frac{18}{24} = \frac{2 \cdot 3 \cdot 3}{2 \cdot 2 \cdot 2 \cdot 3}
  $$

  We then cancel out the factors held in common between the numerator and
  denominator:

  $$
  \\frac{\cancel{2} \cdot \cancel{3} \cdot 3}{\cancel{2} \cdot 2 \cdot 2 \cdot \cancel{3}}
  $$

  This gives us:

  $$
  \\frac{3}{2 \cdot 2}
  $$

  We then simplify the fraction as normal to its lowest term (conducting any
  multiplications required by what is left from the prime factorization):

  $$
  \\frac{3}{4}
  $$

  ## Reducing fractions that contain variables

  Superficially this looks to be more difficult but in fact we can apply the same
  prime factorization method to get the result.

  ### Demonstration

  _Reduce the following fraction to its lowest terms: $$\frac{25a^3b}{40a^2b^3}$$_

  The prime factors of the numerator and denominator:

  $$
  \\begin{split}
  25 = {5, 5} \\
  40 = {2,2,2,5}
  \\end{split}
  $$

  Now we apply canceling but we include the variable parts, treating them exactly
  the same as the coefficients. We break them out of their exponents however.

  $$\frac{25a^3b}{40a^2b^3} =\frac{5 \cdot 5 \cdot a \cdot a \cdot a \cdot b}{2 \cdot 2 \cdot 2 \cdot 5 \cdot a \cdot a \cdot b \cdot b \cdot b }$$

  Canceled:

  $$\frac{\cancel{5} \cdot 5 \cdot  \cancel{a} \cdot \cancel{a} \cdot a \cdot \cancel{b}}{2 \cdot 2 \cdot 2 \cdot \cancel{5} \cdot \cancel{a} \cdot \cancel{a} \cdot \cancel{b} \cdot b \cdot b }$$

  Which gives us:

  $$
  \\frac{5 \cdot a}{2 \cdot 2 \cdot 2 \cdot b \cdot b} = \frac{5a}{8b^2}
  $$

  ## Reducing fractions that contain negative values

  _Reduce the following fraction to its lowest terms: $$\frac{14y^5}{-35y^3}$$_

  - This fraction is an instance of a
    [fraction with unlike terms](Handling%20negative%20fractions.md#fractions-with-unlike-terms).

  - Apply [Prime factorization](Prime%20factorization.md):

    ![draw.io-Page-8.drawio.png](draw.io-Page-8.drawio.png)

  - Cancel the coefficients and variable parts

    $$
    \\frac{14y^5}{-35y^3}=\frac{5 \cdot 7 \cdot 2 \cdot y \cdot y \cdot y \cdot y \cdot y}{-5 \cdot 7  \cdot y \cdot y \cdot y} = - \frac{2y^2}{5}
    $$

  _Reduce the following fraction to its lowest terms:
  $$\frac{- 12xy^2}{ - 18xy^2}$$_

  - This fraction is an instance of a
    [fraction with like terms](Handling%20negative%20fractions.md#fractions-with-like-terms).

  - Apply [Prime factorization](Prime%20factorization.md):

  ![draw.io-Page-8.drawio 1.png](draw.io-Page-8.drawio%201.png)

  - Cancel the coefficients and variable parts

    $$

    * \\frac{12xy^2}{18xy^2}=\frac{3 \cdot 2 \cdot 2 \cdot x \cdot y \cdot y}{3 \cdot 7  \cdot 3 \cdot 2 \cdot x \cdot x \cdot y} = - \frac{2y}{3x}
    $$

raw_content:
  ---
  tags:
    - prealgebra
    - fractions
  ---

  # Reducing fractions to their lowest terms

  > A fraction is said to be _reduced to its lowest terms_ if the
  > [greatest common divisor](Factors%20and%20divisors.md#greatest-common-divisor)
  > of the numerator and the denominator is $1$.

  > Whenever we reduce a fraction, the resultant fraction will always be
  > [equivalent](Equivalent%20fractions.md) to the fraction we started with.

  Thus the fraction $\frac{2}{3}$ is reduced to its lowest terms because the
  greatest common divisor is 1. Neither the numerator or the denominator can be
  reduced to any lower terms. In contrast, the fraction $\frac{4}{6}$ is not
  reduced to its lowest terms because the greatest common divisor of both 4 and 6
  is 2, not 1.

  ### 1. Reducing with repeated application of divisors

  The following demonstrates the process of reducing a fraction to its lowest
  terms in a series of steps:

  $$
  \\frac{18}{24} = \frac{18/2}{24/2} = \frac{9}{12} = \frac{9/3}{12/3} = \frac{3}{4}
  $$

  \_Once we get to $\frac{3}{4}$ the greatest common divisor is 1, therefore
  $\frac{18}{24}$ has been reduced to its lowest terms \_.

  ### 2. Reducing in one step with the highest common divisor

  In the previous example the reduction took two steps: first we divided by two
  and then we divided by three. There is a more efficient way: find the
  [highest common divisor](Factors%20and%20divisors.md#greatest-common-divisor) of
  the numerator and denominator and then use this as the basis for the reduction.
  With this method, the reduction can be completed in a single step.

  The greatest common divisor of 18 and 24 is 6, thus:

  $$
  \frac{18}{24} = \frac{18/6}{24/6} = \frac{3}{4}
  $$

  Note how our earlier two divisors 2 and 3 are
  [factors](Factors%20and%20divisors.md#factors) of 6, showing the consistency
  between the two methods.

  ### 3. Reducing with factors and cancellation

  The two methods above are not very systematic and are quite heuristic. The third
  approach is more systematic and relies on the
  [interchangeability of factors and divisors](Factors%20and%20divisors.md).

  Instead of thinking asking what is the greatest common divisor of 18 and 24 we
  could ask: which single number can we multiply by to get 18 and 24? Obviously
  both numbers are in the six times table. This is therefore to say that 6 is a
  [factor](Factors%20and%20divisors.md#factors) of both: we can multiply some
  number by 6 to arrive at both 18 and 24. The numbers are 3 and 4 respectively:

  $$
  \\begin{split}
  3 \cdot 6 = 18 \\
  4 \cdot 6  = 24
  \\end{split}
  $$

  Here, 3 and 4 are the multiplicands of the factor 6. As $\frac{3}{4}$ doesn't
  have a lower common factor, it is therefore defined in its lowest terms.

  Once we have reached this point, we no longer need the common factor 6, we can
  therefore cancel it out, leaving the multiplicands as the reduced fraction:

  $$
  \\begin{split}
  3  \cancel{\cdot6= 18}\\
  4  \cancel{\cdot6= 24}
  \\end{split}
  $$

  ### 4. Reducing with prime factorisation

  This is still a bit long-winded however particularly when finding the factors of
  larger numbers because we have to go through the factors of both numbers to find
  the largest held in common.

  A better method is to utilise [prime factorization](Prime%20factorization.md)
  combined with the canceling technique.

  First we find the prime factors of both the numerator and denominator:
  ![drawio-Page-7.drawio.png](drawio-Page-7.drawio.png)

  This gives us:

  $$
  \\frac{18}{24} = \frac{2 \cdot 3 \cdot 3}{2 \cdot 2 \cdot 2 \cdot 3}
  $$

  We then cancel out the factors held in common between the numerator and
  denominator:

  $$
  \\frac{\cancel{2} \cdot \cancel{3} \cdot 3}{\cancel{2} \cdot 2 \cdot 2 \cdot \cancel{3}}
  $$

  This gives us:

  $$
  \\frac{3}{2 \cdot 2}
  $$

  We then simplify the fraction as normal to its lowest term (conducting any
  multiplications required by what is left from the prime factorization):

  $$
  \\frac{3}{4}
  $$

  ## Reducing fractions that contain variables

  Superficially this looks to be more difficult but in fact we can apply the same
  prime factorization method to get the result.

  ### Demonstration

  _Reduce the following fraction to its lowest terms: $$\frac{25a^3b}{40a^2b^3}$$_

  The prime factors of the numerator and denominator:

  $$
  \\begin{split}
  25 = {5, 5} \\
  40 = {2,2,2,5}
  \\end{split}
  $$

  Now we apply canceling but we include the variable parts, treating them exactly
  the same as the coefficients. We break them out of their exponents however.

  $$\frac{25a^3b}{40a^2b^3} =\frac{5 \cdot 5 \cdot a \cdot a \cdot a \cdot b}{2 \cdot 2 \cdot 2 \cdot 5 \cdot a \cdot a \cdot b \cdot b \cdot b }$$

  Canceled:

  $$\frac{\cancel{5} \cdot 5 \cdot  \cancel{a} \cdot \cancel{a} \cdot a \cdot \cancel{b}}{2 \cdot 2 \cdot 2 \cdot \cancel{5} \cdot \cancel{a} \cdot \cancel{a} \cdot \cancel{b} \cdot b \cdot b }$$

  Which gives us:

  $$
  \\frac{5 \cdot a}{2 \cdot 2 \cdot 2 \cdot b \cdot b} = \frac{5a}{8b^2}
  $$

  ## Reducing fractions that contain negative values

  _Reduce the following fraction to its lowest terms: $$\frac{14y^5}{-35y^3}$$_

  - This fraction is an instance of a
    [fraction with unlike terms](Handling%20negative%20fractions.md#fractions-with-unlike-terms).

  - Apply [Prime factorization](Prime%20factorization.md):

    ![draw.io-Page-8.drawio.png](draw.io-Page-8.drawio.png)

  - Cancel the coefficients and variable parts

    $$
    \\frac{14y^5}{-35y^3}=\frac{5 \cdot 7 \cdot 2 \cdot y \cdot y \cdot y \cdot y \cdot y}{-5 \cdot 7  \cdot y \cdot y \cdot y} = - \frac{2y^2}{5}
    $$

  _Reduce the following fraction to its lowest terms:
  $$\frac{- 12xy^2}{ - 18xy^2}$$_

  - This fraction is an instance of a
    [fraction with like terms](Handling%20negative%20fractions.md#fractions-with-like-terms).

  - Apply [Prime factorization](Prime%20factorization.md):

  ![draw.io-Page-8.drawio 1.png](draw.io-Page-8.drawio%201.png)

  - Cancel the coefficients and variable parts

    $$

    * \\frac{12xy^2}{18xy^2}=\frac{3 \cdot 2 \cdot 2 \cdot x \cdot y \cdot y}{3 \cdot 7  \cdot 3 \cdot 2 \cdot x \cdot x \cdot y} = - \frac{2y}{3x}
    $$


path: Register_and_cache_memory.md
sortable_path: Register_and_cache_memory.md
title: Register and cache memory
lead: The CPU has its own memory devices in the form of registers and cache memory.
checksum: 51b2d2998fb892d96af6bbd7f6f496ba943b2aac343058bda58d60208e691687
created: 2024-10-22 16:52:58.62864472+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"created":"Friday, July 12, 2024","tags":["memory","CPU"]}
body:
  The CPU has its own memory devices in the form of registers and cache memory.

  [Registers](./CPU_architecture.md#Registers) are a form of memory that are
  positioned on the same chip as the CPU. They are very fast but can only store a
  small amount of data. They are used to store the results of calculations and the
  addresses of the next instructions to be processed.

  The cache is SRAM memory that is separate from the DRAM memory which comprises
  the main memory. It exists in order to boost perfomance when executing the
  read/request cycles of the steps detailed above.

  There are two types of cache memory:

  - L1 cache
    - Situated on the CPU chip itself
    - Fastest to access but stores less
  - L2 cache
    - Situated outside of the CPU on its own chip
    - Slower to access than L1 but can store more data

  The L1 cache is the fastest since the data has less distance to travel when
  moving to and from the CPU. This said, the L2 cache is still very fast when
  compared to the main memory, both because it is SRAM rather than DRAM and
  because it is closer to the processor than the main memory.

  Cache controllers use complex algorithms to determine what should go into the
  cache to facilitate the best performance, but generally they work on the
  principle that what has been previously used by the CPU will be requested again
  soon. If the CPU has just asked for an instruction at memory location 555 it's
  very likely that it will next ask for the one at 556, and after that the one at
  557 and so on. The cache's controller circuits therefore go ahead and fetch
  these from slow [DRAM to fast SRAM](./DRAM_and_SRAM_memory.md).

raw_content:
  ---
  tags: [memory, CPU]
  created: Friday, July 12, 2024
  ---

  # Register and cache memory

  The CPU has its own memory devices in the form of registers and cache memory.

  [Registers](./CPU_architecture.md#Registers) are a form of memory that are
  positioned on the same chip as the CPU. They are very fast but can only store a
  small amount of data. They are used to store the results of calculations and the
  addresses of the next instructions to be processed.

  The cache is SRAM memory that is separate from the DRAM memory which comprises
  the main memory. It exists in order to boost perfomance when executing the
  read/request cycles of the steps detailed above.

  There are two types of cache memory:

  - L1 cache
    - Situated on the CPU chip itself
    - Fastest to access but stores less
  - L2 cache
    - Situated outside of the CPU on its own chip
    - Slower to access than L1 but can store more data

  The L1 cache is the fastest since the data has less distance to travel when
  moving to and from the CPU. This said, the L2 cache is still very fast when
  compared to the main memory, both because it is SRAM rather than DRAM and
  because it is closer to the processor than the main memory.

  Cache controllers use complex algorithms to determine what should go into the
  cache to facilitate the best performance, but generally they work on the
  principle that what has been previously used by the CPU will be requested again
  soon. If the CPU has just asked for an instruction at memory location 555 it's
  very likely that it will next ask for the one at 556, and after that the one at
  557 and so on. The cache's controller circuits therefore go ahead and fetch
  these from slow [DRAM to fast SRAM](./DRAM_and_SRAM_memory.md).


path: Regular_expressions_in_SQL.md
sortable_path: Regular_expressions_in_SQL.md
title: Regular expressions in SQL
lead:
  ```sql
  SELECT name FROM country WHERE name RLIKE '[xy][ai]' ORDER BY name;

checksum: 469db48f16db9b164763ed11a358fbaae7d72cb583f93941b711411ba3907f51
created: 2024-10-22 16:52:58.641219063+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["SQL","regex","databases"]}
body:
  ```sql
  SELECT name FROM country WHERE name RLIKE '[xy][ai]' ORDER BY name;

  Guyana
  Mexico
  Myanmar
  ```

raw_content:
  ---
  tags:
    - SQL
    - regex
    - databases
  ---

  # Regular expressions in SQL

  ```sql
  SELECT name FROM country WHERE name RLIKE '[xy][ai]' ORDER BY name;

  Guyana
  Mexico
  Myanmar
  ```


path: Reiteration.md
sortable_path: Reiteration.md
title: Reiteration
checksum: c34e71c60a917d4c17d97eb702b766dd9e8abb7c8e7490c01b8dac6e50b052b0
created: 2024-10-22 16:52:58.645753039+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["logic","propositional-logic"]}
lead:
  **Reiteration (R)** allows us to restate any proposition already in the proof
  within the main proof or a more deeply nested sub-proof. Reiteration allows us
  to reuse any assumptions, or propositions derived from assumptions, without
  having to introduce a new dependency with another assumption.

body:
  **Reiteration (R)** allows us to restate any proposition already in the proof
  within the main proof or a more deeply nested sub-proof. Reiteration allows us
  to reuse any assumptions, or propositions derived from assumptions, without
  having to introduce a new dependency with another assumption.

  ![](/img/reiteration.png)

raw_content:
  ---
  tags:
    - logic
    - propositional-logic
  ---

  # Reiteration

  **Reiteration (R)** allows us to restate any proposition already in the proof
  within the main proof or a more deeply nested sub-proof. Reiteration allows us
  to reuse any assumptions, or propositions derived from assumptions, without
  having to introduce a new dependency with another assumption.

  ![](/img/reiteration.png)


path: Relation_between_kernel_and_CPU.md
sortable_path: Relation_between_kernel_and_CPU.md
title: Relation between the kernel and CPU
checksum: 03708b1d4de3d38c26d1e4127f1c2ab1e63041df594afe2bcc8ea04d86533ce2
created: 2024-10-22 16:52:58.651967512+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["CPU","computer-architecture","operating-systems"]}
lead:
  It can be confusing to understand how the kernel and CPU interact with one
  another. Whilst it is true to say the kernel mediates between the hardware and
  user processes, this led me to think that the kernel was somehow 'over and
  above' the actions of the CPU. This is wrong and if you think about it, how
  could this be since any process, kernel included, requires the processor? The
  kernel is executed by and loaded into the CPU just like any other instruction in
  the
  [fetch, decode, execute cycle](Fetch_decode_execute.md)
  of the CPU.

body:
  It can be confusing to understand how the kernel and CPU interact with one
  another. Whilst it is true to say the kernel mediates between the hardware and
  user processes, this led me to think that the kernel was somehow 'over and
  above' the actions of the CPU. This is wrong and if you think about it, how
  could this be since any process, kernel included, requires the processor? The
  kernel is executed by and loaded into the CPU just like any other instruction in
  the
  [fetch, decode, execute cycle](Fetch_decode_execute.md)
  of the CPU.

  However as a process, the kernel is the 'first amongst equals' given that it is
  the core embodiment of the operating system. At boot time, the kernel is
  injected into memory and at this point the CPU _looks to this address_ in memory
  in order to source its first instruction. That's how the kernel gets going. It
  is the first instruction that the CPU fetches and this is what allows the kernel
  to play its mediatory role. However most of the fetch, decode, execute cycles of
  the CPU take place independently of the kernel.

  ![](/img/kernel-cpu-interaction.svg)

  > Fetch decode and execute refer to processor pipeline stages. They occur
  > automatically as part of normal processor operation, the kernel doesn’t
  > generally have any direct control over how that happens...When the boot loader
  > loads the kernel, it points the CPU’s program counter to an entry point in the
  > kernel’s code. From there, the CPU will continue executing (fetching decoding
  > and executing) kernel code until you point the program counter to a userspace
  > program, where it will continue executing userspace code until an interrupt
  > points the program counter back into kernel code. That’s basically what the
  > CPU will be doing from boot up to shut down; switching between executing
  > kernel code and executing userspace code
  > ([Reddit]())

  This is because of [context switching](), when the CPU is running its cycle, the
  kernel is idle waiting for it to complete. Once control switches back to the
  kernel it can then run its next duty which will be either process, memory or
  device management or system calls. Then it will hand control over to either the
  processor or user space.

raw_content:
  ---
  tags:
    - CPU
    - computer-architecture
    - operating-systems
  ---

  # Relation between the kernel and CPU

  It can be confusing to understand how the kernel and CPU interact with one
  another. Whilst it is true to say the kernel mediates between the hardware and
  user processes, this led me to think that the kernel was somehow 'over and
  above' the actions of the CPU. This is wrong and if you think about it, how
  could this be since any process, kernel included, requires the processor? The
  kernel is executed by and loaded into the CPU just like any other instruction in
  the
  [fetch, decode, execute cycle](Fetch_decode_execute.md)
  of the CPU.

  However as a process, the kernel is the 'first amongst equals' given that it is
  the core embodiment of the operating system. At boot time, the kernel is
  injected into memory and at this point the CPU _looks to this address_ in memory
  in order to source its first instruction. That's how the kernel gets going. It
  is the first instruction that the CPU fetches and this is what allows the kernel
  to play its mediatory role. However most of the fetch, decode, execute cycles of
  the CPU take place independently of the kernel.

  ![](/img/kernel-cpu-interaction.svg)

  > Fetch decode and execute refer to processor pipeline stages. They occur
  > automatically as part of normal processor operation, the kernel doesn’t
  > generally have any direct control over how that happens...When the boot loader
  > loads the kernel, it points the CPU’s program counter to an entry point in the
  > kernel’s code. From there, the CPU will continue executing (fetching decoding
  > and executing) kernel code until you point the program counter to a userspace
  > program, where it will continue executing userspace code until an interrupt
  > points the program counter back into kernel code. That’s basically what the
  > CPU will be doing from boot up to shut down; switching between executing
  > kernel code and executing userspace code
  > ([Reddit]())

  This is because of [context switching](), when the CPU is running its cycle, the
  kernel is idle waiting for it to complete. Once control switches back to the
  kernel it can then run its next duty which will be either process, memory or
  device management or system calls. Then it will hand control over to either the
  processor or user space.


path: Relational_database_architecture.md
sortable_path: Relational_database_architecture.md
title: Relational database architecture
lead: Tables, fields and records are the basic building blocks of databases
checksum: e0e93a044653f33e6d84868926d59c0c445a11f54ef5c9b8f5f697fab468c4d1
created: 2024-10-22 16:52:58.65983709+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["databases"]}
body:
  Tables, fields and records are the basic building blocks of databases

  ![](/img/FL-Databases-1.5_terminology.gif)

  ## Table

  A group of similar data with rows for **records** and columns for each
  **field**.

  ## Record

  A horizontal row: a collection of items which may be of different data types all
  relating to the individual or object that the record describes. A single _entry_
  in the table. Sometimes called **row** interchangeably.

  ## Field

  A vertical column: stores a single particular unit of data for each record. Each
  field must use the same data type. Sometimes called **field** interchangeably.

  Each individual field has **properties:** such as the data type, length or the
  total memory allocation.

raw_content:
  ---
  tags:
    - databases
  ---

  # Relational database architecture

  Tables, fields and records are the basic building blocks of databases

  ![](/img/FL-Databases-1.5_terminology.gif)

  ## Table

  A group of similar data with rows for **records** and columns for each
  **field**.

  ## Record

  A horizontal row: a collection of items which may be of different data types all
  relating to the individual or object that the record describes. A single _entry_
  in the table. Sometimes called **row** interchangeably.

  ## Field

  A vertical column: stores a single particular unit of data for each record. Each
  field must use the same data type. Sometimes called **field** interchangeably.

  Each individual field has **properties:** such as the data type, length or the
  total memory allocation.


path: Relays.md
sortable_path: Relays.md
title: Relays
lead: A relay is an electrically operated switch.
checksum: 9ac0d0a04e61d1e64dd26710c70c57a00103a50502c4bc15fb7f696c9336bc8d
created: 2024-10-22 16:52:58.664314201+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"created":"Monday, September 09, 2024","tags":["electronics"]}
body:
  A relay is an electrically operated switch.

  It uses an electromagnet to mechancially operate a switch. A switch in one
  circuit can be used the trigger the operation of another circuit.

  ## Core components

  - electromagnet
    - a coil of wire around an iron core
  - armature
    - a movable magnetic strip
  - contacts
    - electrical connections that can be open or closed
  - spring
    - returns the armature to its resting position when not energised

  ## Operation

  ![Operation of a relay](../img/relay.gif)

  Current flows through the electromagnet which creates a magnetic field. This
  field attracts the armature which then moves to open or close the contacts. When
  the current stops, this returns the armature to its original position.

  ## Usage

  During the
  [electro-mechanical era of computing](The_History_of_Computing_Swade.md) they
  were used as precursors to vacuum tubes (and later, transistors) to implement
  logic gates. While they were superseded in this domain, they are still used in
  industrial control systems and automobiles.

raw_content:
  ---
  tags: [electronics]
  created: Monday, September 09, 2024
  ---

  # Relays

  A relay is an electrically operated switch.

  It uses an electromagnet to mechancially operate a switch. A switch in one
  circuit can be used the trigger the operation of another circuit.

  ## Core components

  - electromagnet
    - a coil of wire around an iron core
  - armature
    - a movable magnetic strip
  - contacts
    - electrical connections that can be open or closed
  - spring
    - returns the armature to its resting position when not energised

  ## Operation

  ![Operation of a relay](../img/relay.gif)

  Current flows through the electromagnet which creates a magnetic field. This
  field attracts the armature which then moves to open or close the contacts. When
  the current stops, this returns the armature to its original position.

  ## Usage

  During the
  [electro-mechanical era of computing](The_History_of_Computing_Swade.md) they
  were used as precursors to vacuum tubes (and later, transistors) to implement
  logic gates. While they were superseded in this domain, they are still used in
  industrial control systems and automobiles.


path: Remote_tracking_branches.md
sortable_path: Remote_tracking_branches.md
title: Remote tracking branches
checksum: 775d9cfcc8eb49533dfa647d6fc6911b93fc101e8c2d79da117d0865f0bb5f3b
created: 2024-10-22 16:52:58.6816692+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["git"]}
lead:
  A remote tracking branch is a **local** snapshot or reference to the state of a
  branch on a remote repository.

body:
  A remote tracking branch is a **local** snapshot or reference to the state of a
  branch on a remote repository.

  When you clone a remote repository, Git automatically creates remote tracking
  branches for all the branches on the remote repository.

  The remote tracking branches have the prefix `origin/` followed by the name of
  the remote branch. For example, if you clone a repository with a branch named
  `develop`, Git creates a remote tracking branch named `origin/develop`.

  When you use commands like `git fetch` and `git pull`, you are interacting with
  the remote repository and having it reflected in the remote tracking branches.

  For instance, `git fetch` makes the remote tracking branch reflect the remote
  changes so that you have a local copy.

  The remote tracking branch is not the same as a local branch. A local branch is
  a branch that may or may not exist on the remote. Once it has been pushed to the
  remote repository, a remote tracking branch will be created for its remote
  version. When you make local changes, the snapshot of the remote tracking branch
  will no longer be up to date: your local branch will be "ahead" of it. Once you
  push, the remote tracking branch snapshot is update and reflects the local state
  of the branch and the two are in sync again.

  The same is true of remote branches. When you checkout a remote branch, your
  local version of it reflects a remote tracking version of it. This goes out of
  sync with your local version when changes are made on the remote.

raw_content:
  ---
  tags: [git]
  ---

  # Remote tracking branches

  A remote tracking branch is a **local** snapshot or reference to the state of a
  branch on a remote repository.

  When you clone a remote repository, Git automatically creates remote tracking
  branches for all the branches on the remote repository.

  The remote tracking branches have the prefix `origin/` followed by the name of
  the remote branch. For example, if you clone a repository with a branch named
  `develop`, Git creates a remote tracking branch named `origin/develop`.

  When you use commands like `git fetch` and `git pull`, you are interacting with
  the remote repository and having it reflected in the remote tracking branches.

  For instance, `git fetch` makes the remote tracking branch reflect the remote
  changes so that you have a local copy.

  The remote tracking branch is not the same as a local branch. A local branch is
  a branch that may or may not exist on the remote. Once it has been pushed to the
  remote repository, a remote tracking branch will be created for its remote
  version. When you make local changes, the snapshot of the remote tracking branch
  will no longer be up to date: your local branch will be "ahead" of it. Once you
  push, the remote tracking branch snapshot is update and reflects the local state
  of the branch and the two are in sync again.

  The same is true of remote branches. When you checkout a remote branch, your
  local version of it reflects a remote tracking version of it. This goes out of
  sync with your local version when changes are made on the remote.


path: Rename_a_branch.md
sortable_path: Rename_a_branch.md
title: Rename a branch
lead: ### In local development
checksum: e8c060552e8fa8aad44a72102793c1db506b6091b9f8a57b3b216a90c49c8030
created: 2024-10-22 16:52:58.686445629+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["git","procedural"]}
body:
  ### In local development

  ```bash
  git branch -m <old_name> <new_name>
  ```

  ### Update the remote

  ```bash
  git push origin -u <new_name>
  ```

raw_content:
  ---
  tags: [git, procedural]
  ---

  # Rename a branch

  ### In local development

  ```bash
  git branch -m <old_name> <new_name>
  ```

  ### Update the remote

  ```bash
  git push origin -u <new_name>
  ```


path: Reset_to_remote_version.md
sortable_path: Reset_to_remote_version.md
title: Reset to remote version of a branch
checksum: 4275e3738538e2f4224803c94c28cbb651c5bde268c007b9541dd886624806d9
created: 2024-10-22 16:52:58.692372751+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["git","procedural"]}
lead:
  The scenario: your local Git history has become corrupted in some way and you
  want a clean history based on the current state of the remote. We will
  demonstrate with `main`.

body:
  The scenario: your local Git history has become corrupted in some way and you
  want a clean history based on the current state of the remote. We will
  demonstrate with `main`.

  ```
  git fetch origin
  git reset --hard origin/main
  ```

raw_content:
  ---
  tags: [git, procedural]
  ---

  # Reset to remote version of a branch

  The scenario: your local Git history has become corrupted in some way and you
  want a clean history based on the current state of the remote. We will
  demonstrate with `main`.

  ```
  git fetch origin
  git reset --hard origin/main
  ```


path: Resistance.md
sortable_path: Resistance.md
title: Resistance
lead: - Resistance is opposition to the flow of [current](./Current.md).
checksum: 7531375cbeecf1f1985d5dfaa5d6f7501bc12fa20ec74096a7b8a65343f11009
created: 2024-10-22 16:52:58.697458112+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["physics","electricity"]}
body:
  - Resistance is opposition to the flow of [current](./Current.md).

  - Different materials have different levels of resistance. For example glass and
    rubber are excellent resistors whereas silver and copper offer little
    resistance to current flow.

  - We utilise **resistors** to control the flow of current in a circuit.

  ## Relationship between _resistance_ and _insulation_

  Resistance and insulation are not the same thing although they relate to the
  same natural phenomena.

  Resistance is a property of an electrical circuit whereas insularity is a
  property of substances found in the world.

  Similarly, insulators are naturally occuring materials that resist the flow of
  current to different degrees whereas a resistor is a man-made electrical
  component that is used to _control_ the flow of current in a circuit.

  ## Factors affecting resistance

  - The larger the diameter of an electric wire, the lower the electrical
    resistance to the current flow. (If we use the analogy of electricity as
    water-flow: the wider the pipe, the more water that can flow.)
  - As a conductor heats up (e.g. copper, aluminium wire), its overall resistance
    increases.

  ## Notation and scientific expression

  ### Resistance

  - We use $R$ to represent resistance
  - The unit of resistance is **ohms** ($\Omega$)

  > One ohm is the resistance of a circuit or circuit element that permits a
  > steady current flow of one [amp](Current.md#formal-expression) (one
  > coulomb/second) when one [volt](Voltage.md#voltage) is applied to the circuit.

  ### Conductance

  - We use $G$ to represent conductance
  - The unit of conductance is **Mho** (ohm spelled backwards) ($\mho$)

  > Conductance is the ability of a material to pass electrons

  > Given that resistance is the opposite of conductance, the relation between the
  > two quantities can be expressed via a recipricol: $R = 1/G$ and $G = 1/R$

  ## Ohm's Law

  The relationship between current, resistance and voltage is expressed in
  [Ohm's Law](Ohms_Law.md).

  ## Resistors

  A resistor is an electrical component that can be used in circuits to introduce
  specific amounts to resistance where needed. This is typically done in order to
  temper the total voltage and so keep the current flowing within certain set
  parameters.

raw_content:
  ---
  tags: [physics, electricity]
  ---

  # Resistance

  - Resistance is opposition to the flow of [current](./Current.md).

  - Different materials have different levels of resistance. For example glass and
    rubber are excellent resistors whereas silver and copper offer little
    resistance to current flow.

  - We utilise **resistors** to control the flow of current in a circuit.

  ## Relationship between _resistance_ and _insulation_

  Resistance and insulation are not the same thing although they relate to the
  same natural phenomena.

  Resistance is a property of an electrical circuit whereas insularity is a
  property of substances found in the world.

  Similarly, insulators are naturally occuring materials that resist the flow of
  current to different degrees whereas a resistor is a man-made electrical
  component that is used to _control_ the flow of current in a circuit.

  ## Factors affecting resistance

  - The larger the diameter of an electric wire, the lower the electrical
    resistance to the current flow. (If we use the analogy of electricity as
    water-flow: the wider the pipe, the more water that can flow.)
  - As a conductor heats up (e.g. copper, aluminium wire), its overall resistance
    increases.

  ## Notation and scientific expression

  ### Resistance

  - We use $R$ to represent resistance
  - The unit of resistance is **ohms** ($\Omega$)

  > One ohm is the resistance of a circuit or circuit element that permits a
  > steady current flow of one [amp](Current.md#formal-expression) (one
  > coulomb/second) when one [volt](Voltage.md#voltage) is applied to the circuit.

  ### Conductance

  - We use $G$ to represent conductance
  - The unit of conductance is **Mho** (ohm spelled backwards) ($\mho$)

  > Conductance is the ability of a material to pass electrons

  > Given that resistance is the opposite of conductance, the relation between the
  > two quantities can be expressed via a recipricol: $R = 1/G$ and $G = 1/R$

  ## Ohm's Law

  The relationship between current, resistance and voltage is expressed in
  [Ohm's Law](Ohms_Law.md).

  ## Resistors

  A resistor is an electrical component that can be used in circuits to introduce
  specific amounts to resistance where needed. This is typically done in order to
  temper the total voltage and so keep the current flowing within certain set
  parameters.


path: Restructure_URLs_lambda.md
sortable_path: Restructure_URLs_lambda.md
title: AWS Lambda examples
lead: ## Reconstruct CloudFront URLs
checksum: 6a911ce9d3aa21da4b42240dc00369ab6f54b0e35dca6f1cd3d11e804463b857
created: 2024-10-22 16:52:58.719452929+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["AWS","aws-lambda"]}
body:
  ## Reconstruct CloudFront URLs

  ```js
  exports.handler = (event, context, callback) => {
    // Extract the request from the CloudFront event that is sent to Lambda@Edge
    var request = event.Records[0].cf.request;

    // Extract the URI from the request
    var olduri = request.uri;

    // Match any '/' that occurs at the end of a URI. Replace it with a default index
    var newuri = olduri.replace(/\/$/, "/index.html");

    // Replace the received URI with the URI that includes the index page
    request.uri = newuri;

    // Return to CloudFront
    return callback(null, request);
  };
  ```

  Let's walk through the above example making reference to the general structure
  of the
  [AWS Lambda handler function](Lambda_handler_function.md):

  1. The `event` parameter is invoked to gain access to the event that triggered
     the function. This is a CloudFront request event which is targetted with
     `event.Records[0].cf.request`
  2. We classify this request as the `olduri` since it doesn't have the form we
     want.
  3. We mutate the original request by replacing `/` with `/index.html`
  4. We then re-assign the `request.uri` value to the value of the new URI.
  5. Finally we invoke the `callback` parameter to signal the completion of the
     transformation and return the new request as the return value.

raw_content:
  ---
  tags:
    - AWS
    - aws-lambda
  ---

  # AWS Lambda examples

  ## Reconstruct CloudFront URLs

  ```js
  exports.handler = (event, context, callback) => {
    // Extract the request from the CloudFront event that is sent to Lambda@Edge
    var request = event.Records[0].cf.request;

    // Extract the URI from the request
    var olduri = request.uri;

    // Match any '/' that occurs at the end of a URI. Replace it with a default index
    var newuri = olduri.replace(/\/$/, "/index.html");

    // Replace the received URI with the URI that includes the index page
    request.uri = newuri;

    // Return to CloudFront
    return callback(null, request);
  };
  ```

  Let's walk through the above example making reference to the general structure
  of the
  [AWS Lambda handler function](Lambda_handler_function.md):

  1. The `event` parameter is invoked to gain access to the event that triggered
     the function. This is a CloudFront request event which is targetted with
     `event.Records[0].cf.request`
  2. We classify this request as the `olduri` since it doesn't have the form we
     want.
  3. We mutate the original request by replacing `/` with `/index.html`
  4. We then re-assign the `request.uri` value to the value of the new URI.
  5. Finally we invoke the `callback` parameter to signal the completion of the
     transformation and return the new request as the return value.


path: Retrieve_data_from_SQL_table.md
sortable_path: Retrieve_data_from_SQL_table.md
title: Retrieve data from table with SQL SELECT statement
checksum: bd9cfcf86d3db4b0d93097db4940a0538df1f7ee46e5928de789034637659e8b
created: 2024-10-22 16:52:58.729396019+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["SQL","databases"]}
lead:
  > Select data from the database. The data returned is stored in a result table
  > called the **result-set**.

body:
  > Select data from the database. The data returned is stored in a result table
  > called the **result-set**.

  _A table named `models`:_

  | modelId | name                 | cores | releaseDate |
  | ------- | -------------------- | ----- | ----------- |
  | 2       | Raspberry Pi Model B | 2     | 2022-11-19  |
  | 3       | Raspberry Pi Model C | 4     | 2022-11-25  |
  | 4       | MacBook Pro M2       | 9     | 2022-03-18  |

  ## Basic usage

  ```sql
  SELECT name FROM models
  ```

  We can pass in a comma-separated **list of fields**:

  ```sql
  SELECT name, releaseDate FROM models
  ```

  ## With parameters

  ### `WHERE`

  The `WHERE` clause specifies a search criterion. The `WHERE` clause should
  always be last in the syntax. The clauses are always written in this order:
  `FROM` followed by `WHERE`

  ```sql
  SELECT name FROM models WHERE cores > 3
  ```

  ### Compound statements

  Compound statements allow you to apply more filters to your clauses within an
  SQL statement. SQL allows you to build complex, combinatorial `WHERE` clauses by
  using Boolean and mathematical operators (i.e `AND` , `OR` , `>` , `<` , `!=` ,
  `<=` ...)

  Multiple clauses:

  ```sql
  SELECT name, ram, release_date
  FROM model
  WHERE release_date > '2018-01-01' AND ram > 512;
  ```

  More complex logic achieve with parentheses:

  ```sql
  SELECT name, cores, release_date
  FROM model
  WHERE (manufacturer_id = 1 OR manufacturer_id = 2) AND cores >= 2;
  ```

  ## Transform returned data

  We can apply **ordering** to the return data

  ```sql
  SELECT name FROM models ORDER BY name, cores
  SELECT name FROM models ORDER BY name, cores DESC
  ```

  > `ORDER BY` always comes last, after the selection and any filtering clauses
  > but _before_ a `WHERE` clause

  ## Paginate

  We can break our returned data into blocks with `LIMIT`

  ```sql
  LIMIT 5 -- Return first five items
  LIMIT 5,5 -- Return first five items from the sixth row
  ```

raw_content:
  ---
  tags:
    - SQL
    - databases
  ---

  # Retrieve data from table with SQL `SELECT` statement

  > Select data from the database. The data returned is stored in a result table
  > called the **result-set**.

  _A table named `models`:_

  | modelId | name                 | cores | releaseDate |
  | ------- | -------------------- | ----- | ----------- |
  | 2       | Raspberry Pi Model B | 2     | 2022-11-19  |
  | 3       | Raspberry Pi Model C | 4     | 2022-11-25  |
  | 4       | MacBook Pro M2       | 9     | 2022-03-18  |

  ## Basic usage

  ```sql
  SELECT name FROM models
  ```

  We can pass in a comma-separated **list of fields**:

  ```sql
  SELECT name, releaseDate FROM models
  ```

  ## With parameters

  ### `WHERE`

  The `WHERE` clause specifies a search criterion. The `WHERE` clause should
  always be last in the syntax. The clauses are always written in this order:
  `FROM` followed by `WHERE`

  ```sql
  SELECT name FROM models WHERE cores > 3
  ```

  ### Compound statements

  Compound statements allow you to apply more filters to your clauses within an
  SQL statement. SQL allows you to build complex, combinatorial `WHERE` clauses by
  using Boolean and mathematical operators (i.e `AND` , `OR` , `>` , `<` , `!=` ,
  `<=` ...)

  Multiple clauses:

  ```sql
  SELECT name, ram, release_date
  FROM model
  WHERE release_date > '2018-01-01' AND ram > 512;
  ```

  More complex logic achieve with parentheses:

  ```sql
  SELECT name, cores, release_date
  FROM model
  WHERE (manufacturer_id = 1 OR manufacturer_id = 2) AND cores >= 2;
  ```

  ## Transform returned data

  We can apply **ordering** to the return data

  ```sql
  SELECT name FROM models ORDER BY name, cores
  SELECT name FROM models ORDER BY name, cores DESC
  ```

  > `ORDER BY` always comes last, after the selection and any filtering clauses
  > but _before_ a `WHERE` clause

  ## Paginate

  We can break our returned data into blocks with `LIMIT`

  ```sql
  LIMIT 5 -- Return first five items
  LIMIT 5,5 -- Return first five items from the sixth row
  ```


path: Role_of_memory_in_computation.md
sortable_path: Role_of_memory_in_computation.md
title: The role of memory in computation
checksum: 201e429a3109d53637859ac37e568ac5254ff329ef4620a4d68cc4b4cb3d4708
created: 2024-10-22 16:52:58.73423137+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"created":"Friday, July 12, 2024","tags":["memory"]}
lead:
  The following steps outline the way in which memory interacts with the processor
  during computational cycles, once the [bootstrapping](Boot_process.md) process
  has completed and the OS kernel is itself loaded into memory.

body:
  The following steps outline the way in which memory interacts with the processor
  during computational cycles, once the [bootstrapping](Boot_process.md) process
  has completed and the OS kernel is itself loaded into memory.

  1. A file is loaded from the harddisk into memory.
  2. The instruction at the first address is sent to the CPU, travelling accross
     the data bus part of the [system bus](Bus.md).
  3. The CPU processes this instruction and then sends a request accross the
     address bus part of the system bus for the next instruction to the memory
     controller within the [chipset](Chipset_and_controllers.md).
  4. The chipset finds where this instruction is stored within the
     [DRAM](Memory.md#dram) and issues a request to have it read out and send to
     the CPU over the data bus.

  > This is a simplified account; it is not the case that only single requests are
  > passed back and forth. This would be inefficient and time-wasting. The kernel
  > sends to the CPU not just the first instruction in the requested file but also
  > a number of instructions that immediately follow it.

  ![Memory flow diagram](/img/memory-flow.svg)

  Every part of the above process - the journey accross the bus, the lookup in the
  controller, the operations on the DRAM, the journey back accross the bus - takes
  multiple CPU clock cycles.

raw_content:
  ---
  tags: [memory]
  created: Friday, July 12, 2024
  ---

  # The role of memory in computation

  The following steps outline the way in which memory interacts with the processor
  during computational cycles, once the [bootstrapping](Boot_process.md) process
  has completed and the OS kernel is itself loaded into memory.

  1. A file is loaded from the harddisk into memory.
  2. The instruction at the first address is sent to the CPU, travelling accross
     the data bus part of the [system bus](Bus.md).
  3. The CPU processes this instruction and then sends a request accross the
     address bus part of the system bus for the next instruction to the memory
     controller within the [chipset](Chipset_and_controllers.md).
  4. The chipset finds where this instruction is stored within the
     [DRAM](Memory.md#dram) and issues a request to have it read out and send to
     the CPU over the data bus.

  > This is a simplified account; it is not the case that only single requests are
  > passed back and forth. This would be inefficient and time-wasting. The kernel
  > sends to the CPU not just the first instruction in the requested file but also
  > a number of instructions that immediately follow it.

  ![Memory flow diagram](/img/memory-flow.svg)

  Every part of the above process - the journey accross the bus, the lookup in the
  controller, the operations on the DRAM, the journey back accross the bus - takes
  multiple CPU clock cycles.


path: Role_of_unique_key_in_relational_databases.md
sortable_path: Role_of_unique_key_in_relational_databases.md
title: Unique key
checksum: 7878d926038dcbc9f1511610d308b67f1133686457a748fc98cf72c4d659466f
created: 2024-10-22 16:52:58.754800788+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["databases"]}
lead:
  > Every table in a relational database should have a **unique key**. A unique
  > key is one **field that uniquely identifies each record**.

body:
  > Every table in a relational database should have a **unique key**. A unique
  > key is one **field that uniquely identifies each record**.

  This is essential for carrying out operations across database tables and for
  creating and deleting database entires in accordance with the
  [ACID principle](ACID_principle.md). It is also a safeguard: it means
  you can always identify a record by itself and don't have to rely on generic
  queries to identify it.

  Sometimes you will have a dedicated field such as `UNIQUE_ID` for the unique
  key. Other times you can use an existing field to fulfil that function. When an
  existing, visible column is used as the unique key it is called the **primary
  key**. Other times the unique key can be hidden from the table.

  In both cases the following constraints **must be met:**

  1. No two records can have the **same** primary key data
  1. The primary key value should **never be reused**. Thus, if a record is
     deleted from the table, it should not be re-allocated to a new record.
  1. A primary key value **must not be modified** once it has been created
  1. A primary key **must have a value**; it cannot be `null`

raw_content:
  ---
  tags:
    - databases
  ---

  # Unique key

  > Every table in a relational database should have a **unique key**. A unique
  > key is one **field that uniquely identifies each record**.

  This is essential for carrying out operations across database tables and for
  creating and deleting database entires in accordance with the
  [ACID principle](ACID_principle.md). It is also a safeguard: it means
  you can always identify a record by itself and don't have to rely on generic
  queries to identify it.

  Sometimes you will have a dedicated field such as `UNIQUE_ID` for the unique
  key. Other times you can use an existing field to fulfil that function. When an
  existing, visible column is used as the unique key it is called the **primary
  key**. Other times the unique key can be hidden from the table.

  In both cases the following constraints **must be met:**

  1. No two records can have the **same** primary key data
  1. The primary key value should **never be reused**. Thus, if a record is
     deleted from the table, it should not be re-allocated to a new record.
  1. A primary key value **must not be modified** once it has been created
  1. A primary key **must have a value**; it cannot be `null`


path: Routing_on_link_click_in_React.md
sortable_path: Routing_on_link_click_in_React.md
title: Test: Routing on link click
lead: ## Component
checksum: 0935aa4e5e1e4051e4ca684aa0735d3b0fb3a555a93412746712f20724dee40b
created: 2024-10-22 16:52:58.76602793+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["javascript","react","testing"]}
body:
  ## Component

  ```jsx
  const CertificateHtmlLink = () => {
    return (
      <div className="certificate-html-link html-version-message">
        <Link className="btn btn-primary ml-2" to="/some-location">
          View HTML version
        </Link>
      </div>
    );
  };
  ```

  ## Test

  ```js
  import { render, fireEvent } from "@testing-library/react";
  import { Router } from "react-router-dom";

  describe("<CertificateHtmlLink", () => {
    it("should link to dynamic HTML certificate page URL on click event", () => {
      // Arrange:
      const history = createMemoryHistory({ initialEntries: ["/"] });

      // Act:
      const { getByText } = render(
        <Router history={history}>
          <CertificateHtmlLink to="/certificate/123" />
        </Router>
      );

      // Assert:
      expect(history.location.pathname).toBe("/");
      fireEvent.click(getByText("View HTML version"));
      expect(history.location.pathname).toBe("/certificate/123");
    });
  });
  ```

raw_content:
  ---
  tags:
    - javascript
    - react
    - testing
  ---

  # Test: Routing on link click

  ## Component

  ```jsx
  const CertificateHtmlLink = () => {
    return (
      <div className="certificate-html-link html-version-message">
        <Link className="btn btn-primary ml-2" to="/some-location">
          View HTML version
        </Link>
      </div>
    );
  };
  ```

  ## Test

  ```js
  import { render, fireEvent } from "@testing-library/react";
  import { Router } from "react-router-dom";

  describe("<CertificateHtmlLink", () => {
    it("should link to dynamic HTML certificate page URL on click event", () => {
      // Arrange:
      const history = createMemoryHistory({ initialEntries: ["/"] });

      // Act:
      const { getByText } = render(
        <Router history={history}>
          <CertificateHtmlLink to="/certificate/123" />
        </Router>
      );

      // Assert:
      expect(history.location.pathname).toBe("/");
      fireEvent.click(getByText("View HTML version"));
      expect(history.location.pathname).toBe("/certificate/123");
    });
  });
  ```


path: Rules_for_operations_on_like_and_unlike_terms.md
sortable_path: Rules_for_operations_on_like_and_unlike_terms.md
title: Rules for operations on like and unlike terms
lead: ## Addition
checksum: c0f6bb553c1a03f13e05bed067559c4fa969af88e93a2efbf8de55d82bf3d7c4
created: 2024-10-22 16:52:58.770344644+00:00
modified: 2024-10-19 08:57:20.737729782+00:00
metadata: {"tags":["prealgebra"]}
body:
  ## Addition

  ### Like terms

  > Sum the absolute values and add the negative sign afterwards.

  $$
  \\begin{split}
  -4 + -3
  \\ = 4 + 3
  \\ = -7
  \\end{split}
  $$

  ### Unlike terms

  > Subtract smaller from larger amount and then affix the sign of the larger
  > amount to the sum.

  #### Negative plus positive

  $$
  \\begin{split}
  -8 + 5
  \\ = 8 - 5
  \\ = 3
  \\ = -3
  \\end{split}
  $$

  #### Positive plus negative

  $$
  \\begin{split}
  4 + -1
  \\ = 4 - 1
  \\ = 3
  \\end{split}
  $$

  ## Subtraction

  ### Like terms

  > Turn the operator and second negative into a plus sign and execute as an
  > addition.

  $$
  \\begin{split}
  -4 - -3 =
  \\ = -4 + 3
  \\ = -1
  \\end{split}
  $$

  ### Unlike terms

  #### Positive subtract negative

  > Turn the negative after the operator to a positive. (Same as previous.)
  >
  > $$
  > \\begin{split}
  > 2 - -3 =
  > \\ = 2 + 3
  > \\ = 5
  > \\end{split}
  > $$

  #### Negative subtract positive

  > Start at the negative value and count backwards on the number-line

  $$
  -2 - 3 = -5
  $$

  ## Multiplication

  ### Like terms

  > The product of two negative numbers will always be a positive number.

  $$
  -15 \cdot -3 = 45
  $$

  ### Unlike terms

  > The product of a positive and a negative number will always be a negative
  > number.

  $$
  -3 \cdot 5 = -15
  $$

  ## Division

  **Division follows the same rules as multiplication.**

  ### Like terms

  > The quotient of two negative numbers will always be a positive number.

  $$
  -15 / -3 = 5
  $$

  ### Unlike terms

  > The quotient of a positive and a negative number will always be a negative
  > number.

  $$
  -15 / 3 = -5
  $$

raw_content:
  ---
  tags:
    - prealgebra
  ---

  # Rules for operations on like and unlike terms

  ## Addition

  ### Like terms

  > Sum the absolute values and add the negative sign afterwards.

  $$
  \\begin{split}
  -4 + -3
  \\ = 4 + 3
  \\ = -7
  \\end{split}
  $$

  ### Unlike terms

  > Subtract smaller from larger amount and then affix the sign of the larger
  > amount to the sum.

  #### Negative plus positive

  $$
  \\begin{split}
  -8 + 5
  \\ = 8 - 5
  \\ = 3
  \\ = -3
  \\end{split}
  $$

  #### Positive plus negative

  $$
  \\begin{split}
  4 + -1
  \\ = 4 - 1
  \\ = 3
  \\end{split}
  $$

  ## Subtraction

  ### Like terms

  > Turn the operator and second negative into a plus sign and execute as an
  > addition.

  $$
  \\begin{split}
  -4 - -3 =
  \\ = -4 + 3
  \\ = -1
  \\end{split}
  $$

  ### Unlike terms

  #### Positive subtract negative

  > Turn the negative after the operator to a positive. (Same as previous.)
  >
  > $$
  > \\begin{split}
  > 2 - -3 =
  > \\ = 2 + 3
  > \\ = 5
  > \\end{split}
  > $$

  #### Negative subtract positive

  > Start at the negative value and count backwards on the number-line

  $$
  -2 - 3 = -5
  $$

  ## Multiplication

  ### Like terms

  > The product of two negative numbers will always be a positive number.

  $$
  -15 \cdot -3 = 45
  $$

  ### Unlike terms

  > The product of a positive and a negative number will always be a negative
  > number.

  $$
  -3 \cdot 5 = -15
  $$

  ## Division

  **Division follows the same rules as multiplication.**

  ### Like terms

  > The quotient of two negative numbers will always be a positive number.

  $$
  -15 / -3 = 5
  $$

  ### Unlike terms

  > The quotient of a positive and a negative number will always be a negative
  > number.

  $$
  -15 / 3 = -5
  $$


path: S3.md
sortable_path: S3.md
title: S3
checksum: 71de289fb1009995825d1050865f4e550269258bd276f23ef7e28099c78b7165
created: 2024-10-22 16:52:58.774319867+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["AWS"]}
lead:
  You can think of S3 as an external mounted harddrive for storing raw binary data
  (images, soundfiles, documents etc).

body:
  You can think of S3 as an external mounted harddrive for storing raw binary data
  (images, soundfiles, documents etc).

  Your use of the S3 service is divided up into **buckets**. Buckets contain files
  and resources. These are just resource names in an online container, the data
  doesn't have any structure beyond that.

  The primary use of S3 is to store resources that an application needs and/or to
  facilitate the uploading and downloading of resources to and from an
  application.

  You can set access and authentication conditions for buckets, making the data
  public or restricted.

raw_content:
  ---
  tags: [AWS]
  ---

  # S3

  You can think of S3 as an external mounted harddrive for storing raw binary data
  (images, soundfiles, documents etc).

  Your use of the S3 service is divided up into **buckets**. Buckets contain files
  and resources. These are just resource names in an online container, the data
  doesn't have any structure beyond that.

  The primary use of S3 is to store resources that an application needs and/or to
  facilitate the uploading and downloading of resources to and from an
  application.

  You can set access and authentication conditions for buckets, making the data
  public or restricted.


path: SAM.md
sortable_path: SAM.md
title: AWS SAM
checksum: f8c24e34ef80334d12ece3ae1f8ab00756aa1fade97ebc1202b2e78096913b62
created: 2024-10-22 16:52:58.780593739+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["AWS"]}
lead:
  SAM stands for **serverless application model**. It is a framework developed by
  AWS to simplify the process of building, deploying and managing serverless
  applications. It provides a concise syntax for defining the components of a
  serverless application, such as
  [Lambda functions](zk/Lambda_programming_model.md),
  [API gateway](/zk/AWS_API_Gateway.md) and database tables.

body:
  SAM stands for **serverless application model**. It is a framework developed by
  AWS to simplify the process of building, deploying and managing serverless
  applications. It provides a concise syntax for defining the components of a
  serverless application, such as
  [Lambda functions](zk/Lambda_programming_model.md),
  [API gateway](/zk/AWS_API_Gateway.md) and database tables.

  The SAM infrastructure is defined in a YAML file which is then deployed to AWS.
  SAM syntax gets transformed into CloudFormation during the deployment process.
  (CloudFormation is a broader and more robust AWS tool for large, highly
  scaleable infrastructures).

  ## Key features of SAM

  - Single deployment configuration
  - Integration with development tools
  - Local testing and debugging
  - Built on AWS CloudFormation

  ## Main technologies required

  ### Docker

  Whilst SAM can be used to create a deployable file for AWS it can also be run as
  a container for local development with Docker.

  ### AWS CLI

  This is installed using Python and allows you to interact directly with AWS via
  the command-line.

  ### AWS SAM CLI

  See
  [https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)

  ## Setting up credentials for the AWS CLI

  You require an access key for the given
  [IAM user](zk/AWS_User_management_and_roles.md#iam). You should create an IAM
  account specific to the project with bounded permissions.

  ```
  aws configure
  AWS Access Key ID [None]: AK*******
  AWS Secret Access Key [None]: ukp******
  Default region name [None]:
  Default output format [None]:
  ```

  This information can be found in the Security Credentials section of the given
  [IAM](zk/AWS_User_management_and_roles.md#iam) user:

  ![](/img/access-key-aws.png)

  ### Switching between credentials

  You should set up a different IAM user for each project.

  You can do this with:

  ```sh
  aws configure --profile <profile-name>
  ```

  This will then ask you to add the credentials for the user.

  You can switch between different credentials for the user as follows:

  ```sh
  AWS_PROFILE=<profile-name> sam build

  ```

  ## Starting a SAM project

  First create a directory for your project which will serve as the repository:

  ```sh
  mkdir aws-sam-learning
  cd aws-sam-learning
  ```

  Then we can use the `sam` cli to bootstrap the project:

  ```sh
  sam init --runtime nodejs16.x
  ```

  We can just click through and accept the basic HelloWorld Lambda.

  This will create the Lambda as well as an API Gateway trigger URL.

  ### `template.yaml`

  This is autogenerated and details the main constituents of the project. There
  are lots of fields but the most important are the following:

  ```yaml
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world/
      Handler: app.lambdaHandler
      Runtime: nodejs16.x
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
  ```

  This details the location of the [handler function](/Lambda_handler_function.md)
  which is contained at the path `hello-world/app.js`:

  ```js
  exports.lambdaHandler = async (event, context) => {
    try {
      // const ret = await axios(url);
      response = {
        statusCode: 200,
        body: JSON.stringify({
          message: "hello world",
          // location: ret.data.trim()
        }),
      };
    } catch (err) {
      console.log(err);
      return err;
    }

    return response;
  };
  ```

  It also lists the `get` event that we can use to call API Gateway and trigger
  the Lambda.

  The full template is below:

  ![](/img/sam-template-yaml.png)

  ## Adding our own code

  We will create our own function and API Gateway trigger.

  We will place our function after the existing `HelloWorldFunction`

  ```yaml
  ClockFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: clock/
      Handler: handler.clock
      Runtime: nodejs16.x
    Events:
      ClockApi:
        Type: Api
        Properties:
          Path: /clock
          Method: get
  ```

  We can test the syntax with:

  ```sh
  sam validate
  ```

  Just like with `HelloWorld`, we will create a directory for this function:
  `clock` and we will initialise it as an `npm` project.

  ```sh
  mkdir clock
  cd clock
  npm init
  ```

  We will use `handler.js` as our root, handler function.

  We have said in the template file that our `Handler: handler.clock`, therefore
  the main function in the `handler` module should be `clock`:

  ```js
  const moment = require("moment");

  exports.clock = async (event) => {
    console.log("Clock function run");
    const message = moment().format();
    const response = {
      statusCode: 200,
      body: JSON.stringify(message),
    };
    return response;
  };
  ```

  The directory structure is as follows:

  ![](/_img/sam-directory.png)

  When we call the API Gateway path `/clock` with `GET`, our function will be
  triggered.

  ## Deploying the project

  We will now deploy our project to AWS from the local environment.

  The process is as follows:

  1. Build
  2. Package
  3. Deploy

  ### Build

  We need to install the runtime dependencies for the function. We do this by
  running `sam build`. This ignores test files and development dependencies and
  installs the project dependencies and source files to a temporary subdirectory.

  ![](/img/sam-build.png)

  The build directory is `.aws-sam/build/`. There will be a subdirectory for each
  of our files.

  ### Package

  As noted, CloudFront handles the deployment of the application. It can only
  receive one file as an input. The packaging process consists in creating that
  single file.

  The packaging proces will first archive all of the project artefacts into a zip
  file and then upload that to [S3](zk/AWS_S3.md). A reference to this S3 entity
  is then provided to CloudFormation.

  ![](/img/s3-package-again.svg)

  The command is as follows:

  ```sh
  sam package
    --template-file template.yaml
    --output-template-file pkg.yml
    --region eu-west-1
  ```

  This will automatically create a hashed bucket name for you in S3 (I have tried
  to add my own naming but it doesn't comply.)

  ### Local development with Docker

  In order to work with your application locally without actually sending requests
  to AWS and using credit, you can run a local instance.

  See [Local AWS Development with SAM](zk/Local_AWS_development_with_SAM.md).

  ### Deploy

  Once you have packaged the app you can deploy with `sam deploy --guided`. This
  will talk you through the defaults and will deploy the package to
  CloudFormation. In CloudFormation each individual project is called a **stack**.

  If we then go to Cloud Formation we will see the deployed application.

  ![](/img/cloud-formation-stack.png)

  ## Call the endpoint

  If we now go to the Lambda console, we will see our function listed, and the API
  Gateway endpoint under `triggers`:

  ![](/img/gateway-trigger.png)

  We can then call this from Postman to check everything is working as it should:

  ![](/img/postman-aws-output.png)

raw_content:
  ---
  tags: [AWS]
  ---

  # AWS SAM

  SAM stands for **serverless application model**. It is a framework developed by
  AWS to simplify the process of building, deploying and managing serverless
  applications. It provides a concise syntax for defining the components of a
  serverless application, such as
  [Lambda functions](zk/Lambda_programming_model.md),
  [API gateway](/zk/AWS_API_Gateway.md) and database tables.

  The SAM infrastructure is defined in a YAML file which is then deployed to AWS.
  SAM syntax gets transformed into CloudFormation during the deployment process.
  (CloudFormation is a broader and more robust AWS tool for large, highly
  scaleable infrastructures).

  ## Key features of SAM

  - Single deployment configuration
  - Integration with development tools
  - Local testing and debugging
  - Built on AWS CloudFormation

  ## Main technologies required

  ### Docker

  Whilst SAM can be used to create a deployable file for AWS it can also be run as
  a container for local development with Docker.

  ### AWS CLI

  This is installed using Python and allows you to interact directly with AWS via
  the command-line.

  ### AWS SAM CLI

  See
  [https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)

  ## Setting up credentials for the AWS CLI

  You require an access key for the given
  [IAM user](zk/AWS_User_management_and_roles.md#iam). You should create an IAM
  account specific to the project with bounded permissions.

  ```
  aws configure
  AWS Access Key ID [None]: AK*******
  AWS Secret Access Key [None]: ukp******
  Default region name [None]:
  Default output format [None]:
  ```

  This information can be found in the Security Credentials section of the given
  [IAM](zk/AWS_User_management_and_roles.md#iam) user:

  ![](/img/access-key-aws.png)

  ### Switching between credentials

  You should set up a different IAM user for each project.

  You can do this with:

  ```sh
  aws configure --profile <profile-name>
  ```

  This will then ask you to add the credentials for the user.

  You can switch between different credentials for the user as follows:

  ```sh
  AWS_PROFILE=<profile-name> sam build

  ```

  ## Starting a SAM project

  First create a directory for your project which will serve as the repository:

  ```sh
  mkdir aws-sam-learning
  cd aws-sam-learning
  ```

  Then we can use the `sam` cli to bootstrap the project:

  ```sh
  sam init --runtime nodejs16.x
  ```

  We can just click through and accept the basic HelloWorld Lambda.

  This will create the Lambda as well as an API Gateway trigger URL.

  ### `template.yaml`

  This is autogenerated and details the main constituents of the project. There
  are lots of fields but the most important are the following:

  ```yaml
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world/
      Handler: app.lambdaHandler
      Runtime: nodejs16.x
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
  ```

  This details the location of the [handler function](/Lambda_handler_function.md)
  which is contained at the path `hello-world/app.js`:

  ```js
  exports.lambdaHandler = async (event, context) => {
    try {
      // const ret = await axios(url);
      response = {
        statusCode: 200,
        body: JSON.stringify({
          message: "hello world",
          // location: ret.data.trim()
        }),
      };
    } catch (err) {
      console.log(err);
      return err;
    }

    return response;
  };
  ```

  It also lists the `get` event that we can use to call API Gateway and trigger
  the Lambda.

  The full template is below:

  ![](/img/sam-template-yaml.png)

  ## Adding our own code

  We will create our own function and API Gateway trigger.

  We will place our function after the existing `HelloWorldFunction`

  ```yaml
  ClockFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: clock/
      Handler: handler.clock
      Runtime: nodejs16.x
    Events:
      ClockApi:
        Type: Api
        Properties:
          Path: /clock
          Method: get
  ```

  We can test the syntax with:

  ```sh
  sam validate
  ```

  Just like with `HelloWorld`, we will create a directory for this function:
  `clock` and we will initialise it as an `npm` project.

  ```sh
  mkdir clock
  cd clock
  npm init
  ```

  We will use `handler.js` as our root, handler function.

  We have said in the template file that our `Handler: handler.clock`, therefore
  the main function in the `handler` module should be `clock`:

  ```js
  const moment = require("moment");

  exports.clock = async (event) => {
    console.log("Clock function run");
    const message = moment().format();
    const response = {
      statusCode: 200,
      body: JSON.stringify(message),
    };
    return response;
  };
  ```

  The directory structure is as follows:

  ![](/_img/sam-directory.png)

  When we call the API Gateway path `/clock` with `GET`, our function will be
  triggered.

  ## Deploying the project

  We will now deploy our project to AWS from the local environment.

  The process is as follows:

  1. Build
  2. Package
  3. Deploy

  ### Build

  We need to install the runtime dependencies for the function. We do this by
  running `sam build`. This ignores test files and development dependencies and
  installs the project dependencies and source files to a temporary subdirectory.

  ![](/img/sam-build.png)

  The build directory is `.aws-sam/build/`. There will be a subdirectory for each
  of our files.

  ### Package

  As noted, CloudFront handles the deployment of the application. It can only
  receive one file as an input. The packaging process consists in creating that
  single file.

  The packaging proces will first archive all of the project artefacts into a zip
  file and then upload that to [S3](zk/AWS_S3.md). A reference to this S3 entity
  is then provided to CloudFormation.

  ![](/img/s3-package-again.svg)

  The command is as follows:

  ```sh
  sam package
    --template-file template.yaml
    --output-template-file pkg.yml
    --region eu-west-1
  ```

  This will automatically create a hashed bucket name for you in S3 (I have tried
  to add my own naming but it doesn't comply.)

  ### Local development with Docker

  In order to work with your application locally without actually sending requests
  to AWS and using credit, you can run a local instance.

  See [Local AWS Development with SAM](zk/Local_AWS_development_with_SAM.md).

  ### Deploy

  Once you have packaged the app you can deploy with `sam deploy --guided`. This
  will talk you through the defaults and will deploy the package to
  CloudFormation. In CloudFormation each individual project is called a **stack**.

  If we then go to Cloud Formation we will see the deployed application.

  ![](/img/cloud-formation-stack.png)

  ## Call the endpoint

  If we now go to the Lambda console, we will see our function listed, and the API
  Gateway endpoint under `triggers`:

  ![](/img/gateway-trigger.png)

  We can then call this from Postman to check everything is working as it should:

  ![](/img/postman-aws-output.png)


path: Save_readonly_Vim_file.md
sortable_path: Save_readonly_Vim_file.md
title: Save read only Vim file
lead:
  ```
  :w !sudo tee % >/dev/null
  ```

body:
  ```
  :w !sudo tee % >/dev/null
  ```

checksum: 13c4e831cb314f4643cba5f5b4c5916209be8b3e3ba9aad98fae1ca25f315bc4
created: 2024-10-22 16:52:58.845144185+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["Linux","procedural"]}
raw_content:
  ---
  tags:
    - Linux
    - procedural
  ---

  # Save read only Vim file

  ```
   :w !sudo tee % >/dev/null
  ```


path: Schema_Definition_Language_in_GraphQL.md
sortable_path: Schema_Definition_Language_in_GraphQL.md
title: Schema Definition Language
lead: SDL is the formal name for the syntax of GraphQL schemas.
checksum: c50d5b10c7f9a915e1d4d5d9bb67ef337b59f126f97bfda5c6bfeba74d25ac1c
created: 2024-10-22 16:52:58.850539839+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["graphql"]}
body:
  SDL is the formal name for the syntax of GraphQL schemas.

  ## Types

  A schema is a collection of object types that contain fields. Each field has a
  type of its own. A field's type can be a primitive/scalar value (such as an
  `Int` or a `String`), or it can be another object type (just like a custom type
  in TS).

  A schema's type can be non-nullable which is to say, a required field. We
  indicate this with `!`.

  A type for a field can be a collection/array of a given type.

  The following example indicates these properties:

  ```gql
  type Person {
    age: Int
    name: String
    pets: [Pet]!
  }

  type Pet {
    species: String
    name: String
    age: Int
  }
  ```

  ## Queries

  A query is also a schema type but of a special sort.

  > The fields of this type are entry points into the rest of the schema. These
  > are the top-level fields that the client can query for.

  For example if we had this type:

  ```graphql
  type Track {
    id: ID!
      author: Author!
    thumbnail: String
    length: Int
    modulesCount: Int
  }
  ```

  We could define a type to access a give `Track` as follows:

  ```graphql
  type Query {
    tracksForHomePage: [Track!]!
  }
  ```

  Then use this type as the basis for a query:

  ```

  ```

  ## Descriptions

  Descriptions are comments that allow you to document your Schema

  Single line:

  ```gql
  "Single line comment"
  ```

  ```gql
  """
  Multi
  line
  comment
  """
  ```

raw_content:
  ---
  tags: [graphql]
  ---

  # Schema Definition Language

  SDL is the formal name for the syntax of GraphQL schemas.

  ## Types

  A schema is a collection of object types that contain fields. Each field has a
  type of its own. A field's type can be a primitive/scalar value (such as an
  `Int` or a `String`), or it can be another object type (just like a custom type
  in TS).

  A schema's type can be non-nullable which is to say, a required field. We
  indicate this with `!`.

  A type for a field can be a collection/array of a given type.

  The following example indicates these properties:

  ```gql
  type Person {
    age: Int
    name: String
    pets: [Pet]!
  }

  type Pet {
    species: String
    name: String
    age: Int
  }
  ```

  ## Queries

  A query is also a schema type but of a special sort.

  > The fields of this type are entry points into the rest of the schema. These
  > are the top-level fields that the client can query for.

  For example if we had this type:

  ```graphql
  type Track {
    id: ID!
      author: Author!
    thumbnail: String
    length: Int
    modulesCount: Int
  }
  ```

  We could define a type to access a give `Track` as follows:

  ```graphql
  type Query {
    tracksForHomePage: [Track!]!
  }
  ```

  Then use this type as the basis for a query:

  ```

  ```

  ## Descriptions

  Descriptions are comments that allow you to document your Schema

  Single line:

  ```gql
  "Single line comment"
  ```

  ```gql
  """
  Multi
  line
  comment
  """
  ```


path: Secrets_or_env_vars_in_AWS.md
sortable_path: Secrets_or_env_vars_in_AWS.md
title: Secrets or environment variables?
checksum: 5dbb25aea37698bb6cf861e39dfd15ee9823b0e0e928f76e9d1d4e3899538a9b
created: 2024-10-22 16:52:58.85457579+00:00
modified: 2024-10-27 11:21:46.971261439+00:00
metadata: {"tags":["AWS","aws-lambda"]}
lead:
  Choosing between environment variables and AWS Secrets Manager hinges on factors
  such as use case, security needs, and the data type. Environment variables are
  easier to use, free, and offer fast access but are less secure and lack features
  like versioning. On the other hand, AWS Secrets Manager provides robust security
  through encryption, access control, audit trails, and supports secret versioning
  and rotation. However, it comes with additional costs and can introduce latency
  and rate limits.

body:
  Choosing between environment variables and AWS Secrets Manager hinges on factors
  such as use case, security needs, and the data type. Environment variables are
  easier to use, free, and offer fast access but are less secure and lack features
  like versioning. On the other hand, AWS Secrets Manager provides robust security
  through encryption, access control, audit trails, and supports secret versioning
  and rotation. However, it comes with additional costs and can introduce latency
  and rate limits.

  General recommendations suggest using AWS Secrets Manager for storing sensitive
  data like API keys and passwords, due to its enhanced security features. For
  less sensitive, non-critical data such as feature flags or environment-specific
  URLs, environment variables are often adequate. Cost-conscious users who don't
  need advanced features may find environment variables more economical.
  Ultimately, the best choice depends on your specific requirements and
  limitations.

raw_content:
  ---
  tags: [AWS, aws-lambda]
  ---

  # Secrets or environment variables?

  Choosing between environment variables and AWS Secrets Manager hinges on factors
  such as use case, security needs, and the data type. Environment variables are
  easier to use, free, and offer fast access but are less secure and lack features
  like versioning. On the other hand, AWS Secrets Manager provides robust security
  through encryption, access control, audit trails, and supports secret versioning
  and rotation. However, it comes with additional costs and can introduce latency
  and rate limits.

  General recommendations suggest using AWS Secrets Manager for storing sensitive
  data like API keys and passwords, due to its enhanced security features. For
  less sensitive, non-critical data such as feature flags or environment-specific
  URLs, environment variables are often adequate. Cost-conscious users who don't
  need advanced features may find environment variables more economical.
  Ultimately, the best choice depends on your specific requirements and
  limitations.


path: Semantic_versioning.md
sortable_path: Semantic_versioning.md
title: Semantic versioning
lead:
  ```
  3.4.1 === major.minor.patch
  ```

checksum: 594b1e9ce9125afba02b6d41efe4af9a53aff06bff3ab7739bc2e89e65f6d664
created: 2024-10-22 16:52:58.859080383+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":[]}
body:
  ```
  3.4.1 === major.minor.patch
  ```

  - Major

    - New feature which may potentially cause breaking changes to applications
      dependent on the previous major version.

  - Minor

    - New features that do not break the existing API

  - Patch

    - Bug fixes for the current minor version

  ## Glob patterns for versioning

  ### Caret

  Interested in any version so long as the major version remains at $n$. E.g if we
  are at `^4.2.1` and we upgrade, we are ok with `4.5.3` or `4.8.2`. We are not
  bothered about the minor or patch version.

  This is equivalent to `4.x`

  ### Tilde

  Interested in any patch version within set major and minor parameters. For
  example `~1.8.3` means you don't mind any patch version so long as it is a patch
  for `1.8`. This is equivalent to `1.8.x`.

  ### No tilde or caret

  Use the _exact_ version specified

raw_content:
  ---
  tags: []
  ---

  # Semantic versioning

  ```
  3.4.1 === major.minor.patch
  ```

  - Major

    - New feature which may potentially cause breaking changes to applications
      dependent on the previous major version.

  - Minor

    - New features that do not break the existing API

  - Patch

    - Bug fixes for the current minor version

  ## Glob patterns for versioning

  ### Caret

  Interested in any version so long as the major version remains at $n$. E.g if we
  are at `^4.2.1` and we upgrade, we are ok with `4.5.3` or `4.8.2`. We are not
  bothered about the minor or patch version.

  This is equivalent to `4.x`

  ### Tilde

  Interested in any patch version within set major and minor parameters. For
  example `~1.8.3` means you don't mind any patch version so long as it is a patch
  for `1.8`. This is equivalent to `1.8.x`.

  ### No tilde or caret

  Use the _exact_ version specified


path: Set_DNS_settings.md
sortable_path: Set_DNS_settings.md
title: Set DNS settings
lead: ## Change the default DNS server
checksum: 8738741f92558b074e041faf3977111bd051bb9d94f0023312135b2c883d4927
created: 2024-10-22 16:52:58.864791216+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"created":"Wednesday, June 12, 2024","id":"2w0e","tags":["Linux","networks"]}
body:
  ## Change the default DNS server

  On Arch and most modern Linux distributions domain name resolution is handled
  via the `systemd-resolved` #systemd configuration.

  Within the system systemd files this will be handled in
  `/etc/systemd/network/wired.network` or `/etc/systemd/network/wlan.network`
  files etc.

  In the file below I have turned of DHCP routing and specified the address of my
  Pi-Hole device to handle domain name resolution:

  ```
  #wlan.network

  [Match]
  Name=wl*

  [Link]
  RequiredForOnline=routable

  [Network]
  DHCP=yes
  DNS=192.168.0.4
  MulticastDNS=yes

  [DHCPv4]
  UseDNS=no
  RouteMetric=600

  [IPv6AcceptRA]
  RouteMetric=600

  ```

  This stops it using the default DNS server of my router and runs the resolutions
  against Pi-Hole.

  This should automatically update the two `resolv.conf` files (which should be in
  syc), viz:

  ```
  # File: /run/NetworkManager/resolv.conf
  # Generated by NetworkManager
  nameserver 192.168.0.4
  nameserver fded:2060:8671:0:681:9bff:fe9b:37f0

  ```

  ```
  # File: /etc/resolv.conf
  # Generated by NetworkManager
  nameserver 192.168.0.4
  nameserver fded:2060:8671:0:681:9bff:fe9b:37f0

  ```

  ## Related notes

  ![Network scanning](./Network_scanning.md)
  ![Network debugging](Network_debugging.md)

raw_content:
  ---
  id: 2w0e
  tags: [Linux, networks]
  created: Wednesday, June 12, 2024
  ---

  # Set DNS settings

  ## Change the default DNS server

  On Arch and most modern Linux distributions domain name resolution is handled
  via the `systemd-resolved` #systemd configuration.

  Within the system systemd files this will be handled in
  `/etc/systemd/network/wired.network` or `/etc/systemd/network/wlan.network`
  files etc.

  In the file below I have turned of DHCP routing and specified the address of my
  Pi-Hole device to handle domain name resolution:

  ```
  #wlan.network

  [Match]
  Name=wl*

  [Link]
  RequiredForOnline=routable

  [Network]
  DHCP=yes
  DNS=192.168.0.4
  MulticastDNS=yes

  [DHCPv4]
  UseDNS=no
  RouteMetric=600

  [IPv6AcceptRA]
  RouteMetric=600

  ```

  This stops it using the default DNS server of my router and runs the resolutions
  against Pi-Hole.

  This should automatically update the two `resolv.conf` files (which should be in
  syc), viz:

  ```
  # File: /run/NetworkManager/resolv.conf
  # Generated by NetworkManager
  nameserver 192.168.0.4
  nameserver fded:2060:8671:0:681:9bff:fe9b:37f0

  ```

  ```
  # File: /etc/resolv.conf
  # Generated by NetworkManager
  nameserver 192.168.0.4
  nameserver fded:2060:8671:0:681:9bff:fe9b:37f0

  ```

  ## Related notes

  ![Network scanning](./Network_scanning.md)
  ![Network debugging](Network_debugging.md)


path: Sets_in_Python.md
sortable_path: Sets_in_Python.md
title: Sets in Python
checksum: 766c8d1f8a0736bf3d4e5e1f15ba881fe068dd8f6f2b0d8888c184a25608328a
created: 2024-10-22 16:52:58.870342704+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["python","data-structures"]}
lead:
  - They are **unordered**
  - You can increase/decrease their length by adding/removing new members
  - They **do not allow duplicate members**
  - **Can only hold immutable objects**

body:
  - They are **unordered**
  - You can increase/decrease their length by adding/removing new members
  - They **do not allow duplicate members**
  - **Can only hold immutable objects**

  > Sets are denoted with `{...}`

  ## Basic usage

  ```python
  basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
  print(basket)  # show that duplicates have been removed
  print(len(basket))
  # {'apple', 'pear', 'banana', 'orange'}
  # 4
  ```

  ## Looping through sets

  ```python
  for item in basket:
      print(item)

  """
  apple
  pear
  banana
  orange
  """
  ```

  ## Check for membership

  ```python
  basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
  print('apple' in basket)
  # True
  ```

  ## Remove items from set

  > `remove` will raise an error if the specified item does not exist, `discard`
  > will not

  ```python
  basket.remove('apple')
  basket.discard('apricot')
  print(basket)
  # {'pear', 'banana', 'orange'}

  basket.clear()
  print(basket)
  #set

  ```

  ## Add items to a set

  ```python
  basket.add('apricot')
  print(basket)
  # {'apricot', 'pear', 'banana', 'orange'}
  ```

  ## Start with empty set

  To declare an empty set you cannot just do:

  ```py
  my_set = {}
  ```

  You have to use a constructor:

  ```py
  my_set = set()
  ```

  ## Apply unions and intersections

  ```python
  s1 = {'apple', 'orange', 'banana'}
  s2 = {'grapefruit', 'lime', 'banana'}
  print('Union:', s1 | s2)
  # Union: {'apple', 'orange', 'grapefruit', 'lime', 'banana'}

  print('Intersection:', s1 & s2)
  # Intersection: {'banana'}


  print('Difference:', s1 - s2)
  # Difference: {'orange', 'apple'}

  print('Symmetric Difference:', s1 ^ s2)
  #Symmetric Difference: {'apple', 'orange', 'grapefruit', 'lime'}

  ```

raw_content:
  ---
  tags: [python, data-structures]
  ---

  # Sets in Python

  - They are **unordered**
  - You can increase/decrease their length by adding/removing new members
  - They **do not allow duplicate members**
  - **Can only hold immutable objects**

  > Sets are denoted with `{...}`

  ## Basic usage

  ```python
  basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
  print(basket)  # show that duplicates have been removed
  print(len(basket))
  # {'apple', 'pear', 'banana', 'orange'}
  # 4
  ```

  ## Looping through sets

  ```python
  for item in basket:
      print(item)

  """
  apple
  pear
  banana
  orange
  """
  ```

  ## Check for membership

  ```python
  basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
  print('apple' in basket)
  # True
  ```

  ## Remove items from set

  > `remove` will raise an error if the specified item does not exist, `discard`
  > will not

  ```python
  basket.remove('apple')
  basket.discard('apricot')
  print(basket)
  # {'pear', 'banana', 'orange'}

  basket.clear()
  print(basket)
  #set

  ```

  ## Add items to a set

  ```python
  basket.add('apricot')
  print(basket)
  # {'apricot', 'pear', 'banana', 'orange'}
  ```

  ## Start with empty set

  To declare an empty set you cannot just do:

  ```py
  my_set = {}
  ```

  You have to use a constructor:

  ```py
  my_set = set()
  ```

  ## Apply unions and intersections

  ```python
  s1 = {'apple', 'orange', 'banana'}
  s2 = {'grapefruit', 'lime', 'banana'}
  print('Union:', s1 | s2)
  # Union: {'apple', 'orange', 'grapefruit', 'lime', 'banana'}

  print('Intersection:', s1 & s2)
  # Intersection: {'banana'}


  print('Difference:', s1 - s2)
  # Difference: {'orange', 'apple'}

  print('Symmetric Difference:', s1 ^ s2)
  #Symmetric Difference: {'apple', 'orange', 'grapefruit', 'lime'}

  ```


path: Setting_up_and_configuring_TypeScript.md
sortable_path: Setting_up_and_configuring_TypeScript.md
title: Setup and configuration
lead: ## Installation and configuration
checksum: 7ad962fc516dcea03815580140ae449dfaeb4c74b9e330cc4b8199af5454edf1
created: 2024-10-22 16:52:58.876718959+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["typescript"]}
body:
  ## Installation and configuration

  TypeScript offers custom installations for most modern JS-based frameworks
  including Webpack, React.js and Vue.js. The instructions below cover minimal TS
  set-up outside of a specific framework. TypeScript adds an additional step to
  any build process since your code must compile to JS before any bundling and
  transpilation can occur.

  ## Installation

  ```
  mkdir typescript-project
  cd typescript-project
  npm i typescript --save-dev
  ```

  ## Initialise project

  ```
  npx tsc --init
  ```

  ## Basic configuration

  ```json
  "compilerOptions": {
  	"target" : "es2020", //es2015 for prod
  	"module" : "es2020",
    	"allowJs": true,
    	"checkJs": true,
    	"strict": true,
    	"esModuleInterop": true
  }
  ```

  ### Specify output paths (for production code)

  ```json
  "compilerOptions": {
  	...
  	"outDir": "dist",
  	"sourceMap": true,
  }
  ```

  ### Compile-time commands

  ```
  tsc --noEmit
  ```

  ```
  tsc --noEmit --watch
  ```

  ## Global requirements

  ```
  npm install -g typescript
  npm install -g ts-node
  ```

  `ts-node` allows you to run TS files the way you would with standard JS, i.e.
  `node [filename]`, you just use `ts-node filename` .

  This is a convenience that saves you compiling from TS → JS and then running
  Node against the compiled JS. Which is useful for debugging and checking values
  as you work.

  ## Imports and exports

  You may wish to define your custom types outside of their application, for
  instance in a `/types/` directory. The custom type or types can then be imported
  using standard ES6 imports:

  ```tsx
  export type Article = {
  		price: number,
  }

  export type AnotherType = {
  ...
  }
  ```

  ```tsx
  import { Article, AnotherType } from "./types/allTypes.js";
  import Article from "./types/allTypes.js";
  ```

  TypeScript also provides a specific import keyword `type` so that you can
  distinguish type imports from other module imports. This is handy when you have
  multiple components in the one file:

  ```tsx
  import type { Article } from "./types/allTypes.js";
  ```

raw_content:
  ---
  tags:
    - typescript
  ---

  # Setup and configuration

  ## Installation and configuration

  TypeScript offers custom installations for most modern JS-based frameworks
  including Webpack, React.js and Vue.js. The instructions below cover minimal TS
  set-up outside of a specific framework. TypeScript adds an additional step to
  any build process since your code must compile to JS before any bundling and
  transpilation can occur.

  ## Installation

  ```
  mkdir typescript-project
  cd typescript-project
  npm i typescript --save-dev
  ```

  ## Initialise project

  ```
  npx tsc --init
  ```

  ## Basic configuration

  ```json
  "compilerOptions": {
  	"target" : "es2020", //es2015 for prod
  	"module" : "es2020",
    	"allowJs": true,
    	"checkJs": true,
    	"strict": true,
    	"esModuleInterop": true
  }
  ```

  ### Specify output paths (for production code)

  ```json
  "compilerOptions": {
  	...
  	"outDir": "dist",
  	"sourceMap": true,
  }
  ```

  ### Compile-time commands

  ```
  tsc --noEmit
  ```

  ```
  tsc --noEmit --watch
  ```

  ## Global requirements

  ```
  npm install -g typescript
  npm install -g ts-node
  ```

  `ts-node` allows you to run TS files the way you would with standard JS, i.e.
  `node [filename]`, you just use `ts-node filename` .

  This is a convenience that saves you compiling from TS → JS and then running
  Node against the compiled JS. Which is useful for debugging and checking values
  as you work.

  ## Imports and exports

  You may wish to define your custom types outside of their application, for
  instance in a `/types/` directory. The custom type or types can then be imported
  using standard ES6 imports:

  ```tsx
  export type Article = {
  		price: number,
  }

  export type AnotherType = {
  ...
  }
  ```

  ```tsx
  import { Article, AnotherType } from "./types/allTypes.js";
  import Article from "./types/allTypes.js";
  ```

  TypeScript also provides a specific import keyword `type` so that you can
  distinguish type imports from other module imports. This is handy when you have
  multiple components in the one file:

  ```tsx
  import type { Article } from "./types/allTypes.js";
  ```


path: Shell_sessions.md
sortable_path: Shell_sessions.md
title: Shell sessions
lead: ## **Types of shell session**
checksum: 26f3c11dcd8dbb538a894ab5e4ce2d6cb0c116f02b0ae59b2745a90995db3502
created: 2024-10-22 16:52:58.881411442+00:00
modified: 2024-10-19 08:57:41.05474052+00:00
metadata: {"tags":["shell"]}
body:
  ## **Types of shell session**

  Shell sessions can be one of or several instances of the following types:

  - **login shell**
    - A session that must be authenticated such as when you access remote
      resources using SSH
  - **non-login shell**
    - Not the above
  - **interactive shell**
    - A shell session that runs in the terminal and thus that the user can
      _interact_ with
  - **non-interactive shell**
    - A shell session that runs without a terminal

  If you are working with a remote server you will be in an **interactive login
  shell**. If you run a script from the command line you will be in a
  **non-interactive non-login shell**.

  ## Shell sessions and access

  The type of shell session that you are currently in affects the
  [environmental and shell variables](Environmental-and-shell-variables-04d5ec7e8e2b486a93f002bf686e4bbb)
  that you can access. This is because the order in which configuration files are
  read on initialisation differs depending on the type of shell.

  - a session defined as a non-login shell will read `/etc/bash.bashrc` and then
    the user-specific `~/.bashrc` file to build its environment.
  - A session started as a login session will read configuration details from the
    `/etc/profile` file first. It will then look for the first login shell
    configuration file in the user’s home directory to get user-specific
    configuration details.

  In Linux, if you want the environmental variable to be accessible from both
  login and non-login shells, you must put them in `~/.bashrc`

raw_content:
  ---
  tags:
    - shell
  ---

  # Shell sessions

  ## **Types of shell session**

  Shell sessions can be one of or several instances of the following types:

  - **login shell**
    - A session that must be authenticated such as when you access remote
      resources using SSH
  - **non-login shell**
    - Not the above
  - **interactive shell**
    - A shell session that runs in the terminal and thus that the user can
      _interact_ with
  - **non-interactive shell**
    - A shell session that runs without a terminal

  If you are working with a remote server you will be in an **interactive login
  shell**. If you run a script from the command line you will be in a
  **non-interactive non-login shell**.

  ## Shell sessions and access

  The type of shell session that you are currently in affects the
  [environmental and shell variables](Environmental-and-shell-variables-04d5ec7e8e2b486a93f002bf686e4bbb)
  that you can access. This is because the order in which configuration files are
  read on initialisation differs depending on the type of shell.

  - a session defined as a non-login shell will read `/etc/bash.bashrc` and then
    the user-specific `~/.bashrc` file to build its environment.
  - A session started as a login session will read configuration details from the
    `/etc/profile` file first. It will then look for the first login shell
    configuration file in the user’s home directory to get user-specific
    configuration details.

  In Linux, if you want the environmental variable to be accessible from both
  login and non-login shells, you must put them in `~/.bashrc`


path: Signed_and_unsigned_numbers.md
sortable_path: Signed_and_unsigned_numbers.md
title: Signed and unsigned numbers
lead: ## Summary
checksum: 0dd060a4662e8b9daf6a58073529520afc171a28313f6d60e2b57179598a782c
created: 2024-10-22 16:52:58.901640928+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["binary"]}
body:
  ## Summary

  - To represent negative integers in binary we use signed numbers._Signed binary_
    includes negative integers, _unsigned binary_ does not.

  - There are different methods of implementing the encoding of signed numbers.
    Two prominant approaches are **two's complement** and **signed magnitude
    representation**.

  ## Related notes

  [[Twos_complement]], [[Binary_encoding]], [[Signed_magnitude_representation]]

raw_content:
  ---
  tags:
    - binary
  ---

  # Signed and unsigned numbers

  ## Summary

  - To represent negative integers in binary we use signed numbers._Signed binary_
    includes negative integers, _unsigned binary_ does not.

  - There are different methods of implementing the encoding of signed numbers.
    Two prominant approaches are **two's complement** and **signed magnitude
    representation**.

  ## Related notes

  [[Twos_complement]], [[Binary_encoding]], [[Signed_magnitude_representation]]


path: Signed_magnitude_representation.md
sortable_path: Signed_magnitude_representation.md
title: Signed magnitude representation
lead: ## Summary
checksum: e67bf5f2f83b3ec515f89bcc2427c89b3922143c936bd0c8130a383a4c654f62
created: 2024-10-22 16:52:58.936761005+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"created":"Wednesday, March 20, 2024","id":"3wsh","tags":["binary"]}
body:
  ## Summary

  ## Detail

  The concept behind SMR is similar to how we designate positive and negative
  integers in denary mathematics: we use a dedicated symbol (`-`) to signpost that
  the integer is negative. In the binary application the dedicated symbol is one
  of the binary digits that comprise the number.

  Typically `0` is used to indicate an unsigned (positive) integer and `1` to
  indicate a signed (negative) number. The rest of the bits are the magnitude (the
  actual numerical value).

  We can demonstrate with an 8-bit binary system, encoding `5`: `0000 0101`. Here
  the furthest bit (aka. the "most significant bit" (MSB)) at the $2^8$ position
  is `0`, designating that the number is unsigned. The signed equivalent is
  `1000 0101`, with the MSB being `1` designating the number as signed. When we
  are working with signed numbers, the MSB is known as the **signed bit**.

  ### Advantages

  ### Limitations

  ## Applications

  ## Related notes

  [[Signed_and_unsigned_numbers|signed_and_unsigned_numbers]]

raw_content:
  ---
  id: 3wsh
  tags:
    - binary
  created: Wednesday, March 20, 2024
  ---

  # Signed magnitude representation

  ## Summary

  ## Detail

  The concept behind SMR is similar to how we designate positive and negative
  integers in denary mathematics: we use a dedicated symbol (`-`) to signpost that
  the integer is negative. In the binary application the dedicated symbol is one
  of the binary digits that comprise the number.

  Typically `0` is used to indicate an unsigned (positive) integer and `1` to
  indicate a signed (negative) number. The rest of the bits are the magnitude (the
  actual numerical value).

  We can demonstrate with an 8-bit binary system, encoding `5`: `0000 0101`. Here
  the furthest bit (aka. the "most significant bit" (MSB)) at the $2^8$ position
  is `0`, designating that the number is unsigned. The signed equivalent is
  `1000 0101`, with the MSB being `1` designating the number as signed. When we
  are working with signed numbers, the MSB is known as the **signed bit**.

  ### Advantages

  ### Limitations

  ## Applications

  ## Related notes

  [[Signed_and_unsigned_numbers|signed_and_unsigned_numbers]]


path: Single_file_Python_scripts.md
sortable_path: Single_file_Python_scripts.md
title: Single file Python scripts
lead: ## Basic architecture
checksum: 2d0d41cfcee32ec153064d4c0d1406b519060f4d9e532ac7c3ec99c23417e087
created: 2024-10-22 16:52:58.945524946+00:00
modified: 2024-10-19 08:57:54.981643088+00:00
metadata: {"created":"Friday, April 26, 2024","id":"atzw","tags":[]}
body:
  ## Basic architecture

  ```py
  #! /usr/local/bin/python3

  import sys

  if __name__ == "__main__":
      all_args = sys.argv
      specific_arg = sys.argv[0]
      # Main functionality...
  ```

  When you run a script (module) Python assigns the string `__main__` to the
  `__name__` attribute to the script that is being executed.

  If you run the script as an import into another script, the `__name__` attribute
  of the imported module is set to the module name, not `__main__`.

  Everything can go under the `__main__` conditional, or, for better readability,
  you can define a `main` function that is then invoked, e.g:

  ```py

  def main():
      # Do some stuff


  if __name__ == "__main__":
      main()

  ```

  ## Related notes

  ![Python modules and imports](./Python_modules_and_imports.md)

raw_content:
  ---
  id: atzw
  tags: []
  created: Friday, April 26, 2024
  ---

  # Single file Python scripts

  ## Basic architecture

  ```py
  #! /usr/local/bin/python3

  import sys

  if __name__ == "__main__":
      all_args = sys.argv
      specific_arg = sys.argv[0]
      # Main functionality...
  ```

  When you run a script (module) Python assigns the string `__main__` to the
  `__name__` attribute to the script that is being executed.

  If you run the script as an import into another script, the `__name__` attribute
  of the imported module is set to the module name, not `__main__`.

  Everything can go under the `__main__` conditional, or, for better readability,
  you can define a `main` function that is then invoked, e.g:

  ```py

  def main():
      # Do some stuff


  if __name__ == "__main__":
      main()

  ```

  ## Related notes

  ![Python modules and imports](./Python_modules_and_imports.md)


path: Singleton_pattern.md
sortable_path: Singleton_pattern.md
title: Singleton pattern
checksum: ebd298f9be5d23d7e04ae08f387e54cf7a68057fd40d34209959ea08cbbe4e70
created: 2024-10-22 16:52:58.950352915+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["OOP","design-patterns"]}
lead:
  As the name suggests, the Singleton pattern is best used in scenarios where you
  only want to use a single, unique instance of a class. This is for functionality
  that should only be executed once in the lifecycle of an application. The
  canonical case would be some kind of bootstrapping process required at
  initialisation, or initial configuration for an app.

body:
  As the name suggests, the Singleton pattern is best used in scenarios where you
  only want to use a single, unique instance of a class. This is for functionality
  that should only be executed once in the lifecycle of an application. The
  canonical case would be some kind of bootstrapping process required at
  initialisation, or initial configuration for an app.

  ## Use-cases

  - Establishing connection to backend database
  - API connection (managing tokens so that you are not sending multiple
    unnecessary calls)
  - Configuration settings for an app
  - Global state stores in Redux and the React Context API

  > With this pattern only one instance of the class can exist. If no instance of
  > the singleton class exists then a new instance is created and returned, but if
  > an instance already exists, then the reference to the existing instance is
  > returned. A singleton should be immutable by the consuming code, and there
  > should be no danger of instantiating more than one of them.

  ## Implementation

  This pattern can be implemented either with a object or a class.

  In the example below, a global store for a modern JS application is created
  using an object.

  ### Using an object

  We ensure singularity by:

  - using `const` for the object so that it cannot be reassigned
  - using `Object.freeze` so that the fields cannot be overwritten, deleted, or
    additional fields added

  ```js
  const _data = [];

  // Methods to add and retrieve data from the store
  const UserStore = {
    add: (item) => _data.push(item),
    get: (id) => _data.find((d) => d.id === id),
  };

  Object.freeze(UserStore);
  export default UserStore;
  ```

  ### Using a class

  The same process can be created using classes:

  ```js
  class UserStore {
    constructor() {
      this._data = [];
    }

    add(item) {
      this._data.push(item);
    }

    get(id) {
      return this._data.find((d) => d.id === id);
    }
  }

  const instance = new UserStore();
  Object.freeze(instance);

  export default instance;
  ```

  Bear in mind that this is really just an ES6 module with `freeze` applied. The
  only real difference between a singleton and a module is that there should only
  be one instance of it.

  With classes, there is a further step that can be taken to prevent more than
  once instance of the class being created. This is necessary if you want to be
  really strict because it is of course possible for a well motivated person to
  get around the safeguards we established above. We do this by having the class
  itself check whether there is already an instance of itself. If so, it just
  returns the already existing instance. We do this by using the self-reference
  inherent in `this` :

  ```jsx
  class UserStore {
    constructor() {
      if (!UserStore.instance) {
        this._data = [];
        UserStore.instance = this;
      }

      return UserStore.instance;
    }

    //rest is the same code as preceding example
  }

  const instance = new UserStore();
  Object.freeze(instance);

  export default instance;
  ```

raw_content:
  ---
  tags:
    - OOP
    - design-patterns
  ---

  # Singleton pattern

  As the name suggests, the Singleton pattern is best used in scenarios where you
  only want to use a single, unique instance of a class. This is for functionality
  that should only be executed once in the lifecycle of an application. The
  canonical case would be some kind of bootstrapping process required at
  initialisation, or initial configuration for an app.

  ## Use-cases

  - Establishing connection to backend database
  - API connection (managing tokens so that you are not sending multiple
    unnecessary calls)
  - Configuration settings for an app
  - Global state stores in Redux and the React Context API

  > With this pattern only one instance of the class can exist. If no instance of
  > the singleton class exists then a new instance is created and returned, but if
  > an instance already exists, then the reference to the existing instance is
  > returned. A singleton should be immutable by the consuming code, and there
  > should be no danger of instantiating more than one of them.

  ## Implementation

  This pattern can be implemented either with a object or a class.

  In the example below, a global store for a modern JS application is created
  using an object.

  ### Using an object

  We ensure singularity by:

  - using `const` for the object so that it cannot be reassigned
  - using `Object.freeze` so that the fields cannot be overwritten, deleted, or
    additional fields added

  ```js
  const _data = [];

  // Methods to add and retrieve data from the store
  const UserStore = {
    add: (item) => _data.push(item),
    get: (id) => _data.find((d) => d.id === id),
  };

  Object.freeze(UserStore);
  export default UserStore;
  ```

  ### Using a class

  The same process can be created using classes:

  ```js
  class UserStore {
    constructor() {
      this._data = [];
    }

    add(item) {
      this._data.push(item);
    }

    get(id) {
      return this._data.find((d) => d.id === id);
    }
  }

  const instance = new UserStore();
  Object.freeze(instance);

  export default instance;
  ```

  Bear in mind that this is really just an ES6 module with `freeze` applied. The
  only real difference between a singleton and a module is that there should only
  be one instance of it.

  With classes, there is a further step that can be taken to prevent more than
  once instance of the class being created. This is necessary if you want to be
  really strict because it is of course possible for a well motivated person to
  get around the safeguards we established above. We do this by having the class
  itself check whether there is already an instance of itself. If so, it just
  returns the already existing instance. We do this by using the self-reference
  inherent in `this` :

  ```jsx
  class UserStore {
    constructor() {
      if (!UserStore.instance) {
        this._data = [];
        UserStore.instance = this;
      }

      return UserStore.instance;
    }

    //rest is the same code as preceding example
  }

  const instance = new UserStore();
  Object.freeze(instance);

  export default instance;
  ```


path: Sliding_window.md
sortable_path: Sliding_window.md
title: Sliding window
checksum: 0875187e1b5247dc2b2f87bb9c15e41780ada662f3b8e6efbbcd79a6d0a7b251
created: 2024-10-22 16:52:58.956029063+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["design-patterns"]}
lead:
  Whereas the [multiple pointer](Multiple_pointers.md) pattern works by keeping
  two array indices running at once and comparing their values on each iteration,
  the sliding window has a running value (the 'window') that is updated at each
  iteration and which compares itself against its most recent previous value.

body:
  Whereas the [multiple pointer](Multiple_pointers.md) pattern works by keeping
  two array indices running at once and comparing their values on each iteration,
  the sliding window has a running value (the 'window') that is updated at each
  iteration and which compares itself against its most recent previous value.

  This is what makes it 'sliding': the value isn't constant, it changes (or
  doesn't) based on what it was previously.

  ## Example: maximum sum of sub-array

  We create a function that takes an array and a sub-array length _n_. The
  objective is to find the maximum value that can be created by summing _n_
  elements of the array. It moves through the array, summing by _n_ keeping track
  of the highest sum value so far and the current sum value. At the end it should
  return the highest possible sum value.

  Here we do this for a sub-array of length 2:

  ```ts
  function maxSubarraySum(arr, subArrLength) {
    let maxSum = 0; // Largest sum value so far
    let tempSum = 0; // Current sum value

    //  Establish the first 'maxSum'
    // At the beginning this will just be the sum of the first two array elements
    for (let i = 0; i < subArrLength; i++) {
      maxSum += arr[i];
    }

    // Map temporary sum to maxSum
    // Accordingly, as we have only mapped the first sub-array, the max sum will be the same
    // as the current sum
    tempSum = maxSum;

    // Move through the array one element at a time (`i++`) via a window starting from the element that is equal to `subArrLength`
    // The first sum calculation is already taken care of in the earlier loop and stored in `maxSum`, so we don't have to worry about missing the elements in indices less than subArrLength
    for (let i = subArrLength; i < arr.length; i++) {
      // Temp sum becomes a moveable window value equal to the subtraction of the previous element and the addition of next element in line
      tempSum = tempSum - arr[i - subArrLength] + arr[i];
      // Max sum is redefined as the largest subArrLengthber between the previous highest and the current value of tempSum
      maxSum = Math.max(maxSum, tempSum);
    }
    return maxSum;
  }

  console.log(maxSubarraySum([100, 200, 300, 400], 2)); // 700
  ```

raw_content:
  ---
  tags:
    - design-patterns
  ---

  # Sliding window

  Whereas the [multiple pointer](Multiple_pointers.md) pattern works by keeping
  two array indices running at once and comparing their values on each iteration,
  the sliding window has a running value (the 'window') that is updated at each
  iteration and which compares itself against its most recent previous value.

  This is what makes it 'sliding': the value isn't constant, it changes (or
  doesn't) based on what it was previously.

  ## Example: maximum sum of sub-array

  We create a function that takes an array and a sub-array length _n_. The
  objective is to find the maximum value that can be created by summing _n_
  elements of the array. It moves through the array, summing by _n_ keeping track
  of the highest sum value so far and the current sum value. At the end it should
  return the highest possible sum value.

  Here we do this for a sub-array of length 2:

  ```ts
  function maxSubarraySum(arr, subArrLength) {
    let maxSum = 0; // Largest sum value so far
    let tempSum = 0; // Current sum value

    //  Establish the first 'maxSum'
    // At the beginning this will just be the sum of the first two array elements
    for (let i = 0; i < subArrLength; i++) {
      maxSum += arr[i];
    }

    // Map temporary sum to maxSum
    // Accordingly, as we have only mapped the first sub-array, the max sum will be the same
    // as the current sum
    tempSum = maxSum;

    // Move through the array one element at a time (`i++`) via a window starting from the element that is equal to `subArrLength`
    // The first sum calculation is already taken care of in the earlier loop and stored in `maxSum`, so we don't have to worry about missing the elements in indices less than subArrLength
    for (let i = subArrLength; i < arr.length; i++) {
      // Temp sum becomes a moveable window value equal to the subtraction of the previous element and the addition of next element in line
      tempSum = tempSum - arr[i - subArrLength] + arr[i];
      // Max sum is redefined as the largest subArrLengthber between the previous highest and the current value of tempSum
      maxSum = Math.max(maxSum, tempSum);
    }
    return maxSum;
  }

  console.log(maxSubarraySum([100, 200, 300, 400], 2)); // 700
  ```


path: Solving_equations.md
sortable_path: Solving_equations.md
title: Solving equations
lead: ## Use inversion of operators
checksum: 9e92801bf8f641a6153875adfecdb611e634a9cc7d61d3dc7f72993961c41550
created: 2024-10-22 16:52:58.968721089+00:00
modified: 2024-10-19 08:58:04.678474029+00:00
metadata: {"tags":["algebra"]}
body:
  ## Use inversion of operators

  When solving equations we frequently make use of the
  [ operator inversion rules](Inversion%20of%20operators.md) to find the
  solutions.

  ### Example: inversion of addition

  For example, the equation $9 = 3 + x$ has the solution $6$ ($x$ is equal to
  $6$). To arrive at this, we can use the inverse of the main operator in the
  equation (addition): $9-3 = 6$.

  ### Example: inversion of subtraction

  Now consider $19 = x - 3$. The solution to this equation is $22$ ($x$ is equal
  to $22$). To arrive at this, we can use the inverse of the main operator in the
  equation (subtraction): $19 + 3 = 22$.

  ### Example: inversion of division

  The equation we want to solve: $$\frac{x}{6} = 4$$

  Now we invert it by multiplying the denominator by the quotient:
  $6\cdot 4 = 24$. Therefore: $$ \frac{24}{6} = 4$$
  The solution is $24$

  ### Example: inversion of multiplication

  The equation we want to solve: $$4x = 36$$ Now we invert it by dividing the
  product by the coefficient: !Add link to 'coefficient'

  $$\frac{36}{4} = 9$$

  Therefore the solution is $9$: $$ 4(9) = 36$$

raw_content:
  ---
  tags: [algebra]
  ---

  # Solving equations

  ## Use inversion of operators

  When solving equations we frequently make use of the
  [ operator inversion rules](Inversion%20of%20operators.md) to find the
  solutions.

  ### Example: inversion of addition

  For example, the equation $9 = 3 + x$ has the solution $6$ ($x$ is equal to
  $6$). To arrive at this, we can use the inverse of the main operator in the
  equation (addition): $9-3 = 6$.

  ### Example: inversion of subtraction

  Now consider $19 = x - 3$. The solution to this equation is $22$ ($x$ is equal
  to $22$). To arrive at this, we can use the inverse of the main operator in the
  equation (subtraction): $19 + 3 = 22$.

  ### Example: inversion of division

  The equation we want to solve: $$\frac{x}{6} = 4$$

  Now we invert it by multiplying the denominator by the quotient:
  $6\cdot 4 = 24$. Therefore: $$ \frac{24}{6} = 4$$
  The solution is $24$

  ### Example: inversion of multiplication

  The equation we want to solve: $$4x = 36$$ Now we invert it by dividing the
  product by the coefficient: !Add link to 'coefficient'

  $$\frac{36}{4} = 9$$

  Therefore the solution is $9$: $$ 4(9) = 36$$


path: Sorting_lists_in_Python.md
sortable_path: Sorting_lists_in_Python.md
title: Sorting lists in Python
lead: ### Using `sorted()` Function:
checksum: ac48196d81b67d9d944918b8fa4b520193798f40404205ad6e31b34dec410986
created: 2024-10-22 16:52:58.984763534+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["python"]}
body:
  ### Using `sorted()` Function:

  The built-in `sorted()` function returns a new list containing all the elements
  of the original list sorted in the specified order (either ascending, which is
  default, or descending).

  ```python
  my_list = [3, 1, 4, 1, 5, 9, 2, 6, 5]
  sorted_list = sorted(my_list)
  print(sorted_list)  # Output: [1, 1, 2, 3, 4, 5, 5, 6, 9]
  ```

  For sorting in descending order, you can use the `reverse=True` argument:

  ```python
  sorted_list_desc = sorted(my_list, reverse=True)
  print(sorted_list_desc)  # Output: [9, 6, 5, 5, 4, 3, 2, 1, 1]
  ```

  ### Using `.sort()` Method:

  The `.sort()` method sorts the elements of a list in-place and returns `None`.
  This modifies the original list.

  ```python
  my_list = [3, 1, 4, 1, 5, 9, 2, 6, 5]
  my_list.sort()
  print(my_list)  # Output: [1, 1, 2, 3, 4, 5, 5, 6, 9]
  ```

  For sorting in descending order:

  ```python
  my_list.sort(reverse=True)
  print(my_list)  # Output: [9, 6, 5, 5, 4, 3, 2, 1, 1]
  ```

  ### Using a Custom Key Function:

  Both `sorted()` and `.sort()` accept a `key` parameter that allows you to
  specify a function to determine the sorting order. The function should take a
  single argument and return a value that will be used for sorting purposes.

  For example, if you have a list of strings and you want to sort them by their
  length:

  ```python
  my_str_list = ["apple", "banana", "cherry", "date"]
  sorted_by_length = sorted(my_str_list, key=len)
  print(sorted_by_length)  # Output: ['date', 'apple', 'cherry', 'banana']
  ```

raw_content:
  ---
  tags: [python]
  ---

  # Sorting lists in Python

  ### Using `sorted()` Function:

  The built-in `sorted()` function returns a new list containing all the elements
  of the original list sorted in the specified order (either ascending, which is
  default, or descending).

  ```python
  my_list = [3, 1, 4, 1, 5, 9, 2, 6, 5]
  sorted_list = sorted(my_list)
  print(sorted_list)  # Output: [1, 1, 2, 3, 4, 5, 5, 6, 9]
  ```

  For sorting in descending order, you can use the `reverse=True` argument:

  ```python
  sorted_list_desc = sorted(my_list, reverse=True)
  print(sorted_list_desc)  # Output: [9, 6, 5, 5, 4, 3, 2, 1, 1]
  ```

  ### Using `.sort()` Method:

  The `.sort()` method sorts the elements of a list in-place and returns `None`.
  This modifies the original list.

  ```python
  my_list = [3, 1, 4, 1, 5, 9, 2, 6, 5]
  my_list.sort()
  print(my_list)  # Output: [1, 1, 2, 3, 4, 5, 5, 6, 9]
  ```

  For sorting in descending order:

  ```python
  my_list.sort(reverse=True)
  print(my_list)  # Output: [9, 6, 5, 5, 4, 3, 2, 1, 1]
  ```

  ### Using a Custom Key Function:

  Both `sorted()` and `.sort()` accept a `key` parameter that allows you to
  specify a function to determine the sorting order. The function should take a
  single argument and return a value that will be used for sorting purposes.

  For example, if you have a list of strings and you want to sort them by their
  length:

  ```python
  my_str_list = ["apple", "banana", "cherry", "date"]
  sorted_by_length = sorted(my_str_list, key=len)
  print(sorted_by_length)  # Output: ['date', 'apple', 'cherry', 'banana']
  ```


path: Soundness.md
sortable_path: Soundness.md
title: Soundness
checksum: d7502906245e61759a05792bb1ac1529a125a95843565c46f58eb1f289042564
created: 2024-10-22 16:52:58.989499188+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  Recall that in the definition of
  [deductive validity](Validity_and_entailment.md#validity)
  we do not say: an argument is valid iff if the premises _are true_ and the
  conclusion _is true_. We say _if it is possible for the premises to be true_.
  This is important: we are not interested in the actual truth of the premises or
  the conclusion.

body:
  Recall that in the definition of
  [deductive validity](Validity_and_entailment.md#validity)
  we do not say: an argument is valid iff if the premises _are true_ and the
  conclusion _is true_. We say _if it is possible for the premises to be true_.
  This is important: we are not interested in the actual truth of the premises or
  the conclusion.

  #### Demonstration

  Therefore this argument is valid:

  ```
  (P1) Oranges are the same colour as bananas.
  (P2) Bananas are yellow.
  ____________________________________________
  (C) Oranges are yellow.
  ```

  Of course oranges are not yellow but _were_ (P1) true, then given (P2), the
  conclusion must also be true.

  This argument is also valid:

  ```
  (P1) Oranges are the same colour as carrots.
  (P2) Carrots are orange.
  ____________________________________________
  (C) Oranges are orange in colour.
  ```

  The difference here is that the premises happen to be true and, given that the
  argument is valid, the conclusion must also be true. What we have defined here
  is **soundness**: the argument is said to be sound as well as valid. This is an
  additional and stronger criterion of evaluation.

  > An argument is sound if and only if it is deductively valid and all its
  > premises are true.

  We must not forget that truth alone is not the sole condition for soundness. We
  can have arguments whose conclusion and premises are all true without the
  argument being sound:

  ```
  (P1) London is the capital of the United Kingdom
  (P2) The capital of the United Kingdom is in the southern part of the United Kingdom.
  (P3) Cambridge is not the capital of the United Kingdom
  ____________________________________________
  (C) London is south of Cambridge
  ```

  All propositions are true here but the argument is not deductively valid: the
  premises are all true but the conclusion is false.

  We can also have arguments which are valid but which are not sound:

  ```
  (P1) Vitamin C prevents colds.
  (P2) Vitamin C does not prevent colds.
  ____________________________________________
  (C) Vitamin C is harmless
  ```

  This argument is valid because we cannot consistently assert the premises and
  deny the conclusion. In either case, the conclusion can be said to follow from
  the premises. The problem is that we cannot consistently assert both premises:
  it is not possible for both propositions to be true at the same time.

raw_content:
  ---
  tags:
    - propositional-logic
    - logic
  ---

  # Soundness

  Recall that in the definition of
  [deductive validity](Validity_and_entailment.md#validity)
  we do not say: an argument is valid iff if the premises _are true_ and the
  conclusion _is true_. We say _if it is possible for the premises to be true_.
  This is important: we are not interested in the actual truth of the premises or
  the conclusion.

  #### Demonstration

  Therefore this argument is valid:

  ```
  (P1) Oranges are the same colour as bananas.
  (P2) Bananas are yellow.
  ____________________________________________
  (C) Oranges are yellow.
  ```

  Of course oranges are not yellow but _were_ (P1) true, then given (P2), the
  conclusion must also be true.

  This argument is also valid:

  ```
  (P1) Oranges are the same colour as carrots.
  (P2) Carrots are orange.
  ____________________________________________
  (C) Oranges are orange in colour.
  ```

  The difference here is that the premises happen to be true and, given that the
  argument is valid, the conclusion must also be true. What we have defined here
  is **soundness**: the argument is said to be sound as well as valid. This is an
  additional and stronger criterion of evaluation.

  > An argument is sound if and only if it is deductively valid and all its
  > premises are true.

  We must not forget that truth alone is not the sole condition for soundness. We
  can have arguments whose conclusion and premises are all true without the
  argument being sound:

  ```
  (P1) London is the capital of the United Kingdom
  (P2) The capital of the United Kingdom is in the southern part of the United Kingdom.
  (P3) Cambridge is not the capital of the United Kingdom
  ____________________________________________
  (C) London is south of Cambridge
  ```

  All propositions are true here but the argument is not deductively valid: the
  premises are all true but the conclusion is false.

  We can also have arguments which are valid but which are not sound:

  ```
  (P1) Vitamin C prevents colds.
  (P2) Vitamin C does not prevent colds.
  ____________________________________________
  (C) Vitamin C is harmless
  ```

  This argument is valid because we cannot consistently assert the premises and
  deny the conclusion. In either case, the conclusion can be said to follow from
  the premises. The problem is that we cannot consistently assert both premises:
  it is not possible for both propositions to be true at the same time.


path: Split_into_array_in_Bash.md
sortable_path: Split_into_array_in_Bash.md
title: Splitting input into an array
lead: ## readarray
checksum: b613cfe6a16f00e820880c671aeeccbf3f0ecdae9e3770fc68016d7b50c6b413
created: 2024-10-22 16:52:59.007685594+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["shell"]}
body:
  ## readarray

  `readarray` makes it really easy to split input into an array based on new
  lines. Say we have this file as input:

  ```
  123
  456
  789
  ```

  Then we can split like so:

  ```bash
  readarray -t name_for_array < ./input.text

  # Print all elements
  echo "${name_for_array[@]}"

  # Print element by index

  echo "${name_for_array[1]}"
  456
  ```

  If we want to read a string directly:

  ```bash
  readarray -t new_name_for_array <<< "here
  is
  some
  text"

  echo "${new_name_for_array[1]}"
  is
  ```

  > The _-t_ flag removes the trailing newline

  See more: https://linuxhint.com/split-string-array-bash/

  ## read

  For delimiters other than a space we have to use `read`, combined with `IFS` the
  **Internal Field Separator**.

  For example, to split by comma:

  ```plaintext
  # comma-input.txt
  something,something else,something more
  ```

  ```bash
  IFS=',' read -a arr < ./comma_inputs.txt
  ```

  > We use the `-a` flag to signal that we want each item read returned as an
  > element in an array

  ## mapfile

  We can use `mapfile` to read the lines of a input file in set segments.

  For the following file:

  ```
  line one
  line two
  line three
  line four
  ```

  We could generate the following output:

  ```
  -----SNIP-----
  line one
  line two
  -----SNIP-----
  line three
  line four
  ```

  With:

  ```bash
  while mapfile -t -n 2 ary && ((${#ary[@]})); do
    printf '%s\n' "${ary[@]}"
    printf -- '--- SNIP ---\n'
   done < $testInput
  ```

  So, while there are input lines, map each line into an array and return it in
  groups of 2.

raw_content:
  ---
  tags:
    - shell
  ---

  # Splitting input into an array

  ## readarray

  `readarray` makes it really easy to split input into an array based on new
  lines. Say we have this file as input:

  ```
  123
  456
  789
  ```

  Then we can split like so:

  ```bash
  readarray -t name_for_array < ./input.text

  # Print all elements
  echo "${name_for_array[@]}"

  # Print element by index

  echo "${name_for_array[1]}"
  456
  ```

  If we want to read a string directly:

  ```bash
  readarray -t new_name_for_array <<< "here
  is
  some
  text"

  echo "${new_name_for_array[1]}"
  is
  ```

  > The _-t_ flag removes the trailing newline

  See more: https://linuxhint.com/split-string-array-bash/

  ## read

  For delimiters other than a space we have to use `read`, combined with `IFS` the
  **Internal Field Separator**.

  For example, to split by comma:

  ```plaintext
  # comma-input.txt
  something,something else,something more
  ```

  ```bash
  IFS=',' read -a arr < ./comma_inputs.txt
  ```

  > We use the `-a` flag to signal that we want each item read returned as an
  > element in an array

  ## mapfile

  We can use `mapfile` to read the lines of a input file in set segments.

  For the following file:

  ```
  line one
  line two
  line three
  line four
  ```

  We could generate the following output:

  ```
  -----SNIP-----
  line one
  line two
  -----SNIP-----
  line three
  line four
  ```

  With:

  ```bash
  while mapfile -t -n 2 ary && ((${#ary[@]})); do
    printf '%s\n' "${ary[@]}"
    printf -- '--- SNIP ---\n'
   done < $testInput
  ```

  So, while there are input lines, map each line into an array and return it in
  groups of 2.


path: Stack_memory.md
sortable_path: Stack_memory.md
title: Stack memory
lead: ## Summary
checksum: a6795eec5cd09a44ca3c911da9b767b0ae15be93d4ca9c1eb6f336d9f6107b2c
created: 2024-10-22 16:52:59.013596588+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"created":"Tuesday, April 16, 2024","id":"1fig","tags":["memory"]}
body:
  ## Summary

  - Along with [[Heap_memory|heap memory]] heap memory, programs make use of
    _stack memory_ during runtime.

  - Best suited for small-sized singular values that are temporary/ephemeral
    rather than large or complex data types that need to persist for a longer
    time.

  - Specifically used to keep track of memory used during function executions
    within a given scope: control flow, local variables, returned value.

  - Uses a LIFO data-structure

  Similarly to the [[Stacks|stack data structure]], it works on the basis of LIFO:
  the last item put on the stack is the first item to be withdrawn.

  Note, this doesn't mean that the data at the memory addresses that comprise the
  stack can _only_ be accessed in a LIFO manner. Any item currently on the stack
  can be accessed (be read or modified) at any time. LIFO applies when it is is
  time to clear memory from the stack: the most recent addition is cleared first
  from the top down.

  ### Stack pointer

  A **stack pointer** is used to manage the items in the stack. This value is
  stored in a [[CPU_architecture#registers|register]] and its value is the memory
  address of the item currently on the top of the stack. Note: _not_ the value
  itself, the _address_ of the value.

  When a new value is added to the top of the stack, the pointer's value is
  adjusted to increase the size of the stack and make room for the new value. So
  this will be a new memory address for the latest item. When an item is removed,
  the reverse happens. The previously top address is cleared and the pointer
  points to the new top item.

  ## Threads

  For every thread that a program spawns, it gets its own stack or "stack frame".
  Stack memory within the thread can only be accessed by processes within that
  thread which is what makes it well suited for managing scoped function memory.

  ### Role in program execution

  1. Function call management: a stack records the key information pertaining to
     the execution of a function: parameters, local variables, the location of the
     address for the returned value.

  2. Local variable storage: storing the variables local to a function's scope and
     removing them once the function returns, avoiding
     [[Memory_leaks|memory leaks]]

  3. Control flow: record from where a function is called and where its return
     value should be stored, meaning the right value is always returned no matter
     how nested it is.

  4. Recursion: individuating each call of a function that calls itself and its
     given step to avoid infinite loops

raw_content:
  ---
  id: 1fig
  tags: [memory]
  created: Tuesday, April 16, 2024
  ---

  # Stack memory

  ## Summary

  - Along with [[Heap_memory|heap memory]] heap memory, programs make use of
    _stack memory_ during runtime.

  - Best suited for small-sized singular values that are temporary/ephemeral
    rather than large or complex data types that need to persist for a longer
    time.

  - Specifically used to keep track of memory used during function executions
    within a given scope: control flow, local variables, returned value.

  - Uses a LIFO data-structure

  Similarly to the [[Stacks|stack data structure]], it works on the basis of LIFO:
  the last item put on the stack is the first item to be withdrawn.

  Note, this doesn't mean that the data at the memory addresses that comprise the
  stack can _only_ be accessed in a LIFO manner. Any item currently on the stack
  can be accessed (be read or modified) at any time. LIFO applies when it is is
  time to clear memory from the stack: the most recent addition is cleared first
  from the top down.

  ### Stack pointer

  A **stack pointer** is used to manage the items in the stack. This value is
  stored in a [[CPU_architecture#registers|register]] and its value is the memory
  address of the item currently on the top of the stack. Note: _not_ the value
  itself, the _address_ of the value.

  When a new value is added to the top of the stack, the pointer's value is
  adjusted to increase the size of the stack and make room for the new value. So
  this will be a new memory address for the latest item. When an item is removed,
  the reverse happens. The previously top address is cleared and the pointer
  points to the new top item.

  ## Threads

  For every thread that a program spawns, it gets its own stack or "stack frame".
  Stack memory within the thread can only be accessed by processes within that
  thread which is what makes it well suited for managing scoped function memory.

  ### Role in program execution

  1. Function call management: a stack records the key information pertaining to
     the execution of a function: parameters, local variables, the location of the
     address for the returned value.

  2. Local variable storage: storing the variables local to a function's scope and
     removing them once the function returns, avoiding
     [[Memory_leaks|memory leaks]]

  3. Control flow: record from where a function is called and where its return
     value should be stored, meaning the right value is always returned no matter
     how nested it is.

  4. Recursion: individuating each call of a function that calls itself and its
     given step to avoid infinite loops


path: Stacks.md
sortable_path: Stacks.md
title: A stack is a linear data structure that observes LIFO
checksum: a141ae765f7298ff9bd03a8d68bdae6ac6ac56e84d1369198460bc254462f9a8
created: 2024-10-22 16:52:59.040483534+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["data-structures"]}
lead:
  Think of a stack like a pile of books: the last book that you add is the nearest
  to the top and therefore the first one that you can remove.

body:
  Think of a stack like a pile of books: the last book that you add is the nearest
  to the top and therefore the first one that you can remove.

  If you want to get a book that is not at the top, you have to first remove the
  books that are above it to get to it.

  This type of data structure is linear and only allows sequential, not random,
  access. It is an example of 'last one in first one out'.

  ## We can build a stack from an array

  A stack is an example of a data structure that can be built by adapting an
  array. If you think about it, all that is needed is an array to store the data,
  an `array push` method to add elements to the 'end' or the 'bottom' of the stack
  and an `array pop` method to remove the element at the top.

  ## Demonstration

  Below we create a stack constructor, using a class. An object created from this
  template will have the following properties and methods:

  - `items[]` → an array to store the data
  - `push()` → a method to add an element to the end of the stack
  - `pop()` → a method to remove an element from the front

  In addition we have the following helpers, which allow us to check the status of
  the stack and retrieve information about it:

  - `isEmpty()` → check if the stack is populated or not
  - `clear()` → empty the stack of its content (therefore making `isEmpty()`
    return `true`)
  - `size` → a property corresponding to the stack's length

  ```js
  class Stack {
    items = []; // the array that will store the elements that comprise the stack
    push = (element) => this.items.push(element); // add an element to the end of the stack
    pop = () => this.items.pop(); // remove and return the last element from the stack

    // We can add some useful helper methods, that return info about the state of the stack:
    isEmpty = () => this.items.length === 0; // return true if the stack is empty
    clear = () => (this.items.length = 0); // empty the stack
    size = () => this.items.length; // count elements in stack
  }
  ```

  ## Run through

  ```js
  let stack = new Stack();
  test.push(1); // Add some elements to the stack
  test.push(2);
  test.push(3);

  // Stack now looks like:
  console.log(stack.items); // [1, 2, 3]

  // Let's try removing the last element
  stack.pop(); // 3 -> this was the last element we added, so it's the first one that comes out

  // Now the stack looks like this:
  // [1,2]

  // Let's add a new element
  test.push(true);

  // Now the stack looks like:
  // [1,2, true]
  ```

  ## Practical applications

  - Any application that wants to got 'back in time' must utilise a stack. For
    example, the 'undo' function in most software is a function of a stack. The
    most recent action is at the top, and under that is the second most recent and
    so on all the way back to the first action.
  - Recursive functions: a function that calls itself repeatedly until a boundary
    condition is met is using a stack structure. As you drill down through the
    function calls you start from the most recent down to the last.
  - Balancing parentheses. Say you want to check if the following string is
    balanced `[()]` . Every time you find an opening parentheses. You push that to
    the front of a stack. You then compare the closing parentheses with the order
    of the stack. The same could be done when seeking to find palindromes. This
    sort of thing could be a code challenge so build an example.

raw_content:
  ---
  tags:
    - data-structures
  ---

  _A stack visualised vertically_ ![stack2.svg](stack2.svg)

  _A stack visualised horizontally_ ![stack1.svg](stack1.svg)

  ## A stack is a linear data structure that observes LIFO

  Think of a stack like a pile of books: the last book that you add is the nearest
  to the top and therefore the first one that you can remove.

  If you want to get a book that is not at the top, you have to first remove the
  books that are above it to get to it.

  This type of data structure is linear and only allows sequential, not random,
  access. It is an example of 'last one in first one out'.

  ## We can build a stack from an array

  A stack is an example of a data structure that can be built by adapting an
  array. If you think about it, all that is needed is an array to store the data,
  an `array push` method to add elements to the 'end' or the 'bottom' of the stack
  and an `array pop` method to remove the element at the top.

  ## Demonstration

  Below we create a stack constructor, using a class. An object created from this
  template will have the following properties and methods:

  - `items[]` → an array to store the data
  - `push()` → a method to add an element to the end of the stack
  - `pop()` → a method to remove an element from the front

  In addition we have the following helpers, which allow us to check the status of
  the stack and retrieve information about it:

  - `isEmpty()` → check if the stack is populated or not
  - `clear()` → empty the stack of its content (therefore making `isEmpty()`
    return `true`)
  - `size` → a property corresponding to the stack's length

  ```js
  class Stack {
    items = []; // the array that will store the elements that comprise the stack
    push = (element) => this.items.push(element); // add an element to the end of the stack
    pop = () => this.items.pop(); // remove and return the last element from the stack

    // We can add some useful helper methods, that return info about the state of the stack:
    isEmpty = () => this.items.length === 0; // return true if the stack is empty
    clear = () => (this.items.length = 0); // empty the stack
    size = () => this.items.length; // count elements in stack
  }
  ```

  ## Run through

  ```js
  let stack = new Stack();
  test.push(1); // Add some elements to the stack
  test.push(2);
  test.push(3);

  // Stack now looks like:
  console.log(stack.items); // [1, 2, 3]

  // Let's try removing the last element
  stack.pop(); // 3 -> this was the last element we added, so it's the first one that comes out

  // Now the stack looks like this:
  // [1,2]

  // Let's add a new element
  test.push(true);

  // Now the stack looks like:
  // [1,2, true]
  ```

  ## Practical applications

  - Any application that wants to got 'back in time' must utilise a stack. For
    example, the 'undo' function in most software is a function of a stack. The
    most recent action is at the top, and under that is the second most recent and
    so on all the way back to the first action.
  - Recursive functions: a function that calls itself repeatedly until a boundary
    condition is met is using a stack structure. As you drill down through the
    function calls you start from the most recent down to the last.
  - Balancing parentheses. Say you want to check if the following string is
    balanced `[()]` . Every time you find an opening parentheses. You push that to
    the front of a stack. You then compare the closing parentheses with the order
    of the stack. The same could be done when seeking to find palindromes. This
    sort of thing could be a code challenge so build an example.


path: Stale_branches_and_pruning.md
sortable_path: Stale_branches_and_pruning.md
title: Stale branches and pruning
checksum: d34c069d9cd0bcb5b5771381334d486c897fc97126ae662b213ca3fd8c8dfd30
created: 2024-10-22 16:52:59.048080428+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["git"]}
lead:
  A **stale branch** is a
  [remote tracking branch](Remote_tracking_branches.md) that **no
  longer tracks anything** because the actual branch on the remote has been
  deleted.

body:
  A **stale branch** is a
  [remote tracking branch](Remote_tracking_branches.md) that **no
  longer tracks anything** because the actual branch on the remote has been
  deleted.

  If _you_ delete the remote branch, the remote tracking branch will also be
  deleted. However if a _collaborator_ deletes the remote branch, your remote
  tracking branch will remain, becoming a stale branch.

  In order to get rid of it, we need to manually remove it. We can do this with
  the general `prune` command:

  ```sh
  git remote prune origin

  # or run a preview of the action

  git remote prune origin --dry-run
  ```

raw_content:
  ---
  tags: [git]
  ---

  # Stale branches and pruning

  A **stale branch** is a
  [remote tracking branch](Remote_tracking_branches.md) that **no
  longer tracks anything** because the actual branch on the remote has been
  deleted.

  If _you_ delete the remote branch, the remote tracking branch will also be
  deleted. However if a _collaborator_ deletes the remote branch, your remote
  tracking branch will remain, becoming a stale branch.

  In order to get rid of it, we need to manually remove it. We can do this with
  the general `prune` command:

  ```sh
  git remote prune origin

  # or run a preview of the action

  git remote prune origin --dry-run
  ```


path: Step_functions_and_state_machines.md
sortable_path: Step_functions_and_state_machines.md
title: AWS Step functions and state machines
checksum: a290427d05b45787caf9f55ac711de8b9361af425c2875ebe09a2fa9ffe38ba2
created: 2024-10-22 16:52:59.057450959+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["AWS"]}
lead:
  _AWS Step Functions_ is an AWS service that allows you to coordinate distributed
  applications and microservices by creating workflows.

body:
  _AWS Step Functions_ is an AWS service that allows you to coordinate distributed
  applications and microservices by creating workflows.

  Workflows are made up of a series of steps and are known as **state machines**.
  A state machine defines:

  - the states of the workflow
  - the transitions between states
  - the inputs and outputs of each state

  State machines are defined via a JSON object which specifies the states and the
  transitions between them. It also includes error catchers and retry logic.

  At the beginning you define a `StartAt` state which is the entrypoint of the
  state machine. This can be manually triggered, or more likely, triggered by
  another AWS service such as a
  [Lambda](Lambda_programming_model.md), an
  [API Gateway](AWS_API_Gateway.md) request or a messaging/queue
  event.

  The state machine ultimately ends at an end state. In between are various
  intermediary states which can include:

  - Task states (do something)
  - Choice states (do something based on conditions)
  - Wait states
  - Success states (if preceding state succeeds do X)
  - Fail states (if preceding state fails do Y)
  - Parallel states

  The diagram below shows the definition of a given state machine. On the left is
  the JSON specification. On the right is a diagramatic representation that shows
  the control flow and all possible steps that comprise a given pathway / state:

  ![](/img/state-machine-definition.png)

  For each execution of the state machine (each time it is triggered) you can
  review the runtime. The flow diagram will highlight green to show the given
  pathway, and you can also see the inputs and outputs for each step and any
  errors:

  ![](/img/state-machine-execution.png)

raw_content:
  ---
  tags: [AWS]
  ---

  # AWS Step functions and state machines

  _AWS Step Functions_ is an AWS service that allows you to coordinate distributed
  applications and microservices by creating workflows.

  Workflows are made up of a series of steps and are known as **state machines**.
  A state machine defines:

  - the states of the workflow
  - the transitions between states
  - the inputs and outputs of each state

  State machines are defined via a JSON object which specifies the states and the
  transitions between them. It also includes error catchers and retry logic.

  At the beginning you define a `StartAt` state which is the entrypoint of the
  state machine. This can be manually triggered, or more likely, triggered by
  another AWS service such as a
  [Lambda](Lambda_programming_model.md), an
  [API Gateway](AWS_API_Gateway.md) request or a messaging/queue
  event.

  The state machine ultimately ends at an end state. In between are various
  intermediary states which can include:

  - Task states (do something)
  - Choice states (do something based on conditions)
  - Wait states
  - Success states (if preceding state succeeds do X)
  - Fail states (if preceding state fails do Y)
  - Parallel states

  The diagram below shows the definition of a given state machine. On the left is
  the JSON specification. On the right is a diagramatic representation that shows
  the control flow and all possible steps that comprise a given pathway / state:

  ![](/img/state-machine-definition.png)

  For each execution of the state machine (each time it is triggered) you can
  review the runtime. The flow diagram will highlight green to show the given
  pathway, and you can also see the inputs and outputs for each step and any
  errors:

  ![](/img/state-machine-execution.png)


path: Strategies_for_constructing_proofs.md
sortable_path: Strategies_for_constructing_proofs.md
title: Strategies for constructing proofs in propositional logic
lead: ## General strategy
checksum: d836e2a38a2df56b856a2642dd68e59a49bec4680cf6d9dae8c194f2536358c0
created: 2024-10-22 16:52:59.075108748+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  ## General strategy

  - Break complex propositions into simpler sentences by using the elimination
    rules
  - Recombine simple propositions into complex propositions using the introduction
    rules.

  ## Goal analysis

  The approach above describes the general form of a proof but of course it will
  not always work and there will be cases where the route to the desired
  derivation is more circuitous. In these instances it is to best to combine this
  general top level strategy with goal analysis.

  Goal analysis is a [recursive](Recursion.md) strategy which
  proceeds by using a 'goal' proposition to guide the construction of intermediary
  derivations.

  Assume that we want to show that an argument is
  [valid](Validity_and_entailment.md#validity). Then our
  ultimate goal is to derive the conclusion from the premises we are given. We
  first ask ourselves: _which propositions if we could derive them, would allow us
  to easily derive the conclusion_? (For example, these propositions might be two
  simple propositions that when combined with
  [Conjunction Introduction](Conjunction_Introduction.md) give us
  the conclusion.) Deriving these propositions then becomes the new intermediate
  goal.

  If arriving at these propositions is not trivial, we then ask ourselves the
  question again: _which propositions would permit us to derive the intermediary
  propositions we need_? You keep working back in this manner until you reach a
  base level. Then it is just a matter or working upwards from each set of derived
  intermediary propositions until you reach the ultimate goal.

  ### Demonstration

  Let's say we want to prove $(L \lor A) \land D$ from the propositions $\lnot N$
  and $((\lnot N \rightarrow L) \land (D \leftrightarrow \lnot N))$.

  First, we consider what is the easiest possible way of achieving the proposition
  $(L \lor A) \land D$. Clearly it is to separately derive each disjunct
  ($L \lor A$ and $D$) and then combine them with
  [Conjunction Introduction](Conditional_Introduction.md). This
  provides us with our first goal: to derive each of the separate conjuncts.

  Let's start with $D$: where does it occur in the assumptions? It occurs in the
  compound $(\lnot N \rightarrow L) \land (D \leftrightarrow \lnot N)$, but only
  in the first conjunct. We can get this simply but applying
  [Conjunction Elimination](Conjunction%20Elimination.md).

  So far we have:

  ![](/img/step1.png)

  Now we just need to get $D$ from the proposition at line 3. This is easy since
  we already have access to the consequent of the biconditional at line 1.
  Therefore we can apply
  [Biconditional Elimination](Biconditional_Elimination.md)) at line
  3 to get $D$. We are now halfway there:

  ![](/img/step2.png)

  Next we need to turn our attention to deriving $L \lor A$. How can we obtain $L$
  ? Well it is contained within the first conjunct of the assumption on line 2.
  Again, we can get this through the application of
  [Conjunction Elimination](Conjunction_Elimination.md). Now, how do
  we get $L$ from $(\lnot N \rightarrow L)$? Well, we already have the antecedent
  $\lnot N$ as an assumption on the first line, so we can use
  [Conditional Elimination](Conditional_Elimination.md) to derive
  $L$. These two steps give us:

  ![](/img/step3.png)

  Now we need to get from $L$ to $L \lor A$. This is really straightforward
  because by using
  [Disjunction Introduction](Disjunction_Introduction.md)) we can
  get from any sentence to a disjunction. Finally, having assembled all the
  constituent parts of the conjunction that is the conclusion, we can combine them
  with [Conjunction Introduction](Conjunction_Introduction.md) as we
  had planned at the outset.

  ![](/img/step4.png)

  ### A further example

  We will seek to prove the following:

  $$
  \{ \lnot L \leftrightarrow [X \land (\lnot S \lor B) ], (E \land C) \rightarrow \lnot L, (E \land R) \land C \} \vdash X \land (\lnot S \lor B)
  $$

  The requirements here could easily mislead us. We see that the target
  proposition is a conjunction so we might think that the best strategy is to seek
  to derive each conjunct and then combine them via
  [Conjunction Introduction](Conjunction_Introduction.md)).

  Actually, if we look more closely, there is a better approach. The target
  proposition is contained in the first premise as the consequent to the
  biconditional ($\lnot L \leftrightarrow [X \land (\lnot S \lor B)]$). A better
  approach is therefore to seek to derive the antecedent ($\lnot L$) and then use
  [Biconditional Elimination](Biconditional_Elimination.md) to
  extract the target sentence which is the consequent.

  ![proof.png](proof.png)

  ## Proving theorems

  When we are proving
  [theorems](Theorems_and_empty_sets.md#theorems-and-empty-sets)
  we do not have a set of assumptions to work from when constructing the proof. We
  must derive the target sentence from the 'empty set' which is the target
  sentence itself. It is therefore like a process of reverse engineering.

  ### Demonstration

  _Prove:_ $\vdash (U \land Y) \rightarrow [L \rightarrow (U \land L)]$

  Our strategy here is to identify the main connective in the proposition we want
  to derive (the material conditional). We then assume the antecedent and attempt
  to derive the consequent from it.

  ![](/img/theoremproof.png)

  ## A complex theorem proof

  _Prove_ $\vdash (\lnot A \lor \lnot B) \leftrightarrow \lnot(A \land B)$

  ![](/img/dsfdsfsdfwe.png)

  ### Walkthrough

  **Lines 1-12**

  - Our auxiliary goal is to prove
    $\lnot (A \lor B) \rightarrow \lnot (A \land B)$.

  - Our starting assumption is to a disjunction. Thus we can apply
    [Disjunction Elimination](Disjunction_Elimination.md) to show
    that our goal sentence $\lnot(A \land B)$ follows from each of the disjuncts
    ($\lnot A$ and $\lnot B$) in dedicated sub-proofs. If we can do this, we have
    the right to derive $\lnot (A \land B)$.

  - In both cases($\lnot A \vdash \lnot (A \land B)$) and
    ($\lnot B \vdash \lnot (A \land B)$ we require another sub-proof to reach the
    target as there is no easy path available. So we derive a negation from
    $A \land  B$ so that we can negate it as $\lnot (A \land B)$.

  - Having done this, we can discharge the
    [Disjunction Elimination](Disjunction_Elimination.md) sub-proofs
    and derive $\lnot (A \land B)$ from $\lnot A \lor \lnot B$

  **Lines 13-26**

  - Our auxiliary goal is to prove
    $\lnot (A \land B) \rightarrow \lnot A \lor  \lnot B$. This will require a
    different approach to the above because we are not working from a disjunction
    anymore, we have a negated conjunction.
  - We will do this by assuming the negation of what we want to prove
    ($\lnot (\lnot A \lor \lnot B)$) and then apply
    [Negation Elimination](Negation_Elimination.md) to get
    $\lnot A \lor \lnot B$.
  - This requires us to derive a contradiction. We get this on lines 23 and 24.
    This requires as previous steps that we have two sub-proofs that use
    [Negation Elimination](Negation_Elimination.md) to release $A$
    and $B$

raw_content:
  ---
  tags:
    - propositional-logic
    - logic
  ---

  # Strategies for constructing proofs in propositional logic

  ## General strategy

  - Break complex propositions into simpler sentences by using the elimination
    rules
  - Recombine simple propositions into complex propositions using the introduction
    rules.

  ## Goal analysis

  The approach above describes the general form of a proof but of course it will
  not always work and there will be cases where the route to the desired
  derivation is more circuitous. In these instances it is to best to combine this
  general top level strategy with goal analysis.

  Goal analysis is a [recursive](Recursion.md) strategy which
  proceeds by using a 'goal' proposition to guide the construction of intermediary
  derivations.

  Assume that we want to show that an argument is
  [valid](Validity_and_entailment.md#validity). Then our
  ultimate goal is to derive the conclusion from the premises we are given. We
  first ask ourselves: _which propositions if we could derive them, would allow us
  to easily derive the conclusion_? (For example, these propositions might be two
  simple propositions that when combined with
  [Conjunction Introduction](Conjunction_Introduction.md) give us
  the conclusion.) Deriving these propositions then becomes the new intermediate
  goal.

  If arriving at these propositions is not trivial, we then ask ourselves the
  question again: _which propositions would permit us to derive the intermediary
  propositions we need_? You keep working back in this manner until you reach a
  base level. Then it is just a matter or working upwards from each set of derived
  intermediary propositions until you reach the ultimate goal.

  ### Demonstration

  Let's say we want to prove $(L \lor A) \land D$ from the propositions $\lnot N$
  and $((\lnot N \rightarrow L) \land (D \leftrightarrow \lnot N))$.

  First, we consider what is the easiest possible way of achieving the proposition
  $(L \lor A) \land D$. Clearly it is to separately derive each disjunct
  ($L \lor A$ and $D$) and then combine them with
  [Conjunction Introduction](Conditional_Introduction.md). This
  provides us with our first goal: to derive each of the separate conjuncts.

  Let's start with $D$: where does it occur in the assumptions? It occurs in the
  compound $(\lnot N \rightarrow L) \land (D \leftrightarrow \lnot N)$, but only
  in the first conjunct. We can get this simply but applying
  [Conjunction Elimination](Conjunction%20Elimination.md).

  So far we have:

  ![](/img/step1.png)

  Now we just need to get $D$ from the proposition at line 3. This is easy since
  we already have access to the consequent of the biconditional at line 1.
  Therefore we can apply
  [Biconditional Elimination](Biconditional_Elimination.md)) at line
  3 to get $D$. We are now halfway there:

  ![](/img/step2.png)

  Next we need to turn our attention to deriving $L \lor A$. How can we obtain $L$
  ? Well it is contained within the first conjunct of the assumption on line 2.
  Again, we can get this through the application of
  [Conjunction Elimination](Conjunction_Elimination.md). Now, how do
  we get $L$ from $(\lnot N \rightarrow L)$? Well, we already have the antecedent
  $\lnot N$ as an assumption on the first line, so we can use
  [Conditional Elimination](Conditional_Elimination.md) to derive
  $L$. These two steps give us:

  ![](/img/step3.png)

  Now we need to get from $L$ to $L \lor A$. This is really straightforward
  because by using
  [Disjunction Introduction](Disjunction_Introduction.md)) we can
  get from any sentence to a disjunction. Finally, having assembled all the
  constituent parts of the conjunction that is the conclusion, we can combine them
  with [Conjunction Introduction](Conjunction_Introduction.md) as we
  had planned at the outset.

  ![](/img/step4.png)

  ### A further example

  We will seek to prove the following:

  $$
  \{ \lnot L \leftrightarrow [X \land (\lnot S \lor B) ], (E \land C) \rightarrow \lnot L, (E \land R) \land C \} \vdash X \land (\lnot S \lor B)
  $$

  The requirements here could easily mislead us. We see that the target
  proposition is a conjunction so we might think that the best strategy is to seek
  to derive each conjunct and then combine them via
  [Conjunction Introduction](Conjunction_Introduction.md)).

  Actually, if we look more closely, there is a better approach. The target
  proposition is contained in the first premise as the consequent to the
  biconditional ($\lnot L \leftrightarrow [X \land (\lnot S \lor B)]$). A better
  approach is therefore to seek to derive the antecedent ($\lnot L$) and then use
  [Biconditional Elimination](Biconditional_Elimination.md) to
  extract the target sentence which is the consequent.

  ![proof.png](proof.png)

  ## Proving theorems

  When we are proving
  [theorems](Theorems_and_empty_sets.md#theorems-and-empty-sets)
  we do not have a set of assumptions to work from when constructing the proof. We
  must derive the target sentence from the 'empty set' which is the target
  sentence itself. It is therefore like a process of reverse engineering.

  ### Demonstration

  _Prove:_ $\vdash (U \land Y) \rightarrow [L \rightarrow (U \land L)]$

  Our strategy here is to identify the main connective in the proposition we want
  to derive (the material conditional). We then assume the antecedent and attempt
  to derive the consequent from it.

  ![](/img/theoremproof.png)

  ## A complex theorem proof

  _Prove_ $\vdash (\lnot A \lor \lnot B) \leftrightarrow \lnot(A \land B)$

  ![](/img/dsfdsfsdfwe.png)

  ### Walkthrough

  **Lines 1-12**

  - Our auxiliary goal is to prove
    $\lnot (A \lor B) \rightarrow \lnot (A \land B)$.

  - Our starting assumption is to a disjunction. Thus we can apply
    [Disjunction Elimination](Disjunction_Elimination.md) to show
    that our goal sentence $\lnot(A \land B)$ follows from each of the disjuncts
    ($\lnot A$ and $\lnot B$) in dedicated sub-proofs. If we can do this, we have
    the right to derive $\lnot (A \land B)$.

  - In both cases($\lnot A \vdash \lnot (A \land B)$) and
    ($\lnot B \vdash \lnot (A \land B)$ we require another sub-proof to reach the
    target as there is no easy path available. So we derive a negation from
    $A \land  B$ so that we can negate it as $\lnot (A \land B)$.

  - Having done this, we can discharge the
    [Disjunction Elimination](Disjunction_Elimination.md) sub-proofs
    and derive $\lnot (A \land B)$ from $\lnot A \lor \lnot B$

  **Lines 13-26**

  - Our auxiliary goal is to prove
    $\lnot (A \land B) \rightarrow \lnot A \lor  \lnot B$. This will require a
    different approach to the above because we are not working from a disjunction
    anymore, we have a negated conjunction.
  - We will do this by assuming the negation of what we want to prove
    ($\lnot (\lnot A \lor \lnot B)$) and then apply
    [Negation Elimination](Negation_Elimination.md) to get
    $\lnot A \lor \lnot B$.
  - This requires us to derive a contradiction. We get this on lines 23 and 24.
    This requires as previous steps that we have two sub-proofs that use
    [Negation Elimination](Negation_Elimination.md) to release $A$
    and $B$


path: Streams_in_NodeJS.md
sortable_path: Streams_in_NodeJS.md
title: Handling streams with fs
checksum: f8299b5e6a1242c1e024952c4e37aaa85dbc54a9b22f1a262b3658605e2ec655
created: 2024-10-22 16:52:59.170660211+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["node-js"]}
lead:
  When reading from a file, the
  [`fs.readFile()`](fs.md) method waits
  until the entire file has been read before executing the callback. It's obvious
  why this might not be ideal in certain cases. If the file is very large you are
  utilising a lot of [memory](Memory.md) for a
  single process. Additionally, the data you need might appear early in the file,
  in which case, once you find the data you want, there is no need to read to the
  end of the file.

body:
  When reading from a file, the
  [`fs.readFile()`](fs.md) method waits
  until the entire file has been read before executing the callback. It's obvious
  why this might not be ideal in certain cases. If the file is very large you are
  utilising a lot of [memory](Memory.md) for a
  single process. Additionally, the data you need might appear early in the file,
  in which case, once you find the data you want, there is no need to read to the
  end of the file.

  > An example of this in practice is watching a Netflix film: we don't have to
  > wait for the whole film to download, we can start watching it immediately
  > because it is passed to us in chunks.

  ## Reading streams

  This is why the ability to read files and data as streams exists in Node.js.
  Streams promote memory and time efficiency. As you are not necessarily reading
  the whole file, you can extract the data you need quickly and you are not
  putting the whole file in memory.

  When read as a stream, the file is broken up into smaller chunks. Readable
  streams raise data events and pass the chunks to our callbacks. Hence we don't
  have to wait for the whole file before registering data chunks.

  ## Implementation

  Instead of, e.g :

  ```js
  fs.readFile("./lorem.md", "UTF-8", function (err, fileContents) {
    console.log(fileContents);
  });
  ```

  We would:

  ```js
  let stream = fs.createReadStream("./lorem.md", "UTF-8");

  let data;

  stream.once("data", (chunk) => {
    console.log("read stream started");
  });

  stream.on("data", (chunk) => {
    data += chunk;
    console.log(data);
  });

  stream.on("end", () => console.log("stream ends"));
  ```

  We use `once` to do something at the moment the stream starts to be read.

  We use `on` to target the `"data"` and `"end"` events. We use this to log the
  chunks via the `data` variable.

  ## Writing streams

  When we write as a stream, we break data up and pass this to a reader as a
  stream. It is what creates a stream in the first place.

  For example we could create a stream that handles `stdin` and writes this to a
  file in chunks:

  ```js
  var stream = fs.createWriteStream("lorem.txt")

  process.stdin.once("data", (data) => {
      stream.write(data, function() => {
          // data is now written.
      })
  })

  process.on("exit", function () {
      stream.close()
  })
  ```

  Note that we always have to close the stream on exit

raw_content:
  ---
  tags:
    - node-js
  ---

  # Handling streams with fs

  When reading from a file, the
  [`fs.readFile()`](fs.md) method waits
  until the entire file has been read before executing the callback. It's obvious
  why this might not be ideal in certain cases. If the file is very large you are
  utilising a lot of [memory](Memory.md) for a
  single process. Additionally, the data you need might appear early in the file,
  in which case, once you find the data you want, there is no need to read to the
  end of the file.

  > An example of this in practice is watching a Netflix film: we don't have to
  > wait for the whole film to download, we can start watching it immediately
  > because it is passed to us in chunks.

  ## Reading streams

  This is why the ability to read files and data as streams exists in Node.js.
  Streams promote memory and time efficiency. As you are not necessarily reading
  the whole file, you can extract the data you need quickly and you are not
  putting the whole file in memory.

  When read as a stream, the file is broken up into smaller chunks. Readable
  streams raise data events and pass the chunks to our callbacks. Hence we don't
  have to wait for the whole file before registering data chunks.

  ## Implementation

  Instead of, e.g :

  ```js
  fs.readFile("./lorem.md", "UTF-8", function (err, fileContents) {
    console.log(fileContents);
  });
  ```

  We would:

  ```js
  let stream = fs.createReadStream("./lorem.md", "UTF-8");

  let data;

  stream.once("data", (chunk) => {
    console.log("read stream started");
  });

  stream.on("data", (chunk) => {
    data += chunk;
    console.log(data);
  });

  stream.on("end", () => console.log("stream ends"));
  ```

  We use `once` to do something at the moment the stream starts to be read.

  We use `on` to target the `"data"` and `"end"` events. We use this to log the
  chunks via the `data` variable.

  ## Writing streams

  When we write as a stream, we break data up and pass this to a reader as a
  stream. It is what creates a stream in the first place.

  For example we could create a stream that handles `stdin` and writes this to a
  file in chunks:

  ```js
  var stream = fs.createWriteStream("lorem.txt")

  process.stdin.once("data", (data) => {
      stream.write(data, function() => {
          // data is now written.
      })
  })

  process.on("exit", function () {
      stream.close()
  })
  ```

  Note that we always have to close the stream on exit


path: Strings_in_Bash.md
sortable_path: Strings_in_Bash.md
title: Strings in bash
lead: ## Return a substring by index
checksum: 2530f2db53c8899fd0d79f4d754052143e4479eb4e64620e2cc4cc233aca8321
created: 2024-10-22 16:52:59.189563323+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["shell","data-types"]}
body:
  ## Return a substring by index

  ```bash
  myString="hello"
  substring=${myString:0:3}
  # hel
  ```

  This is often used when looping through each character in a string.

  ## Loop through each character in a string

  ```bash
  str="hallelujah"
  stringLength=$(expr length str)

  for (( i=0; i<=${stringLength}; i++ )); do
      echo "${str:$i:1}"
  done
  ```

raw_content:
  ---
  tags:
    - shell
    - data-types
  ---

  # Strings in bash

  ## Return a substring by index

  ```bash
  myString="hello"
  substring=${myString:0:3}
  # hel
  ```

  This is often used when looping through each character in a string.

  ## Loop through each character in a string

  ```bash
  str="hallelujah"
  stringLength=$(expr length str)

  for (( i=0; i<=${stringLength}; i++ )); do
      echo "${str:$i:1}"
  done
  ```


path: Strings_in_Python.md
sortable_path: Strings_in_Python.md
title: Strings in Python
checksum: 3281a0dcc11d012d63621b70be662326f2a19865f9e34b84f32e7679bff270a7
created: 2024-10-22 16:52:59.196349096+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["python","data-types"]}
lead:
  > Generally, anything that changes a string will be a method on the `str` class,
  > rather than a built-in function like `len()`, as such it will use dot notation

body:
  > Generally, anything that changes a string will be a method on the `str` class,
  > rather than a built-in function like `len()`, as such it will use dot notation

  - Strings are **immutable**: string operations produce a new string.

  ```python
  # Working with Strings

  my_variable = 'Bob'
  print(my_variable)
  # Bob

  my_variable = "Eloise"
  print(my_variable)
  # Eloise

  # A multi line string
  my_variable = """
  Hello
    World
  """
  print(my_variable)
  """
  Hello
    World
  """

  my_string = 'Hello World'
  print(len(my_string))
  # 11

  string_1 = 'Good'
  string_2 = " day"
  string_3 = string_1 + string_2
  print(string_3)
  # Good day

  msg = 'Hello Lloyd you are ' + str(21)
  print(msg)
  # Hello Lloyd you are 21

  # Range of String operations
  msg = 'Hello World'
  print(msg.replace("Hello", "Goodbye"))
  # Goodbye World

  print('Edward Alan Rawlings'.find('Alan'))
  # 7

  print('Edward John Rawlings'.find('Alan'))
  # -1

  print('James' == 'James') # prints True
  print('James' != 'John') # prints True

  print("msg.startswith('H')", msg.startswith('H'))
  # msg.startswith('H') True

  print("msg.endswith('d')", msg.endswith('d'))
  # msg.endswith('d') TRUE

  print('some_string.upper()', msg.upper())
  # some_string.upper() HELLO WORLD

  print('sub string: ', 'Hello-World'[1:5])
  # sub string: ello

  # String interpolation
  user_age = input("Please enter your age: ")
  print(f'You are {user_age}')
  ```

  ## `str.split()`

  The `split()` function in Python is used to divide a string into multiple parts
  at the occurrence of a given separator. This function returns a
  [list](Lists_in_Python.md) of substrings.

  ### General syntax

  ```py
  str.split(separator, maxsplit)
  ```

  - The `separator` is optional. It specifies the separator to use when splitting
    the string. If no separator is provided, it will default to using whitespace.

  - `maxsplit` is also optional and specifies how many splits to do. Default value
    is -1, which is "all occurrences".

  ### Examples

  ```py

  text = "Hello world, how are you?"

  # Default usage:
  x = text.split()
  print(x)
  # ['Hello', 'world', 'how', 'are', 'you?']

  # Using a specific separator
  x = text.split(",")
  print(x)
  # ['Hello', 'world how are you?']

  # Specifiying a maxplit value
  x = text.split(" ", 1)
  print(x)
  # ['Hello']
  ```

raw_content:
  ---
  tags: [python, data-types]
  ---

  # Strings in Python

  > Generally, anything that changes a string will be a method on the `str` class,
  > rather than a built-in function like `len()`, as such it will use dot notation

  - Strings are **immutable**: string operations produce a new string.

  ```python
  # Working with Strings

  my_variable = 'Bob'
  print(my_variable)
  # Bob

  my_variable = "Eloise"
  print(my_variable)
  # Eloise

  # A multi line string
  my_variable = """
  Hello
    World
  """
  print(my_variable)
  """
  Hello
    World
  """

  my_string = 'Hello World'
  print(len(my_string))
  # 11

  string_1 = 'Good'
  string_2 = " day"
  string_3 = string_1 + string_2
  print(string_3)
  # Good day

  msg = 'Hello Lloyd you are ' + str(21)
  print(msg)
  # Hello Lloyd you are 21

  # Range of String operations
  msg = 'Hello World'
  print(msg.replace("Hello", "Goodbye"))
  # Goodbye World

  print('Edward Alan Rawlings'.find('Alan'))
  # 7

  print('Edward John Rawlings'.find('Alan'))
  # -1

  print('James' == 'James') # prints True
  print('James' != 'John') # prints True

  print("msg.startswith('H')", msg.startswith('H'))
  # msg.startswith('H') True

  print("msg.endswith('d')", msg.endswith('d'))
  # msg.endswith('d') TRUE

  print('some_string.upper()', msg.upper())
  # some_string.upper() HELLO WORLD

  print('sub string: ', 'Hello-World'[1:5])
  # sub string: ello

  # String interpolation
  user_age = input("Please enter your age: ")
  print(f'You are {user_age}')
  ```

  ## `str.split()`

  The `split()` function in Python is used to divide a string into multiple parts
  at the occurrence of a given separator. This function returns a
  [list](Lists_in_Python.md) of substrings.

  ### General syntax

  ```py
  str.split(separator, maxsplit)
  ```

  - The `separator` is optional. It specifies the separator to use when splitting
    the string. If no separator is provided, it will default to using whitespace.

  - `maxsplit` is also optional and specifies how many splits to do. Default value
    is -1, which is "all occurrences".

  ### Examples

  ```py

  text = "Hello world, how are you?"

  # Default usage:
  x = text.split()
  print(x)
  # ['Hello', 'world', 'how', 'are', 'you?']

  # Using a specific separator
  x = text.split(",")
  print(x)
  # ['Hello', 'world how are you?']

  # Specifiying a maxplit value
  x = text.split(" ", 1)
  print(x)
  # ['Hello']
  ```


path: Structuring_Express_applications.md
sortable_path: Structuring_Express_applications.md
title: Structuring Express applications
lead: In general, the following application structure works well:
checksum: 7a70ba15689a6e7e95f95149fa25048890b62d3003d249060f54bf4a76b99b6d
created: 2024-10-22 16:52:59.206169182+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["node-js","REST","APIs"]}
body:
  In general, the following application structure works well:

  ```
  \
      routes\
      models\
      middlewear\
      config\
      public\
    index.js

  ```

  - **`routes/`**

    - This should house all our RESTful API endpoints for a given resource which
      we define as a module. For instance if we are handling API requests to
      `/api/courses/` we would have a module at `/routes/courses.js`

  - **`models/`**
    - Here we define our database schemas: definitions for the structure of the
      data objects we want to return from the database.
    - In the case of Mongo we would have a file for each of our Mongoose models
      which will belong to an overall collection of which there are document
      instances.
    - Our model files should map onto our route modules; there should generally be
      at least one model file for each module. The model stands as an abstract
      representation of the data that we will be handling with our request paths
      in the routes file.

raw_content:
  ---
  tags:
    - node-js
    - REST
    - APIs
  ---

  # Structuring Express applications

  In general, the following application structure works well:

  ```
  \
      routes\
      models\
      middlewear\
      config\
      public\
    index.js

  ```

  - **`routes/`**

    - This should house all our RESTful API endpoints for a given resource which
      we define as a module. For instance if we are handling API requests to
      `/api/courses/` we would have a module at `/routes/courses.js`

  - **`models/`**
    - Here we define our database schemas: definitions for the structure of the
      data objects we want to return from the database.
    - In the case of Mongo we would have a file for each of our Mongoose models
      which will belong to an overall collection of which there are document
      instances.
    - Our model files should map onto our route modules; there should generally be
      at least one model file for each module. The model stands as an abstract
      representation of the data that we will be handling with our request paths
      in the routes file.


path: Swap_space.md
sortable_path: Swap_space.md
title: Swap space
checksum: 97e8698380c0d8084842f783b7b5347d4c22bff686d8e8150f37e223ec3d98d8
created: 2024-10-22 16:52:59.21599001+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["memory","disks","Linux","operating-systems"]}
lead:
  A swap partition is a partition on a disk that is not intended to be used as a
  filesystem. Instead, it is a part of the disk that is used to augment the main
  memory.

body:
  A swap partition is a partition on a disk that is not intended to be used as a
  filesystem. Instead, it is a part of the disk that is used to augment the main
  memory.

  If you run out of memory and have set up a swap partition, the OS will be able
  to move pieces of memory to and from disk storage. This is called _swapping_
  because pieces of idle programs are swapped to the disk in exchange for active
  pieces residing on the disk.

  ## View current swap usage

  If you have a swap space established, the command `free` will show current
  usage:

  ```bash
  free
                 total        used        free      shared  buff/cache   available
  Mem:        16099420     3031572    10157652     1153144     2910196    11605820
  Swap:        3145724           0     3145724
  ```

  ## Create a swap partition

  To use an existing disk partition as a swap you can run the command
  `mkswap [device]` and then `swapon [device]` to register the space with the
  [kernel](The_kernel.md).

  ### Add to `fstab`

  You will want the swap to be activated every time the OS boots so add the
  following line to the [fstab](Filesystems.md#fstab),
  where `/sda3` is used as the example partition:

  ```bash
  /dev/sda3e none swap sw 0 0
  ```

  ## Create a swap file

  // Add info here

raw_content:
  ---
  tags:
    - memory
    - disks
    - Linux
    - operating-systems
  ---

  # Swap space

  A swap partition is a partition on a disk that is not intended to be used as a
  filesystem. Instead, it is a part of the disk that is used to augment the main
  memory.

  If you run out of memory and have set up a swap partition, the OS will be able
  to move pieces of memory to and from disk storage. This is called _swapping_
  because pieces of idle programs are swapped to the disk in exchange for active
  pieces residing on the disk.

  ## View current swap usage

  If you have a swap space established, the command `free` will show current
  usage:

  ```bash
  free
                 total        used        free      shared  buff/cache   available
  Mem:        16099420     3031572    10157652     1153144     2910196    11605820
  Swap:        3145724           0     3145724
  ```

  ## Create a swap partition

  To use an existing disk partition as a swap you can run the command
  `mkswap [device]` and then `swapon [device]` to register the space with the
  [kernel](The_kernel.md).

  ### Add to `fstab`

  You will want the swap to be activated every time the OS boots so add the
  following line to the [fstab](Filesystems.md#fstab),
  where `/sda3` is used as the example partition:

  ```bash
  /dev/sda3e none swap sw 0 0
  ```

  ## Create a swap file

  // Add info here


path: Syllogism.md
sortable_path: Syllogism.md
title: Syllogism
checksum: efa3d071823652b3ce7959d58ea9a4b400250d7f5cb9fe963479e825295704cd
created: 2024-10-22 16:52:59.238744943+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  In order to make assertions about the relative
  [consistency](Logical_consistency.md) or inconsistency
  of a set of propositions we advance arguments. Consider everyday life: if we are
  having an argument with someone, we believe that they are wrong. A more logical
  way to say this is that we believe that their beliefs are inconsistent. In order
  to change their viewpoint or point out why they are wrong we advance an argument
  intended to show that belief A conflicts with belief B. Or if C is true, then
  you cannot believe that D.

body:
  In order to make assertions about the relative
  [consistency](Logical_consistency.md) or inconsistency
  of a set of propositions we advance arguments. Consider everyday life: if we are
  having an argument with someone, we believe that they are wrong. A more logical
  way to say this is that we believe that their beliefs are inconsistent. In order
  to change their viewpoint or point out why they are wrong we advance an argument
  intended to show that belief A conflicts with belief B. Or if C is true, then
  you cannot believe that D.

  In formal terms **an argument is a set of propositions comprising one or more
  premises and a conclusion. The conclusion is taken to be supported by the
  premises.**

  > The terms **argument** and **syllogism** are used interchangeably in logic to
  > describe the above feature of a set of propositions.

  ### Demonstration

  ```
  (P1) All men are mortal.
  (P2) Socrates is a man.
  _____________________
  (C) Socrates is mortal
  ```

raw_content:
  ---
  tags:
    - propositional-logic
    - logic
  ---

  # Syllogism

  In order to make assertions about the relative
  [consistency](Logical_consistency.md) or inconsistency
  of a set of propositions we advance arguments. Consider everyday life: if we are
  having an argument with someone, we believe that they are wrong. A more logical
  way to say this is that we believe that their beliefs are inconsistent. In order
  to change their viewpoint or point out why they are wrong we advance an argument
  intended to show that belief A conflicts with belief B. Or if C is true, then
  you cannot believe that D.

  In formal terms **an argument is a set of propositions comprising one or more
  premises and a conclusion. The conclusion is taken to be supported by the
  premises.**

  > The terms **argument** and **syllogism** are used interchangeably in logic to
  > describe the above feature of a set of propositions.

  ### Demonstration

  ```
  (P1) All men are mortal.
  (P2) Socrates is a man.
  _____________________
  (C) Socrates is mortal
  ```


path: Symlinks.md
sortable_path: Symlinks.md
title: Syntax
lead:
  ```
  ln -s -f ~/[existing_file] ~/.[file_you_want_to_symbolise]
  ```

checksum: 4b58c5eb0129339e5fe3e417845dbf94a8b384f545a3b991c3e52bc6659425ca
created: 2024-10-22 16:52:59.250416579+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["shell","Linux","procedural"]}
body:
  ```
  ln -s -f ~/[existing_file] ~/.[file_you_want_to_symbolise]
  ```

  Real example:

  ```
  ln -s -f ~/dotfiles/.vimrc ~/.vimrc
  ```

raw_content:
  ---
  tags:
    - shell
    - Linux
    - procedural
  ---

  > A symbolic link, also termed a soft link, is a special kind of file that
  > points to another file. Unlike a hard link, a symbolic link does not contain
  > the data in the target file. It simply points to another entry somewhere in
  > the file system.

  # Syntax

  ```
  ln -s -f ~/[existing_file] ~/.[file_you_want_to_symbolise]
  ```

  Real example:

  ```
  ln -s -f ~/dotfiles/.vimrc ~/.vimrc
  ```


path: Syntax_of_propositional_logic.md
sortable_path: Syntax_of_propositional_logic.md
title: Syntax of propositional logic
lead: ## Syntax of formal languages versus semantics
checksum: 62edccda4593be282121ee3b522e1dc1bd9a989fa7475c32470c54ea34730039
created: 2024-10-22 16:52:59.257550516+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  ## Syntax of formal languages versus semantics

  > The syntactical study of a language is the study of the expressions of the
  > language and the relations among them _without regard_ to the possible
  > interpretations or 'meaning' of these expressions.

  Syntax is talking about the order and placement of propositions relative to
  connectives and what constitutes a well-formed expression in these terms.
  Semantics is about what the connectives mean, in other words: truth-functions
  and truth-values and not just placement and order.

  ## Formal specification of the syntax of the language of Sentential Logic

  ### Vocabulary

  Propositions in SL are capitalised Roman letters (non-bold) with or without
  natural number subscripts. We may call these proposition letters. For example:

  $$
    P, Q, R,... P_{1}, Q_{1}, R_{1}, ...
  $$

  The connectives of SL are the five truth-functional connectives:

  $$
    \lnot, \land, \lor, \rightarrow, \leftrightarrow
  $$

  The punctuation marks of SL consist in the left and right parentheses:

  $$
    ( )
  $$

  ### Grammar

  1. Every letter in a statement is a proposition.
  2. If $P$ is a proposition then $\lnot P$ is a proposition.
  3. If $P$ and $Q$ are propositions, then $P \land Q$ is a proposition
  4. If $P$ and $Q$ are propositions, then $P \lor Q$ is a proposition
  5. If $P$ and $Q$ are propositions, then $P \rightarrow Q$ is a proposition
  6. If $P$ and $Q$ are propositions, then $P \leftrightarrow Q$ is a proposition
  7. Nothing is a proposition unless it can be formed by repeated application of
     rules 1-6

  ### Additional syntactic concepts

  We also distinguish:

  - the **main connective**
  - **immediate sentential components**
  - **sentential components**
  - **atomic components**

  These definitions provide a formal specification of the concepts of
  [atomic and molecular propositions](Atomic_and_molecular_propositions.md)
  introduced previously.

  1. If $P$ is an atomic proposition, $P$ contains no connectives and hence does
     not have a main connective. $P$ has no immediate propositional components.
  1. If $P$ is of the form $\lnot Q$ where $Q$ is a proposition, then the main
     connective of $P$ is the negation symbol that occurs before $Q$ and $Q$ is
     the immediate propositional component of $P$
  1. If P is of the form:
     1. $Q \land R$
     1. $Q \lor R$
     1. $Q \rightarrow R$
     1. $Q \leftrightarrow R$

  where $Q$ and $R$ are propositions, then the main connective of $P$ is the
  connective that occurs between $Q$ and $R$ and $Q$ and $R$ are the immediate
  propositional components of $P$.

raw_content:
  ---
  tags:
    - propositional-logic
    - logic
  ---

  # Syntax of propositional logic

  ## Syntax of formal languages versus semantics

  > The syntactical study of a language is the study of the expressions of the
  > language and the relations among them _without regard_ to the possible
  > interpretations or 'meaning' of these expressions.

  Syntax is talking about the order and placement of propositions relative to
  connectives and what constitutes a well-formed expression in these terms.
  Semantics is about what the connectives mean, in other words: truth-functions
  and truth-values and not just placement and order.

  ## Formal specification of the syntax of the language of Sentential Logic

  ### Vocabulary

  Propositions in SL are capitalised Roman letters (non-bold) with or without
  natural number subscripts. We may call these proposition letters. For example:

  $$
    P, Q, R,... P_{1}, Q_{1}, R_{1}, ...
  $$

  The connectives of SL are the five truth-functional connectives:

  $$
    \lnot, \land, \lor, \rightarrow, \leftrightarrow
  $$

  The punctuation marks of SL consist in the left and right parentheses:

  $$
    ( )
  $$

  ### Grammar

  1. Every letter in a statement is a proposition.
  2. If $P$ is a proposition then $\lnot P$ is a proposition.
  3. If $P$ and $Q$ are propositions, then $P \land Q$ is a proposition
  4. If $P$ and $Q$ are propositions, then $P \lor Q$ is a proposition
  5. If $P$ and $Q$ are propositions, then $P \rightarrow Q$ is a proposition
  6. If $P$ and $Q$ are propositions, then $P \leftrightarrow Q$ is a proposition
  7. Nothing is a proposition unless it can be formed by repeated application of
     rules 1-6

  ### Additional syntactic concepts

  We also distinguish:

  - the **main connective**
  - **immediate sentential components**
  - **sentential components**
  - **atomic components**

  These definitions provide a formal specification of the concepts of
  [atomic and molecular propositions](Atomic_and_molecular_propositions.md)
  introduced previously.

  1. If $P$ is an atomic proposition, $P$ contains no connectives and hence does
     not have a main connective. $P$ has no immediate propositional components.
  1. If $P$ is of the form $\lnot Q$ where $Q$ is a proposition, then the main
     connective of $P$ is the negation symbol that occurs before $Q$ and $Q$ is
     the immediate propositional component of $P$
  1. If P is of the form:
     1. $Q \land R$
     1. $Q \lor R$
     1. $Q \rightarrow R$
     1. $Q \leftrightarrow R$

  where $Q$ and $R$ are propositions, then the main connective of $P$ is the
  connective that occurs between $Q$ and $R$ and $Q$ and $R$ are the immediate
  propositional components of $P$.


path: Tags_in_Git.md
sortable_path: Tags_in_Git.md
title: Tags in Git
checksum: b684f8ef44eb962d1c0262ba8934999fc798aa1d14d52b9dacc2cb0b680da031
created: 2024-10-22 16:52:59.268976748+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["git"]}
lead:
  Tags exist to reference specific commits in a more meaningful way than using a
  SHA reference. They are a **named reference to a commit**. As they refer to a
  singular commit they do not update but are a fixed point in the Git history.

body:
  Tags exist to reference specific commits in a more meaningful way than using a
  SHA reference. They are a **named reference to a commit**. As they refer to a
  singular commit they do not update but are a fixed point in the Git history.

  > Tags do not care about branches. They are an objective reference to a commit
  > regardless of which branch it occured on. You can reference a tag without
  > needing to be in the branch where the tagging originally took place.

  Tags are frequently used to mark software release versions. But they can also be
  used for other designations such as the addition of a key feature or bug.

  ## Creating a tag

  ```
  git tag -am "Version 1.0" v1.0 dd5c4539a0
  ```

  `am` is for "annotated + message". The actual tag name is `v1.0`

  Now you can refer to that commit with `v1.0` rather than use the SHA

  ## Deleting a tag

  ```
  git tag -d v1.0
  ```

  > When you delete the tag you are only deleting the tag reference not the commit
  > that it refers to.

  ## Listing tags

  ```
  git tag --list
  ```

  ## Examples of use

  ```
  git show v1.1

  git diff v1.0..v1.1
  ```

  ## Pushing tags to a remote

  In the examples so far, all the tags have been **local tags**. They only exist
  in our local workflow and are not accessible to our collaborators. If we do a
  `git push`, this doesn't transfer our local tags. You have to do:

  ```
  git push origin [tag-name]
  ```

  Push all tags:

  ```
  git push origin --tags

  # Delete remote tag

  git push -d origin [tag-name]
  ```

  ## Getting tags from the remote

  ```
  git fetch
  ```

  This automatically fetches tags too.

raw_content:
  ---
  tags: [git]
  ---

  # Tags in Git

  Tags exist to reference specific commits in a more meaningful way than using a
  SHA reference. They are a **named reference to a commit**. As they refer to a
  singular commit they do not update but are a fixed point in the Git history.

  > Tags do not care about branches. They are an objective reference to a commit
  > regardless of which branch it occured on. You can reference a tag without
  > needing to be in the branch where the tagging originally took place.

  Tags are frequently used to mark software release versions. But they can also be
  used for other designations such as the addition of a key feature or bug.

  ## Creating a tag

  ```
  git tag -am "Version 1.0" v1.0 dd5c4539a0
  ```

  `am` is for "annotated + message". The actual tag name is `v1.0`

  Now you can refer to that commit with `v1.0` rather than use the SHA

  ## Deleting a tag

  ```
  git tag -d v1.0
  ```

  > When you delete the tag you are only deleting the tag reference not the commit
  > that it refers to.

  ## Listing tags

  ```
  git tag --list
  ```

  ## Examples of use

  ```
  git show v1.1

  git diff v1.0..v1.1
  ```

  ## Pushing tags to a remote

  In the examples so far, all the tags have been **local tags**. They only exist
  in our local workflow and are not accessible to our collaborators. If we do a
  `git push`, this doesn't transfer our local tags. You have to do:

  ```
  git push origin [tag-name]
  ```

  Push all tags:

  ```
  git push origin --tags

  # Delete remote tag

  git push -d origin [tag-name]
  ```

  ## Getting tags from the remote

  ```
  git fetch
  ```

  This automatically fetches tags too.


path: Test_values_in_Bash.md
sortable_path: Test_values_in_Bash.md
title: Test values in Bash
checksum: d30c66e930746ebe15a95780057d6e630825c2911fddc143375e875c159e3141
created: 2024-10-22 16:52:59.275881224+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["shell","regex"]}
lead:
  `test` is a built-in command that is used to compare values or determine whether
  something is the case.

body:
  `test` is a built-in command that is used to compare values or determine whether
  something is the case.

  We can use the command `test` but it is more common to test a condition
  implicity by using square brackets. The square brackets are an alias for `test`.
  We use this alias when we use `IF` logic.

  When we run a test the result we get back is a return status of a `0` or a `1`.
  `0` indicates that the test was a success and `1` means failure. (Bear in mind
  this is in contrast to most all other programming languages.)

  If we run a test in the command line we won't get a `0` or a `1` or back, there
  will just be silence from the shell. We can explicitly invoke the return value
  with variable `$?`, e.g:

  ```bash
  [ -d ~ ] # is the home directory a directory?
  echo $?
  0   # yes

  [ -d /bin/zsh ] # is this binary a directory?
  echo $
  1  # no
  ```

  ## Test structures

  Many tests can be run with flags as a shorthand like we saw above:

  ### File operators

  ```
  -a FILE        True if file exists.
  -d FILE        True if file is a directory.
  -e FILE        True if file exists.
  -h FILE        True if file is a symbolic link
  -s FILE        True if file exists and is not empty.
  -N FILE        True if the file has been modified since it was last read.
  ```

  ### String operators

  ```
  -z STRING      True if string is empty.
  -n STRING      True if string is not empty.
  ```

  ## Differences between comparing numbers and strings

  - `=` is reserved for comparing strings
  - For numbers we use, e.g, `[ 4 -lt 5 ]`

  ## Negation

  We can negate a test condition with `!`:

  ```bash
  [ ! 4 -lt 3 ]; echo $?
  0
  ```

  ## Running process if test succeeds or fails

  We can use the following structure to run a process if a test condition obtains:

  ```bash
  [[ -d ~ ]] && echo "/home is a directory"
  ```

  ## Extended test: `[[...]]`

  When we use **double brackets** we are using _extended_ `test`.

  The extended test supports the standard `test` comparisons and adds other
  features:

  - The use of Boolean operators:
    ```
    [[ -d ~ || -a /bin/mash ]]; echo $?
    ```

  ### Using regular expressions

  Extended test also allows us to use regular expressions as part of our test
  conditions. In order to test against a regular expression we use `=~` as the
  comparison operator.

  ```bash
  [[ "thomas" =~ t.* ]]; echo $?
  ```

  Here the test succeeds because "thomas" begins with "t" followed by any other
  character.

  ## Further examples

  **Test if a character exists in two strings**

  ```bash
  string1="hello"
  string2="world"
  char="l"

  if [[ "$string1" == *"$char"* ]] && [[ "$string2" == *"$char"* ]]; then
      echo "Character '$char' exists in both strings."
  else
      echo "Character '$char' does not exist in both strings."
  fi
  ```

  > Note: this syntax can also be used to test if a given element exists in an
  > [array](Lists_and_arrays_in_Bash.md).

raw_content:
  ---
  tags:
    - shell
    - regex
  ---

  # Test values in Bash

  `test` is a built-in command that is used to compare values or determine whether
  something is the case.

  We can use the command `test` but it is more common to test a condition
  implicity by using square brackets. The square brackets are an alias for `test`.
  We use this alias when we use `IF` logic.

  When we run a test the result we get back is a return status of a `0` or a `1`.
  `0` indicates that the test was a success and `1` means failure. (Bear in mind
  this is in contrast to most all other programming languages.)

  If we run a test in the command line we won't get a `0` or a `1` or back, there
  will just be silence from the shell. We can explicitly invoke the return value
  with variable `$?`, e.g:

  ```bash
  [ -d ~ ] # is the home directory a directory?
  echo $?
  0   # yes

  [ -d /bin/zsh ] # is this binary a directory?
  echo $
  1  # no
  ```

  ## Test structures

  Many tests can be run with flags as a shorthand like we saw above:

  ### File operators

  ```
  -a FILE        True if file exists.
  -d FILE        True if file is a directory.
  -e FILE        True if file exists.
  -h FILE        True if file is a symbolic link
  -s FILE        True if file exists and is not empty.
  -N FILE        True if the file has been modified since it was last read.
  ```

  ### String operators

  ```
  -z STRING      True if string is empty.
  -n STRING      True if string is not empty.
  ```

  ## Differences between comparing numbers and strings

  - `=` is reserved for comparing strings
  - For numbers we use, e.g, `[ 4 -lt 5 ]`

  ## Negation

  We can negate a test condition with `!`:

  ```bash
  [ ! 4 -lt 3 ]; echo $?
  0
  ```

  ## Running process if test succeeds or fails

  We can use the following structure to run a process if a test condition obtains:

  ```bash
  [[ -d ~ ]] && echo "/home is a directory"
  ```

  ## Extended test: `[[...]]`

  When we use **double brackets** we are using _extended_ `test`.

  The extended test supports the standard `test` comparisons and adds other
  features:

  - The use of Boolean operators:
    ```
    [[ -d ~ || -a /bin/mash ]]; echo $?
    ```

  ### Using regular expressions

  Extended test also allows us to use regular expressions as part of our test
  conditions. In order to test against a regular expression we use `=~` as the
  comparison operator.

  ```bash
  [[ "thomas" =~ t.* ]]; echo $?
  ```

  Here the test succeeds because "thomas" begins with "t" followed by any other
  character.

  ## Further examples

  **Test if a character exists in two strings**

  ```bash
  string1="hello"
  string2="world"
  char="l"

  if [[ "$string1" == *"$char"* ]] && [[ "$string2" == *"$char"* ]]; then
      echo "Character '$char' exists in both strings."
  else
      echo "Character '$char' does not exist in both strings."
  fi
  ```

  > Note: this syntax can also be used to test if a given element exists in an
  > [array](Lists_and_arrays_in_Bash.md).


path: Testing_Python_code.md
sortable_path: Testing_Python_code.md
title: Testing Python code
lead: ## `pytest`
checksum: f21ee764b3d4c67afa18fdc25fbee482906dd1434c85f34660de4f8726cbaeab
created: 2024-10-22 16:52:59.287462931+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["python","testing"]}
body:
  ## `pytest`

  Pytest is the most popular testing library for Python. It is not included with
  the Python standard library so it must be installed with
  [pip](Python_package_management.md).
  While it does not include a declaration library, it is robust enough to handle
  most scenarios having a rich and expressive set of constructs and decorators
  that let you declare what your tests should do, under what conditions they
  should run, and how they should interact with the rest of your system.

  ### Using `pytest`

  - Pytest will automatically detect test files so long as they are named
    appropriately. E.g. for a module called `lorem`, it will detzect the unit test
    files `lorem_test.py` and `test_lorem.py`.
  - In order to detect tests it should be run from a directory level above them

  ### Examples

  Here is a basic example of using pytest for a local module callled `palindrome`:

  ```py
  import palindrome

  def test_is_palindrome():
      assert palindrome.is_palindrome('soros')
      assert palindrome.is_palindrome('torot')
      assert not palindrome.is_palindrome('chair')
  ```

  ## Mocking

  `patch()` and `Mock` enable us to mock objects whilst testing (classes,
  functions, methods and properties belonging). They are used in combination.

  The `@patch` decorator temporarily replaces a specified object in your code with
  a mock object and restores the original object after the test is complete

  A Mock object simulates the object it replaces so that the object behaves as
  expected during testing. For example, if your code calls
  `some_function.some_method()`, and `some_method` is mocked, calling
  `some_method` will not execute real logic but will interact with the Mock object
  instead. Mock objects record details about how they have been used, like what
  methods have been called, with what arguments, etc., allowing you to make
  assertions about how they have been used.

  > `@patch` and `Mock` work together because a patch is used to replace an object
  > or attribute with a Mock object. `Mock` handles the simulated functionality,
  > and `@patch` designates the real value we are replacing with the mock.

  ### Example case

  I will use the following example from one my projects:

  ```py
  # get_articles.py
  def get_articles(article_type: str) -> Optional[Dict[str, Any]]:
      """Retrieve articles from Pocket API"""

      if POCKET_LAMBDA_ENDPOINT is None:
          logging.error("Error: POCKET_LAMBDA_ENDPOINT envinronment variable is not set")
          return None
      else:
          # Interpolate the article_type into the Pocket request URL
          endpoint = POCKET_LAMBDA_ENDPOINT.format(article_type=article_type)

      try:
          response = requests.get(endpoint)
          response.raise_for_status()
          return response.json()

      except RequestException as e:
          print(f"An error occurred: {e}")
          return None
  ```

  This function: sources a URL from an environment variable, interpolates a query
  string into the URL (which comes in as a parameter), makes a request to the URL,
  and returns the response as JSON.

  It has some safeguards in place:

  - It checks that the environment variable is set
  - It checks that the request was successful

  In the example we could use a Mock object to simulate the response from the
  Pocket API. This would allow us to test the function without having to make a
  real request to the API:

  ```py
  def test_successful_request():
      # Replace the requests.get function with a Mock object (mock_get)
      with patch("requests.get") as mock_get:
          # Specify the return value of the mock_get object)
          mock_get.return_value = Mock(ok=true)
          mock_get.return_value.json.return_value = {"value": "test"}

          # Call the function under test
          result = get_articles("gaby")

      # Assert expected outcomes
      mock_get.assert_called_once_with(endpoint)
      assert result == mock_json_response
  ```

  The example above follows the **Arrange, Act, Assert** pattern:

  | Stage   | Action                                                                          |
  | ------- | ------------------------------------------------------------------------------- |
  | Arrange | Replace the `requests.get` function with `patch` and set properties with `Mock` |
  | Act     | Call the function under test                                                    |
  | Assert  | Assert that the function under test behaved as expected                         |

  ### Alternative mock syntax

  The `with patch(...) as mock_name` syntax is fine for small-scale mocking but
  can become cumbersome when you are mocking several dependencies.

  There is another syntax (which I actually find clearer). Say we have a function
  with three dependencies: `update_worksheet`, `process_articles`, `get_articles`.
  We could mock like so:

  ```py
  @patch("app.update_worksheet")
  @patch("app.process_articles")
  @patch("app.get_articles")
  def test_success(
      mock_get_articles, mock_process_articles, mock_update_worksheet
  ):
      mock_get_articles.return_value = [1, 2, 3]
  ```

  Here the patching is done by the decorator and the mocks are defined as
  parameters to the test function (always in reverse order)

  ### Mock assertion lexicon

  #### `return_value`

  State what the mock should return

  ```py
  my_mocked_function.return_value = ['one', 'two', 'three']
  ```

  #### `call_count`

  Test how many times a dependent function is called

  ```py
  assert my_mocked_function.call_count = 3
  ```

  #### `assert_any_call()`

  Test that a given mock is called at least once during the execution of the
  function under test

  ```py
  my_mocked_function.assert_any_call(some_mocked_return_value)
  ```

  When the output of one function is used as a parameter to another, and we don't
  particularly care about the details of what is concerned we can just pass the
  executed function, e.g:

  ```py
  my_mocked_function.assert_any_call(preceding_function())
  ```

  #### `call_args_list`

  Get a list of all the arguments that a mock object was called with during the
  test.

  `call_args_list` is useful when you want to check the arguments that a mock
  object was called with during the test, especially if the mock object was called
  multiple times with different arguments. You can use it to inspect the arguments
  of each call and make assertions based on them.

  ```py
  second_my_mocked_function_call = my_mocked_function.call_args_list[1]

  # check the first argument of the second call:

  assert second_my_mocked_function_call[0][0] == "expected arg"
  ```

  #### `side_effect`

  Use to trigger a side effect when returning a value from a mock. Most useful for
  mocking exceptions.

  ```py
  my_mocked_function.side_effect = Exception("Some exception raised")
  ```

  ## Testing exceptions with `raises`

  Testing exceptions is quite straightforward. You can use the `raises` helper
  provided by pytest, and combine this with `excinfo` ("exception info") to
  inspect the exception message.

  ```py
  if POCKET_LAMBDA_ENDPOINT is None:
      raise ValueError(
          "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set"
      )
  ```

  Then to test this, we would use pytest's `excinfo` fixture along with `raises`:

  ```py
   with pytest.raises(ValueError) as excinfo:  # Watch for the ValueError
      get_articles("some_type")

      assert "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set" in str(
          excinfo.value
      )
  ```

  We could actually simplify the above test by using the `match` parameter with
  `raise`. This way we do not need the separate assertion:

  ```py
  with pytest.raises(ValueError, match="Error: POCKET_LAMBDA_ENDPOINT environment variable is not set"):
      get_articles("some_type")
  ```

  Note that `excinfo` is best used for testing the exception text that you the
  developer explicitly `raise`. For exceptions tha may occur naturaly in the code
  you are testing, you should use `caplog` or `capsys` (see below).

  ## Before-each and after-each

  When testing functions, we achieve this in Python using `setup_function` and
  `teardown_function` methods. These methods are called before and after each test
  method respectively.

  To apply a "before each" to _every test_ just put your setup function and/or
  teardown function at the top level of your test module.

  For example, below we set and remove an env var before and after each test:

  ```py
  @pytest.fixture(scope="function") # specify that this fixture should be run before each function test
  def setup_function():
      print("Setting up test environment...")
      os.environ["POCKET_LAMBDA_ENDPOINT"] = "https://some_endpoint.com/{article_type}"


  def teardown_function():
      print("Tearing down test environment...")
      del os.environ["POCKET_LAMBDA_ENDPOINT"]
  ```

  If the setup/teardown should only be applied to a subset of tests, just pass the
  name of the fixture as a parameter to the test function:

  ```py
  def some_function(setup_function):
      # setup_function will be run before this test
  ```

  You don't need to use the names `setup_function` and `teardown_function` so long
  as you are passing the fixture as a parameter.

  You can also use `yield` to combine the setup and teardown into a single
  function:

  ```py
  @pytest.fixture(scope="function")
  def setup_function():
      os.environ["POCKET_LAMBDA_ENDPOINT"] = "https://some_endpoint.com/{article_type}"
      yield
      del os.environ["POCKET_LAMBDA_ENDPOINT"]
  ```

  ### Another example:

  The following test suite uses the same three mocked functions in every test. The
  following set-up assigns the mocks before each test and resets after each
  individual test has run:

  ```py
  @pytest.fixture(scope="function")
  def setup_function():
      with patch("app.get_articles") as mock_get_articles, patch(
          "app.process_articles"
      ) as mock_process_articles, patch("app.update_worksheet") as mock_update_worksheet:
          yield mock_get_articles, mock_process_articles, mock_update_worksheet
  ```

  Then to use:

  ```py
  def individual_test(setup_function):
      mock_get_articles, mock_process_articles, mock_update_worksheet = setup_function
      # Now each mock can be referenced using the vars above

  ```

  ## Parameterized tests

  For a sequence of tests that are repetitive, to avoid repeating the same code
  over and over again, we can use parameterized tests. This is where we pass in a
  list of parameters to the test function and the test function is run once for
  each parameter.

  For example, in the function below I am handling numerous possible Exceptions
  that could be raised by the `requests.get` method:

  ```py
      try:
          response = requests.get(endpoint)
          response.raise_for_status()
          return response.json()

      except HTTPError as http_err:
          logging.error(f"HTTP Error occurred: {http_err}")

      except ConnectionError as conn_err:
          logging.error(f"Connection Error occurred: {conn_err}")

      except Timeout as timeout_err:
          logging.error(f"Timeout Error occurred: {timeout_err}")

      except RequestException as e:
          logging.error(f"Request Exception occurred: {e}")

      return None
  ```

  Instead of writing something like the following for each of the four exceptions:

  ```py

   def test_exception_generic(caplog):
      with patch("requests.get", side_effect=RequestException("Some error")):
          result = get_articles("some_type")

      assert "Request Exception occurred" in caplog.text
      assert result is None
  ```

  I could parameterize like so:

  ```py
  @pytest.mark.parametrize(
      "exception_type, log_message",
      [
          (RequestException, "Request Exception occurred: "),
          (HTTPError, "HTTP Error occurred: "),
          (Timeout, "Timeout Error occurred: "),
          (ConnectionError, "Connection Error occurred: "),
      ],
  )
  def test_exceptions(caplog, exception_type, log_message):
      with patch("requests.get", side_effect=exception_type("Some error")):
          result = get_articles("some_type")

      assert log_message in caplog.text
      assert result is None
  ```

  ## Caplog, syslog, excinfo

  `caplog` and `capsys` are built-in pytest fixtures. `caplog` lets you test log
  messages. `capsys` lets you test stdout and stderr. As such they are very useful
  when testing that error messages are logged correctly.

  ### `caplog`

  In our example, if the endpoing environment is not set, we log an error message.
  We can test that this message is logged correctly using `caplog`:

  ```py
  def test_no_endpoint_env_var(caplog):
      os.environ.pop("POCKET_LAMBDA_ENDPOINT", None)  # Remove env variable if it exists

      with caplog.at_level(logging.ERROR):
          result = get_articles("some_type")

      assert (
          "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set" in caplog.text
      )
      assert result is None
  ```

  > Note tha we pass in `caplog` as a parameter to the test function. This is how
  > pytest knows to use it as a fixture.

  ### `capsys`

  In our example, if the request is unsuccessful, we log an error message with
  `print` rather than `logging`. We can test that this message is printed
  correctly using `capsys` to check the stdout:

  ```py
  def test_http_error(capsys):
      with patch("requests.get") as mock_get:
          mock_get.return_value = Mock(ok=False, status_code=404)

          # Raise an HTTP error when raise_for_status is called
          mock_get.return_value.raise_for_status.side_effect = RequestException()

          result = get_articles("some_type")

      captured = capsys.readouterr()
      assert "An error occurred" in captured.out
  ```

  ### `excinfo`

raw_content:
  ---
  tags: [python, testing]
  ---

  # Testing Python code

  ## `pytest`

  Pytest is the most popular testing library for Python. It is not included with
  the Python standard library so it must be installed with
  [pip](Python_package_management.md).
  While it does not include a declaration library, it is robust enough to handle
  most scenarios having a rich and expressive set of constructs and decorators
  that let you declare what your tests should do, under what conditions they
  should run, and how they should interact with the rest of your system.

  ### Using `pytest`

  - Pytest will automatically detect test files so long as they are named
    appropriately. E.g. for a module called `lorem`, it will detzect the unit test
    files `lorem_test.py` and `test_lorem.py`.
  - In order to detect tests it should be run from a directory level above them

  ### Examples

  Here is a basic example of using pytest for a local module callled `palindrome`:

  ```py
  import palindrome

  def test_is_palindrome():
      assert palindrome.is_palindrome('soros')
      assert palindrome.is_palindrome('torot')
      assert not palindrome.is_palindrome('chair')
  ```

  ## Mocking

  `patch()` and `Mock` enable us to mock objects whilst testing (classes,
  functions, methods and properties belonging). They are used in combination.

  The `@patch` decorator temporarily replaces a specified object in your code with
  a mock object and restores the original object after the test is complete

  A Mock object simulates the object it replaces so that the object behaves as
  expected during testing. For example, if your code calls
  `some_function.some_method()`, and `some_method` is mocked, calling
  `some_method` will not execute real logic but will interact with the Mock object
  instead. Mock objects record details about how they have been used, like what
  methods have been called, with what arguments, etc., allowing you to make
  assertions about how they have been used.

  > `@patch` and `Mock` work together because a patch is used to replace an object
  > or attribute with a Mock object. `Mock` handles the simulated functionality,
  > and `@patch` designates the real value we are replacing with the mock.

  ### Example case

  I will use the following example from one my projects:

  ```py
  # get_articles.py
  def get_articles(article_type: str) -> Optional[Dict[str, Any]]:
      """Retrieve articles from Pocket API"""

      if POCKET_LAMBDA_ENDPOINT is None:
          logging.error("Error: POCKET_LAMBDA_ENDPOINT envinronment variable is not set")
          return None
      else:
          # Interpolate the article_type into the Pocket request URL
          endpoint = POCKET_LAMBDA_ENDPOINT.format(article_type=article_type)

      try:
          response = requests.get(endpoint)
          response.raise_for_status()
          return response.json()

      except RequestException as e:
          print(f"An error occurred: {e}")
          return None
  ```

  This function: sources a URL from an environment variable, interpolates a query
  string into the URL (which comes in as a parameter), makes a request to the URL,
  and returns the response as JSON.

  It has some safeguards in place:

  - It checks that the environment variable is set
  - It checks that the request was successful

  In the example we could use a Mock object to simulate the response from the
  Pocket API. This would allow us to test the function without having to make a
  real request to the API:

  ```py
  def test_successful_request():
      # Replace the requests.get function with a Mock object (mock_get)
      with patch("requests.get") as mock_get:
          # Specify the return value of the mock_get object)
          mock_get.return_value = Mock(ok=true)
          mock_get.return_value.json.return_value = {"value": "test"}

          # Call the function under test
          result = get_articles("gaby")

      # Assert expected outcomes
      mock_get.assert_called_once_with(endpoint)
      assert result == mock_json_response
  ```

  The example above follows the **Arrange, Act, Assert** pattern:

  | Stage   | Action                                                                          |
  | ------- | ------------------------------------------------------------------------------- |
  | Arrange | Replace the `requests.get` function with `patch` and set properties with `Mock` |
  | Act     | Call the function under test                                                    |
  | Assert  | Assert that the function under test behaved as expected                         |

  ### Alternative mock syntax

  The `with patch(...) as mock_name` syntax is fine for small-scale mocking but
  can become cumbersome when you are mocking several dependencies.

  There is another syntax (which I actually find clearer). Say we have a function
  with three dependencies: `update_worksheet`, `process_articles`, `get_articles`.
  We could mock like so:

  ```py
  @patch("app.update_worksheet")
  @patch("app.process_articles")
  @patch("app.get_articles")
  def test_success(
      mock_get_articles, mock_process_articles, mock_update_worksheet
  ):
      mock_get_articles.return_value = [1, 2, 3]
  ```

  Here the patching is done by the decorator and the mocks are defined as
  parameters to the test function (always in reverse order)

  ### Mock assertion lexicon

  #### `return_value`

  State what the mock should return

  ```py
  my_mocked_function.return_value = ['one', 'two', 'three']
  ```

  #### `call_count`

  Test how many times a dependent function is called

  ```py
  assert my_mocked_function.call_count = 3
  ```

  #### `assert_any_call()`

  Test that a given mock is called at least once during the execution of the
  function under test

  ```py
  my_mocked_function.assert_any_call(some_mocked_return_value)
  ```

  When the output of one function is used as a parameter to another, and we don't
  particularly care about the details of what is concerned we can just pass the
  executed function, e.g:

  ```py
  my_mocked_function.assert_any_call(preceding_function())
  ```

  #### `call_args_list`

  Get a list of all the arguments that a mock object was called with during the
  test.

  `call_args_list` is useful when you want to check the arguments that a mock
  object was called with during the test, especially if the mock object was called
  multiple times with different arguments. You can use it to inspect the arguments
  of each call and make assertions based on them.

  ```py
  second_my_mocked_function_call = my_mocked_function.call_args_list[1]

  # check the first argument of the second call:

  assert second_my_mocked_function_call[0][0] == "expected arg"
  ```

  #### `side_effect`

  Use to trigger a side effect when returning a value from a mock. Most useful for
  mocking exceptions.

  ```py
  my_mocked_function.side_effect = Exception("Some exception raised")
  ```

  ## Testing exceptions with `raises`

  Testing exceptions is quite straightforward. You can use the `raises` helper
  provided by pytest, and combine this with `excinfo` ("exception info") to
  inspect the exception message.

  ```py
  if POCKET_LAMBDA_ENDPOINT is None:
      raise ValueError(
          "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set"
      )
  ```

  Then to test this, we would use pytest's `excinfo` fixture along with `raises`:

  ```py
   with pytest.raises(ValueError) as excinfo:  # Watch for the ValueError
      get_articles("some_type")

      assert "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set" in str(
          excinfo.value
      )
  ```

  We could actually simplify the above test by using the `match` parameter with
  `raise`. This way we do not need the separate assertion:

  ```py
  with pytest.raises(ValueError, match="Error: POCKET_LAMBDA_ENDPOINT environment variable is not set"):
      get_articles("some_type")
  ```

  Note that `excinfo` is best used for testing the exception text that you the
  developer explicitly `raise`. For exceptions tha may occur naturaly in the code
  you are testing, you should use `caplog` or `capsys` (see below).

  ## Before-each and after-each

  When testing functions, we achieve this in Python using `setup_function` and
  `teardown_function` methods. These methods are called before and after each test
  method respectively.

  To apply a "before each" to _every test_ just put your setup function and/or
  teardown function at the top level of your test module.

  For example, below we set and remove an env var before and after each test:

  ```py
  @pytest.fixture(scope="function") # specify that this fixture should be run before each function test
  def setup_function():
      print("Setting up test environment...")
      os.environ["POCKET_LAMBDA_ENDPOINT"] = "https://some_endpoint.com/{article_type}"


  def teardown_function():
      print("Tearing down test environment...")
      del os.environ["POCKET_LAMBDA_ENDPOINT"]
  ```

  If the setup/teardown should only be applied to a subset of tests, just pass the
  name of the fixture as a parameter to the test function:

  ```py
  def some_function(setup_function):
      # setup_function will be run before this test
  ```

  You don't need to use the names `setup_function` and `teardown_function` so long
  as you are passing the fixture as a parameter.

  You can also use `yield` to combine the setup and teardown into a single
  function:

  ```py
  @pytest.fixture(scope="function")
  def setup_function():
      os.environ["POCKET_LAMBDA_ENDPOINT"] = "https://some_endpoint.com/{article_type}"
      yield
      del os.environ["POCKET_LAMBDA_ENDPOINT"]
  ```

  ### Another example:

  The following test suite uses the same three mocked functions in every test. The
  following set-up assigns the mocks before each test and resets after each
  individual test has run:

  ```py
  @pytest.fixture(scope="function")
  def setup_function():
      with patch("app.get_articles") as mock_get_articles, patch(
          "app.process_articles"
      ) as mock_process_articles, patch("app.update_worksheet") as mock_update_worksheet:
          yield mock_get_articles, mock_process_articles, mock_update_worksheet
  ```

  Then to use:

  ```py
  def individual_test(setup_function):
      mock_get_articles, mock_process_articles, mock_update_worksheet = setup_function
      # Now each mock can be referenced using the vars above

  ```

  ## Parameterized tests

  For a sequence of tests that are repetitive, to avoid repeating the same code
  over and over again, we can use parameterized tests. This is where we pass in a
  list of parameters to the test function and the test function is run once for
  each parameter.

  For example, in the function below I am handling numerous possible Exceptions
  that could be raised by the `requests.get` method:

  ```py
      try:
          response = requests.get(endpoint)
          response.raise_for_status()
          return response.json()

      except HTTPError as http_err:
          logging.error(f"HTTP Error occurred: {http_err}")

      except ConnectionError as conn_err:
          logging.error(f"Connection Error occurred: {conn_err}")

      except Timeout as timeout_err:
          logging.error(f"Timeout Error occurred: {timeout_err}")

      except RequestException as e:
          logging.error(f"Request Exception occurred: {e}")

      return None
  ```

  Instead of writing something like the following for each of the four exceptions:

  ```py

   def test_exception_generic(caplog):
      with patch("requests.get", side_effect=RequestException("Some error")):
          result = get_articles("some_type")

      assert "Request Exception occurred" in caplog.text
      assert result is None
  ```

  I could parameterize like so:

  ```py
  @pytest.mark.parametrize(
      "exception_type, log_message",
      [
          (RequestException, "Request Exception occurred: "),
          (HTTPError, "HTTP Error occurred: "),
          (Timeout, "Timeout Error occurred: "),
          (ConnectionError, "Connection Error occurred: "),
      ],
  )
  def test_exceptions(caplog, exception_type, log_message):
      with patch("requests.get", side_effect=exception_type("Some error")):
          result = get_articles("some_type")

      assert log_message in caplog.text
      assert result is None
  ```

  ## Caplog, syslog, excinfo

  `caplog` and `capsys` are built-in pytest fixtures. `caplog` lets you test log
  messages. `capsys` lets you test stdout and stderr. As such they are very useful
  when testing that error messages are logged correctly.

  ### `caplog`

  In our example, if the endpoing environment is not set, we log an error message.
  We can test that this message is logged correctly using `caplog`:

  ```py
  def test_no_endpoint_env_var(caplog):
      os.environ.pop("POCKET_LAMBDA_ENDPOINT", None)  # Remove env variable if it exists

      with caplog.at_level(logging.ERROR):
          result = get_articles("some_type")

      assert (
          "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set" in caplog.text
      )
      assert result is None
  ```

  > Note tha we pass in `caplog` as a parameter to the test function. This is how
  > pytest knows to use it as a fixture.

  ### `capsys`

  In our example, if the request is unsuccessful, we log an error message with
  `print` rather than `logging`. We can test that this message is printed
  correctly using `capsys` to check the stdout:

  ```py
  def test_http_error(capsys):
      with patch("requests.get") as mock_get:
          mock_get.return_value = Mock(ok=False, status_code=404)

          # Raise an HTTP error when raise_for_status is called
          mock_get.return_value.raise_for_status.side_effect = RequestException()

          result = get_articles("some_type")

      captured = capsys.readouterr()
      assert "An error occurred" in captured.out
  ```

  ### `excinfo`


path: Testing_with_Jest.md
sortable_path: Testing_with_Jest.md
title: Testing with Jest
lead: ## Mocking classes/modules
checksum: 073f7ee1dd7b138b9e24d7765cf0ae3b60643eb0ef5c9f391187fd72c1e98968
created: 2024-10-22 16:52:59.299290876+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["javascript","testing","jest"]}
body:
  ## Mocking classes/modules

  ### Classes

  Let' say we have this class:

  ```js
  // database.js
  class Database {
    connect() {}
    save(data) {}
  }
  ```

  Then to mock:

  ```js
  import Database from "./database";

  // This will mock the whole Database class, replacing all methods with jest mock functions.
  jest.mock("./database");

  test("should use mocked save method", () => {
    const dbInstance = new Database();

    // Mocking the save method with a specific return value
    dbInstance.save.mockReturnValue(true);

    const result = dbInstance.save({ key: "value" });

    expect(result).toBe(true);
    expect(dbInstance.save).toHaveBeenCalledWith({ key: "value" });

    // The connect method is still a mock function (but without a specific behavior).
    dbInstance.connect();
    expect(dbInstance.connect).toHaveBeenCalled();
  });
  ```

  ### Modules

  Say we have the following module file:

  ```js
  // utils.js
  export const doSomething = () => {
    // ...
  };

  export const fetchUserData = async (userId) => {
    const response = await axios.get(`/api/users/${userId}`);
    return response.data;
  };
  ```

  Mocked:

  ```js
  jest.mock("./utils", () => {
    return {
      doSomething: jest.fn(() => "mocked doSomething"),
      fetchUserData: jest.fn((userId) =>
        Promise.resolve({ id: userId, name: "Mock User" })
      ),
    };
  });

  test("should use mocked module functions", () => {
    expect(utils.doSomething()).toBe("mocked doSomething");
    expect(utils.doSomething).toHaveBeenCalled();

    const result = await utils.fetchUserData(123);

    expect(result).toEqual({ id: 123, name: "Mock User" });
    expect(utils.fetchUserData).toHaveBeenCalledWith(123);

  });
  ```

  ### Inline mocking versus "per test" mocking

  There are two different architectures that we can use when mocking modules and
  classes: **inline** and **per test** mocking.

  Here is the inline case:

  ```js
  jest.mock("./some_module.js", () => {
    return {
      someFunction: jest.fn(() => "value"),
      someFunctionWithParam: jest.fn((param) => ({
        property: param,
      })),
      someAsyncFunction: jest.fn(() => Promise.resolve("value")),
    };
  });
  ```

  Here is the per test case:

  ```js
  import { someModule } from "./some_module.js";

  let someModuleMock;

  someModuleMock = {
    someFunction: jest.fn(() => "value"),
    someFunctionWithParam: jest.fn((param) => ({
      property: param,
    })),
    someAsyncFunction: jest.fn(() => Promise.resolve("value")),
  };

  someModule.mockImplementation(() => someModuleMock);

  it("should do something", () => {
    const newValue = "new value";
    someModule.someFunction.mockReturnValue(newValue);
  });
  ```

  The benefits of inline:

  - Inline is good because everything is set up in one place
  - Inline keeps consistency accross tests: every test case in the file will use
    the same mocked function unless overwritten within a test
  - It lends itself to being a _global_ mock that can be used accross test files
    in a `__mocks__/` directory

  The benefits of per-test:

  - You can very mock implementations within the file, providing more granular
    control. You can redefine `someModuleMock` or parts of it
    (`someModule.someFunction`) throughout your test file to accomodate varied
    requirements between tests
  - It’s beneficial when your tests have divergent requirements, as you can
    perform more detailed setups and overrides for each individual test case or
    suite, ensuring mocks are configured exactly as required.

  #### Overriding inline mocks

  Per test mocking makes it straightforward to change the test parameters of the
  mocked module or class but you can also override inline mocks.

  If we were using the `someModule` inline mock and we wanted to override the
  `someFunction` function that we have defined inline, we would first import the
  `someFunction` function and then use `mockImplementation` against it:

  ```js
  import { someFunction } from "./some_module.js";
  someFunction.mockImplementation(() => "custom value");
  expect(someFunction()).toBe("custom value");

  // Optional: Restore the original mock implementation after the test
  someFunction.mockRestore();
  ```

  Note: although we are importing `someFunction` we are not actually importing the
  real function tha belongs to the module. Because Jest mocks all of its
  properties and methods with the inline syntax, we are actually just importing
  that which Jest has aready mocked, but the syntax is a bit misleading.

  #### Applied to classes

  The same approaches (with minor differences) can be used with classes:

  Using inline (where the class is not the default export):

  ```js
  jest.mock("./SomeClass", () => {
    return {
      SomeClass: jest.fn().mockImplementation(() => {
        return {
          someFunction: jest.fn(() => "value"),
          someFunctionWithParam: jest.fn((param) => ({ property: param })),
          someAsyncFunction: jest.fn(() => Promise.resolve("value")),
          someOtherFunctionThatResolves: jest.fn().mockResolvedValue("some data"),
        };
      }),
    };
  });
  ```

  Using per test:

  ```js
  import SomeClass from "./someClass";

  jest.mock("./someClass");

  let someClassMock = {
    someFunction: jest.fn(() => "value"),
    someFunctionWithParam: jest.fn((param) => ({ property: param })),
    someAsyncFunction: jest.fn(() => Promise.resolve("value")),
  };

  // Mock class implementation
  SomeClass.mockImplementation(() => someClassMock);

  it("should do something", () => {
    const newValue = "new value";
    someClassMock.someFunction.mockReturnValue(newValue);
  });
  ```

  ## Check that a function has been called within another function

  ```js
  function toBeCalledFunction() {
    console.log("Original function called");
  }

  function callerFunction() {
    toBeCalledFunction();
  }
  ```

  ```js
  test("spy on toBeCalledFunction", () => {
    const spy = jest.spyOn(global, "toBeCalledFunction"); // Replace `global` with the appropriate object/context if the function is not global
    callerFunction();
    expect(spy).toHaveBeenCalled();
    spy.mockRestore(); // Restore the original function after spying
  });
  ```

  ## Mock a function that needs to resolve to something within another function

  We have two functions, one that gets data and another that processes it. We want
  to mock the function that gets data and return a value that the processing
  function can use.

  ```js
  async function getData() {
    // ... Fetch some data from an API or database
    return fetchedData;
  }

  async function processData() {
    const data = await getData();
    // ... Process the data
    return processedData;
  }
  ```

  The mocking part:

  ```js
  const mockData = { key: "value" }; // Mocked data

  jest.mock("./path-to-file-where-getData-is", () => ({
    getData: jest.fn().mockResolvedValue(mockData),
  }));

  test("test processData function", async () => {
    const result = await processData();
    // Now, result contains the processed version of mockData
    expect(result).toEqual(/* expected processed data based on mockData */);
  });
  ```

  We could also combine the above with a spy to check that the `getData` function
  was called:

  ```js
  const getDataSpy = jest
    .spyOn(moduleContainingGetData, "getData")
    .mockResolvedValue(mockData);

  const result = await processData();
  expect(getDataSpy).toHaveBeenCalled();
  expect(result).toEqual(/* expected processed data based on mockData */);
  getDataSpy.mockRestore();
  ```

  ## Mock a function that takes arguments

  ```js
  function addPrefix(str) {
    return `prefix-${str}`;
  }
  ```

  ```js
  test("dynamic mock for addPrefix function", () => {
    const mockFunction = jest.fn((str) => `mock-${str}`);

    // Example usage of mockFunction
    const result1 = mockFunction("test");
    const result2 = mockFunction("example");

    expect(result1).toBe("mock-test");
    expect(result2).toBe("mock-example");
  });
  ```

  ## Mocking network requests

  ### Mocking Axios

  ```js
  jest.mock("axios", () => ({
    get: jest.fn().mockResolvedValue(mockData),
    post: jest.fn().mockResolvedValue(mockData),
  }));
  ```

  Or we could implement this way:

  ```js
  jest.mock("axios");
  axios.get.mockResolvedValue({ data: "mockedData" });
  axios.post.mockResolvedValue({ data: "mockedData" });
  ```

  Then we can use the mocked axios functions in our tests:

  ```js
  const result = await fetchData(); // the function that uses Axios `get``
  expect(result).toBe("mockedGetData");

  const result = await sendData({ key: "value" }); // the function tha uses Axios `post`
  expect(result).toBe("mockedPostData");
  ```

  ### `mockImplementation`

  For more configurable cases we can use `mockImplementation`:

  ```js
  it("sends data", async () => {
    // Mock axios.post using mockImplementation
    axios.post.mockImplementation((url, data) => {
      if (data.key === "value") {
        return Promise.resolve({ data: "mockedPostData" });
      } else {
        return Promise.reject({ error: "An error occurred" });
      }
    });

    const result = await sendData({ key: "value" });

    expect(result).toBe("mockedPostData");
  });
  ```

  If we want to change the `get` and `post` values in different tests, we can do
  so by using `mockImplementation`:

  ## Mocking exceptions

  Again we use `mockImplementation`:

  Say we have the following function:

  ```js
  // fetchData.js
  import axios from "axios";

  const fetchData = async (url) => {
    try {
      const response = await axios.get(url);
      return response.data;
    } catch (error) {
      throw new Error("Error fetching data");
    }
  };

  export default fetchData;
  ```

  We would mock the success and the error as follows:

  ```js
  import axios from "axios";
  import fetchData from "./fetchData";

  jest.mock("axios");

  describe("fetchData", () => {
    it("fetches data successfully", async () => {
      axios.get.mockResolvedValue({ data: "mockedData" });

      const result = await fetchData("https://api.example.com/data");

      expect(result).toBe("mockedData");
    });

    it("throws an error when fetching fails", async () => {
      axios.get.mockImplementation(() => {
        throw new Error("API error");
      });

      // We use an asynchronous assertion here because we're expecting a promise to reject
      await expect(fetchData("https://api.example.com/data")).rejects.toThrow(
        "Error fetching data"
      );
    });
  });
  ```

  ## Parameterization

  The following offers a good opportunity for parameterisation:

  ```js
  it("should return page for deletion from `ipages-live`", async () => {
    // preview = false, isInternal = false
    await deletePageFromS3("url", false, false);
    const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
    expect(deleteObjectCommand.input).toEqual({
      Bucket: "bbc-ise-ipages-live",
      Key: "url/index.html",
    });
  });

  it("should return page for deletion from `preview`", async () => {
    // preview = true, isInternal = false
    await deletePageFromS3("url", true, false);
    const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
    expect(deleteObjectCommand.input).toEqual({
      Bucket: "staff.bbc.com-preview",
      Key: "preview/url/index.html",
    });
  });

  ...
  ```

  Each time we are passing in three parameters to the `deletePageFromS3` function
  which is the object under test. Each time there are different variations in the
  object that is output.

  To parameterize the process rather than use repeated `it` blocks we can combine
  the input paramters and outputs into an array:

  ```js
  const testParams = [
    {
      preview: false,
      isInternal: false,
      bucket: "ipages-live",
      key: "url/index.html",
    },
    {
      preview: true,
      isInternal: false,
      bucket: "staff.com-preview",
      key: "preview/url/index.html",
    },
  ];
  ```

  Then use `it.each` to loop through all possible parameter combinations:

  ```js
  it.each(testParams)(
    "should return page for deletion from %s",
    async ({ preview, isInternal, bucket, key }) => {
      await deletePageFromS3("url", preview, isInternal);
      const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
      expect(deleteObjectCommand.input).toEqual({
        Bucket: bucket,
        Key: key,
      });
    }
  );
  ```

  This uses the `%s` variable to print the parameters from each test, which
  outputs:

  ```
    ✓ should return page for deletion from {
    preview: false,
    isInternal: false,
    bucket: 'ipages-live',
    key: 'url/index.html'
  } (1 ms)
      ✓ should return page for deletion from {
    preview: true,
    isInternal: false,
    bucket: 'staff.com-preview',
    key: 'preview/url/index.html'
  }
  ```

raw_content:
  ---
  tags: [javascript, testing, jest]
  ---

  # Testing with Jest

  ## Mocking classes/modules

  ### Classes

  Let' say we have this class:

  ```js
  // database.js
  class Database {
    connect() {}
    save(data) {}
  }
  ```

  Then to mock:

  ```js
  import Database from "./database";

  // This will mock the whole Database class, replacing all methods with jest mock functions.
  jest.mock("./database");

  test("should use mocked save method", () => {
    const dbInstance = new Database();

    // Mocking the save method with a specific return value
    dbInstance.save.mockReturnValue(true);

    const result = dbInstance.save({ key: "value" });

    expect(result).toBe(true);
    expect(dbInstance.save).toHaveBeenCalledWith({ key: "value" });

    // The connect method is still a mock function (but without a specific behavior).
    dbInstance.connect();
    expect(dbInstance.connect).toHaveBeenCalled();
  });
  ```

  ### Modules

  Say we have the following module file:

  ```js
  // utils.js
  export const doSomething = () => {
    // ...
  };

  export const fetchUserData = async (userId) => {
    const response = await axios.get(`/api/users/${userId}`);
    return response.data;
  };
  ```

  Mocked:

  ```js
  jest.mock("./utils", () => {
    return {
      doSomething: jest.fn(() => "mocked doSomething"),
      fetchUserData: jest.fn((userId) =>
        Promise.resolve({ id: userId, name: "Mock User" })
      ),
    };
  });

  test("should use mocked module functions", () => {
    expect(utils.doSomething()).toBe("mocked doSomething");
    expect(utils.doSomething).toHaveBeenCalled();

    const result = await utils.fetchUserData(123);

    expect(result).toEqual({ id: 123, name: "Mock User" });
    expect(utils.fetchUserData).toHaveBeenCalledWith(123);

  });
  ```

  ### Inline mocking versus "per test" mocking

  There are two different architectures that we can use when mocking modules and
  classes: **inline** and **per test** mocking.

  Here is the inline case:

  ```js
  jest.mock("./some_module.js", () => {
    return {
      someFunction: jest.fn(() => "value"),
      someFunctionWithParam: jest.fn((param) => ({
        property: param,
      })),
      someAsyncFunction: jest.fn(() => Promise.resolve("value")),
    };
  });
  ```

  Here is the per test case:

  ```js
  import { someModule } from "./some_module.js";

  let someModuleMock;

  someModuleMock = {
    someFunction: jest.fn(() => "value"),
    someFunctionWithParam: jest.fn((param) => ({
      property: param,
    })),
    someAsyncFunction: jest.fn(() => Promise.resolve("value")),
  };

  someModule.mockImplementation(() => someModuleMock);

  it("should do something", () => {
    const newValue = "new value";
    someModule.someFunction.mockReturnValue(newValue);
  });
  ```

  The benefits of inline:

  - Inline is good because everything is set up in one place
  - Inline keeps consistency accross tests: every test case in the file will use
    the same mocked function unless overwritten within a test
  - It lends itself to being a _global_ mock that can be used accross test files
    in a `__mocks__/` directory

  The benefits of per-test:

  - You can very mock implementations within the file, providing more granular
    control. You can redefine `someModuleMock` or parts of it
    (`someModule.someFunction`) throughout your test file to accomodate varied
    requirements between tests
  - It’s beneficial when your tests have divergent requirements, as you can
    perform more detailed setups and overrides for each individual test case or
    suite, ensuring mocks are configured exactly as required.

  #### Overriding inline mocks

  Per test mocking makes it straightforward to change the test parameters of the
  mocked module or class but you can also override inline mocks.

  If we were using the `someModule` inline mock and we wanted to override the
  `someFunction` function that we have defined inline, we would first import the
  `someFunction` function and then use `mockImplementation` against it:

  ```js
  import { someFunction } from "./some_module.js";
  someFunction.mockImplementation(() => "custom value");
  expect(someFunction()).toBe("custom value");

  // Optional: Restore the original mock implementation after the test
  someFunction.mockRestore();
  ```

  Note: although we are importing `someFunction` we are not actually importing the
  real function tha belongs to the module. Because Jest mocks all of its
  properties and methods with the inline syntax, we are actually just importing
  that which Jest has aready mocked, but the syntax is a bit misleading.

  #### Applied to classes

  The same approaches (with minor differences) can be used with classes:

  Using inline (where the class is not the default export):

  ```js
  jest.mock("./SomeClass", () => {
    return {
      SomeClass: jest.fn().mockImplementation(() => {
        return {
          someFunction: jest.fn(() => "value"),
          someFunctionWithParam: jest.fn((param) => ({ property: param })),
          someAsyncFunction: jest.fn(() => Promise.resolve("value")),
          someOtherFunctionThatResolves: jest.fn().mockResolvedValue("some data"),
        };
      }),
    };
  });
  ```

  Using per test:

  ```js
  import SomeClass from "./someClass";

  jest.mock("./someClass");

  let someClassMock = {
    someFunction: jest.fn(() => "value"),
    someFunctionWithParam: jest.fn((param) => ({ property: param })),
    someAsyncFunction: jest.fn(() => Promise.resolve("value")),
  };

  // Mock class implementation
  SomeClass.mockImplementation(() => someClassMock);

  it("should do something", () => {
    const newValue = "new value";
    someClassMock.someFunction.mockReturnValue(newValue);
  });
  ```

  ## Check that a function has been called within another function

  ```js
  function toBeCalledFunction() {
    console.log("Original function called");
  }

  function callerFunction() {
    toBeCalledFunction();
  }
  ```

  ```js
  test("spy on toBeCalledFunction", () => {
    const spy = jest.spyOn(global, "toBeCalledFunction"); // Replace `global` with the appropriate object/context if the function is not global
    callerFunction();
    expect(spy).toHaveBeenCalled();
    spy.mockRestore(); // Restore the original function after spying
  });
  ```

  ## Mock a function that needs to resolve to something within another function

  We have two functions, one that gets data and another that processes it. We want
  to mock the function that gets data and return a value that the processing
  function can use.

  ```js
  async function getData() {
    // ... Fetch some data from an API or database
    return fetchedData;
  }

  async function processData() {
    const data = await getData();
    // ... Process the data
    return processedData;
  }
  ```

  The mocking part:

  ```js
  const mockData = { key: "value" }; // Mocked data

  jest.mock("./path-to-file-where-getData-is", () => ({
    getData: jest.fn().mockResolvedValue(mockData),
  }));

  test("test processData function", async () => {
    const result = await processData();
    // Now, result contains the processed version of mockData
    expect(result).toEqual(/* expected processed data based on mockData */);
  });
  ```

  We could also combine the above with a spy to check that the `getData` function
  was called:

  ```js
  const getDataSpy = jest
    .spyOn(moduleContainingGetData, "getData")
    .mockResolvedValue(mockData);

  const result = await processData();
  expect(getDataSpy).toHaveBeenCalled();
  expect(result).toEqual(/* expected processed data based on mockData */);
  getDataSpy.mockRestore();
  ```

  ## Mock a function that takes arguments

  ```js
  function addPrefix(str) {
    return `prefix-${str}`;
  }
  ```

  ```js
  test("dynamic mock for addPrefix function", () => {
    const mockFunction = jest.fn((str) => `mock-${str}`);

    // Example usage of mockFunction
    const result1 = mockFunction("test");
    const result2 = mockFunction("example");

    expect(result1).toBe("mock-test");
    expect(result2).toBe("mock-example");
  });
  ```

  ## Mocking network requests

  ### Mocking Axios

  ```js
  jest.mock("axios", () => ({
    get: jest.fn().mockResolvedValue(mockData),
    post: jest.fn().mockResolvedValue(mockData),
  }));
  ```

  Or we could implement this way:

  ```js
  jest.mock("axios");
  axios.get.mockResolvedValue({ data: "mockedData" });
  axios.post.mockResolvedValue({ data: "mockedData" });
  ```

  Then we can use the mocked axios functions in our tests:

  ```js
  const result = await fetchData(); // the function that uses Axios `get``
  expect(result).toBe("mockedGetData");

  const result = await sendData({ key: "value" }); // the function tha uses Axios `post`
  expect(result).toBe("mockedPostData");
  ```

  ### `mockImplementation`

  For more configurable cases we can use `mockImplementation`:

  ```js
  it("sends data", async () => {
    // Mock axios.post using mockImplementation
    axios.post.mockImplementation((url, data) => {
      if (data.key === "value") {
        return Promise.resolve({ data: "mockedPostData" });
      } else {
        return Promise.reject({ error: "An error occurred" });
      }
    });

    const result = await sendData({ key: "value" });

    expect(result).toBe("mockedPostData");
  });
  ```

  If we want to change the `get` and `post` values in different tests, we can do
  so by using `mockImplementation`:

  ## Mocking exceptions

  Again we use `mockImplementation`:

  Say we have the following function:

  ```js
  // fetchData.js
  import axios from "axios";

  const fetchData = async (url) => {
    try {
      const response = await axios.get(url);
      return response.data;
    } catch (error) {
      throw new Error("Error fetching data");
    }
  };

  export default fetchData;
  ```

  We would mock the success and the error as follows:

  ```js
  import axios from "axios";
  import fetchData from "./fetchData";

  jest.mock("axios");

  describe("fetchData", () => {
    it("fetches data successfully", async () => {
      axios.get.mockResolvedValue({ data: "mockedData" });

      const result = await fetchData("https://api.example.com/data");

      expect(result).toBe("mockedData");
    });

    it("throws an error when fetching fails", async () => {
      axios.get.mockImplementation(() => {
        throw new Error("API error");
      });

      // We use an asynchronous assertion here because we're expecting a promise to reject
      await expect(fetchData("https://api.example.com/data")).rejects.toThrow(
        "Error fetching data"
      );
    });
  });
  ```

  ## Parameterization

  The following offers a good opportunity for parameterisation:

  ```js
  it("should return page for deletion from `ipages-live`", async () => {
    // preview = false, isInternal = false
    await deletePageFromS3("url", false, false);
    const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
    expect(deleteObjectCommand.input).toEqual({
      Bucket: "bbc-ise-ipages-live",
      Key: "url/index.html",
    });
  });

  it("should return page for deletion from `preview`", async () => {
    // preview = true, isInternal = false
    await deletePageFromS3("url", true, false);
    const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
    expect(deleteObjectCommand.input).toEqual({
      Bucket: "staff.bbc.com-preview",
      Key: "preview/url/index.html",
    });
  });

  ...
  ```

  Each time we are passing in three parameters to the `deletePageFromS3` function
  which is the object under test. Each time there are different variations in the
  object that is output.

  To parameterize the process rather than use repeated `it` blocks we can combine
  the input paramters and outputs into an array:

  ```js
  const testParams = [
    {
      preview: false,
      isInternal: false,
      bucket: "ipages-live",
      key: "url/index.html",
    },
    {
      preview: true,
      isInternal: false,
      bucket: "staff.com-preview",
      key: "preview/url/index.html",
    },
  ];
  ```

  Then use `it.each` to loop through all possible parameter combinations:

  ```js
  it.each(testParams)(
    "should return page for deletion from %s",
    async ({ preview, isInternal, bucket, key }) => {
      await deletePageFromS3("url", preview, isInternal);
      const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
      expect(deleteObjectCommand.input).toEqual({
        Bucket: bucket,
        Key: key,
      });
    }
  );
  ```

  This uses the `%s` variable to print the parameters from each test, which
  outputs:

  ```
    ✓ should return page for deletion from {
    preview: false,
    isInternal: false,
    bucket: 'ipages-live',
    key: 'url/index.html'
  } (1 ms)
      ✓ should return page for deletion from {
    preview: true,
    isInternal: false,
    bucket: 'staff.com-preview',
    key: 'preview/url/index.html'
  }
  ```


path: Text_manipulation_in_Bash.md
sortable_path: Text_manipulation_in_Bash.md
title: Text manipulation
lead: ## Sorting strings: `sort`
checksum: 16bd858534a5e8bb07228d40c6a8984bad8a4b74cf7a8c1b32bae9ae2ba8dfe1
created: 2024-10-22 16:52:59.305439043+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["shell"]}
body:
  ## Sorting strings: `sort`

  If you have a `.txt` file containing text strings, each on a new line you can
  use the sort function to quickly put them in alphabetical order:

  ```bash
  sort file.txt
  ```

  Note that this will not save the sort, it only presents it as a standard output.
  To save the sort you need to direct the sort to a file in the standard way:

  ```bash
  sort file.txt > output.txt
  ```

  ### Options

  - `-r`
    - reverse sort
  - `c`
    - check if file is already sorted. If not, it will highlight the strings which
      are not sorted

  ## Find and replace: `sed`

  The `sed` programme can be used to implement find and replace procedures. In
  `sed`, find and replace are covered by the substitution option: `/s` :

  ```bash
  sed ‘s/word/replacement word/’ file.txt
  ```

  This however will only change the first instance of word to be replaced, in
  order to apply to every instance you need to add the global option: `-g` .

  As sed is a stream editor, any changes you make using it, will only occur within
  the standard output , they will not be saved to file. In order to save to file
  you need to specify a new file output (using `> output.txt`) in addition to the
  original file. This hasthe benefit of leaving the original file untouched whilst
  ensuring the desired outcome is stored permanently.

  Alternatively, you can use the `-i` option which will make the changes take
  place in the source file as well as in standard input.

  Note that this will overwrite the original version of the file and it cannot be
  regained. If this is an issue then it is recommended to include a backup command
  in the overall argument like so:

  ```bash
  sed -i.bak ‘s/word/replacement word/’ file.txt
  ```

  This will create the file `file.txt.bak` in the directory you are working within
  which is the original file before the replacement was carried out.

  ### Remove duplicates

  We can use the `sort -u` command can be used to remove duplicates:

  ```bash
  sort -u file.txt
  ```

  It is important to sort before attempting to remove duplicates since the `-u`
  flag works on the basis of the strings being adjacent.

  ## Split a large file into multiple smaller files: `split`

  Suppose you have a file containing 1000 lines. You want to break the file up
  into five separate files, each containing two hundred lines. You can use `split`
  to accomplish this, like so:

  ```bash
  split -l 200 big-file.txt new-files
  ```

  `split` will categorise the resulting five files as follows:

  - new-file-aa,
  - new-file-ab
  - new-file-ac,
  - newfile-ad,
  - new-file-ae.

  If you would rather have numeric suffixes, use the option `-d` . You can also
  split a file by its number of bytes, using the option `-b` and specifying a
  constituent file size.

  ## Merge multiple files into one with `cat`

  We can use `cat` read multiple files at once and then append a redirect to save
  them to a file:

  ```bash
  cat file_a.txt file_b.txt file_c.txt > merged-file.txt
  ```

  ## Count lines, words, etc: `wc`

  To count words:

  ```bash
  wc file.txt
  ```

  When we use the command three numbers are outputted, in order: lines, words,
  bytes.

  You can use modifiers to get just one of the numbers: `-l`, `-w` , `-b` .

raw_content:
  ---
  tags:
    - shell
  ---

  # Text manipulation

  ## Sorting strings: `sort`

  If you have a `.txt` file containing text strings, each on a new line you can
  use the sort function to quickly put them in alphabetical order:

  ```bash
  sort file.txt
  ```

  Note that this will not save the sort, it only presents it as a standard output.
  To save the sort you need to direct the sort to a file in the standard way:

  ```bash
  sort file.txt > output.txt
  ```

  ### Options

  - `-r`
    - reverse sort
  - `c`
    - check if file is already sorted. If not, it will highlight the strings which
      are not sorted

  ## Find and replace: `sed`

  The `sed` programme can be used to implement find and replace procedures. In
  `sed`, find and replace are covered by the substitution option: `/s` :

  ```bash
  sed ‘s/word/replacement word/’ file.txt
  ```

  This however will only change the first instance of word to be replaced, in
  order to apply to every instance you need to add the global option: `-g` .

  As sed is a stream editor, any changes you make using it, will only occur within
  the standard output , they will not be saved to file. In order to save to file
  you need to specify a new file output (using `> output.txt`) in addition to the
  original file. This hasthe benefit of leaving the original file untouched whilst
  ensuring the desired outcome is stored permanently.

  Alternatively, you can use the `-i` option which will make the changes take
  place in the source file as well as in standard input.

  Note that this will overwrite the original version of the file and it cannot be
  regained. If this is an issue then it is recommended to include a backup command
  in the overall argument like so:

  ```bash
  sed -i.bak ‘s/word/replacement word/’ file.txt
  ```

  This will create the file `file.txt.bak` in the directory you are working within
  which is the original file before the replacement was carried out.

  ### Remove duplicates

  We can use the `sort -u` command can be used to remove duplicates:

  ```bash
  sort -u file.txt
  ```

  It is important to sort before attempting to remove duplicates since the `-u`
  flag works on the basis of the strings being adjacent.

  ## Split a large file into multiple smaller files: `split`

  Suppose you have a file containing 1000 lines. You want to break the file up
  into five separate files, each containing two hundred lines. You can use `split`
  to accomplish this, like so:

  ```bash
  split -l 200 big-file.txt new-files
  ```

  `split` will categorise the resulting five files as follows:

  - new-file-aa,
  - new-file-ab
  - new-file-ac,
  - newfile-ad,
  - new-file-ae.

  If you would rather have numeric suffixes, use the option `-d` . You can also
  split a file by its number of bytes, using the option `-b` and specifying a
  constituent file size.

  ## Merge multiple files into one with `cat`

  We can use `cat` read multiple files at once and then append a redirect to save
  them to a file:

  ```bash
  cat file_a.txt file_b.txt file_c.txt > merged-file.txt
  ```

  ## Count lines, words, etc: `wc`

  To count words:

  ```bash
  wc file.txt
  ```

  When we use the command three numbers are outputted, in order: lines, words,
  bytes.

  You can use modifiers to get just one of the numbers: `-l`, `-w` , `-b` .


path: The_History_of_Computing_Swade.md
sortable_path: The_History_of_Computing_Swade.md
title: History of Computing (Swade, 2022 )
checksum: 74218045b9ac4980e8b4c9e5813ac1e0dd6a8fb48516ad0a52fa92aa50c9959c
created: 2024-10-22 16:52:59.311816146+00:00
modified: 2024-11-04 15:47:46.82814079+00:00
metadata: {"created":"Friday, August 23, 2024","tags":["literature","computer-history"]}
lead:
  | Title                                               | Author      | Publication date | Resource type |
  | --------------------------------------------------- | ----------- | ---------------- | ------------- |
  | The History of Computing: A Very Short Introduction | Doron Swade | 2022             | Book          |

body:
  | Title                                               | Author      | Publication date | Resource type |
  | --------------------------------------------------- | ----------- | ---------------- | ------------- |
  | The History of Computing: A Very Short Introduction | Doron Swade | 2022             | Book          |

  ## Timeline

  A typical timeline approach rooted in major innovations.

  - Ancient aids to counting: knotted cords and notched sticks
  - Ancient aids to calculation: counting boards and abacii
  - Early mechanical calculator devices in the 17th century (number wheels,
    Pascal, Leibniz)
  - Modern aids to calculation: slide rules following the discovery of
    [logarithms](Logarithms.md)
  - Mechanised, automated calculating engines of Babbage in the 19th century
  - Punched-card machines leading to IBM in the early 20th century
  - Analogue and electro-mechanical computers of the early 20th century inclusive
    of wartime computers
  - Early valve-based (vacuum-tubed) digital computers (again wartime and early
    Cold War)
  - The invention of the transistor and first fully-digital computers
  - The invention of [integrated_circuits](Integrated_circuits.md)
  - Supercomputers
  - Minicomputers
  - Consumer personal computers
  - Internet and later, Web
  - Smart phones

  First three phases of digital electronic computers:

  - Wartime up to 1950s vacuum-tube era
  - Transistor era up to 1963
  - First microchip era ending in early 1970s

  ## Terms

  | Term            | Definition                                                                                                                                                                  |
  | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | automatic       | can execute programs, process data and perform functions on its own once it has been started. Can monitor its own processes and make adjustments without human intervention |
  | general-purpose | a computer designed to be able to solve a wide variety of problems and perform many different types of tasks as opposed to a computer built for a specific, limited purpose |
  | programmable    | the ability to change the operations a computer peforms without physically altering its hardware                                                                            |

  ## Mechanical calculating devices in the 17th century

  Focus was chiefly on creating a desktop calculator capable of four-function
  arithmetic.

  ![Photograph of Pascaline](../img/pascaline.jpg)

  ![Photograph of replica of Leibniz stepped drum machine](../img/leibniz-stepped-drum.jpg)

  The main contenders were the Pascaline of Pascal (which only did cumulative
  addition) and the wheel or "stepped drum" calculator of Leibniz that could do
  all operations (in theory).

  Subsequent designs were based on these artefacts. In practice, neither worked
  consistently well with the carriage of tens remaining a sticking point.

  ![Photograph of Arithmometer](../img/arithmometer.jpg)

  ![Photograph of Comptometer](../img/comptometer.png)

  The arithmometer (crank driven) and comptometer (key-driven) were descendents of
  the Leibniz design that became commercially viable by the 19th century along
  with other mechanical calculators. In the US, Burroughs dominated the market.

  ## Babbage: mechanized, automated calculation

  > I wish to God these calculations had been executed by Steam (Babbage)

  With Babbage's machines we see an approach to computation that can only be
  understood against the backdrop of the Industrial Revolution in which they were
  conceived.

  The idea is that the machine is a factory and number is the product. In the same
  way as the mechanised looms created textiles. It is the extension of a model of
  industrial production from goods/commodities to information.

  Babbage conceived two machines: the Difference Engine (DE) and the Analytical
  Engine (AE). Neither were successfully built in his lifetime. The DE preceded
  the AE and was basically an advanced mechanical calculator whereas the AE
  approximated a general purpose computer.

  Despite this, with the Difference Engine, in contrast to preceding _aids to
  calculation_, the steps of the computational algorithm were no longer directed
  by human intelligence but by internal rules embodied in the mechanism and
  automatically generated.

  ### Difference Engine

  The DE's single purpose was to calculate and output mathematical tables such as
  the results of polynomial equations. The idea was that you would input the
  answer a bell would ring and the machine would _halt_. This influenced Turing
  later. It was non-programmable and designed for a specific set of calculations.

  ### Analytical Engine

  Conceived as a general-purpose computing machine capable of performing a wide
  range of calculations, programmable using punched cards similar to those used
  with Jacquard looms.

  It more resembled modern computers in that Babbage used concepts that would
  later translate into the [von Neumann architecture](CPU_architecture.md) There
  was a "mill" (CPU), "store" (memory) and input/output mechanisms. It also had a
  concept of looping and conditional branching.

  ### Lovelace's insight

  A central idea of Ada Lovelace, expressed in her notes on the Analytical Engine
  is that **number can represent entities other than quantity**.

  If we assign meaning to number, results arrived at by operating on number
  according to rules can say things about the world when mapped back onto the
  world using the meanings assigned to them.

  Lovelace's insight was that the potential of computing lay in the power of
  machines to manipulate representations of the world contained in symbols.

  ## Analogue computers

  Both digital and analogue computers are automatic. They differ in _how they
  represent quantities_ and how their outputs are derived.

  With digital machines, quantity is represented as a string of discrete digits.

  With analogue machines, quantity is a physical property _in itself_ rather than
  a representation. This could be, for example: the lowering of a weight, the flow
  of a liquid, or an electrical charge. This physical behaviour is **analagous**
  to the system that is being modelled. Quantities are continuously variable
  values rather than discrete (discontinuous values).

  Digital machines produce results by _calculation_ whereas analogue machines
  produce results by _measurement_, e.g. the height of liquid in a tank or the
  time it taks for a tank to be emptied.

  ### Examples of analogue computers

  #### The Phillips Hydraulic Computer

  This used fluid to model the workings of the British economy. It consisted of a
  series of transparent plastic tanks and pipes which were fastened to a wooden
  board.

  ![Philips Hydraulic Computer](../img/hydraulic_computer.jpg)

  Each tank represented some aspect of the UK national economy and the flow of
  money around the economy was illustrated by coloured water. At the top of the
  board was a large tank called the treasury. Water (representing money) flowed
  from the treasury to other tanks representing the various ways in which a
  country could spend its money. For example, there were tanks for health and
  education. To increase spending on health care a tap could be opened to drain
  water from the treasury to the tank which represented health spending.

  #### Bush's Differential Analyser

  This was designed to solve differential equations by integration. In contrast to
  the Philips Computer it was general enough to be used to solve problems from
  different contexts. Examples of these contexts: heat flow, ballistics,
  mechanics, population growth, chemical interactions, astronomy.

  ![Differential Analyser](../img/differential_analyser_bush.jpg)

  It was about the size of a room and used shafts, motors, discs and wheels to
  work.

  ### Historiography

  There is a tendency in the history of computing to downplay or diminish the
  contribution of analogue computing devices and to present them as just an
  inferior precursor to the inevitable dominance of digital electronic computers.

  This is ahistorical and inaccurate.

  Analogue (and electromechanical devices) overlapped with and coexisted with
  digital devices for 40 years, spanning the first three generations of digital
  electronic devices. The term "analogue" itself only came about when the need
  arose to distinguish digital devices from other types of computer. They were not
  "rivals" before this.

  ### Electro-mechanical computers

  Electro-mechanical devices (also known as "electronic analogue computers") are a
  sort of midway between full digital devices and analogue computers, forming a
  bridge between the two eras.

  Their heyday was roughly 1935 - 1945.

  Their key components were:

  - [relays](Relays.md) for logic operations
  - rotating shafts and gears for performing calculations
  - punched cards or paper tape for input instructions and outputs

  They were slower than fully digital computers as they were limited by the speed
  of moving components rather than the flow of electric charge. In addition the
  various mechanical parts were prone to wear and needed frequent replacement.

  ### Examples of electro-mechanical computers

  #### Harvard Mark 1 (1937-1944) - Aikin, IBM

  Designed by Howard Aiken and built by IBM (1937-1944). Also known as the
  _Automatic Sequence Controlled Calculator_ (ASCC). A general-purpose
  electro-mechanical computer it was most famously used at Los Alamos by von
  Neumann to calculate the blast yield of the atomic bomb.

  ![Harvard Mark One](../img/Harvard_Mark_1.jpg)

  It was more than 15m in length and weighed 5 tonnes comprising over 750,000
  parts. It used paper tape and punched cards for input/output.

  #### Complex Number Calculator (1940) - Bell Labs, Stibitz

  Not general purpose nor was it programmable. It was hardwired to perform a
  specific set of operations on numbers and nothing else. It used relays like the
  others. Its distinguishing feature was that it used a teletype for input rather
  than cards or paper tape.

  ![Calculating panel of the CMC](../img/complex-number-calculator-bell-labs.jpg)

  ![Teletype input of CMC](../img/stibitz-teletype.jpg)

  It comprised a panel (the calculating unit) and teletype (the input). One could
  remotely access the computer from the teletype in another location, providing it
  was connected to Bell Lab's telephone network.

  #### Z3 (1941) - Konrad Zuse

  ![Z3](../img/Zuse-Z3.jpeg)

  First designed in 1938 and completed in 1941. Considered the first fully
  automatic, programmable digital computer although relay-based. Programs were
  stored on external punched tape and binary was used as the number system.
  Destroyed by Allied bombing in the War but a replica (pictured) was built
  in 1998.

  ## Vacuum-tube devices

  Vacuum-tube devices were the first fully electronic digital devices as they used
  vacuum-tubes for implementing binary logic rather than relay switches.

  Canonical examples from this era are the ABC computer, the Collossus and most
  famously, the ENIAC.

  ### The Antanasoff-Berry Computer (1939 - 1942)

  ![ABC Computer](../img/abc-computer.jpg)

  Fully automatic, digital electronic computer but not programmable or really
  general-purpose, being created to do linear equations. It took decimal input and
  output but operated internally using binary arithmetic. The input was punched
  cards and the output was via a front-panel display.

  Its importance stems from the fact that it was the first to use vacuum-tubes for
  the arithmetic calculations of the processor.

  ### Colossus (1943-1945) - Tommy Flowers/Max Newman

  Designed and built at the Post Office Research Station at Dollis Hill. Used for
  cracking the Lorenz messages of the German high command.

  Mainly important (beyond its role in the War) for establishing the viability of
  vacuum-tubes over relays. It comprised 2.4 thousand tubes.

  Debatable the extent to which the Collosus can be called the _first digital
  computer_ since it was not general purpose and not fully programmable.

  Furthermore it was not even a complete solution in its context of application.
  The codebreakers would be given a certain advantage using the Collosus
  (narrowing the field of possible deciphering keys) but still need to use manual
  methods to break the code. It made decipher time shorter rather than executing
  the full diciphering. In contrast to, say, the Bombe computer that would
  identify the key for the days transmissions.

  It's actual influence is also hard to gauge because of the secrecy that remained
  in place after the War. Apart from those who worked on it, its technical
  advancements were not public knowledge until the late 1970s.

  ### ENIAC (1945) John Mauchly/J.P Eckert

  The _Electronic Numerical Calculator and Integrator_

  The main motivation for creating the ENIAC was military: the US Army needed
  speed and accuracy when calculating artiliary firing tables and munition
  trajectory so that shells could hit their intended targets. Accuracy depended on
  myriad factors: gun elevation, shell shape and weight, explosive charge,
  distance, wind, temperature etc. Previously this had been done by human
  computers under the aegis of the Ballistic Research Laboratory.

  ![The main control panel of the ENIAC](../img/eniac.jpg)

  It was not completed until after VE day but was used at Los Alamos after the War
  and retired in 1955.

  The BRL commissioned the Moore School of Engineering at the University of
  Pennsylvania for the construction of an automatic electronic calculator.

  It used vacuum-tubes, along with switches and realys. It used decimal rather
  than binary and had punch card input and output. It was crudely programmable and
  could be set up to work on different problems by rearranging the different
  components using switches and plugboards. But this took roughly two days.

  Its design was very influential because it was made public and not classified.

  ### EDVAC (1944-49) John Mauchly/J.P Eckert/ John von Neumann

  Built at the Moore School for the BRL as a successor to ENIAC by Mauchly and
  Eckert whilst ENIAC was still operational. It's architecture arose out of
  discussions between von Neumann and Mauchly and Eckert and was necessitated by
  the need to calculate the behaviour of the fissile materials in the atomic bomb.

  It resulted in the famous _First Draft of a Report on the EDVAC_ by von Neumann
  which is seen as the definitive statement of the modern **digital programmable
  general-purpose computer**.

  As a result of the paper, von Neumann effectively got the credit for the
  invention even though it synthesised ideas from the ENIAC and the contributions
  of others to the EDVAC.

  There was lots of disputed provenance and claims of plagiarism generally.
  Antanasoff won a legal claim against Eckert and Mauchly for stealing ideas from
  the ABC to make ENIAC.

  // Add more on the hardware.

  ### Concept of the _internal stored program_

  - Key innovation of the EDVAC was the _internal stored program_ (ISP).

  - Solution to the problem of preceding computers requiring down-time when
    switching between applications and problem sets.

  - Circuitry would need to be reconfigured before the computer could run on the
    next problem

  - ISP removed the friction:

    - the instructions comprising the program would be prepared on tape or punched
      cards and read into electronic memory
    - the hardware configuration of the machine would remain the same accross
      different programs and require no reconfiguration, only the input program
      data would change

  - This was basically the invention of software. Both instructions and data
    shared the same memory space once they were read-in. Before, the data would be
    stored in memory but the instructions would be read one-by-one from the
    external storage media.

  - Key consequences:
    - flexibility (no need for re-wiring)
    - speed: fetching instructions from memory is quicker than fetching from
      external storage devicde
    - self-modification: since they were in memory, programs could modify
      themselves during their execution (e.g. use branching conditions) and
      treating one program as data for another. This enabled more complex
      algorithms and also opened the possibility of higher-level languages and
      compilers
    - simplified architecture: both programs and data in memory
    - generalisation: computers could switch between different tasks simply by
      loading different programs

  ### Post-EDVAC devices

  There were several notable devices which attempted to implement the architecture
  described in the _First Draft_ using vacuum tubes for logic operations and a
  variety of different electronic methods for memory:

  - the Manchester "Baby"
  - the Cambridge EDSAC
  - Turing's ACE computer

  #### Manchester Baby (1948)

  ![The Manchester Baby Computer](../img/manchester_baby.png)

  - An experimental computer intended to create the
    [von Neumann architecture](CPU_architecture.md) using
    [Williams_Tube_RAM](Williams_Tube_RAM.md)

  - Developed at the Univesity of Manchester and completed in 1948.

  - Considered the first electronic stored-program computer and first to contain
    all the elements of a modern electronic digital computer.

  #### EDSAC (1949) J.Wilkes et al.

  ![EDSAC](../img/EDSAC.jpg)

  - _Electronic Delay Storge Automatic Computer_

  - Constructed by Maurice Wilkes and others at the Mathematical Laboratory of the
    University of Cambridge.

  - The second digital stored-program computer after the Manchester Baby.

  - It used vacuum-tubes for the arithmetical operations in the ALU and mercury
    [delay line memory](Delay_line_memory.md) for the RAM.

  - Designed to be used by relatively non-specialist practitioners from other
    university departments who were expected to program it themselves. To this
    end, a formal programming paradigm was forged for the EDSAC which established
    the following:

    - subroutines as a library of common procedures available to programmers (e.g.
      printing a result, reading input tape, program checking, mathematical
      operations)
    - diagnostics: techniques for verifying program code and its correctness

  ![Programming Digital Electronic Computer book](../img/programming_a_digital_electronic_computter.jpg)

  - The key players wrote the first textbook on programming in 1951: _The
    Preparation of Programs for an Electronic Digital Computer_ (Wilkes, Wheeler,
    and Gill)

  #### The Automatic Computing Engine (ACE) (1946) Alan Turing

  - Technical design provided by Alan Turing working at the Mathematics Division
    of the National Physical Laboratory. The product of Turing's theoretical work
    in "On Computable Numbers" where he proposes the concept of a
    [Turing_machine](Turing_machines.md) and based on his experience with early
    single-purpose computing devices at Bletchley.

  - Due to secrecy over wartime work, it was hard for get Turing to build a fully
    electronic implementation since this would disclose advances made during the
    War. Tommy Flowers was intended to be brought in to build it but again this
    was scuppered over the classification of wartime technology. Had this not been
    the case, it is likely it would've been seen as on par with the EDVAC in its
    design, if not dwarfing it.

  - Turing proposed a stored program architecture with high-speed memory. It would
    be more perfomant than the EDVAC as a result.

  ![The Pilot ACE computer](../img/pilot-ace.jpg)

  - Turing's actual design was not implemented as it was thought too ambitious
    given how powerful it would need to be. Instead a smaller prototype was made,
    the Pilot ACE. It used vacuum-tubes for logic, and mercury delay lines for
    memory. Data and memory was input via punched cards.

  ### Further applications of the EDSAC architecture

  ### LEO I

  - Built by Lyons Tea Company to manage business affairs (payroll, inventory,
    stock management)

  - Marked a shift from military and academic contexts for computers to business
    and data management.

  ### IAS machines

  ![The MANIAC computer](../img/MANIAC_computer.jpg)

  Several machines were built at the Institute for Advanced Study utilising the
  "von Neumann" architecture and associated advancements such as vacuum-tubes and
  Williams' tubes. There was the original IAS machine (1952) as well as the
  JOHNNIAC (1954) and MANIAC (1956).

  Although their purpose was military (Los Alamos), their designs were public and
  widely studied making them influential outside of academia.

  ### UNIVAC (1951)

  ![UNIVAC_computer](../img/univac-1.jpg)

  Mauchley and Eckert, who had designed the ENIAC left the Moore School and went
  into business: Eckert-Mauchley Computer Corporation. This was bought by
  Remington Rand and in subsequent years became the main competitor to IBM.

  In this capacity they built the UNIVAC: _Universal Automatic Computer_. It's
  name being an embodiment of its nature as a general-purpose, electronic digital
  computer.

  It used vacuum-tubes for logic and mercury delay lines for memory. It had
  multiple means of input/output including: directly via an operator console
  (basically a typewriter keyboard), magnetic tape for input and output, along
  with punched cards.

  ![UNIVAC operator console](../img/univac-operator-console.jpeg)

  It was the first computer specifically designed to include business and
  administrative use. This was underscored by its first client: the US Census
  Bureau.

  A key event was its succesful prediction of the 1952 general election. It
  correctly predicted a landslide for Eisenhower (against expectations). (This was
  so unlikely, they actually fudged the data because they thought the machine was
  way off.) It was a novelty on the results night but it cemented a certain
  concept of the computer in the public imagination - large, room sized machines
  with blinking lights.

  Swade notes that Eckert and Mauchley effectively launched the US commercial
  computer industry with the UNIVAC.

  ## IBM

  - First created in 1911 as a merger of four companies all involved in some
    capacity in business data processing. The most significant of these was the
    Hollerith Tabulating Machine Company who's punched card machines had been used
    in the 1890 census, greatly reducing its collation time

  - Named IBM in 1924

  - UNIVAC represented the encroachment of computing devices into IBM's market of
    punched-card based data-processing and effectively put IBM on notice.

  - To attack the threat IBM brought out three crucial machines: the 701, 702
    and 650.

  - IBM's approach to marketing their computers was to emphasise the comfort of
    continuity over the shock of the new represented by the UNIVAC. IBM had
    already captured the business market. Their computers would be more of the
    same, just faster and more powerful than their punched card machines due to
    the electronics. To this end they initially did not call their devices
    'computers'.

  - IBM were ultimately totally successful in their pivot to the computing market.
    By 1960 it owned over 70% of the data processing and computer market.

  ### IBM 701 ("Defense Calculator") (1952)

  ![Control unit of the IBM 701](../img/ibm-701-control-unit.jpeg)

  IBM's first electronic computer. It directly competed with the UNIVAC for
  government contracts. It followed the prevailing approach of vaccuum tubes for
  logic and had a variety of storage methods: Williams tubes,
  [magnetic tape](Magnetic_tape.md) , and magentic drums.

  701s were used at Los Alamos and avionics companies. Some were also used to
  manage payroll in business contexts.

  ### IBM 702 ("Tape Processing Machine") (1953)

  ![Advertisement for the IBM 702](../img/ibm-add.jpeg)

  Focused primarily on business applications and targetted at businesses rather
  than government contracts. Less powerful than the 701.

  ### IBM 650 (1953)

  ![IBM 650](../img/ibm-650.jpg)

  Low-cost general purpose machine using magnetic drum memory. Marketed as slower
  but more affordable than the 700 range. It was mass-produced unlike the others
  which were built for specific customers. It was in fact the first mass-produced
  computer in the world. It proved the breakout star in IBMs initial line up and
  sometimes called "IBM's Model T".

  ![IBM_650 console panel](../img/ibm-650-console-panel.jpg)

  IBM offerred 650s to univesities at a 60% discount on the condition that the
  universities would establish courses in computing. This was shrewd as it meant
  that a whole generation of engineers and computer scientists learned on 650s and
  would prefer them in their research. It also created a pool of skilled users
  that would go on to work at IBM.

  Donald Knuth dedicated _The Art of Computer Programming_ to the 650.

  It used [magnetic drum memory](./Magnetic_drum_memory.md) along with
  vacuum-tubes.

  ### IBM 1401 (1959)

  ![IBM 1401](../img/ibm-1401.jpg)

  Another IBM mainframe. Transistors replaced vacuum-tubes. Magnetic core storage
  replaced magnetic drum storage. It was housed in rectangular light-blue cabinets
  and the ubiquity of the 1401 in industry earned IBM the moniker 'Big Blue'.

  ### IBM System/360 (1964)

  ![IBM 360](../img/ibm-360.jpg)

  Named to suggest all-round compatibility - a family of mainframes designed to
  cover commercial and scientific applications. Considered one of history's most
  successful computers.

  Up until this point all IBM computers had a programming language unique to the
  specific processor. At that point there were about seven IBM computer models in
  active use and they were all incompatible with each other.

  ![IBM 360 promo poster](../img/ibm-360-in-use.jpeg)

  In contrast the 360 computers all used the same programming language. This meant
  they were interoperable with each others. Because the there were variants in the
  360 ranges, customers could purchase a smaller system knowing they could expand
  it if their needs grew, without reprogramming their application software.

  ## Magnetic core devices: Whirlwind and SAGE

  Two devices that leveraged the new technology of
  [magnetic_core_memory](Magnetic_core_memory.md) where the Whirlwind and SAGE
  computers. The ENIAC was also updated to use magnetic cores.

  ![Whirlwind magnetic core memory](../img/whirlwind-magnetic-cores.JPG)

  The Whirlwind computer (1953) was a flight simulator and the first to use
  magnetic cores. Crucially was able to operate in realtime for output.

  The SAGE computer (_Semi-Automatic Ground Environment_) emerged as a response to
  the Soviet acquisition of the atomic bomb in 1949. The Americans realised they
  needed a much better early warning and air defence system that would allow them
  to identify bombers in their airspace and dispatch fighters.

  To achieve this, it was necessary to detect enemy bombers with radar and compute
  an interception course. Alterations in the bomber flight path required
  continuous updates and real-time computation of directions for the responding
  fighter.

  The resulting computer was SAGE (made by IBM) which was modelled on Whirlwind,
  using magnetic cores. SAGE computers were spread accross the continental US in
  sectors and managed by NORAD.

  ![SAGE command post](../img/SAGE_command_post.jpg)

  The SAGE system was operation between 1958-1984, receiving continual
  improvements and updates. In addition to magnetic cores it introduced many
  technological innovations that influenced computers generally: interactive
  screens, printed circuit boards, mass-storage devices, digital communication
  over telephone lines, time-sharing, along with advances in software.

  Because so many private companies worked as contractors on SAGE (IBM, Burroughs,
  Bell Labs, RAND Corp), the military technology (both hardware and software) made
  its way from the military context to business and consumer computing.

  An example of a civilian application of SAGE technology was airline booking
  systems, where booking reservation data needed to be processed in realtime. IBM
  worked with American Airlines to introduce this.

  ## Transistor and integrated circuit revolution

  The arc of modern computing history (and its main eras) can be presented as

  - vacuum-tubes
  - [transistors](Transistors.md)
  - [integrated_circuits](Integrated_circuits.md)

  The transistor was invented at Bell Labs in 1947 by William Shockley and others.
  They were not created with computers in mind. Instead they were first used in
  hearing aids (1953) and transistor radios (1954).

  From the mid-1950s onwards they started being used in military computers.

  They had several advantages over vacuum-tubes: they were durable and reliable
  with no required startup time, they were power efficient and small.

  The actual period where transistors alone were supreme in the form of a
  transistor board was relatively short-lived and rapidly gave way to ICs.

  Computers of the transistor era had the following components separate from each
  other: the transistors, wired connections, resistors and capacitors. Integrated
  circuits put these all on the same piece of silicon.

  They were first manufactured by Fairchild Semiconductor which was an offshoot of
  Shockley's Shockley Semiconductor Laboratory.

  The initial marked for ICs was military and scientific: the Minuteman ICBM and
  computers of the Apollo spacecraft. This laid the groundworkd for later
  commercial use and the personal computer revolution.

  ## Mini-computers

  Mini computers did not threaten mainframes but they opened up a new class of
  user. They were vastly cheaper than a mainframe and only the size of a small
  fridge.

  The main player was DEC (Digital Equipment Corporation) starting in the
  mid-1960s. The introduced the PDP-8 in 1965 (Programmed Data Processor). The
  PDP-8 used transisotrs and magnetic core memory. It was affordable to smaller
  businesses if not yet, consumers.

  ![The PDP-8](<../img/540px-PDP-8_(1).jpg>)

  The internals were made public and DEC encouraged making the machine extensible
  by users being permitted to create their own programs and specialised
  applications. This was a very different culture to IBM where technicians were
  required and machines were typically leased.

  By 1988, DEC was the second largest computer company after IBM. This led to
  additional companies entering the mini-computer market: Data General, Honeywell,
  Hewlett-Packard.

  Mini-computers died as a category in the mid-1990s in the wake of the PC.

  ## The Personal Computer

  The PC emerged out of advances in IC chip design - this was the engine of the
  PC.

  Intel (newly founded by Robert Noyce and Gordon Moore) were charged with making
  custom chips for a Japanese calculator company. Instead of creating the custom
  chips, they advised the company to use a general purpose chip that could handle
  general computing as well as the basic calculator functions. This generic chip -
  the Intel 4004 - was the microprocessor combined with three other ICs
  (comprising 2.3k transistors) that comprised the fundamental building blocks of
  the von Neumann architecture.

  ![Intel 4004 chip](../img/intel-4004.jpg)

  However it took until the Apple II in 1977 for the phrase "personal computer" to
  enter the lexicon and become an identifiable class.

  The broader context for this was the computer hobbyists in Silicon Valley/Menlo
  Park (particularly the Homebrew Club of amateur computer engineers) along with
  idealistic figures from the 60s counterculture (such as Stuart Brand) who began
  associating computing technology as a tool of personal liberty and fulfillment.

  ### Microsoft and Apple

  The PC "boom" began with the Altair 8800. This was a mail-order kit that
  required manual assembly. It was released by Micro Instrumentation Telemetry
  Systems (MITS) in 1975. It couldn't do much but had expansion capability
  (memory, teletype interface, casette player for data storage).

  ![Altair 8800](../img/altair-8800.jpg)

  Bill Gates and Paul Allen made a proposal to MITS: they would write software
  that would allow users to program the Altair in BASIC. They agreed and were
  prepared to distribute the software. As a result Gates and Allen founded
  Microsoft in 1975.

  Around the same time, Steve Wozniak (member of the Homebrew club) built the
  Apple I as a single-board hobbyist project. He made this available to buy via
  mail order and formed Apple with Steve Jobs in 1976 to manage the enterprise.

  ![Apple I](../img/apple1.jpg)

  By 1977 they had investment capital and brought out the Apple II. This was sold
  preassembled with casing and required no soldering. It had expansion slots for
  third-party vendors to create compatible devices and had colour graphics.

  The design of the Apple II was published in _Byte_ in order to invite
  crowd-sourced plugins and extensions. One of these was the Z-80 softcard which
  allowed it to run the CP/M OS (created by the company Digital Research) giving
  ready-made access to software such as a word-processor, spreadsheets and
  databases.

  ![Apple 2](../img/apple2.jpg)

  Other competitors in the PC market at this time were Radioshack's TRS-80 and the
  Commodore PET.

  ### IBM

  All the while it was hobbyist and tinkerers driving the PC market, IBM had no
  real interest. When PCSs started being used by businesses and their capacity
  grew to include typical business applications (databases, spreadsheets) it
  sensed an incursion into its market.

  ![IBM PC](../img/ibm-pc.jpg)

  Thus, in 1981, IBM launched the "IBM Personal Computer" which rapidly became the
  industry standard. This had the effect of legitimising the concept of a PC.
  There was overwhelming demand. In their marketing IBM melded home and office use
  to capture both markets at once.

  The internal specification of the IBM PC was freely available (dubbed 'open
  architecture'), in contrast to IBM's business computers, to encourage software
  expansion. Cards could be created to fit in its expansion slots adding graphics,
  sound, additional memory and networking capability.

  IBM originally contracted Digital Research to create software based on CP/M but
  this fell through. Microsoft got the deal to supply the OS which became MS-DOS
  (Microsoft Disk Operating System). Henceforth every PC and PC-clone came with
  DOS bundled.

raw_content:
  ---
  tags: [literature, computer-history]
  created: Friday, August 23, 2024
  ---

  # History of Computing (Swade, 2022 )

  | Title                                               | Author      | Publication date | Resource type |
  | --------------------------------------------------- | ----------- | ---------------- | ------------- |
  | The History of Computing: A Very Short Introduction | Doron Swade | 2022             | Book          |

  ## Timeline

  A typical timeline approach rooted in major innovations.

  - Ancient aids to counting: knotted cords and notched sticks
  - Ancient aids to calculation: counting boards and abacii
  - Early mechanical calculator devices in the 17th century (number wheels,
    Pascal, Leibniz)
  - Modern aids to calculation: slide rules following the discovery of
    [logarithms](Logarithms.md)
  - Mechanised, automated calculating engines of Babbage in the 19th century
  - Punched-card machines leading to IBM in the early 20th century
  - Analogue and electro-mechanical computers of the early 20th century inclusive
    of wartime computers
  - Early valve-based (vacuum-tubed) digital computers (again wartime and early
    Cold War)
  - The invention of the transistor and first fully-digital computers
  - The invention of [integrated_circuits](Integrated_circuits.md)
  - Supercomputers
  - Minicomputers
  - Consumer personal computers
  - Internet and later, Web
  - Smart phones

  First three phases of digital electronic computers:

  - Wartime up to 1950s vacuum-tube era
  - Transistor era up to 1963
  - First microchip era ending in early 1970s

  ## Terms

  | Term            | Definition                                                                                                                                                                  |
  | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | automatic       | can execute programs, process data and perform functions on its own once it has been started. Can monitor its own processes and make adjustments without human intervention |
  | general-purpose | a computer designed to be able to solve a wide variety of problems and perform many different types of tasks as opposed to a computer built for a specific, limited purpose |
  | programmable    | the ability to change the operations a computer peforms without physically altering its hardware                                                                            |

  ## Mechanical calculating devices in the 17th century

  Focus was chiefly on creating a desktop calculator capable of four-function
  arithmetic.

  ![Photograph of Pascaline](../img/pascaline.jpg)

  ![Photograph of replica of Leibniz stepped drum machine](../img/leibniz-stepped-drum.jpg)

  The main contenders were the Pascaline of Pascal (which only did cumulative
  addition) and the wheel or "stepped drum" calculator of Leibniz that could do
  all operations (in theory).

  Subsequent designs were based on these artefacts. In practice, neither worked
  consistently well with the carriage of tens remaining a sticking point.

  ![Photograph of Arithmometer](../img/arithmometer.jpg)

  ![Photograph of Comptometer](../img/comptometer.png)

  The arithmometer (crank driven) and comptometer (key-driven) were descendents of
  the Leibniz design that became commercially viable by the 19th century along
  with other mechanical calculators. In the US, Burroughs dominated the market.

  ## Babbage: mechanized, automated calculation

  > I wish to God these calculations had been executed by Steam (Babbage)

  With Babbage's machines we see an approach to computation that can only be
  understood against the backdrop of the Industrial Revolution in which they were
  conceived.

  The idea is that the machine is a factory and number is the product. In the same
  way as the mechanised looms created textiles. It is the extension of a model of
  industrial production from goods/commodities to information.

  Babbage conceived two machines: the Difference Engine (DE) and the Analytical
  Engine (AE). Neither were successfully built in his lifetime. The DE preceded
  the AE and was basically an advanced mechanical calculator whereas the AE
  approximated a general purpose computer.

  Despite this, with the Difference Engine, in contrast to preceding _aids to
  calculation_, the steps of the computational algorithm were no longer directed
  by human intelligence but by internal rules embodied in the mechanism and
  automatically generated.

  ### Difference Engine

  The DE's single purpose was to calculate and output mathematical tables such as
  the results of polynomial equations. The idea was that you would input the
  answer a bell would ring and the machine would _halt_. This influenced Turing
  later. It was non-programmable and designed for a specific set of calculations.

  ### Analytical Engine

  Conceived as a general-purpose computing machine capable of performing a wide
  range of calculations, programmable using punched cards similar to those used
  with Jacquard looms.

  It more resembled modern computers in that Babbage used concepts that would
  later translate into the [von Neumann architecture](CPU_architecture.md) There
  was a "mill" (CPU), "store" (memory) and input/output mechanisms. It also had a
  concept of looping and conditional branching.

  ### Lovelace's insight

  A central idea of Ada Lovelace, expressed in her notes on the Analytical Engine
  is that **number can represent entities other than quantity**.

  If we assign meaning to number, results arrived at by operating on number
  according to rules can say things about the world when mapped back onto the
  world using the meanings assigned to them.

  Lovelace's insight was that the potential of computing lay in the power of
  machines to manipulate representations of the world contained in symbols.

  ## Analogue computers

  Both digital and analogue computers are automatic. They differ in _how they
  represent quantities_ and how their outputs are derived.

  With digital machines, quantity is represented as a string of discrete digits.

  With analogue machines, quantity is a physical property _in itself_ rather than
  a representation. This could be, for example: the lowering of a weight, the flow
  of a liquid, or an electrical charge. This physical behaviour is **analagous**
  to the system that is being modelled. Quantities are continuously variable
  values rather than discrete (discontinuous values).

  Digital machines produce results by _calculation_ whereas analogue machines
  produce results by _measurement_, e.g. the height of liquid in a tank or the
  time it taks for a tank to be emptied.

  ### Examples of analogue computers

  #### The Phillips Hydraulic Computer

  This used fluid to model the workings of the British economy. It consisted of a
  series of transparent plastic tanks and pipes which were fastened to a wooden
  board.

  ![Philips Hydraulic Computer](../img/hydraulic_computer.jpg)

  Each tank represented some aspect of the UK national economy and the flow of
  money around the economy was illustrated by coloured water. At the top of the
  board was a large tank called the treasury. Water (representing money) flowed
  from the treasury to other tanks representing the various ways in which a
  country could spend its money. For example, there were tanks for health and
  education. To increase spending on health care a tap could be opened to drain
  water from the treasury to the tank which represented health spending.

  #### Bush's Differential Analyser

  This was designed to solve differential equations by integration. In contrast to
  the Philips Computer it was general enough to be used to solve problems from
  different contexts. Examples of these contexts: heat flow, ballistics,
  mechanics, population growth, chemical interactions, astronomy.

  ![Differential Analyser](../img/differential_analyser_bush.jpg)

  It was about the size of a room and used shafts, motors, discs and wheels to
  work.

  ### Historiography

  There is a tendency in the history of computing to downplay or diminish the
  contribution of analogue computing devices and to present them as just an
  inferior precursor to the inevitable dominance of digital electronic computers.

  This is ahistorical and inaccurate.

  Analogue (and electromechanical devices) overlapped with and coexisted with
  digital devices for 40 years, spanning the first three generations of digital
  electronic devices. The term "analogue" itself only came about when the need
  arose to distinguish digital devices from other types of computer. They were not
  "rivals" before this.

  ### Electro-mechanical computers

  Electro-mechanical devices (also known as "electronic analogue computers") are a
  sort of midway between full digital devices and analogue computers, forming a
  bridge between the two eras.

  Their heyday was roughly 1935 - 1945.

  Their key components were:

  - [relays](Relays.md) for logic operations
  - rotating shafts and gears for performing calculations
  - punched cards or paper tape for input instructions and outputs

  They were slower than fully digital computers as they were limited by the speed
  of moving components rather than the flow of electric charge. In addition the
  various mechanical parts were prone to wear and needed frequent replacement.

  ### Examples of electro-mechanical computers

  #### Harvard Mark 1 (1937-1944) - Aikin, IBM

  Designed by Howard Aiken and built by IBM (1937-1944). Also known as the
  _Automatic Sequence Controlled Calculator_ (ASCC). A general-purpose
  electro-mechanical computer it was most famously used at Los Alamos by von
  Neumann to calculate the blast yield of the atomic bomb.

  ![Harvard Mark One](../img/Harvard_Mark_1.jpg)

  It was more than 15m in length and weighed 5 tonnes comprising over 750,000
  parts. It used paper tape and punched cards for input/output.

  #### Complex Number Calculator (1940) - Bell Labs, Stibitz

  Not general purpose nor was it programmable. It was hardwired to perform a
  specific set of operations on numbers and nothing else. It used relays like the
  others. Its distinguishing feature was that it used a teletype for input rather
  than cards or paper tape.

  ![Calculating panel of the CMC](../img/complex-number-calculator-bell-labs.jpg)

  ![Teletype input of CMC](../img/stibitz-teletype.jpg)

  It comprised a panel (the calculating unit) and teletype (the input). One could
  remotely access the computer from the teletype in another location, providing it
  was connected to Bell Lab's telephone network.

  #### Z3 (1941) - Konrad Zuse

  ![Z3](../img/Zuse-Z3.jpeg)

  First designed in 1938 and completed in 1941. Considered the first fully
  automatic, programmable digital computer although relay-based. Programs were
  stored on external punched tape and binary was used as the number system.
  Destroyed by Allied bombing in the War but a replica (pictured) was built
  in 1998.

  ## Vacuum-tube devices

  Vacuum-tube devices were the first fully electronic digital devices as they used
  vacuum-tubes for implementing binary logic rather than relay switches.

  Canonical examples from this era are the ABC computer, the Collossus and most
  famously, the ENIAC.

  ### The Antanasoff-Berry Computer (1939 - 1942)

  ![ABC Computer](../img/abc-computer.jpg)

  Fully automatic, digital electronic computer but not programmable or really
  general-purpose, being created to do linear equations. It took decimal input and
  output but operated internally using binary arithmetic. The input was punched
  cards and the output was via a front-panel display.

  Its importance stems from the fact that it was the first to use vacuum-tubes for
  the arithmetic calculations of the processor.

  ### Colossus (1943-1945) - Tommy Flowers/Max Newman

  Designed and built at the Post Office Research Station at Dollis Hill. Used for
  cracking the Lorenz messages of the German high command.

  Mainly important (beyond its role in the War) for establishing the viability of
  vacuum-tubes over relays. It comprised 2.4 thousand tubes.

  Debatable the extent to which the Collosus can be called the _first digital
  computer_ since it was not general purpose and not fully programmable.

  Furthermore it was not even a complete solution in its context of application.
  The codebreakers would be given a certain advantage using the Collosus
  (narrowing the field of possible deciphering keys) but still need to use manual
  methods to break the code. It made decipher time shorter rather than executing
  the full diciphering. In contrast to, say, the Bombe computer that would
  identify the key for the days transmissions.

  It's actual influence is also hard to gauge because of the secrecy that remained
  in place after the War. Apart from those who worked on it, its technical
  advancements were not public knowledge until the late 1970s.

  ### ENIAC (1945) John Mauchly/J.P Eckert

  The _Electronic Numerical Calculator and Integrator_

  The main motivation for creating the ENIAC was military: the US Army needed
  speed and accuracy when calculating artiliary firing tables and munition
  trajectory so that shells could hit their intended targets. Accuracy depended on
  myriad factors: gun elevation, shell shape and weight, explosive charge,
  distance, wind, temperature etc. Previously this had been done by human
  computers under the aegis of the Ballistic Research Laboratory.

  ![The main control panel of the ENIAC](../img/eniac.jpg)

  It was not completed until after VE day but was used at Los Alamos after the War
  and retired in 1955.

  The BRL commissioned the Moore School of Engineering at the University of
  Pennsylvania for the construction of an automatic electronic calculator.

  It used vacuum-tubes, along with switches and realys. It used decimal rather
  than binary and had punch card input and output. It was crudely programmable and
  could be set up to work on different problems by rearranging the different
  components using switches and plugboards. But this took roughly two days.

  Its design was very influential because it was made public and not classified.

  ### EDVAC (1944-49) John Mauchly/J.P Eckert/ John von Neumann

  Built at the Moore School for the BRL as a successor to ENIAC by Mauchly and
  Eckert whilst ENIAC was still operational. It's architecture arose out of
  discussions between von Neumann and Mauchly and Eckert and was necessitated by
  the need to calculate the behaviour of the fissile materials in the atomic bomb.

  It resulted in the famous _First Draft of a Report on the EDVAC_ by von Neumann
  which is seen as the definitive statement of the modern **digital programmable
  general-purpose computer**.

  As a result of the paper, von Neumann effectively got the credit for the
  invention even though it synthesised ideas from the ENIAC and the contributions
  of others to the EDVAC.

  There was lots of disputed provenance and claims of plagiarism generally.
  Antanasoff won a legal claim against Eckert and Mauchly for stealing ideas from
  the ABC to make ENIAC.

  // Add more on the hardware.

  ### Concept of the _internal stored program_

  - Key innovation of the EDVAC was the _internal stored program_ (ISP).

  - Solution to the problem of preceding computers requiring down-time when
    switching between applications and problem sets.

  - Circuitry would need to be reconfigured before the computer could run on the
    next problem

  - ISP removed the friction:

    - the instructions comprising the program would be prepared on tape or punched
      cards and read into electronic memory
    - the hardware configuration of the machine would remain the same accross
      different programs and require no reconfiguration, only the input program
      data would change

  - This was basically the invention of software. Both instructions and data
    shared the same memory space once they were read-in. Before, the data would be
    stored in memory but the instructions would be read one-by-one from the
    external storage media.

  - Key consequences:
    - flexibility (no need for re-wiring)
    - speed: fetching instructions from memory is quicker than fetching from
      external storage devicde
    - self-modification: since they were in memory, programs could modify
      themselves during their execution (e.g. use branching conditions) and
      treating one program as data for another. This enabled more complex
      algorithms and also opened the possibility of higher-level languages and
      compilers
    - simplified architecture: both programs and data in memory
    - generalisation: computers could switch between different tasks simply by
      loading different programs

  ### Post-EDVAC devices

  There were several notable devices which attempted to implement the architecture
  described in the _First Draft_ using vacuum tubes for logic operations and a
  variety of different electronic methods for memory:

  - the Manchester "Baby"
  - the Cambridge EDSAC
  - Turing's ACE computer

  #### Manchester Baby (1948)

  ![The Manchester Baby Computer](../img/manchester_baby.png)

  - An experimental computer intended to create the
    [von Neumann architecture](CPU_architecture.md) using
    [Williams_Tube_RAM](Williams_Tube_RAM.md)

  - Developed at the Univesity of Manchester and completed in 1948.

  - Considered the first electronic stored-program computer and first to contain
    all the elements of a modern electronic digital computer.

  #### EDSAC (1949) J.Wilkes et al.

  ![EDSAC](../img/EDSAC.jpg)

  - _Electronic Delay Storge Automatic Computer_

  - Constructed by Maurice Wilkes and others at the Mathematical Laboratory of the
    University of Cambridge.

  - The second digital stored-program computer after the Manchester Baby.

  - It used vacuum-tubes for the arithmetical operations in the ALU and mercury
    [delay line memory](Delay_line_memory.md) for the RAM.

  - Designed to be used by relatively non-specialist practitioners from other
    university departments who were expected to program it themselves. To this
    end, a formal programming paradigm was forged for the EDSAC which established
    the following:

    - subroutines as a library of common procedures available to programmers (e.g.
      printing a result, reading input tape, program checking, mathematical
      operations)
    - diagnostics: techniques for verifying program code and its correctness

  ![Programming Digital Electronic Computer book](../img/programming_a_digital_electronic_computter.jpg)

  - The key players wrote the first textbook on programming in 1951: _The
    Preparation of Programs for an Electronic Digital Computer_ (Wilkes, Wheeler,
    and Gill)

  #### The Automatic Computing Engine (ACE) (1946) Alan Turing

  - Technical design provided by Alan Turing working at the Mathematics Division
    of the National Physical Laboratory. The product of Turing's theoretical work
    in "On Computable Numbers" where he proposes the concept of a
    [Turing_machine](Turing_machines.md) and based on his experience with early
    single-purpose computing devices at Bletchley.

  - Due to secrecy over wartime work, it was hard for get Turing to build a fully
    electronic implementation since this would disclose advances made during the
    War. Tommy Flowers was intended to be brought in to build it but again this
    was scuppered over the classification of wartime technology. Had this not been
    the case, it is likely it would've been seen as on par with the EDVAC in its
    design, if not dwarfing it.

  - Turing proposed a stored program architecture with high-speed memory. It would
    be more perfomant than the EDVAC as a result.

  ![The Pilot ACE computer](../img/pilot-ace.jpg)

  - Turing's actual design was not implemented as it was thought too ambitious
    given how powerful it would need to be. Instead a smaller prototype was made,
    the Pilot ACE. It used vacuum-tubes for logic, and mercury delay lines for
    memory. Data and memory was input via punched cards.

  ### Further applications of the EDSAC architecture

  ### LEO I

  - Built by Lyons Tea Company to manage business affairs (payroll, inventory,
    stock management)

  - Marked a shift from military and academic contexts for computers to business
    and data management.

  ### IAS machines

  ![The MANIAC computer](../img/MANIAC_computer.jpg)

  Several machines were built at the Institute for Advanced Study utilising the
  "von Neumann" architecture and associated advancements such as vacuum-tubes and
  Williams' tubes. There was the original IAS machine (1952) as well as the
  JOHNNIAC (1954) and MANIAC (1956).

  Although their purpose was military (Los Alamos), their designs were public and
  widely studied making them influential outside of academia.

  ### UNIVAC (1951)

  ![UNIVAC_computer](../img/univac-1.jpg)

  Mauchley and Eckert, who had designed the ENIAC left the Moore School and went
  into business: Eckert-Mauchley Computer Corporation. This was bought by
  Remington Rand and in subsequent years became the main competitor to IBM.

  In this capacity they built the UNIVAC: _Universal Automatic Computer_. It's
  name being an embodiment of its nature as a general-purpose, electronic digital
  computer.

  It used vacuum-tubes for logic and mercury delay lines for memory. It had
  multiple means of input/output including: directly via an operator console
  (basically a typewriter keyboard), magnetic tape for input and output, along
  with punched cards.

  ![UNIVAC operator console](../img/univac-operator-console.jpeg)

  It was the first computer specifically designed to include business and
  administrative use. This was underscored by its first client: the US Census
  Bureau.

  A key event was its succesful prediction of the 1952 general election. It
  correctly predicted a landslide for Eisenhower (against expectations). (This was
  so unlikely, they actually fudged the data because they thought the machine was
  way off.) It was a novelty on the results night but it cemented a certain
  concept of the computer in the public imagination - large, room sized machines
  with blinking lights.

  Swade notes that Eckert and Mauchley effectively launched the US commercial
  computer industry with the UNIVAC.

  ## IBM

  - First created in 1911 as a merger of four companies all involved in some
    capacity in business data processing. The most significant of these was the
    Hollerith Tabulating Machine Company who's punched card machines had been used
    in the 1890 census, greatly reducing its collation time

  - Named IBM in 1924

  - UNIVAC represented the encroachment of computing devices into IBM's market of
    punched-card based data-processing and effectively put IBM on notice.

  - To attack the threat IBM brought out three crucial machines: the 701, 702
    and 650.

  - IBM's approach to marketing their computers was to emphasise the comfort of
    continuity over the shock of the new represented by the UNIVAC. IBM had
    already captured the business market. Their computers would be more of the
    same, just faster and more powerful than their punched card machines due to
    the electronics. To this end they initially did not call their devices
    'computers'.

  - IBM were ultimately totally successful in their pivot to the computing market.
    By 1960 it owned over 70% of the data processing and computer market.

  ### IBM 701 ("Defense Calculator") (1952)

  ![Control unit of the IBM 701](../img/ibm-701-control-unit.jpeg)

  IBM's first electronic computer. It directly competed with the UNIVAC for
  government contracts. It followed the prevailing approach of vaccuum tubes for
  logic and had a variety of storage methods: Williams tubes,
  [magnetic tape](Magnetic_tape.md) , and magentic drums.

  701s were used at Los Alamos and avionics companies. Some were also used to
  manage payroll in business contexts.

  ### IBM 702 ("Tape Processing Machine") (1953)

  ![Advertisement for the IBM 702](../img/ibm-add.jpeg)

  Focused primarily on business applications and targetted at businesses rather
  than government contracts. Less powerful than the 701.

  ### IBM 650 (1953)

  ![IBM 650](../img/ibm-650.jpg)

  Low-cost general purpose machine using magnetic drum memory. Marketed as slower
  but more affordable than the 700 range. It was mass-produced unlike the others
  which were built for specific customers. It was in fact the first mass-produced
  computer in the world. It proved the breakout star in IBMs initial line up and
  sometimes called "IBM's Model T".

  ![IBM_650 console panel](../img/ibm-650-console-panel.jpg)

  IBM offerred 650s to univesities at a 60% discount on the condition that the
  universities would establish courses in computing. This was shrewd as it meant
  that a whole generation of engineers and computer scientists learned on 650s and
  would prefer them in their research. It also created a pool of skilled users
  that would go on to work at IBM.

  Donald Knuth dedicated _The Art of Computer Programming_ to the 650.

  It used [magnetic drum memory](./Magnetic_drum_memory.md) along with
  vacuum-tubes.

  ### IBM 1401 (1959)

  ![IBM 1401](../img/ibm-1401.jpg)

  Another IBM mainframe. Transistors replaced vacuum-tubes. Magnetic core storage
  replaced magnetic drum storage. It was housed in rectangular light-blue cabinets
  and the ubiquity of the 1401 in industry earned IBM the moniker 'Big Blue'.

  ### IBM System/360 (1964)

  ![IBM 360](../img/ibm-360.jpg)

  Named to suggest all-round compatibility - a family of mainframes designed to
  cover commercial and scientific applications. Considered one of history's most
  successful computers.

  Up until this point all IBM computers had a programming language unique to the
  specific processor. At that point there were about seven IBM computer models in
  active use and they were all incompatible with each other.

  ![IBM 360 promo poster](../img/ibm-360-in-use.jpeg)

  In contrast the 360 computers all used the same programming language. This meant
  they were interoperable with each others. Because the there were variants in the
  360 ranges, customers could purchase a smaller system knowing they could expand
  it if their needs grew, without reprogramming their application software.

  ## Magnetic core devices: Whirlwind and SAGE

  Two devices that leveraged the new technology of
  [magnetic_core_memory](Magnetic_core_memory.md) where the Whirlwind and SAGE
  computers. The ENIAC was also updated to use magnetic cores.

  ![Whirlwind magnetic core memory](../img/whirlwind-magnetic-cores.JPG)

  The Whirlwind computer (1953) was a flight simulator and the first to use
  magnetic cores. Crucially was able to operate in realtime for output.

  The SAGE computer (_Semi-Automatic Ground Environment_) emerged as a response to
  the Soviet acquisition of the atomic bomb in 1949. The Americans realised they
  needed a much better early warning and air defence system that would allow them
  to identify bombers in their airspace and dispatch fighters.

  To achieve this, it was necessary to detect enemy bombers with radar and compute
  an interception course. Alterations in the bomber flight path required
  continuous updates and real-time computation of directions for the responding
  fighter.

  The resulting computer was SAGE (made by IBM) which was modelled on Whirlwind,
  using magnetic cores. SAGE computers were spread accross the continental US in
  sectors and managed by NORAD.

  ![SAGE command post](../img/SAGE_command_post.jpg)

  The SAGE system was operation between 1958-1984, receiving continual
  improvements and updates. In addition to magnetic cores it introduced many
  technological innovations that influenced computers generally: interactive
  screens, printed circuit boards, mass-storage devices, digital communication
  over telephone lines, time-sharing, along with advances in software.

  Because so many private companies worked as contractors on SAGE (IBM, Burroughs,
  Bell Labs, RAND Corp), the military technology (both hardware and software) made
  its way from the military context to business and consumer computing.

  An example of a civilian application of SAGE technology was airline booking
  systems, where booking reservation data needed to be processed in realtime. IBM
  worked with American Airlines to introduce this.

  ## Transistor and integrated circuit revolution

  The arc of modern computing history (and its main eras) can be presented as

  - vacuum-tubes
  - [transistors](Transistors.md)
  - [integrated_circuits](Integrated_circuits.md)

  The transistor was invented at Bell Labs in 1947 by William Shockley and others.
  They were not created with computers in mind. Instead they were first used in
  hearing aids (1953) and transistor radios (1954).

  From the mid-1950s onwards they started being used in military computers.

  They had several advantages over vacuum-tubes: they were durable and reliable
  with no required startup time, they were power efficient and small.

  The actual period where transistors alone were supreme in the form of a
  transistor board was relatively short-lived and rapidly gave way to ICs.

  Computers of the transistor era had the following components separate from each
  other: the transistors, wired connections, resistors and capacitors. Integrated
  circuits put these all on the same piece of silicon.

  They were first manufactured by Fairchild Semiconductor which was an offshoot of
  Shockley's Shockley Semiconductor Laboratory.

  The initial marked for ICs was military and scientific: the Minuteman ICBM and
  computers of the Apollo spacecraft. This laid the groundworkd for later
  commercial use and the personal computer revolution.

  ## Mini-computers

  Mini computers did not threaten mainframes but they opened up a new class of
  user. They were vastly cheaper than a mainframe and only the size of a small
  fridge.

  The main player was DEC (Digital Equipment Corporation) starting in the
  mid-1960s. The introduced the PDP-8 in 1965 (Programmed Data Processor). The
  PDP-8 used transisotrs and magnetic core memory. It was affordable to smaller
  businesses if not yet, consumers.

  ![The PDP-8](<../img/540px-PDP-8_(1).jpg>)

  The internals were made public and DEC encouraged making the machine extensible
  by users being permitted to create their own programs and specialised
  applications. This was a very different culture to IBM where technicians were
  required and machines were typically leased.

  By 1988, DEC was the second largest computer company after IBM. This led to
  additional companies entering the mini-computer market: Data General, Honeywell,
  Hewlett-Packard.

  Mini-computers died as a category in the mid-1990s in the wake of the PC.

  ## The Personal Computer

  The PC emerged out of advances in IC chip design - this was the engine of the
  PC.

  Intel (newly founded by Robert Noyce and Gordon Moore) were charged with making
  custom chips for a Japanese calculator company. Instead of creating the custom
  chips, they advised the company to use a general purpose chip that could handle
  general computing as well as the basic calculator functions. This generic chip -
  the Intel 4004 - was the microprocessor combined with three other ICs
  (comprising 2.3k transistors) that comprised the fundamental building blocks of
  the von Neumann architecture.

  ![Intel 4004 chip](../img/intel-4004.jpg)

  However it took until the Apple II in 1977 for the phrase "personal computer" to
  enter the lexicon and become an identifiable class.

  The broader context for this was the computer hobbyists in Silicon Valley/Menlo
  Park (particularly the Homebrew Club of amateur computer engineers) along with
  idealistic figures from the 60s counterculture (such as Stuart Brand) who began
  associating computing technology as a tool of personal liberty and fulfillment.

  ### Microsoft and Apple

  The PC "boom" began with the Altair 8800. This was a mail-order kit that
  required manual assembly. It was released by Micro Instrumentation Telemetry
  Systems (MITS) in 1975. It couldn't do much but had expansion capability
  (memory, teletype interface, casette player for data storage).

  ![Altair 8800](../img/altair-8800.jpg)

  Bill Gates and Paul Allen made a proposal to MITS: they would write software
  that would allow users to program the Altair in BASIC. They agreed and were
  prepared to distribute the software. As a result Gates and Allen founded
  Microsoft in 1975.

  Around the same time, Steve Wozniak (member of the Homebrew club) built the
  Apple I as a single-board hobbyist project. He made this available to buy via
  mail order and formed Apple with Steve Jobs in 1976 to manage the enterprise.

  ![Apple I](../img/apple1.jpg)

  By 1977 they had investment capital and brought out the Apple II. This was sold
  preassembled with casing and required no soldering. It had expansion slots for
  third-party vendors to create compatible devices and had colour graphics.

  The design of the Apple II was published in _Byte_ in order to invite
  crowd-sourced plugins and extensions. One of these was the Z-80 softcard which
  allowed it to run the CP/M OS (created by the company Digital Research) giving
  ready-made access to software such as a word-processor, spreadsheets and
  databases.

  ![Apple 2](../img/apple2.jpg)

  Other competitors in the PC market at this time were Radioshack's TRS-80 and the
  Commodore PET.

  ### IBM

  All the while it was hobbyist and tinkerers driving the PC market, IBM had no
  real interest. When PCSs started being used by businesses and their capacity
  grew to include typical business applications (databases, spreadsheets) it
  sensed an incursion into its market.

  ![IBM PC](../img/ibm-pc.jpg)

  Thus, in 1981, IBM launched the "IBM Personal Computer" which rapidly became the
  industry standard. This had the effect of legitimising the concept of a PC.
  There was overwhelming demand. In their marketing IBM melded home and office use
  to capture both markets at once.

  The internal specification of the IBM PC was freely available (dubbed 'open
  architecture'), in contrast to IBM's business computers, to encourage software
  expansion. Cards could be created to fit in its expansion slots adding graphics,
  sound, additional memory and networking capability.

  IBM originally contracted Digital Research to create software based on CP/M but
  this fell through. Microsoft got the deal to supply the OS which became MS-DOS
  (Microsoft Disk Operating System). Henceforth every PC and PC-clone came with
  DOS bundled.


path: The_Little_Man_computer.md
sortable_path: The_Little_Man_computer.md
title: The Little Man Computer
checksum: 7ade3a76827f3f0535ca49bfb1e97fd63aba137e7816612848aa576e8cf04234
created: 2024-10-22 16:52:59.377745506+00:00
modified: 2024-10-19 08:59:33.256642074+00:00
metadata: {"tags":["CPU"]}
lead:
  The [Little Man Computer]() is a simplified computer that works on Von Neuman
  principles. It has all the CPU components we have detailed above. It is
  programmed in machine code but for simplicity it uses the denary rather than the
  binary number system.

body:
  The [Little Man Computer]() is a simplified computer that works on Von Neuman
  principles. It has all the CPU components we have detailed above. It is
  programmed in machine code but for simplicity it uses the denary rather than the
  binary number system.

  ![LMC_5.gif](LMC_5.gif)

  On the left is the instruction set. Each number constitutes and execution
  routine and the `xx` stand for the address in RAM that the execution will work
  on.

  Each row of the RAM has a denary address, 1 through to 99. Each address can hold
  three digits.

  - So the instruction `560` would mean _load the number at address 60._
  - The instruction `340` would mean _store a datum at address 40_

  ### Working through a basic computation

  We are going to add two numbers together as a basic example.

  1. First we need to place the two numbers in RAM we are going to use `5` and `3`
     - At address `60` we will put the number `5` and at address `61` we will put
       the number `3`
     - We are going to start at address `0` in the top left of the RAM grid
  1. The first instruction will be _load address 60_ which in the assembly will be
     `560` . We put this in address `0`, our starting point.
  1. This first instruction is now stored in the accumulator.
  1. Now we want to _add this number (in the accumulator) to the number in address
     61_
  1. This second instruction is `161` . We write this in address `1`
  1. Finally we want to store the output of the calculation in the RAM, let's say
     at address `62`
  1. So we store the command `362` at address `2`

raw_content:
  ---
  tags: [CPU]
  ---

  # The Little Man Computer

  The [Little Man Computer]() is a simplified computer that works on Von Neuman
  principles. It has all the CPU components we have detailed above. It is
  programmed in machine code but for simplicity it uses the denary rather than the
  binary number system.

  ![LMC_5.gif](LMC_5.gif)

  On the left is the instruction set. Each number constitutes and execution
  routine and the `xx` stand for the address in RAM that the execution will work
  on.

  Each row of the RAM has a denary address, 1 through to 99. Each address can hold
  three digits.

  - So the instruction `560` would mean _load the number at address 60._
  - The instruction `340` would mean _store a datum at address 40_

  ### Working through a basic computation

  We are going to add two numbers together as a basic example.

  1. First we need to place the two numbers in RAM we are going to use `5` and `3`
     - At address `60` we will put the number `5` and at address `61` we will put
       the number `3`
     - We are going to start at address `0` in the top left of the RAM grid
  1. The first instruction will be _load address 60_ which in the assembly will be
     `560` . We put this in address `0`, our starting point.
  1. This first instruction is now stored in the accumulator.
  1. Now we want to _add this number (in the accumulator) to the number in address
     61_
  1. This second instruction is `161` . We write this in address `1`
  1. Finally we want to store the output of the calculation in the RAM, let's say
     at address `62`
  1. So we store the command `362` at address `2`


path: The_PATH.md
sortable_path: The_PATH.md
title: The $PATH
checksum: 4f73dff47c7baf9d241cb89b6685f5c22038ece2bf4e6585bfc08810220df656
created: 2024-10-22 16:52:59.383972531+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["shell"]}
lead:
  We know that `$PATH` is an
  [environment variable](Environmental_and_shell_variables.md).
  This variable keeps track of directories **where executables are found**.

body:
  We know that `$PATH` is an
  [environment variable](Environmental_and_shell_variables.md).
  This variable keeps track of directories **where executables are found**.

  Whenever any command is run, the shell looks up the directories contained in the
  `PATH` for the target executable file and runs it. We can see this is the case
  by using the `which` command which traces the executable of bash commands. Take
  the `echo` program:

  ```bash
  which echo
  # echo: shell built-in command
  ```

  Or `npm` :

  ```bash
  which npm
  /home/thomas/.nvm/versions/node/v19.4.0/bin/npm
  ```

  By default the path will always contain the following locations:

  - `/usr/bin`
  - `/usr/sbin`
  - `/usr/local/bin`
  - `/usr/local/sbin`
  - `/bin`
  - `/sbin`

  All the inbuilt terminal programs reside at these locations and most of them are
  at `/usr/bin`. This is why they run automatically without error. If you attempt
  to run a program that doesn’t reside at these locations then you will get an
  error along the lines of `program x is not found in PATH`.

  ## Structure of the PATH

  ```bash
  /home/thomas/.nvm/versions/node/v19.4.0/bin:
  /usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:
  /usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
  ```

  ## Adding to the PATH

  Only the default directories load to the PATH on every session. How then can we
  add custom directories to the path without them being lost every time we start a
  new session? Remember that the user config `.bashrc` loads on init for every
  bash session. Therefore, if we set the custom path in this file, it will be
  created every time we start a session. This is why when you add a new program it
  often ask you to append a script to the bottom of your `.bashrc` .

  For example, at the bottom of my `.zshrc` on my work computer I have:

  ```bash
  export CHROME_BIN=/mnt/c/Program\\ Files\\ \\(x86\\)/Google/Chrome/Application/chrome.exe
  ```

  This enables me to access the Chromium binaries from my terminal session (needed
  for running Angular tests) but it doesn’t add it to the path, it creates an
  environment variable on every session.

  For demonstration, let’s add a user’s desktop directory to the PATH.

  First we go to the `.bashrc` and add the `export` command. Remember that this is
  the command for creating a new environment variable:

  ```bash
  export PATH="$PATH=:~/Desktop"
  ```

  We force a reload of the `.bashrc` with the command:

  ```bash
  source ~/.bashrc
  ```

  Then we can check this directory has been added to the path with an echo

  ```bash
  echo $PATH
  ...:~/Desktop
  ```

raw_content:
  ---
  tags:
    - shell
  ---

  # The `$PATH`

  We know that `$PATH` is an
  [environment variable](Environmental_and_shell_variables.md).
  This variable keeps track of directories **where executables are found**.

  Whenever any command is run, the shell looks up the directories contained in the
  `PATH` for the target executable file and runs it. We can see this is the case
  by using the `which` command which traces the executable of bash commands. Take
  the `echo` program:

  ```bash
  which echo
  # echo: shell built-in command
  ```

  Or `npm` :

  ```bash
  which npm
  /home/thomas/.nvm/versions/node/v19.4.0/bin/npm
  ```

  By default the path will always contain the following locations:

  - `/usr/bin`
  - `/usr/sbin`
  - `/usr/local/bin`
  - `/usr/local/sbin`
  - `/bin`
  - `/sbin`

  All the inbuilt terminal programs reside at these locations and most of them are
  at `/usr/bin`. This is why they run automatically without error. If you attempt
  to run a program that doesn’t reside at these locations then you will get an
  error along the lines of `program x is not found in PATH`.

  ## Structure of the PATH

  ```bash
  /home/thomas/.nvm/versions/node/v19.4.0/bin:
  /usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:
  /usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
  ```

  ## Adding to the PATH

  Only the default directories load to the PATH on every session. How then can we
  add custom directories to the path without them being lost every time we start a
  new session? Remember that the user config `.bashrc` loads on init for every
  bash session. Therefore, if we set the custom path in this file, it will be
  created every time we start a session. This is why when you add a new program it
  often ask you to append a script to the bottom of your `.bashrc` .

  For example, at the bottom of my `.zshrc` on my work computer I have:

  ```bash
  export CHROME_BIN=/mnt/c/Program\\ Files\\ \\(x86\\)/Google/Chrome/Application/chrome.exe
  ```

  This enables me to access the Chromium binaries from my terminal session (needed
  for running Angular tests) but it doesn’t add it to the path, it creates an
  environment variable on every session.

  For demonstration, let’s add a user’s desktop directory to the PATH.

  First we go to the `.bashrc` and add the `export` command. Remember that this is
  the command for creating a new environment variable:

  ```bash
  export PATH="$PATH=:~/Desktop"
  ```

  We force a reload of the `.bashrc` with the command:

  ```bash
  source ~/.bashrc
  ```

  Then we can check this directory has been added to the path with an echo

  ```bash
  echo $PATH
  ...:~/Desktop
  ```


path: The_Pragmatic_Programmer_1999.md
sortable_path: The_Pragmatic_Programmer_1999.md
title: The Pragmatic Programmer (Hunt/Thomas, 1999)
lead: ## General
checksum: b889e9eee019a923f22718f398b0395d5f234d3045f4a6c3fcc063893630b0b4
created: 2024-10-22 16:52:59.397426981+00:00
modified: 2024-10-19 09:00:04.743850954+00:00
metadata: {"tags":[]}
body:
  ## General

  ### Meyer's Uniform Access Principle

  > All services offered by a module should be available through a uniform
  > notation, which does not betray whether they are implemented through storage
  > or through computation

  This is a clear recommendation for using getters and setters with classes. You
  should not see method calls outside of the class, they should appear as
  properties of the object.

  ## Don't Repeat Yourself

  > Every piece of knowledge must have a single, unambiguous, authoritative
  > representation within a system

  ## The Principle of Orthogonality

  This notion comes from geometry. Two lines are orthogonal to each other if they
  form a right-angle when they meet.

  Their meeting isn't the important part. Think of a simple x, y graph:

  > If you move along one of the lines, **your position projected onto the other
  > doesn't change**

  In computing this is expressed in terms of **decoupling** and is implemented
  through modular, component-based architectures. As much as possible code should
  be scoped narrowly so that a change in one area does not cause changes in
  others. By keeping components discrete it is easier to make changes, refactor,
  improve and extend the codebase.

  > We want to design components that are self-contained: independent and with a
  > single, well-defined purpose. When components are isolated from one another,
  > you know that you can change one without having to worry about the rest. As
  > long as you don't change that component's external interfaces, you can be
  > comfortable that you won't cause problems that ripple through the entire
  > system.

  ### Benefits of orthogonality: productivity

  - Changes are localised so development time and testing time are reduced
  - Orthogonality promotes reuse: if components have specific, well-defined
    responsibilities, they can be combined with new components in ways that were
    not envisioned by their original implementors. The more loosely coupled your
    systems, the easier they are to reconfigure and reengineer.
  - Assume that one component does _M_ distinct things and another does _N_
    things. If they are orthogonal and you combine them, the result does _M x N_
    things. However if the two components are not orthogonal, there will be
    overlap, and the result will do less. You get more functionality per unit
    effort by combining orthogonal components.

  ### Benefits of orthogonality: reduced risk

  - Diseased sections of code are isolated. If a module is sick, it is less likely
    to spread the symptoms around the rest of the system.
  - Overall the system is less fragile: make small changes to a particular area
    and any problems you generate will be restricted to that area.
  - Orthogonal systems are better tested because it is easier to run and design
    discrete tests on modularised components.

  > Building a unit test us itself a an interesting test of orthogonality: what
  > does it take to build and link a unit test? Do you have to drag in a large
  > percentage of the rest of the system just to get a test to compile or link?
  > **If so, you've found a module that is not well decoupled from the rest of the
  > system**

  ### Relationship between DRY and orthogonality

  With DRY you're looking to minimize duplication within a system, whereas with
  orthogonality, you reduce the interdependency among the system's components. If
  you use the principle of orthogonality combined closely with the DRY principle,
  you'll find that the systems you develop are more flexible, more understandable
  and easier to debug, test, and maintain.

  ### Reversibility

  The principles of orthogonality and DRY result in code that is reversible. This
  means it is able to change in an agile way when the circumstances of its use and
  deployment change. This is important because when developing software in a
  business setting, the best decisions are not always made the first time around.
  By following the principles it should be relatively easy to change your
  program's interfaces, platform and scale. In other words, with the principle of
  orthogonality and DRY, refactoring becomes less of a chore.

  ## Prototyping and Tracer Bullets

  'Tracer bullets' are used by the military for night warfare. They are
  phosphorous bullets that are included in the magazines of guns alongside normal
  bullets. They are not intended to kill but instead light-up the surrounding area
  making it easier to see the terrain and target more efficiently.

  The authors use the notion of tracer bullets as a metaphor for developing
  software at the early stages of a project. This is **not** the same thing as
  prototyping. A tracer bullet model is useful for building things that haven't
  been built before. They exist to 'shed light' on the project's needs and to help
  the client understand what they want.

  They differ from prototypes in that they include integrated overall
  functionality but in a rough state. Whereas prototypes are more for singular,
  specific subcomponents of the project. Because tracer bullet models are
  joined-up in this way, even if they turn out to be inappropriate in some regard,
  they can be adapted and developed into a better form, without losing the core
  functionality.

  > Tracer bullets work because they operate in the same environment and under the
  > same constraints as the real bullets. They get to the target fast, so the
  > gunner gets immediate feedback. And from a practical standpoint they are a
  > relatively cheap solution. To get the same effect in code, we're looking for
  > something that gets us from a requirement to some aspect of the final system
  > quickly, visibly and repeatably.

  > Tracer code is not disposable: you write it for keeps. It contains all the
  > error-checking, structuring, documentation and self-checking that a piece of
  > production code has. It simply is not fully functional. However, once you have
  > made an end-to-end connection among the components of your system, you can
  > check how close to the target you are, adjusting as necessary.

  ### Distinguishing from prototyping

  > Prototyping generates disposable code. Tracer code is lean but complete, and
  > forms part of the skeleton of the final system. Think of prototyping as the
  > reconnaissance and intelligence gathering that takes place before a single
  > tracer bullet is fired.

  ## Design by contract

  To understand DBC we have to think of a computational process as involving two
  stages: the call and the execution of the routine that happens in response to
  the call (henceforth **caller** and **routine**).

  - the caller could be a function expression that invokes a function and passes
    arguments to it expecting a given output. The function that executes is the
    routine
  - the caller could be an object instantiation that calls a method belonging to
    its parent class
  - the caller could be a parent React component that passes props to a child
    component

  Design by contract means specifying clear and inviolable rules detailing what
  must obtain at both the call stage and the routine stage if the process is to
  execute.

  Every function and method in a software system does something. Before it starts
  that something, the routine may have some expectation of the state of the world
  and it may be able to make a statement about the state of the world when it
  concludes. These expectations are defined in terms of preconditions,
  postconditions, and invariants. They form that basis of a **contract** between
  the caller and the routine. Hence _design by contract\*\*._\*\*

  ### Preconditions

  Preconditions specify what must be true in order for the routine to be called.
  In other words, the requirements of the routine. What it needs and what should
  be the case before it even considers executing the task. A **routine should
  never get called when its preconditions would be violated**.

  ### Postconditions

  Providing the preconditions are met, this is what the routine is guaranteed to
  do. In other words: the state of affairs that must obtain after the routine has
  ran.

  ### Invariants

  Once established, the preconditions and postconditions should not change. If
  they need to change, that is a separate process and contract. In the processing
  of a routine, the data may be variant relative to the contract, but by the end
  the overall conditions establish the equilibrium of the contract.

  There is an analogue here with functional programming philosophy: the function
  should always return the same sort of output, without ancillary processes
  happening, i.e side-effects.

  One way to achieve this is to be miserly when setting up the contract, which
  overlaps with orthogonality. Only specify the minimum return on a contract
  rather than multiple postconditions. This only increases the likelihood that the
  contract will be breached at some point. If you need multiple postconditions,
  spread them out an achieve them in a compositional way, with multiple separate
  and modular processes.

  > Be strict in what you will accept before you begin, and promise as little as
  > possible in return. If your contract indicates that you'll accept anything and
  > promise the world in return, then you've got a lot of code to write!

  ### Division of responsibilities

  > If all the routine's preconditions are met by the caller, the routine shall
  > guarantee that all postconditions and invariants will be true when it
  > completes.

  Note that the emphasis of responsibilities is on the caller.

  Imagine that we have a function that returns the count of an array of integers.
  It is not the job of the count routine to verify that it has been passed
  integers and then to execute the count. Or, in the event that it is not passed
  integers, to mutate the data to integers and then execute.

  This should be resolved by the caller: it is the responsibility of the caller to
  pass integers. If it doesn't, the routine simply crashes or raises an exception.
  It doesn't try to accommodate the input because that does not come down on its
  side of the contract. The caller has failed to meet the preconditions. If, due
  to some bug, the routine receives integers and fails to output the sum, then it
  has failed on its side

  ### Example: type checking

  An obvious example of this philosophy is when you perform checks or validation
  within your code (although validation is more of an issue when you are dealing
  with user data, not your own internal code). For instance using type checking
  with dynamically-typed languages.

  When we use the `prop-types` library with React we are specifying preconditions:
  so long at the prop (effectively the caller) passed to the component
  (effectively the routine) is of type X, the component will render invariantly as
  R. If the prop is of type Y, an exception will be raised highlighting a breach
  in the contract.

  Another example would be more advanced type checking with Javascript written
  using Typescript.

  ## The Law of Demeter

  Demeter's Law has applicability chiefly when programming with classes.

  It's a fancy name for a simple principle summarised by 'don't talk to
  strangers'. Demeter's law is violated when code has more than one step between
  classes. You should avoid invoking methods of an object returned by another
  method. You should only use your own methods when dealing with it.

  ### Formal

  A method _m_ of object _O_ may only invoke the methods of the following kinds of
  objects:

  - _O_ itself
  - _m_'s parameters
  - any objects created or instantiated within _m_
  - _O_'s direct component objects (in other words nested objects)
  - a global variable (over and above _O_) accessible by _O_, within the scope of
    _m_

  ## Model, View, Controller design pattern

  The key concept behind the MVC idiom is separating the model from both the GUI
  that represents it and the controls that manage the view.

  - **Model**
    - The abstract data model representing the target object
    - The model has no direct knowledge of any views or controllers
  - **View**
    - A way to interpret the model. It subscribes to changes in the model and
      logical events from the controller
  - **Controller**
    - A way to control the view and provide the model with new data. It publishes
      events to both the model and the view

  For comparison, distinguish React from MVC. In React data is unidirectional: the
  JSX component as controller cannot change the state. The state is passed down to
  the controller. Also MVC lends itself to separation of technologies: code used
  to create the View is different from Code that manages Controller and data
  Model. In React it's all one integrated system.

  ## Refactoring

  > Rewriting, reworking, and re-architecting code is collectively known as
  > refactoring

  ### When to refactor

  - **Duplication**: you've discovered a violation of the DRY principle
  - **Non-orthogonal design**: you've discovered some code or design that could be
    made more orthogonal
  - **Outdated knowledge**: your knowledge about the problem and you skills at
    implementing a solution have changed since the code was first written. Update
    and improve the code to reflect these changes
  - **Performance: y**ou need to move functionality from one area of the system to
    another to improve performance

  ### Tips when refactoring

  - Don't try to refactor and add new functionality at the same time!
  - Make sure you have good tests before you begin refactoring. Run the tests as
    you refactor. That way you will know quickly if your changes have broken
    anything
  - Take short, deliberative steps. Refactoring often involves making many
    localised changes that result in a larger-scale change.

  ## Testing

  > Most developers hate testing. They tend to test-gently, subconsciously knowing
  > where the code will break and avoiding the weak spots. Pragmatic Programmers
  > are different. We are _driven_ to find our bugs _now_, so we don't have to
  > endure the shame of others finding our bugs later.

  ### Unit testing

  A unit test is code that exercises a module. It consists in testing each module
  in isolation to verify its behaviour. Unit testing is the foundation of all
  other forms of testing. If the parts don't work by themselves, they probably
  won't work well together. All the modules you are using must pass their own unit
  tests before you can proceed.

  We can think of unit testing as **testing against contract** (detailed above).
  We want to test that the module delivers the functionality it promises over a
  wide range of test cases and boundary conditions.

  Scope for unit testing should cover:

  - Obviously, returning the expected value/outcome
  - Ensuring that faulty arguments/ types are rejected and initiate error handling
    (deliberately breaking your code to ensure it is handled appropriately)
  - Pass in the boundary and maximum value
  - Pass in values between the zero and the maximum expressible argument to cover
    a range of cases

  Benefits of unit testing include:

  - It creates an example to other developers how to use all of the functionality
    of a given module
  - It is a means to build **regression tests** which can be used to validate any
    future changes to the code. In other words, the future changes should pass the
    older tests to prove they are consistent with the code base

  ### Integration testing

  Integration testing shows that the major subsystems that make up the project
  work and play well with each other.

  Integration testing is really just an extension of the unit testing described,
  only know you're testing how entire subsystems honour their contracts.

  ## Commenting your code

  In general, comments should detail **why** something is done, its purpose and
  its goal. The code already shows _how_ it's done, so commenting on this is
  redundant, and violates the DRY principle.

  > We like to see a simple module-level comment, comments for significant data
  > and type declarations, and a brief class and per-method header describing how
  > the function is used and anything it does that is not obvious

  ```js
  /*
  Find the highest value within a specified data range of samples

  Parameter: aRange = range of dates to search for data
  Parameter: aThreshold = minimum value to consider

  Return: the value, or null if no value found that is greater than or equal to the threshold
  */
  ```

raw_content:
  ---
  tags: []
  ---

  # The Pragmatic Programmer (Hunt/Thomas, 1999)

  ## General

  ### Meyer's Uniform Access Principle

  > All services offered by a module should be available through a uniform
  > notation, which does not betray whether they are implemented through storage
  > or through computation

  This is a clear recommendation for using getters and setters with classes. You
  should not see method calls outside of the class, they should appear as
  properties of the object.

  ## Don't Repeat Yourself

  > Every piece of knowledge must have a single, unambiguous, authoritative
  > representation within a system

  ## The Principle of Orthogonality

  This notion comes from geometry. Two lines are orthogonal to each other if they
  form a right-angle when they meet.

  Their meeting isn't the important part. Think of a simple x, y graph:

  > If you move along one of the lines, **your position projected onto the other
  > doesn't change**

  In computing this is expressed in terms of **decoupling** and is implemented
  through modular, component-based architectures. As much as possible code should
  be scoped narrowly so that a change in one area does not cause changes in
  others. By keeping components discrete it is easier to make changes, refactor,
  improve and extend the codebase.

  > We want to design components that are self-contained: independent and with a
  > single, well-defined purpose. When components are isolated from one another,
  > you know that you can change one without having to worry about the rest. As
  > long as you don't change that component's external interfaces, you can be
  > comfortable that you won't cause problems that ripple through the entire
  > system.

  ### Benefits of orthogonality: productivity

  - Changes are localised so development time and testing time are reduced
  - Orthogonality promotes reuse: if components have specific, well-defined
    responsibilities, they can be combined with new components in ways that were
    not envisioned by their original implementors. The more loosely coupled your
    systems, the easier they are to reconfigure and reengineer.
  - Assume that one component does _M_ distinct things and another does _N_
    things. If they are orthogonal and you combine them, the result does _M x N_
    things. However if the two components are not orthogonal, there will be
    overlap, and the result will do less. You get more functionality per unit
    effort by combining orthogonal components.

  ### Benefits of orthogonality: reduced risk

  - Diseased sections of code are isolated. If a module is sick, it is less likely
    to spread the symptoms around the rest of the system.
  - Overall the system is less fragile: make small changes to a particular area
    and any problems you generate will be restricted to that area.
  - Orthogonal systems are better tested because it is easier to run and design
    discrete tests on modularised components.

  > Building a unit test us itself a an interesting test of orthogonality: what
  > does it take to build and link a unit test? Do you have to drag in a large
  > percentage of the rest of the system just to get a test to compile or link?
  > **If so, you've found a module that is not well decoupled from the rest of the
  > system**

  ### Relationship between DRY and orthogonality

  With DRY you're looking to minimize duplication within a system, whereas with
  orthogonality, you reduce the interdependency among the system's components. If
  you use the principle of orthogonality combined closely with the DRY principle,
  you'll find that the systems you develop are more flexible, more understandable
  and easier to debug, test, and maintain.

  ### Reversibility

  The principles of orthogonality and DRY result in code that is reversible. This
  means it is able to change in an agile way when the circumstances of its use and
  deployment change. This is important because when developing software in a
  business setting, the best decisions are not always made the first time around.
  By following the principles it should be relatively easy to change your
  program's interfaces, platform and scale. In other words, with the principle of
  orthogonality and DRY, refactoring becomes less of a chore.

  ## Prototyping and Tracer Bullets

  'Tracer bullets' are used by the military for night warfare. They are
  phosphorous bullets that are included in the magazines of guns alongside normal
  bullets. They are not intended to kill but instead light-up the surrounding area
  making it easier to see the terrain and target more efficiently.

  The authors use the notion of tracer bullets as a metaphor for developing
  software at the early stages of a project. This is **not** the same thing as
  prototyping. A tracer bullet model is useful for building things that haven't
  been built before. They exist to 'shed light' on the project's needs and to help
  the client understand what they want.

  They differ from prototypes in that they include integrated overall
  functionality but in a rough state. Whereas prototypes are more for singular,
  specific subcomponents of the project. Because tracer bullet models are
  joined-up in this way, even if they turn out to be inappropriate in some regard,
  they can be adapted and developed into a better form, without losing the core
  functionality.

  > Tracer bullets work because they operate in the same environment and under the
  > same constraints as the real bullets. They get to the target fast, so the
  > gunner gets immediate feedback. And from a practical standpoint they are a
  > relatively cheap solution. To get the same effect in code, we're looking for
  > something that gets us from a requirement to some aspect of the final system
  > quickly, visibly and repeatably.

  > Tracer code is not disposable: you write it for keeps. It contains all the
  > error-checking, structuring, documentation and self-checking that a piece of
  > production code has. It simply is not fully functional. However, once you have
  > made an end-to-end connection among the components of your system, you can
  > check how close to the target you are, adjusting as necessary.

  ### Distinguishing from prototyping

  > Prototyping generates disposable code. Tracer code is lean but complete, and
  > forms part of the skeleton of the final system. Think of prototyping as the
  > reconnaissance and intelligence gathering that takes place before a single
  > tracer bullet is fired.

  ## Design by contract

  To understand DBC we have to think of a computational process as involving two
  stages: the call and the execution of the routine that happens in response to
  the call (henceforth **caller** and **routine**).

  - the caller could be a function expression that invokes a function and passes
    arguments to it expecting a given output. The function that executes is the
    routine
  - the caller could be an object instantiation that calls a method belonging to
    its parent class
  - the caller could be a parent React component that passes props to a child
    component

  Design by contract means specifying clear and inviolable rules detailing what
  must obtain at both the call stage and the routine stage if the process is to
  execute.

  Every function and method in a software system does something. Before it starts
  that something, the routine may have some expectation of the state of the world
  and it may be able to make a statement about the state of the world when it
  concludes. These expectations are defined in terms of preconditions,
  postconditions, and invariants. They form that basis of a **contract** between
  the caller and the routine. Hence _design by contract\*\*._\*\*

  ### Preconditions

  Preconditions specify what must be true in order for the routine to be called.
  In other words, the requirements of the routine. What it needs and what should
  be the case before it even considers executing the task. A **routine should
  never get called when its preconditions would be violated**.

  ### Postconditions

  Providing the preconditions are met, this is what the routine is guaranteed to
  do. In other words: the state of affairs that must obtain after the routine has
  ran.

  ### Invariants

  Once established, the preconditions and postconditions should not change. If
  they need to change, that is a separate process and contract. In the processing
  of a routine, the data may be variant relative to the contract, but by the end
  the overall conditions establish the equilibrium of the contract.

  There is an analogue here with functional programming philosophy: the function
  should always return the same sort of output, without ancillary processes
  happening, i.e side-effects.

  One way to achieve this is to be miserly when setting up the contract, which
  overlaps with orthogonality. Only specify the minimum return on a contract
  rather than multiple postconditions. This only increases the likelihood that the
  contract will be breached at some point. If you need multiple postconditions,
  spread them out an achieve them in a compositional way, with multiple separate
  and modular processes.

  > Be strict in what you will accept before you begin, and promise as little as
  > possible in return. If your contract indicates that you'll accept anything and
  > promise the world in return, then you've got a lot of code to write!

  ### Division of responsibilities

  > If all the routine's preconditions are met by the caller, the routine shall
  > guarantee that all postconditions and invariants will be true when it
  > completes.

  Note that the emphasis of responsibilities is on the caller.

  Imagine that we have a function that returns the count of an array of integers.
  It is not the job of the count routine to verify that it has been passed
  integers and then to execute the count. Or, in the event that it is not passed
  integers, to mutate the data to integers and then execute.

  This should be resolved by the caller: it is the responsibility of the caller to
  pass integers. If it doesn't, the routine simply crashes or raises an exception.
  It doesn't try to accommodate the input because that does not come down on its
  side of the contract. The caller has failed to meet the preconditions. If, due
  to some bug, the routine receives integers and fails to output the sum, then it
  has failed on its side

  ### Example: type checking

  An obvious example of this philosophy is when you perform checks or validation
  within your code (although validation is more of an issue when you are dealing
  with user data, not your own internal code). For instance using type checking
  with dynamically-typed languages.

  When we use the `prop-types` library with React we are specifying preconditions:
  so long at the prop (effectively the caller) passed to the component
  (effectively the routine) is of type X, the component will render invariantly as
  R. If the prop is of type Y, an exception will be raised highlighting a breach
  in the contract.

  Another example would be more advanced type checking with Javascript written
  using Typescript.

  ## The Law of Demeter

  Demeter's Law has applicability chiefly when programming with classes.

  It's a fancy name for a simple principle summarised by 'don't talk to
  strangers'. Demeter's law is violated when code has more than one step between
  classes. You should avoid invoking methods of an object returned by another
  method. You should only use your own methods when dealing with it.

  ### Formal

  A method _m_ of object _O_ may only invoke the methods of the following kinds of
  objects:

  - _O_ itself
  - _m_'s parameters
  - any objects created or instantiated within _m_
  - _O_'s direct component objects (in other words nested objects)
  - a global variable (over and above _O_) accessible by _O_, within the scope of
    _m_

  ## Model, View, Controller design pattern

  The key concept behind the MVC idiom is separating the model from both the GUI
  that represents it and the controls that manage the view.

  - **Model**
    - The abstract data model representing the target object
    - The model has no direct knowledge of any views or controllers
  - **View**
    - A way to interpret the model. It subscribes to changes in the model and
      logical events from the controller
  - **Controller**
    - A way to control the view and provide the model with new data. It publishes
      events to both the model and the view

  For comparison, distinguish React from MVC. In React data is unidirectional: the
  JSX component as controller cannot change the state. The state is passed down to
  the controller. Also MVC lends itself to separation of technologies: code used
  to create the View is different from Code that manages Controller and data
  Model. In React it's all one integrated system.

  ## Refactoring

  > Rewriting, reworking, and re-architecting code is collectively known as
  > refactoring

  ### When to refactor

  - **Duplication**: you've discovered a violation of the DRY principle
  - **Non-orthogonal design**: you've discovered some code or design that could be
    made more orthogonal
  - **Outdated knowledge**: your knowledge about the problem and you skills at
    implementing a solution have changed since the code was first written. Update
    and improve the code to reflect these changes
  - **Performance: y**ou need to move functionality from one area of the system to
    another to improve performance

  ### Tips when refactoring

  - Don't try to refactor and add new functionality at the same time!
  - Make sure you have good tests before you begin refactoring. Run the tests as
    you refactor. That way you will know quickly if your changes have broken
    anything
  - Take short, deliberative steps. Refactoring often involves making many
    localised changes that result in a larger-scale change.

  ## Testing

  > Most developers hate testing. They tend to test-gently, subconsciously knowing
  > where the code will break and avoiding the weak spots. Pragmatic Programmers
  > are different. We are _driven_ to find our bugs _now_, so we don't have to
  > endure the shame of others finding our bugs later.

  ### Unit testing

  A unit test is code that exercises a module. It consists in testing each module
  in isolation to verify its behaviour. Unit testing is the foundation of all
  other forms of testing. If the parts don't work by themselves, they probably
  won't work well together. All the modules you are using must pass their own unit
  tests before you can proceed.

  We can think of unit testing as **testing against contract** (detailed above).
  We want to test that the module delivers the functionality it promises over a
  wide range of test cases and boundary conditions.

  Scope for unit testing should cover:

  - Obviously, returning the expected value/outcome
  - Ensuring that faulty arguments/ types are rejected and initiate error handling
    (deliberately breaking your code to ensure it is handled appropriately)
  - Pass in the boundary and maximum value
  - Pass in values between the zero and the maximum expressible argument to cover
    a range of cases

  Benefits of unit testing include:

  - It creates an example to other developers how to use all of the functionality
    of a given module
  - It is a means to build **regression tests** which can be used to validate any
    future changes to the code. In other words, the future changes should pass the
    older tests to prove they are consistent with the code base

  ### Integration testing

  Integration testing shows that the major subsystems that make up the project
  work and play well with each other.

  Integration testing is really just an extension of the unit testing described,
  only know you're testing how entire subsystems honour their contracts.

  ## Commenting your code

  In general, comments should detail **why** something is done, its purpose and
  its goal. The code already shows _how_ it's done, so commenting on this is
  redundant, and violates the DRY principle.

  > We like to see a simple module-level comment, comments for significant data
  > and type declarations, and a brief class and per-method header describing how
  > the function is used and anything it does that is not obvious

  ```js
  /*
  Find the highest value within a specified data range of samples

  Parameter: aRange = range of dates to search for data
  Parameter: aThreshold = minimum value to consider

  Return: the value, or null if no value found that is greater than or equal to the threshold
  */
  ```


