# notes

path: Formal_conventions.md
sortable_path: Formal_conventions.md
title: Formal conventions
lead: ## Grouping symbols
checksum: fa1ce008fb14d5df60574e86e15eb3c9b8b9c1f1af84a92134591cb053a70738
created: 2024-10-22 16:52:56.892420624+00:00
modified: 2024-10-18 18:14:07.069915515+00:00
metadata: {"tags":["prealgebra"]}
body:
  ## Grouping symbols

  We use parentheses to delimit the part of an expression we want evaluated first.
  If grouping symbols are nested, evaluate the expression in the innermost pair of
  grouping symbols first,

  ## Writing mathematical statements: placement of $=$

  We only write one equals sign per line. For example, if we are resolving
  parentheses:

  $$
  \begin{equation} \begin{split} 2 + \[3 + (4+5)\] = 2 + \[3 +9\] \\ = 2 + 12
  \\ = 14 \end{split} \end{equation}
  $$

  We call parentheses (`()`), brackets (`[]`) and braces `{}`) grouping symbols.
  When groupings (say parentheses) are used, the expression inside any pair of
  parentheses must be evaluated first.

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

  # Formal conventions

  ## Grouping symbols

  We use parentheses to delimit the part of an expression we want evaluated first.
  If grouping symbols are nested, evaluate the expression in the innermost pair of
  grouping symbols first,

  ## Writing mathematical statements: placement of $=$

  We only write one equals sign per line. For example, if we are resolving
  parentheses:

  $$
  \begin{equation} \begin{split} 2 + \[3 + (4+5)\] = 2 + \[3 +9\] \\ = 2 + 12
  \\ = 14 \end{split} \end{equation}
  $$

  We call parentheses (`()`), brackets (`[]`) and braces `{}`) grouping symbols.
  When groupings (say parentheses) are used, the expression inside any pair of
  parentheses must be evaluated first.


path: Formal_proofs_in_propositional_logic.md
sortable_path: Formal_proofs_in_propositional_logic.md
title: Formal proofs in propositional logic
checksum: a122046abb81e7039c1b4c3d9a2e1ae493e2d3b61c43bbaefcfb61f1b46481eb
created: 2024-10-22 16:52:56.895260886+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  When we construct a formal proof in logic we are seeking to show that a certain
  proposition is **derivable** from other propositions. We use the words
  _derivation_ and _proof_ interchangeably.

body:
  When we construct a formal proof in logic we are seeking to show that a certain
  proposition is **derivable** from other propositions. We use the words
  _derivation_ and _proof_ interchangeably.

  > A sentence $P$ is derivable in a system of propositional logic from a finite
  > set of sentences if and only if there is a derivation in this system in which
  > all and only the members of the set are **primary assumptions** and $P$ is the
  > sentence on the last line.

  We express the above symbolically as $\Gamma \vdash P$ . (Be careful not to
  confuse _derivable_ ($\vdash$) from _entails_ ($\vDash$).

  Derivability is therefore a property that a proposition can possess relative to
  a set.

  For instance to demonstrate derivability for:

  $$
  \{ \lnot F \lor D, F, D \rightarrow (G \land H) \} \vdash G \land H
  $$

  We would establish $\lnot F \lor D, F, D \rightarrow (G \land H)$ as primary
  assumptions and then, using the derivation rules of the system conclude with
  $G\land H$. Every sentence in the derivation is either a **primary assumption**
  or an **auxiliary** assumption or justified by the rules of the derivation.

  An auxiliary assumption is an assumption belonging to a sub-derivation. Primary
  assumptions belong to the main derivation. For any given derivation of the form
  $\Gamma \vdash P$ there may be a number of ways of demonstrating the derivation
  (more than one application of the rules governing the system) but one alone is
  sufficient to establish derivability.

  > We will tend to use the terms _derivation_ and _proof_ interchangeably but we
  > should note that there is a technical distinction in that a **proof is a
  > derivation in which all of the assumptions have been discharged**

  ## Constructing proofs

  We place assumptions above derivations and mark them _A_ in the right-hand
  margin. We use a shorthand for the derivation rules and also place these in the
  right-hand margin.

  We divide assumptions from derivations with a horizontal line. We number each
  line and use this to refer to the line we are applying the derivation to.
  Sub-proofs follow this structure recursively. This is known as _Fitch notation_

  _Schematically_:

  ![](/img/proofs-drawio-Page-5.drawio.png)

  _Applied example_:

  ![](/img/proofs-drawio-Page-6.drawio.png)

  ## Sub-proofs

  When a sub-proof is terminated, the assumption with which it starts is said to
  be _discharged_. It's conclusion can then be drawn upon and invoked within the
  main proof by reference to its start and end line number. However statements
  within the sub-proof cannot be referred to again from within the main proof,
  only its result.

  ## Derivation rules

  Derivation rules are
  [syntactic](Syntax_of_sentential_logic.md) rather
  than semantic. They are applied on the basis of their form rather than on the
  basis of the truth conditions of the sentences they are applied to.

  > Derivation rules can be applied without having an interpretation of the
  > symbols in mind. A derivation rule tells us that: given a group of symbols
  > with a certain structure, we can write down another group of symbols with a
  > certain structure.

  Each of the main logical connectives has an associated derivation rule. The
  binary connectives have pairs of rules, one for the introduction of the
  connective and one for its elimination.

  The main derivation rules:

  - [Negation Introduction](Negation_Introduction.md)
  - [Negation Elimination](Negation_Elimination.md)
  - [Conjunction Introduction](Conjunction_Introduction.md)
  - [Conjunction Elimination](Conditional_Elimination.md)
  - [Disjunction Introduction](Disjunction_Introduction.md)
  - [Disjunction Elimination](Disjunction_Elimination.md)
  - [Conditional Introduction](Conditional_Introduction.md)
  - [Disjunction Elimination](Disjunction_Elimination.md)
  - [Biconditional Introduction](Biconditional_Introduction.md)
  - [Biconditional Elimination](Biconditional_Elimination.md)

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

  # Formal proofs in propositional logic

  When we construct a formal proof in logic we are seeking to show that a certain
  proposition is **derivable** from other propositions. We use the words
  _derivation_ and _proof_ interchangeably.

  > A sentence $P$ is derivable in a system of propositional logic from a finite
  > set of sentences if and only if there is a derivation in this system in which
  > all and only the members of the set are **primary assumptions** and $P$ is the
  > sentence on the last line.

  We express the above symbolically as $\Gamma \vdash P$ . (Be careful not to
  confuse _derivable_ ($\vdash$) from _entails_ ($\vDash$).

  Derivability is therefore a property that a proposition can possess relative to
  a set.

  For instance to demonstrate derivability for:

  $$
  \{ \lnot F \lor D, F, D \rightarrow (G \land H) \} \vdash G \land H
  $$

  We would establish $\lnot F \lor D, F, D \rightarrow (G \land H)$ as primary
  assumptions and then, using the derivation rules of the system conclude with
  $G\land H$. Every sentence in the derivation is either a **primary assumption**
  or an **auxiliary** assumption or justified by the rules of the derivation.

  An auxiliary assumption is an assumption belonging to a sub-derivation. Primary
  assumptions belong to the main derivation. For any given derivation of the form
  $\Gamma \vdash P$ there may be a number of ways of demonstrating the derivation
  (more than one application of the rules governing the system) but one alone is
  sufficient to establish derivability.

  > We will tend to use the terms _derivation_ and _proof_ interchangeably but we
  > should note that there is a technical distinction in that a **proof is a
  > derivation in which all of the assumptions have been discharged**

  ## Constructing proofs

  We place assumptions above derivations and mark them _A_ in the right-hand
  margin. We use a shorthand for the derivation rules and also place these in the
  right-hand margin.

  We divide assumptions from derivations with a horizontal line. We number each
  line and use this to refer to the line we are applying the derivation to.
  Sub-proofs follow this structure recursively. This is known as _Fitch notation_

  _Schematically_:

  ![](/img/proofs-drawio-Page-5.drawio.png)

  _Applied example_:

  ![](/img/proofs-drawio-Page-6.drawio.png)

  ## Sub-proofs

  When a sub-proof is terminated, the assumption with which it starts is said to
  be _discharged_. It's conclusion can then be drawn upon and invoked within the
  main proof by reference to its start and end line number. However statements
  within the sub-proof cannot be referred to again from within the main proof,
  only its result.

  ## Derivation rules

  Derivation rules are
  [syntactic](Syntax_of_sentential_logic.md) rather
  than semantic. They are applied on the basis of their form rather than on the
  basis of the truth conditions of the sentences they are applied to.

  > Derivation rules can be applied without having an interpretation of the
  > symbols in mind. A derivation rule tells us that: given a group of symbols
  > with a certain structure, we can write down another group of symbols with a
  > certain structure.

  Each of the main logical connectives has an associated derivation rule. The
  binary connectives have pairs of rules, one for the introduction of the
  connective and one for its elimination.

  The main derivation rules:

  - [Negation Introduction](Negation_Introduction.md)
  - [Negation Elimination](Negation_Elimination.md)
  - [Conjunction Introduction](Conjunction_Introduction.md)
  - [Conjunction Elimination](Conditional_Elimination.md)
  - [Disjunction Introduction](Disjunction_Introduction.md)
  - [Disjunction Elimination](Disjunction_Elimination.md)
  - [Conditional Introduction](Conditional_Introduction.md)
  - [Disjunction Elimination](Disjunction_Elimination.md)
  - [Biconditional Introduction](Biconditional_Introduction.md)
  - [Biconditional Elimination](Biconditional_Elimination.md)


path: Format_specifiers_in_C.md
sortable_path: Format_specifiers_in_C.md
title: Format specifiers in C
checksum: 23d38cde9eb660d8275d3c932f35918e2587ba0e8565d75e0d687184fab4344c
created: 2024-10-22 16:52:56.925017699+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"created":"Thursday, February 29, 2024 | 17:41","id":"5faz2y2e","tags":["C"]}
lead:
  Format specifiers define the type of data to be printed on standard output. You
  need to use format specifiers whenever you are
  [printing-values-in-c](printing-values-in-c.md).

body:
  Format specifiers define the type of data to be printed on standard output. You
  need to use format specifiers whenever you are
  [printing-values-in-c](printing-values-in-c.md).

  | Specifier | Usage                      |
  | --------- | -------------------------- |
  | `%c`      | single character           |
  | `%s`      | string                     |
  | `%n`      | nothing                    |
  | `%d`      | integer (assuming base 10) |
  | `%f`      | float                      |
  | `%%`      | The percent sign itself    |

raw_content:
  ---
  id: 5faz2y2e
  tags:
    - C
  created: Thursday, February 29, 2024 | 17:41
  ---

  # Format specifiers in C

  Format specifiers define the type of data to be printed on standard output. You
  need to use format specifiers whenever you are
  [printing-values-in-c](printing-values-in-c.md).

  | Specifier | Usage                      |
  | --------- | -------------------------- |
  | `%c`      | single character           |
  | `%s`      | string                     |
  | `%n`      | nothing                    |
  | `%d`      | integer (assuming base 10) |
  | `%f`      | float                      |
  | `%%`      | The percent sign itself    |


path: Formatting_output_text_in_Bash.md
sortable_path: Formatting_output_text_in_Bash.md
title: Formatting output text in Bash
checksum: fac1b5b4deee65d0a974c706274a2198a56bd3dfc53a818655e33e362e7fff6d
created: 2024-10-22 16:52:56.931830997+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["shell"]}
lead:
  We can use the `-e` flag with `echo` to have greater control over the output of
  text. `-e` allows us to use linebreaks, tabs and other formattings within a
  string.

body:
  We can use the `-e` flag with `echo` to have greater control over the output of
  text. `-e` allows us to use linebreaks, tabs and other formattings within a
  string.

  ## Output text in columns

  ```bash
  echo -e  "Name\t\tNumber"; echo -e "Thomas\t\t123";

  Name		Number
  Thomas		123
  ```

  ## Break text over several lines

  ```bash
  echo -e "This text\nbreaks over\nthree lines"

  This text
  breaks over
  three lines
  ```

  ## Colour outputs

  ![](/img/terminal_colours.png)

  ```bash
  echo -e "\033[31;40mColoured Text\033[0m"
  ```

  The coloured section is prepended by `\033[` and ended with `[0m`. The
  foreground colour is given first, and then the background colour.

  We can also change the text style. We do this by adding an extra value after the
  first square bracket:

  ```bash
  echo -e "\033[4;31;40mColoured Text\033[0m"
  ```

  This underlines the output.

  We can create a script that simplifies the construction of colour-formatted
  text, e.g

  ```bash
  ulinered="\033[4;31;40m"
  red="\033[31;40m"
  none="\033[0m"

  echo -e $ulinered"ERROR:"$none$red" Something went wrong."$none
  ```

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

  # Formatting output text in Bash

  We can use the `-e` flag with `echo` to have greater control over the output of
  text. `-e` allows us to use linebreaks, tabs and other formattings within a
  string.

  ## Output text in columns

  ```bash
  echo -e  "Name\t\tNumber"; echo -e "Thomas\t\t123";

  Name		Number
  Thomas		123
  ```

  ## Break text over several lines

  ```bash
  echo -e "This text\nbreaks over\nthree lines"

  This text
  breaks over
  three lines
  ```

  ## Colour outputs

  ![](/img/terminal_colours.png)

  ```bash
  echo -e "\033[31;40mColoured Text\033[0m"
  ```

  The coloured section is prepended by `\033[` and ended with `[0m`. The
  foreground colour is given first, and then the background colour.

  We can also change the text style. We do this by adding an extra value after the
  first square bracket:

  ```bash
  echo -e "\033[4;31;40mColoured Text\033[0m"
  ```

  This underlines the output.

  We can create a script that simplifies the construction of colour-formatted
  text, e.g

  ```bash
  ulinered="\033[4;31;40m"
  red="\033[31;40m"
  none="\033[0m"

  echo -e $ulinered"ERROR:"$none$red" Something went wrong."$none
  ```


path: Forms_in_React.md
sortable_path: Forms_in_React.md
title: Forms using hooks
checksum: cc03c143d64a475e9141718c7a5ba8853cc79336b8e8bfb2aec36cfac5776c82
created: 2024-10-22 16:52:56.934110599+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["javascript","react"]}
lead:
  With hooks, form processing is exactly the same as
  [classes](Forms_in_React.md) in terms of the overall
  methodology, but the syntax is slightly different as a result of the `useState`
  hook.

body:
  With hooks, form processing is exactly the same as
  [classes](Forms_in_React.md) in terms of the overall
  methodology, but the syntax is slightly different as a result of the `useState`
  hook.

  >

  ## Basic approach

  Instead of using `this.state` and `this.setState` . We just have the `useState`
  hook. But the controlled component principle is the same. Let's say we have a
  simple email input:

  ```jsx
  const [email, setEmail] = useState("");
  ```

  As this is a form, the state change is going to be the result of user input. So
  we need to prep our form to enable this.

  ```html
  <input type="text" value="{email}" onChange="{setEmail}" />
  ```

  Now we just need to make good on the `setEmail` method we declared when we
  initialised the state:

  ```jsx
  const handleChange = (event) => {
    setEmail(event.target.value);
  };
  ```

  ### Applied example

  Here is an applied example of the above approach for a form that has three input
  fields. This component outputs the inputs as JSON on submit:

  ```jsx

  function FormHook() {
    const [email, setEmail] = useState("");
    const [phone, setPhone] = useState("");
    const [age, setAge] = useState("");
    const [formOutput, setFormOutput] = useState("Form output");

    const handleSubmit = (event) => {
      event.preventDefault();
      setFormOutput(
        JSON.stringify({ email: email, phone: phone, age: age }, null, 2)
      );
    };
  	return (
  		<form onSubmit={handleSubmit}>
  			<input type="text" value={email} onChange={(event) => setEmail(event.target.value)}>
  			<input type="text" value={phone} onChange={(event) => setPhone(event.target.value)}>
  			<input type="number" value={age} onChange={(event) => setAge(event.target.value)}>
  			<button type="submit">Submit</button>
  		</form>
  	)
  };
  ```

  ## More complex forms

  The above is fine if you only have one form with a couple of inputs. But if you
  are managing multiple forms or forms with a complex array of inputs, you would
  need to create `useState` declaration for every single input with a custom
  `onChange` event for each one which is repetitious and not very clean.

  So instead of this, just like with class-based controlled components, we use the
  `name` HTML attribute to distinguish each input and create a generic `onChange`
  function that distinguishes each separate input by destructuring a key, value
  object using the `name`.

  ```jsx
  <form onSubmit={handleSubmit}>
  			<input type="text" name="email" value={formValues.email} onChange={handleChange}>
  			<input type="text" name="phone" value={formValues.phone} onChange={handleChange}>
  			<input type="number" name="age" value={formValues.age} onChange={handleChange}>
  			<button type="submit">Submit</button>
  </form>
  ```

  ```jsx
  const initialState = {
    email: "",
    phone: "",
    age: "",
  };

  const [formValues, setFormValues] = useState(initialState);

  const handleChange = (event) => {
    const { name, value } = event.target;
    setFormValues({ ...formValues, [name]: value });
  };
  ```

  There are three parts:

  1. First we create the initial state.
  2. Next we store this initial state as the variable in the `useState`
     initialisation: `formValues` . We also provide a method `setFormValues` which
     will be used by the change handler to log the user's inputs.
  3. Finally we create the function that will log the user changes. First we use
     object destructuring on the change event to enable us to retrieve the `name`
     and `value` attributes of the HTML inputs in the component. Then we use
     spread syntax to say that for each input pair, retrieve its value, using the
     destructured `name` variable as the key.

  ### Applied example

  Below I have updated the previous context to this time reflect the new,
  abstracted logic:

  ```jsx
  function FormHookAbstracted() {
    const initialState = {
      email: "",
      phone: "",
      age: "",
    };
  	const [formValues, setFormValues] = useState(initialState);
  	const handleChange = (event) => {
      const { name, value } = event.target;
      setFormValues({ ...formValues, [name]: value });
    };
    const handleSubmit = (event) => {
      event.preventDefault();
      setFormOutput(
        JSON.stringify({ email: email, phone: phone, age: age }, null, 2)
      );
    };
  	return (
  		<form onSubmit={handleSubmit}>
  			<input type="text" name="email" value={formValues.email} onChange={handleChange}>
  			<input type="text" name="phone" value={formValues.phone} onChange={handleChange}>
  			<input type="number" name="age" value={formValues.age} onChange={handleChange}>
  			<button type="submit">Submit</button>
  		</form>
  	)
  };
  export default FormHookAbstracted;
  ```

  Note that instead of individual variables `email` , `phone`, `age` , this
  approach returns a single object `formValues` . We could therefore access the
  individual values with e.g `[formValues.email](formvalues.email)` .

  As it is an object, it makes resetting to the original state very easy, viz:

  ```jsx
  const handleReset = () => {
    Object.values(formValues).map((x) => setFormValues(initialState));
  };
  ```

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

  # Forms using hooks

  With hooks, form processing is exactly the same as
  [classes](Forms_in_React.md) in terms of the overall
  methodology, but the syntax is slightly different as a result of the `useState`
  hook.

  >

  ## Basic approach

  Instead of using `this.state` and `this.setState` . We just have the `useState`
  hook. But the controlled component principle is the same. Let's say we have a
  simple email input:

  ```jsx
  const [email, setEmail] = useState("");
  ```

  As this is a form, the state change is going to be the result of user input. So
  we need to prep our form to enable this.

  ```html
  <input type="text" value="{email}" onChange="{setEmail}" />
  ```

  Now we just need to make good on the `setEmail` method we declared when we
  initialised the state:

  ```jsx
  const handleChange = (event) => {
    setEmail(event.target.value);
  };
  ```

  ### Applied example

  Here is an applied example of the above approach for a form that has three input
  fields. This component outputs the inputs as JSON on submit:

  ```jsx

  function FormHook() {
    const [email, setEmail] = useState("");
    const [phone, setPhone] = useState("");
    const [age, setAge] = useState("");
    const [formOutput, setFormOutput] = useState("Form output");

    const handleSubmit = (event) => {
      event.preventDefault();
      setFormOutput(
        JSON.stringify({ email: email, phone: phone, age: age }, null, 2)
      );
    };
  	return (
  		<form onSubmit={handleSubmit}>
  			<input type="text" value={email} onChange={(event) => setEmail(event.target.value)}>
  			<input type="text" value={phone} onChange={(event) => setPhone(event.target.value)}>
  			<input type="number" value={age} onChange={(event) => setAge(event.target.value)}>
  			<button type="submit">Submit</button>
  		</form>
  	)
  };
  ```

  ## More complex forms

  The above is fine if you only have one form with a couple of inputs. But if you
  are managing multiple forms or forms with a complex array of inputs, you would
  need to create `useState` declaration for every single input with a custom
  `onChange` event for each one which is repetitious and not very clean.

  So instead of this, just like with class-based controlled components, we use the
  `name` HTML attribute to distinguish each input and create a generic `onChange`
  function that distinguishes each separate input by destructuring a key, value
  object using the `name`.

  ```jsx
  <form onSubmit={handleSubmit}>
  			<input type="text" name="email" value={formValues.email} onChange={handleChange}>
  			<input type="text" name="phone" value={formValues.phone} onChange={handleChange}>
  			<input type="number" name="age" value={formValues.age} onChange={handleChange}>
  			<button type="submit">Submit</button>
  </form>
  ```

  ```jsx
  const initialState = {
    email: "",
    phone: "",
    age: "",
  };

  const [formValues, setFormValues] = useState(initialState);

  const handleChange = (event) => {
    const { name, value } = event.target;
    setFormValues({ ...formValues, [name]: value });
  };
  ```

  There are three parts:

  1. First we create the initial state.
  2. Next we store this initial state as the variable in the `useState`
     initialisation: `formValues` . We also provide a method `setFormValues` which
     will be used by the change handler to log the user's inputs.
  3. Finally we create the function that will log the user changes. First we use
     object destructuring on the change event to enable us to retrieve the `name`
     and `value` attributes of the HTML inputs in the component. Then we use
     spread syntax to say that for each input pair, retrieve its value, using the
     destructured `name` variable as the key.

  ### Applied example

  Below I have updated the previous context to this time reflect the new,
  abstracted logic:

  ```jsx
  function FormHookAbstracted() {
    const initialState = {
      email: "",
      phone: "",
      age: "",
    };
  	const [formValues, setFormValues] = useState(initialState);
  	const handleChange = (event) => {
      const { name, value } = event.target;
      setFormValues({ ...formValues, [name]: value });
    };
    const handleSubmit = (event) => {
      event.preventDefault();
      setFormOutput(
        JSON.stringify({ email: email, phone: phone, age: age }, null, 2)
      );
    };
  	return (
  		<form onSubmit={handleSubmit}>
  			<input type="text" name="email" value={formValues.email} onChange={handleChange}>
  			<input type="text" name="phone" value={formValues.phone} onChange={handleChange}>
  			<input type="number" name="age" value={formValues.age} onChange={handleChange}>
  			<button type="submit">Submit</button>
  		</form>
  	)
  };
  export default FormHookAbstracted;
  ```

  Note that instead of individual variables `email` , `phone`, `age` , this
  approach returns a single object `formValues` . We could therefore access the
  individual values with e.g `[formValues.email](formvalues.email)` .

  As it is an object, it makes resetting to the original state very easy, viz:

  ```jsx
  const handleReset = () => {
    Object.values(formValues).map((x) => setFormValues(initialState));
  };
  ```


path: Four_bit_adder.md
sortable_path: Four_bit_adder.md
title: Four-bit adder
checksum: a803c519509da2a87666647933e8f7817c59adbc65482c5dde208fb806b5bcd7
created: 2024-10-22 16:52:56.939362297+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["logic-gates","binary"]}
lead:
  A single
  [half adder](Half_adder_and_full_adder.md#half-adder)
  and
  [full adder](Half_adder_and_full_adder.md#fufll-adder)
  allows us to calculate the sum of two 1-bit numbers, but this is not much use in
  practice. To approximate what is really happening at the circuit level in
  computers we need to be able to add bigger binary numbers. We will demonstrate
  how this can be achieved for a four-bit number (nibble) using repeated full
  adders and half adders.

body:
  A single
  [half adder](Half_adder_and_full_adder.md#half-adder)
  and
  [full adder](Half_adder_and_full_adder.md#fufll-adder)
  allows us to calculate the sum of two 1-bit numbers, but this is not much use in
  practice. To approximate what is really happening at the circuit level in
  computers we need to be able to add bigger binary numbers. We will demonstrate
  how this can be achieved for a four-bit number (nibble) using repeated full
  adders and half adders.

  We want to be able to calculate the following sum:

  ```
  0010
  0011
  ____
  0101
  ```

  We will achieve this by using three full adders and one half adder, moving from
  right to left. The half adder will be used at the beginning to calculate the
  least significant bit as it will have no carry-in. The subsequent three bits
  will all be added using a full adder.

  Let's walk through the process:

  >

  1. HA receives the bits $0$ and $1$ as inputs. It outputs $1$ as the sum bit and
     $0$ as the carry-out.
  2. FA1 receives $0$ as the carry-in bit plus $1$ and $1$ as its input. This
     means it has the following calculation to execute: $1 + 1 + 0$. This gives
     $0$ as the sum bit and $1$ as the carry-out bit.
  3. FA2 receives $1$ as the carry-in (the carry-out from FA1) plus $0$ and $0$ as
     its own inputs. $0 + 0 + 1$ gives us $1$ as the sum bit and $0$ as the
     carry-out.
  4. Finally FA3 receives $0$ as its carry-in plus $0$ and $0$ as its inputs.
     $0 + 0 + 0 = 0$, therefore its sum bit is $0$ and the overall sum of the four
     bits is $0101$. There is no carry out as there are no more bits to add.

  ## Ripple carry adder

  We notice that as the calculation proceeds, the carry bits propagate or _ripple_
  through the circuit. For this reason, a four-bit adder constructed in the manner
  of this circuit is called a **ripple carry adder**.

  There are two important consequences to note about this type of circuit:

  - Each carry bit that ripples to the next full adder introduces a small delay.
    Therefore extending the circuit to handle more bits will make the circuit
    slower overall.
  - Because the value of the bits changes through the course of the ripple, the
    output of the circuit will be innacurate until all the carry bits have had
    time to propagate.

raw_content:
  ---
  tags:
    - logic-gates
    - binary
  ---

  # Four-bit adder

  A single
  [half adder](Half_adder_and_full_adder.md#half-adder)
  and
  [full adder](Half_adder_and_full_adder.md#fufll-adder)
  allows us to calculate the sum of two 1-bit numbers, but this is not much use in
  practice. To approximate what is really happening at the circuit level in
  computers we need to be able to add bigger binary numbers. We will demonstrate
  how this can be achieved for a four-bit number (nibble) using repeated full
  adders and half adders.

  We want to be able to calculate the following sum:

  ```
  0010
  0011
  ____
  0101
  ```

  We will achieve this by using three full adders and one half adder, moving from
  right to left. The half adder will be used at the beginning to calculate the
  least significant bit as it will have no carry-in. The subsequent three bits
  will all be added using a full adder.

  Let's walk through the process:

  >

  1. HA receives the bits $0$ and $1$ as inputs. It outputs $1$ as the sum bit and
     $0$ as the carry-out.
  2. FA1 receives $0$ as the carry-in bit plus $1$ and $1$ as its input. This
     means it has the following calculation to execute: $1 + 1 + 0$. This gives
     $0$ as the sum bit and $1$ as the carry-out bit.
  3. FA2 receives $1$ as the carry-in (the carry-out from FA1) plus $0$ and $0$ as
     its own inputs. $0 + 0 + 1$ gives us $1$ as the sum bit and $0$ as the
     carry-out.
  4. Finally FA3 receives $0$ as its carry-in plus $0$ and $0$ as its inputs.
     $0 + 0 + 0 = 0$, therefore its sum bit is $0$ and the overall sum of the four
     bits is $0101$. There is no carry out as there are no more bits to add.

  ## Ripple carry adder

  We notice that as the calculation proceeds, the carry bits propagate or _ripple_
  through the circuit. For this reason, a four-bit adder constructed in the manner
  of this circuit is called a **ripple carry adder**.

  There are two important consequences to note about this type of circuit:

  - Each carry bit that ripples to the next full adder introduces a small delay.
    Therefore extending the circuit to handle more bits will make the circuit
    slower overall.
  - Because the value of the bits changes through the course of the ripple, the
    output of the circuit will be innacurate until all the carry bits have had
    time to propagate.


path: Frequency_counters.md
sortable_path: Frequency_counters.md
title: Frequency counters
lead:
  Here is a concise method for counting the number instances of each type of array
  element.

checksum: 2cf0f15b006fadb1c49fd3ea77798d83b846acd0594cf66aedc372f533b3edf2
created: 2024-10-22 16:52:56.951032088+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["data-structures"]}
body:
  Here is a concise method for counting the number instances of each type of array
  element.

  ```js
  function frequencyCounter(...inputArr) {
    let counter = {};
    inputArr.forEach((item) => {
      // If item doesn't exist as a key in the counter, make 0 the val and add one
      // If item already exists as key in the counter, increment it by one
      counter[item] = (counter[item] || 0) + 1;
    });
    return counter;
  }
  ```

  An application of this would be checking for duplicates in an array:

  ```js
  const testArr = [1, 4, 3, 3];
  const arrayFreq = frequencyCounter(testArr);
  let count = Object.values(arrayFreq);

  if (count.some((ele) => ele > 1)) {
    return "There are duplicates";
  } else return "No duplicates";
  ```

raw_content:
  ---
  tags:
    - data-structures
  ---

  # Frequency counters

  Here is a concise method for counting the number instances of each type of array
  element.

  ```js
  function frequencyCounter(...inputArr) {
    let counter = {};
    inputArr.forEach((item) => {
      // If item doesn't exist as a key in the counter, make 0 the val and add one
      // If item already exists as key in the counter, increment it by one
      counter[item] = (counter[item] || 0) + 1;
    });
    return counter;
  }
  ```

  An application of this would be checking for duplicates in an array:

  ```js
  const testArr = [1, 4, 3, 3];
  const arrayFreq = frequencyCounter(testArr);
  let count = Object.values(arrayFreq);

  if (count.some((ele) => ele > 1)) {
    return "There are duplicates";
  } else return "No duplicates";
  ```


path: Full_Node_API_example.md
sortable_path: Full_Node_API_example.md
title: Creating a RESTful API: Full example
checksum: c680971e4773cff184aed8b4d873cd6ee883270aac85577a1361b8d44a705755
created: 2024-10-22 16:52:56.953593939+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["node-js","REST","APIs"]}
lead:
  ```js
  const express = require("express");
  const app = express(); // convention to name Express as the app
  const port = process.env.PORT || 3000;
  const Joi = require("joi");
  const helmet = require("helmet");
  const morgan = require("morgan");
  const courses = require("./routes/courses");

body:
  ```js
  const express = require("express");
  const app = express(); // convention to name Express as the app
  const port = process.env.PORT || 3000;
  const Joi = require("joi");
  const helmet = require("helmet");
  const morgan = require("morgan");
  const courses = require("./routes/courses");

  // Routes
  app.use("/api/courses", courses);

  // Middlewear
  app.use(express.json());
  app.use(helmet());

  const courses = [
    {
      id: 1,
      name: "First course",
    },
    {
      id: 2,
      name: "Second course",
    },
    {
      id: 3,
      name: "Third course",
    },
  ];

  if (app.get("env") === "development") {
    app.use(morgan("common"));
  }

  app.listen(port, () => console.log(`Listening on ${port}`));

  function validateCourse(course) {
    const schema = Joi.object({
      name: Joi.string().min(3).required(),
    });

    const { error } = schema.validate(course);
    return error;
  }

  /**
   *  Note that the following request handlers would not be stored in `index.js` they would be in their dedicated routing file.
   **/

  // Return all data from API
  courses.get("/", (req, res) => {
    res.send(courses);
  });

  // Return a specific value
  courses.get("/:id", (req, res) => {
    const course = courses.find((c) => c.id === parseInt(req.params.id));
    if (!course) res.status(404).send("A course with the given ID was not found");
    res.send(course);
  });

  // Add a new course
  courses.post("/", (req, res) => {
    const schema = Joi.object({
      name: Joi.string().min(3).required(),
    });

    const { error } = schema.validate(req.body);
    if (error)
      return error.details.map((joiErr) => res.status(400).send(joiErr.message));

    const course = {
      id: courses.length + 1,
      name: req?.body.name,
    };
    courses.push(course);
    res.send(course);
  });

  // Update a course
  courses.put("/:id", (req, res) => {
    const course = courses.find((c) => c.id === parseInt(req.params.id));

    if (!course)
      return res.status(404).send("A course with the given ID was not found");
    const { error } = validateCourse(req.body);

    if (error)
      return error.details.map((joiErr) => res.status(400).send(joiErr.message));

    course.name = req.body.name;
    res.send(course);
  });

  // Delete a course
  courses.delete("/:id", (req, res) => {
    const course = courses.find((c) => c.id === parseInt(req.params.id));
    if (!course)
      return res.status(404).send("A course with the given ID was not found");

    courses.indexOf(course);
    courses.splice(index, 1);

    res.send(course);
  });
  ```

raw_content:
  ---
  tags:
    - node-js
    - REST
    - APIs
  ---

  # Creating a RESTful API: Full example

  ```js
  const express = require("express");
  const app = express(); // convention to name Express as the app
  const port = process.env.PORT || 3000;
  const Joi = require("joi");
  const helmet = require("helmet");
  const morgan = require("morgan");
  const courses = require("./routes/courses");

  // Routes
  app.use("/api/courses", courses);

  // Middlewear
  app.use(express.json());
  app.use(helmet());

  const courses = [
    {
      id: 1,
      name: "First course",
    },
    {
      id: 2,
      name: "Second course",
    },
    {
      id: 3,
      name: "Third course",
    },
  ];

  if (app.get("env") === "development") {
    app.use(morgan("common"));
  }

  app.listen(port, () => console.log(`Listening on ${port}`));

  function validateCourse(course) {
    const schema = Joi.object({
      name: Joi.string().min(3).required(),
    });

    const { error } = schema.validate(course);
    return error;
  }

  /**
   *  Note that the following request handlers would not be stored in `index.js` they would be in their dedicated routing file.
   **/

  // Return all data from API
  courses.get("/", (req, res) => {
    res.send(courses);
  });

  // Return a specific value
  courses.get("/:id", (req, res) => {
    const course = courses.find((c) => c.id === parseInt(req.params.id));
    if (!course) res.status(404).send("A course with the given ID was not found");
    res.send(course);
  });

  // Add a new course
  courses.post("/", (req, res) => {
    const schema = Joi.object({
      name: Joi.string().min(3).required(),
    });

    const { error } = schema.validate(req.body);
    if (error)
      return error.details.map((joiErr) => res.status(400).send(joiErr.message));

    const course = {
      id: courses.length + 1,
      name: req?.body.name,
    };
    courses.push(course);
    res.send(course);
  });

  // Update a course
  courses.put("/:id", (req, res) => {
    const course = courses.find((c) => c.id === parseInt(req.params.id));

    if (!course)
      return res.status(404).send("A course with the given ID was not found");
    const { error } = validateCourse(req.body);

    if (error)
      return error.details.map((joiErr) => res.status(400).send(joiErr.message));

    course.name = req.body.name;
    res.send(course);
  });

  // Delete a course
  courses.delete("/:id", (req, res) => {
    const course = courses.find((c) => c.id === parseInt(req.params.id));
    if (!course)
      return res.status(404).send("A course with the given ID was not found");

    courses.indexOf(course);
    courses.splice(index, 1);

    res.send(course);
  });
  ```


path: Function_overloads.md
sortable_path: Function_overloads.md
title: Function overloads
checksum: 415bd2066b1c9503246da7a8771ee4ad0b2417e36b28bac5a4ff3ddf1b95fcb9
created: 2024-10-22 16:52:56.956451589+00:00
modified: 2024-10-19 08:57:01.654073344+00:00
metadata: {"tags":["typescript"]}
lead:
  Function overloading is not a feature of JavaScript but something close to it
  can be achieved with TypeScript. It proceeds by defining multiple function types
  (defined above the function) that may serve as the actual function's parameters.
  Then with the actual function, you leave the changeable parameters open as
  optional unions and/or `unknown` :

body:
  Function overloading is not a feature of JavaScript but something close to it
  can be achieved with TypeScript. It proceeds by defining multiple function types
  (defined above the function) that may serve as the actual function's parameters.
  Then with the actual function, you leave the changeable parameters open as
  optional unions and/or `unknown` :

  ```ts
  // First oveload type:
  function logSearch(term: string, options?: string): void;

  // Second overload type:
  function logSearch(term: string, options?: number): void;

  // Implementation:
  function logSearch(term: string, p2?: unknown) {
    let query = `https://searchdatabase/${term}`;
    if (typeof p2 === "string") {
      query = `${query}/tag=${p2}`;
      console.log(query);
    } else {
      query = `${query}/page=${p2}`;
      console.log(query);
    }
  }

  logSearch("apples", "braeburn");
  logSearch("bananas", 3);
  ```

  ```ts
  // First overload type:
  function logSearchUnion(term: string, options?: string): void;

  // Second overload type:
  function logSearchUnion(term: string, options?: number): void;

  // Implementation:
  function logSearchUnion(term: string, p2?: string | number) {
    let query = `https://searchdatabase/${term}`;
    if (typeof p2 === "string") {
      query = `${query}/tag=${p2}`;
      console.log(query);
    } else {
      query = `${query}/page=${p2}`;
      console.log(query);
    }
  }

  logSearchUnion("melon", "honey-dew");
  logSearchUnion("oranges", 4);
  ```

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

  # Function overloads

  Function overloading is not a feature of JavaScript but something close to it
  can be achieved with TypeScript. It proceeds by defining multiple function types
  (defined above the function) that may serve as the actual function's parameters.
  Then with the actual function, you leave the changeable parameters open as
  optional unions and/or `unknown` :

  ```ts
  // First oveload type:
  function logSearch(term: string, options?: string): void;

  // Second overload type:
  function logSearch(term: string, options?: number): void;

  // Implementation:
  function logSearch(term: string, p2?: unknown) {
    let query = `https://searchdatabase/${term}`;
    if (typeof p2 === "string") {
      query = `${query}/tag=${p2}`;
      console.log(query);
    } else {
      query = `${query}/page=${p2}`;
      console.log(query);
    }
  }

  logSearch("apples", "braeburn");
  logSearch("bananas", 3);
  ```

  ```ts
  // First overload type:
  function logSearchUnion(term: string, options?: string): void;

  // Second overload type:
  function logSearchUnion(term: string, options?: number): void;

  // Implementation:
  function logSearchUnion(term: string, p2?: string | number) {
    let query = `https://searchdatabase/${term}`;
    if (typeof p2 === "string") {
      query = `${query}/tag=${p2}`;
      console.log(query);
    } else {
      query = `${query}/page=${p2}`;
      console.log(query);
    }
  }

  logSearchUnion("melon", "honey-dew");
  logSearchUnion("oranges", 4);
  ```


path: Functions.md
sortable_path: Functions.md
title: Functions
checksum: d07fc49bfba92f05596275674fd922ea512e4efde44e593414c7127dfe821e8e
created: 2024-10-22 16:52:56.959384408+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["typescript","react"]}
lead:
  Continuing from the other examples of React Typescript, we could do standard
  listing function, like:

body:
  Continuing from the other examples of React Typescript, we could do standard
  listing function, like:

  ```tsx
  <ul>
    {people.map((person) => {
      return <li>{person.name}</li>;
    })}
  </ul>
  ```

  But it's neater to do it with a function defined within the `List` component:

  ```tsx
  const renderList = (): JSX.Element[] => {
    return people.map((person) => {
      return (
        <li>
          <div>{person.name}</div>
          <div>{person.age}</div>
        </li>
      );
    });
  };
  ```

  And then change the eariler list to a function invocation:

  ```tsx
      <ul>{renderList()}<ul>
  ```

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

  # Functions

  Continuing from the other examples of React Typescript, we could do standard
  listing function, like:

  ```tsx
  <ul>
    {people.map((person) => {
      return <li>{person.name}</li>;
    })}
  </ul>
  ```

  But it's neater to do it with a function defined within the `List` component:

  ```tsx
  const renderList = (): JSX.Element[] => {
    return people.map((person) => {
      return (
        <li>
          <div>{person.name}</div>
          <div>{person.age}</div>
        </li>
      );
    });
  };
  ```

  And then change the eariler list to a function invocation:

  ```tsx
      <ul>{renderList()}<ul>
  ```


path: Functions_in_Bash.md
sortable_path: Functions_in_Bash.md
title: Functions in Bash
checksum: 1436902153eb2fb5b49756f000141710ad80901576075f3acc535f4985194065
created: 2024-10-22 16:52:56.962187025+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["shell"]}
lead:
  We don't name function parameters in the function declaration. Instead we have
  an implied index of arguments: `$1, $2, $3,...`. When the function is called,
  the first value after the function name becomes `$1` by default, then the
  subsequent arguments.

body:
  We don't name function parameters in the function declaration. Instead we have
  an implied index of arguments: `$1, $2, $3,...`. When the function is called,
  the first value after the function name becomes `$1` by default, then the
  subsequent arguments.

  ```bash
  function expandRange() {
      declare -a expandedRange=()
      for (( i=$1; i<=$2; i++ )); do
          expandedRange+=($i)
      done
      echo "${expandedRange[@]}"
  }
  ```

  ```bash
  expandedRange=$(expandRange 1 4)
  echo $expandedRange
  # 1 2 3 4
  ```

  ## Get all arguments as an array

  We can access all the arguments passed to a function using the `$@` syntax we
  encountered before when
  [passing arguments to scripts](Passing_arguments_to_Bash_scripts.md).
  (Here a function is a kind of script in miniature so the process is the same.)

  ```sh
  function numberThings() {
    i=1
    for f in "$@"; do
      echo $i: "$f"
      (( i++ ))
    done
  }
  ```

  ## Local variables

  ```sh
  var1="I'm variable 1"

  function myfunction() {
    var2="I'm variable 2"
    local var3="I'm variable 3"
  }

  myfunction
  echo $var1
  echo $var2
  echo $var3

  # I'm variable 1
  # I'm variable 2
  ```

  > The convention is to put functions at the top of the script, after the shebang
  > and after the global variables

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

  # Functions in Bash

  We don't name function parameters in the function declaration. Instead we have
  an implied index of arguments: `$1, $2, $3,...`. When the function is called,
  the first value after the function name becomes `$1` by default, then the
  subsequent arguments.

  ```bash
  function expandRange() {
      declare -a expandedRange=()
      for (( i=$1; i<=$2; i++ )); do
          expandedRange+=($i)
      done
      echo "${expandedRange[@]}"
  }
  ```

  ```bash
  expandedRange=$(expandRange 1 4)
  echo $expandedRange
  # 1 2 3 4
  ```

  ## Get all arguments as an array

  We can access all the arguments passed to a function using the `$@` syntax we
  encountered before when
  [passing arguments to scripts](Passing_arguments_to_Bash_scripts.md).
  (Here a function is a kind of script in miniature so the process is the same.)

  ```sh
  function numberThings() {
    i=1
    for f in "$@"; do
      echo $i: "$f"
      (( i++ ))
    done
  }
  ```

  ## Local variables

  ```sh
  var1="I'm variable 1"

  function myfunction() {
    var2="I'm variable 2"
    local var3="I'm variable 3"
  }

  myfunction
  echo $var1
  echo $var2
  echo $var3

  # I'm variable 1
  # I'm variable 2
  ```

  > The convention is to put functions at the top of the script, after the shebang
  > and after the global variables


path: Functions_in_Python.md
sortable_path: Functions_in_Python.md
title: Functions in Python
checksum: 02169f9893dea8f188e6c3ea80dca601bb3edef2f5ad05b6f5cf5875b8760e09
created: 2024-10-22 16:52:56.970391158+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["python"]}
lead:
  - Convention is to leave a double line-break after a function definition (but
    not with nested functions - here, a single linebreak is sufficient)
  - Scope within functions is demarcated by indents, as everything in Python
  - We use a docstring _within_ the function body, to document our function. This
    text will then show up in Intellisense etc.

body:
  - Convention is to leave a double line-break after a function definition (but
    not with nested functions - here, a single linebreak is sufficient)
  - Scope within functions is demarcated by indents, as everything in Python
  - We use a docstring _within_ the function body, to document our function. This
    text will then show up in Intellisense etc.

  ## Basic examples

  ```py

  # No params, no return
  def print_msg():
      """ A function that prints hello world """
      print('Hello World!')

  print_msg()
  # Hello World!

  print(type(print_msg))
  # <class 'function'>

  # Params, no return
  def print_my_msg(msg):
      """ A simple function to print a message """
      print(msg)

  print_my_msg('Good day')
  # Good day

  # Params and return
  def square(n):
      return n * n

  print(square(2))
  # 4

  result = square(4)
  print(result)

  # 16

  ```

  ## Default parameters

  ```py
  def greeter(name, message='Live Long and Prosper'):
      print('Welcome', name, '-', message)

  greeter('Eloise')

  # Welcome Eloise - Live Long and Prosper
  ```

  ## Optional parameters

  ````py
  def func_with_optional(non_optional, optional_param=None):
    if optional_param is not None:
      # Do something with specific value
    else:
      # Run standard process


  ## Function with arbitrary parameter list

  ```python
  def greeter(*args):
      for name in args:
          print('Welcome', name)

  greeter('John', 'Denise', 'Phoebe', 'Adam', 'Gryff', 'Natalia')

  """
  Welcome John
  Welcome Denise
  Welcome Phoebe
  Welcome Adam
  Welcome Gryff
  Welcome Natalia
  """
  ````

  ## Scoping

  Function variables are locally scoped by default.

  They can access variables that are outer to them and can redefine them within
  their own scope _and_ within the global scope using the keywords `global` and
  `nonlocal`.

  Below a global variable is accessed and changed but only internally within a
  function scope

  ```py
  max = 100
  print('initial value of max:', max)

  def print_max():
      global max
      max = max + 1
      print('inside function:', max)


  print_max()
  print('outside function:', max)

  """
  initial value of max: 100
  inside function: 101
  outside function: 101
  """
  ```

  Below a higher-scoped variable is redefined from within the lower scope:

  ```py
  def myfunc1():
    x = "John"
    def myfunc2():
      nonlocal x
      x = "hello"
    myfunc2()
    return x

  print(myfunc1())

  # hello
  ```

  We cannot however redefine a global variable from a function scope permanently.
  It will remain whatever it is in global scope, after the function has run.

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

  # Functions in Python

  - Convention is to leave a double line-break after a function definition (but
    not with nested functions - here, a single linebreak is sufficient)
  - Scope within functions is demarcated by indents, as everything in Python
  - We use a docstring _within_ the function body, to document our function. This
    text will then show up in Intellisense etc.

  ## Basic examples

  ```py

  # No params, no return
  def print_msg():
      """ A function that prints hello world """
      print('Hello World!')

  print_msg()
  # Hello World!

  print(type(print_msg))
  # <class 'function'>

  # Params, no return
  def print_my_msg(msg):
      """ A simple function to print a message """
      print(msg)

  print_my_msg('Good day')
  # Good day

  # Params and return
  def square(n):
      return n * n

  print(square(2))
  # 4

  result = square(4)
  print(result)

  # 16

  ```

  ## Default parameters

  ```py
  def greeter(name, message='Live Long and Prosper'):
      print('Welcome', name, '-', message)

  greeter('Eloise')

  # Welcome Eloise - Live Long and Prosper
  ```

  ## Optional parameters

  ````py
  def func_with_optional(non_optional, optional_param=None):
    if optional_param is not None:
      # Do something with specific value
    else:
      # Run standard process


  ## Function with arbitrary parameter list

  ```python
  def greeter(*args):
      for name in args:
          print('Welcome', name)

  greeter('John', 'Denise', 'Phoebe', 'Adam', 'Gryff', 'Natalia')

  """
  Welcome John
  Welcome Denise
  Welcome Phoebe
  Welcome Adam
  Welcome Gryff
  Welcome Natalia
  """
  ````

  ## Scoping

  Function variables are locally scoped by default.

  They can access variables that are outer to them and can redefine them within
  their own scope _and_ within the global scope using the keywords `global` and
  `nonlocal`.

  Below a global variable is accessed and changed but only internally within a
  function scope

  ```py
  max = 100
  print('initial value of max:', max)

  def print_max():
      global max
      max = max + 1
      print('inside function:', max)


  print_max()
  print('outside function:', max)

  """
  initial value of max: 100
  inside function: 101
  outside function: 101
  """
  ```

  Below a higher-scoped variable is redefined from within the lower scope:

  ```py
  def myfunc1():
    x = "John"
    def myfunc2():
      nonlocal x
      x = "hello"
    myfunc2()
    return x

  print(myfunc1())

  # hello
  ```

  We cannot however redefine a global variable from a function scope permanently.
  It will remain whatever it is in global scope, after the function has run.


path: Fundamental_Theorem_of_Arithmetic.md
sortable_path: Fundamental_Theorem_of_Arithmetic.md
title: Fundamental Theorem of Arithmetic
checksum: 6adaf24b44c95ae548c33892d9c8a671dcdc6b0701da1fe8ed6b98ffb9fdcf85
created: 2024-10-22 16:52:56.973477979+00:00
modified: 2024-10-18 18:14:32.793732542+00:00
metadata: {"tags":["prealgebra","theorems"]}
lead:
  > Every integer greater than one is either a prime number itself or is product
  > of a unique combination of primes.

body:
  > Every integer greater than one is either a prime number itself or is product
  > of a unique combination of primes.

  This is also known as the **Unique Factorisation Theorem**.

  'Unique' means that there is not more than one way to derive the whole number.
  Once you reduce the factorisation to primes, there can only be one set of
  numbers that results in the target number.

  For example, $24$ has the following factors: ${12, 24}$ and $6, 4$ but these are
  composite numbers. The unique factorisation combination for 24 is $2, 2, 3$.

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

  # Fundamental Theorem of Arithmetic

  > Every integer greater than one is either a prime number itself or is product
  > of a unique combination of primes.

  This is also known as the **Unique Factorisation Theorem**.

  'Unique' means that there is not more than one way to derive the whole number.
  Once you reduce the factorisation to primes, there can only be one set of
  numbers that results in the target number.

  For example, $24$ has the following factors: ${12, 24}$ and $6, 4$ but these are
  composite numbers. The unique factorisation combination for 24 is $2, 2, 3$.


path: Further_examples_of_TS_generics.md
sortable_path: Further_examples_of_TS_generics.md
title: Further examples of generics in TypeScript
lead: ## Basic function
checksum: 02599d71a25bee261d052b3ee96b7d8605f670235fcf4d65b46eeac1344375fc
created: 2024-10-22 16:52:56.976137161+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["typescript"]}
body:
  ## Basic function

  In the code below we have a simple JavaScript function that receives a value and
  an an array as parameters. It returns a new array comprising the original array
  plus the additional value:

  ```js
  function generateArray(existingArray, newValue) {
    return [...existingArray, newValue];
  }
  ```

  Imagine that we want to ensure that each of the parameters share the same data
  type. In other words: if the function is passed a string array, the second
  parameter must also be a string. For example, it should not be the case that you
  can append a string to an array of numbers.

  Now imagine that we don't know in advance what type the value or array will be,
  we just know that the data types of the parameters must match.

  In converting the function to TypeScript, one way of overcoming our lack of
  foreknowledge would be to deploy `any`. This way it doesn't matter which types
  are passed to the function:

  ```ts
  function generateArray(existingArray: any[], newValue: any): any[] {
    return [...existingArray, newValue];
  }
  ```

  But this is no solution at all. The problem — as always with `any` — is that it
  strips our function of any type checks whatsoever and would therefore invite
  calls of form: `generateArray([1,2,3], 'lorem')`.

  Enter generics:

  ```ts
  function generateArray<T>(existingArr: T[], newValue: T): T[] {
    return [...existingArr, newValue];
  }
  ```

  Now, whilst we haven't asserted ahead of time which types will be used,
  whichever types we do pass in, must match. The function header is saying:

  - both arguments must be of the same type (represented by `T`)
  - the function will return an array of this same `T` type.

  If I then tried to run the function with unmatched types (for example
  `generateArray([1,2,3,4], true)` ) TypeScript would raise the following error:

  ```
  Argument of type 'boolean' is not assignable to parameter of type 'number'
  ```

  Note that even though the function in question does not express any preference
  for number types, given that our first parameter is a number, TypeScript knows
  that the second parameter must also be a number.

  > In the generic function we have used `T` as our placeholder for a generic type
  > as this is the convention. However there is no compunction to do so. We could
  > have used any letter or string, providing that the string is not a reserved
  > term.

  ### More advanced function

  This example demonstrates how we can use generics to reduce repetition when
  writing functions and is also a more realistic use case.

  Let's say we have two types or interfaces:

  ```tsx
  type VideoFormatUrls = {
    format720p: URL;
    format1080p: URL;
  };
  ```

  ```tsx
  type SubtitleFormatUrls = {
    english: URL;
    german: URL;
  };
  ```

  An example of an object matching these type definitions:

  ```tsx
  const videoFormats: VideoFormatUrls = {
  	format720p: https://www.format720p.co.uk,
  	format1080p: https://www.format1080p.co.uk
  }
  ```

  Imagine we wanted to be able to check whether a given film is available in a
  certain video format. We could write a function like this:

  ```tsx
  function isFormatAvailable(
    obj: VideoFormatUrls,
    format: string
  ): format is keyof VideoFormatUrls {
    return format in obj;
  }
  ```

  Now imagine that we need to do the same thing with subtitles, but given that
  `isFormatAvailable()` is typed to the `VideoFormatUrls` type we would get an
  error if we used this function for subtitles. But we also don't want to write a
  near identical function typed to `SubtitleFormatUrls` to subtitles just to
  ensure adequate type safety.

  Alternatively we could use a union type, for example:

  ```tsx
  function isFormatAvailable(
    obj: VideoFormatUrls | SubtitleFormatUrls,
    format: string
  ): format is keyof VideoFormatUrls {
    return format in obj;
  }
  ```

  But this quickly becomes unwieldy if we, for the sake of argument have a great
  many URL types that we want the function to utilise.

  This is where generics become super helpful. Here is how we would rewrite the
  function as a generic:

  ```tsx
  function isAvailable<Formats>(obj: Formats, key: string): key is keyof Formats {
    return key in obj;
  }
  ```

  We could then explicitly type our calls of this function, viz:

  ```tsx
  isFormatAvailable<SubtitleFormatUrls>(subtitles, "english");
  ```

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

  # Further examples of generics in TypeScript

  ## Basic function

  In the code below we have a simple JavaScript function that receives a value and
  an an array as parameters. It returns a new array comprising the original array
  plus the additional value:

  ```js
  function generateArray(existingArray, newValue) {
    return [...existingArray, newValue];
  }
  ```

  Imagine that we want to ensure that each of the parameters share the same data
  type. In other words: if the function is passed a string array, the second
  parameter must also be a string. For example, it should not be the case that you
  can append a string to an array of numbers.

  Now imagine that we don't know in advance what type the value or array will be,
  we just know that the data types of the parameters must match.

  In converting the function to TypeScript, one way of overcoming our lack of
  foreknowledge would be to deploy `any`. This way it doesn't matter which types
  are passed to the function:

  ```ts
  function generateArray(existingArray: any[], newValue: any): any[] {
    return [...existingArray, newValue];
  }
  ```

  But this is no solution at all. The problem — as always with `any` — is that it
  strips our function of any type checks whatsoever and would therefore invite
  calls of form: `generateArray([1,2,3], 'lorem')`.

  Enter generics:

  ```ts
  function generateArray<T>(existingArr: T[], newValue: T): T[] {
    return [...existingArr, newValue];
  }
  ```

  Now, whilst we haven't asserted ahead of time which types will be used,
  whichever types we do pass in, must match. The function header is saying:

  - both arguments must be of the same type (represented by `T`)
  - the function will return an array of this same `T` type.

  If I then tried to run the function with unmatched types (for example
  `generateArray([1,2,3,4], true)` ) TypeScript would raise the following error:

  ```
  Argument of type 'boolean' is not assignable to parameter of type 'number'
  ```

  Note that even though the function in question does not express any preference
  for number types, given that our first parameter is a number, TypeScript knows
  that the second parameter must also be a number.

  > In the generic function we have used `T` as our placeholder for a generic type
  > as this is the convention. However there is no compunction to do so. We could
  > have used any letter or string, providing that the string is not a reserved
  > term.

  ### More advanced function

  This example demonstrates how we can use generics to reduce repetition when
  writing functions and is also a more realistic use case.

  Let's say we have two types or interfaces:

  ```tsx
  type VideoFormatUrls = {
    format720p: URL;
    format1080p: URL;
  };
  ```

  ```tsx
  type SubtitleFormatUrls = {
    english: URL;
    german: URL;
  };
  ```

  An example of an object matching these type definitions:

  ```tsx
  const videoFormats: VideoFormatUrls = {
  	format720p: https://www.format720p.co.uk,
  	format1080p: https://www.format1080p.co.uk
  }
  ```

  Imagine we wanted to be able to check whether a given film is available in a
  certain video format. We could write a function like this:

  ```tsx
  function isFormatAvailable(
    obj: VideoFormatUrls,
    format: string
  ): format is keyof VideoFormatUrls {
    return format in obj;
  }
  ```

  Now imagine that we need to do the same thing with subtitles, but given that
  `isFormatAvailable()` is typed to the `VideoFormatUrls` type we would get an
  error if we used this function for subtitles. But we also don't want to write a
  near identical function typed to `SubtitleFormatUrls` to subtitles just to
  ensure adequate type safety.

  Alternatively we could use a union type, for example:

  ```tsx
  function isFormatAvailable(
    obj: VideoFormatUrls | SubtitleFormatUrls,
    format: string
  ): format is keyof VideoFormatUrls {
    return format in obj;
  }
  ```

  But this quickly becomes unwieldy if we, for the sake of argument have a great
  many URL types that we want the function to utilise.

  This is where generics become super helpful. Here is how we would rewrite the
  function as a generic:

  ```tsx
  function isAvailable<Formats>(obj: Formats, key: string): key is keyof Formats {
    return key in obj;
  }
  ```

  We could then explicitly type our calls of this function, viz:

  ```tsx
  isFormatAvailable<SubtitleFormatUrls>(subtitles, "english");
  ```


path: GPU_versus_CPU.md
sortable_path: GPU_versus_CPU.md
title: GPU versus CPU
checksum: 132c91acd7f4f95fe91448d7b07df376dc5f8028fee99951dcb411a17cec74ae
created: 2024-10-22 16:52:56.979181129+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"created":"Sunday, September 22, 2024","tags":["computer-architecture"]}
lead:
  - GPU stands for _Graphics Processing Unit_. Originally designed for rendering
    graphics and video but now used for other applications of parallel computing
    eg. machine learning.

body:
  - GPU stands for _Graphics Processing Unit_. Originally designed for rendering
    graphics and video but now used for other applications of parallel computing
    eg. machine learning.

  - The standard CPU uses sequential processing whereas the GPU is designed for
    parallel processing, specialising in performing many simple calculations
    simultaneously.

  - Harnessing the GPU requires specialised programming approaches.

  - Most standard laptops and desktops (if they are not designed for
    graphically-intensive tasks) incorporate graphics processing within the CPU.
    The CPU chip is on the same chip as the CPU chip. This is called _integrated_
    graphics processing.

  - Some laptops, especially those marketed for gaming or professional work, have
    a separate GPU chip. This is a more powerful graphics processor, separate from
    the CPU.

raw_content:
  ---
  tags: [computer-architecture]
  created: Sunday, September 22, 2024
  ---

  # GPU versus CPU

  - GPU stands for _Graphics Processing Unit_. Originally designed for rendering
    graphics and video but now used for other applications of parallel computing
    eg. machine learning.

  - The standard CPU uses sequential processing whereas the GPU is designed for
    parallel processing, specialising in performing many simple calculations
    simultaneously.

  - Harnessing the GPU requires specialised programming approaches.

  - Most standard laptops and desktops (if they are not designed for
    graphically-intensive tasks) incorporate graphics processing within the CPU.
    The CPU chip is on the same chip as the CPU chip. This is called _integrated_
    graphics processing.

  - Some laptops, especially those marketed for gaming or professional work, have
    a separate GPU chip. This is a more powerful graphics processor, separate from
    the CPU.


path: Generics_in_TypeScript.md
sortable_path: Generics_in_TypeScript.md
title: Generics
checksum: 74484845b015dd880dd9a5e01c698353617bd818b86e244c6946d64ab002a41c
created: 2024-10-22 16:52:56.98251366+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["typescript"]}
lead:
  Generics are a powerful feature in TypeScript that enables you to write reusable
  and flexible code while maintaining strong typing. With generics, you can create
  functions, classes, and interfaces that work with various types while preserving
  type information.

body:
  Generics are a powerful feature in TypeScript that enables you to write reusable
  and flexible code while maintaining strong typing. With generics, you can create
  functions, classes, and interfaces that work with various types while preserving
  type information.

  The main purpose of generics is to allow developers to write code that can
  **operate on different data types without knowing the specific type
  beforehand**. This helps to keep the code DRY and maintainable.

  ## Basic usage

  ### Functions

  ```ts
  function identity<T>(arg: T): T {
    return arg;
  }
  ```

  Here, `T` is a generic type variable. The `identity` function is a generic
  function that takes an argument of type `T` and returns a value of type `T`.

  To use this generic function you can either explicitly provide the type within
  the angle brackets, or let TypeScript infer the type based on the value passed:

  ```ts
  // Explicitly specifying the type
  let output1 = identity<string>("hello");

  // TypeScript infers the type as `number`
  let output2 = identity(42);
  ```

  #### Restricting the available types

  In the previous example any type could be used with the `identity` function. The
  only constraint that we place on usage is that the types must be consistent: if
  we pass a string as an argument, then a string must be returned.

  However we can add further restrictions on types by using the `extend` keyword,
  combined with an interface.

  ```ts
  interface Lengthwise {
    length: number;
  }

  function logLength<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
  }
  ```

  In this example the `logLength` generic function is limited to types that
  implement the `Lengthwise` interface. So: any argument that is passed to
  `logLength` and any value that is returned by it must match the shape of
  `Lengthwise`, having the `length` property.

  ## Interfaces and classes

  Generics can also be used profitably when working with stricter OOP constructs.

  ```ts
  interface KeyValuePair<K, V> {
    key: K;
    value: V;
  }
  ```

  The `KeyValuePair` interface has two generic type parameters: K for the key and
  V for the value. The interface defines an object shape that must possess the
  properties `key` and `value`, e.g:

  ```ts
  {
    key: 2,
    value: 'something'
  }
  ```

  In this form the generic specifies that whatever types are used for the key and
  the pair must be used consistently, e.g. this would be wrong:

  ```ts
  {
    key: 'age',
    value: 32
  }
  ```

  The following class uses the `KeyValuePair` interface:

  ```ts
  class Storage<K, V> {
    private items: KeyValuePair<K, V>[] = [];

    add(item: KeyValuePair<K, V>): void {
      this.items.push(item);
    }

    getByKey(key: K): V | undefined {
      const foundItem = this.items.find((item) => item.key === key);
      return foundItem ? foundItem.value : undefined;
    }

    getAll(): KeyValuePair<K, V>[] {
      return this.items;
    }
  }
  ```

  This class stores objects that match the `KeyValuesPair` interface in an array
  and provides and add and list method for accessing/returning them.

  The `add` method takes an item of type `KeyValuePair<K, V>` and adds it to the
  `items` array. The `getByKey` method takes a key of type `K` and returns the
  value of type `V` associated with that key or undefined if the key is not found.
  The `getAll` method returns all stored key-value pairs.

  Here is an example of instantiating the class:

  ```ts
  const storage = new Storage<number, string>();

  storage.add({ key: 1, value: "one" });
  storage.add({ key: 2, value: "two" });

  const value = storage.getByKey(1);
  const allItems = storage.getAll();

  console.log(value);
  // value: "one"

  console.log(allItems);

  // allItems: [
  //   { key: 1, value: "one" },
  //   { key: 2, value: "two" }
  // ]
  ```

  ## Real examples

  ### GraphQL client for query and mutation requests over `fetch`

  ```ts
  type GraphQlResult<T> = {
    data: T;
    errors?: Array<{
      message: string;
      locations: Array<{ line: number; column: number }>;
      path: Array<string | number>;
    }>;
  };

  export class GraphQlClient {
    private endpoint: string;

    constructor(endpoint: string) {
      this.endpoint = endpoint;
    }
    async request<T>(
      query: string,
      variables?: Record<string, unknown>
    ): Promise<T> {
      try {
        const response = await fetch(this.endpoint, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ query, variables }),
        });

        if (!response.ok) {
          throw new Error(
            `Network error: ${response.status} - ${response.statusText}`
          );
        }

        const result: GraphQlResult<T> = await response.json();

        if (result.errors) {
          throw new Error(`GraphQL error: ${JSON.stringify(result.errors)}`);
        }

        return result.data;
      } catch (error) {
        console.error(error);
        throw error;
      }
    }
  }
  ```

  ### VSCode extension TreeView generator

  In VSCode a TreeView is a list of values that may have nested values, like a
  directory. The following generic is a helper function that generates a TreeView
  based on a given class that is passed in as an argument, along with the class's
  constructor values (`args` in the example). It also calls a method `refresh` on
  each instance of the class.

  ```ts
  function createTreeView<
    T extends IndexHyperlinksProvider | IndexMetadataProvider,
    U extends LinkTypes | MetadataTypes
  >(
    viewType: string,
    ProviderClass: new (...args: any[]) => T,
    type: U,
    activeEditor?: string | undefined,
    ...args: ConstructorParameters<typeof ProviderClass>
  ): T {
    const view = new ProviderClass(...args, type);
    if (view instanceof IndexHyperlinksProvider) {
      view.refresh(activeEditor, type as LinkTypes);
    } else if (view instanceof IndexMetadataProvider) {
      view.refreshIndex();
    }
    vscode.window.registerTreeDataProvider(viewType, view);
    return view;
  }
  ```

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

  # Generics

  Generics are a powerful feature in TypeScript that enables you to write reusable
  and flexible code while maintaining strong typing. With generics, you can create
  functions, classes, and interfaces that work with various types while preserving
  type information.

  The main purpose of generics is to allow developers to write code that can
  **operate on different data types without knowing the specific type
  beforehand**. This helps to keep the code DRY and maintainable.

  ## Basic usage

  ### Functions

  ```ts
  function identity<T>(arg: T): T {
    return arg;
  }
  ```

  Here, `T` is a generic type variable. The `identity` function is a generic
  function that takes an argument of type `T` and returns a value of type `T`.

  To use this generic function you can either explicitly provide the type within
  the angle brackets, or let TypeScript infer the type based on the value passed:

  ```ts
  // Explicitly specifying the type
  let output1 = identity<string>("hello");

  // TypeScript infers the type as `number`
  let output2 = identity(42);
  ```

  #### Restricting the available types

  In the previous example any type could be used with the `identity` function. The
  only constraint that we place on usage is that the types must be consistent: if
  we pass a string as an argument, then a string must be returned.

  However we can add further restrictions on types by using the `extend` keyword,
  combined with an interface.

  ```ts
  interface Lengthwise {
    length: number;
  }

  function logLength<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);
    return arg;
  }
  ```

  In this example the `logLength` generic function is limited to types that
  implement the `Lengthwise` interface. So: any argument that is passed to
  `logLength` and any value that is returned by it must match the shape of
  `Lengthwise`, having the `length` property.

  ## Interfaces and classes

  Generics can also be used profitably when working with stricter OOP constructs.

  ```ts
  interface KeyValuePair<K, V> {
    key: K;
    value: V;
  }
  ```

  The `KeyValuePair` interface has two generic type parameters: K for the key and
  V for the value. The interface defines an object shape that must possess the
  properties `key` and `value`, e.g:

  ```ts
  {
    key: 2,
    value: 'something'
  }
  ```

  In this form the generic specifies that whatever types are used for the key and
  the pair must be used consistently, e.g. this would be wrong:

  ```ts
  {
    key: 'age',
    value: 32
  }
  ```

  The following class uses the `KeyValuePair` interface:

  ```ts
  class Storage<K, V> {
    private items: KeyValuePair<K, V>[] = [];

    add(item: KeyValuePair<K, V>): void {
      this.items.push(item);
    }

    getByKey(key: K): V | undefined {
      const foundItem = this.items.find((item) => item.key === key);
      return foundItem ? foundItem.value : undefined;
    }

    getAll(): KeyValuePair<K, V>[] {
      return this.items;
    }
  }
  ```

  This class stores objects that match the `KeyValuesPair` interface in an array
  and provides and add and list method for accessing/returning them.

  The `add` method takes an item of type `KeyValuePair<K, V>` and adds it to the
  `items` array. The `getByKey` method takes a key of type `K` and returns the
  value of type `V` associated with that key or undefined if the key is not found.
  The `getAll` method returns all stored key-value pairs.

  Here is an example of instantiating the class:

  ```ts
  const storage = new Storage<number, string>();

  storage.add({ key: 1, value: "one" });
  storage.add({ key: 2, value: "two" });

  const value = storage.getByKey(1);
  const allItems = storage.getAll();

  console.log(value);
  // value: "one"

  console.log(allItems);

  // allItems: [
  //   { key: 1, value: "one" },
  //   { key: 2, value: "two" }
  // ]
  ```

  ## Real examples

  ### GraphQL client for query and mutation requests over `fetch`

  ```ts
  type GraphQlResult<T> = {
    data: T;
    errors?: Array<{
      message: string;
      locations: Array<{ line: number; column: number }>;
      path: Array<string | number>;
    }>;
  };

  export class GraphQlClient {
    private endpoint: string;

    constructor(endpoint: string) {
      this.endpoint = endpoint;
    }
    async request<T>(
      query: string,
      variables?: Record<string, unknown>
    ): Promise<T> {
      try {
        const response = await fetch(this.endpoint, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ query, variables }),
        });

        if (!response.ok) {
          throw new Error(
            `Network error: ${response.status} - ${response.statusText}`
          );
        }

        const result: GraphQlResult<T> = await response.json();

        if (result.errors) {
          throw new Error(`GraphQL error: ${JSON.stringify(result.errors)}`);
        }

        return result.data;
      } catch (error) {
        console.error(error);
        throw error;
      }
    }
  }
  ```

  ### VSCode extension TreeView generator

  In VSCode a TreeView is a list of values that may have nested values, like a
  directory. The following generic is a helper function that generates a TreeView
  based on a given class that is passed in as an argument, along with the class's
  constructor values (`args` in the example). It also calls a method `refresh` on
  each instance of the class.

  ```ts
  function createTreeView<
    T extends IndexHyperlinksProvider | IndexMetadataProvider,
    U extends LinkTypes | MetadataTypes
  >(
    viewType: string,
    ProviderClass: new (...args: any[]) => T,
    type: U,
    activeEditor?: string | undefined,
    ...args: ConstructorParameters<typeof ProviderClass>
  ): T {
    const view = new ProviderClass(...args, type);
    if (view instanceof IndexHyperlinksProvider) {
      view.refresh(activeEditor, type as LinkTypes);
    } else if (view instanceof IndexMetadataProvider) {
      view.refreshIndex();
    }
    vscode.window.registerTreeDataProvider(viewType, view);
    return view;
  }
  ```


path: Git_bisect.md
sortable_path: Git_bisect.md
title: Bisect
lead:
  Bisect can be used to identify commits that introduce a bug or regression in our
  code.

checksum: 582180e21ae5adc1089dce7b0205d74e9345d25d43bb03b7a0d473da0f4a3774
created: 2024-10-22 16:52:56.985102368+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["git"]}
body:
  Bisect can be used to identify commits that introduce a bug or regression in our
  code.

  It is most useful for when we know there is a problem in the code and we know
  there was a point in the past where the bug did not exist. We can compare the
  two points and try to identify where things went wrong.

  We mark the last good revision and the first bad revision. Bisect will the reset
  the code to the midpoint between the good and bad versions and let you test it.
  You mark that as a good or bad version and then bisect repeats the process.

  ![](/img/git-bisect.svg)

  ## Procedure

  ```
  git bisect start

  git bisect good <SHA, tag, branch>

  git bisect bad <SHA, tag, branch>
  ```

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

  # Bisect

  Bisect can be used to identify commits that introduce a bug or regression in our
  code.

  It is most useful for when we know there is a problem in the code and we know
  there was a point in the past where the bug did not exist. We can compare the
  two points and try to identify where things went wrong.

  We mark the last good revision and the first bad revision. Bisect will the reset
  the code to the midpoint between the good and bad versions and let you test it.
  You mark that as a good or bad version and then bisect repeats the process.

  ![](/img/git-bisect.svg)

  ## Procedure

  ```
  git bisect start

  git bisect good <SHA, tag, branch>

  git bisect bad <SHA, tag, branch>
  ```


path: Git_rebasing.md
sortable_path: Git_rebasing.md
title: Rebasing
checksum: be09e17373363834833d5deb64d2732b4c33199b79b14ab0e6dfd63676670f80
created: 2024-10-22 16:52:56.988474933+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["git"]}
lead:
  Rebasing is a way to integrate changes from one branch into another. In this
  regarding it is like merging a branch B into another branch A. However rebasing
  differs from normal merging in the way in which it modifies the Git history.

body:
  Rebasing is a way to integrate changes from one branch into another. In this
  regarding it is like merging a branch B into another branch A. However rebasing
  differs from normal merging in the way in which it modifies the Git history.

  In a normal merge of branch B into branch A, Git creates a new commit that
  combines the changes of branches B and A. This is known as the merge commit and
  is evident from the following automatic commit message that is generated:

  ```
  Merge branch B of github.com:thomasabishop/remote-repository into A
  ```

  ![](/img/normal-merge-again.svg)

  In this scenario the merge commit has two or more parent commits each
  representing the history of the merged branches. The resulting history of A will
  include the commits of B. Basically the two histories are combined.

  This would give us a history that looks like the following, with different
  colours for the separate SHAs of each merged branch:

  ![](/img/combined-merge-hist.svg)

  If we were to create a rebase branch of A from B, there would be a new singular
  history without distinguishing multiple parents that combines the commits of A
  and B. The rebased branch's commits are recreated with new commit IDs and new
  timestamps, based on the current state of the branch that you are rebasing onto.
  This is obviously potentially destructive because it does not preserve the
  respective branch's separate history. It rewrites history as a continuous stream
  of commits in a single branch.

  When a rebase is applied, it will put the diverging B commits at the tip of A
  like so:

  ![](/img/rebase-tip-chage.svg)

  And then rebrand the previous A commits to be continuous with B presenting a
  flat and linear Git history like the following:

  ![](/img/single-git-history-rebase.svg)

  ## Benefits, use-cases

  > The purpose of rebasing is to ensure that the commit history of a project is
  > as linear and simple as possible, by incorporating changes from one branch
  > into another, as if the changes had been made on the other branch all along.

  A common use-case is with feature branches as it makes the features fit more
  seamlessly with the `main` or `develop` branch.

  It is also a technique that can be used to integrate recent commits without
  merging.

  When to use one over the other:

  - **Merge** to allow commits to stand out or to be clearly grouped
  - **Merge** to bring large feature branches back into main
  - **Rebase** to add minor commits in a main branch into a feature branch
  - **Rebase** when you need to move commits from one branch to another

  So in terms of my personal workflow, I should be using

  - Merge when I want to bring a feature into develop
  - Merge when I want to bring develop into main for a release
  - Merge any time the feature branch is already public and being used by others.
  - Rebase to capture small changes in the parent branch
  - Rebase when I have branched from a branch because a refactor or something has
    become more complex than originally anticipated.

  ## Syntax

  For example if we are working on a feature branch off of `main`, we would run
  the following from this feature branch:

  ```
  git rebase main
  ```

  This will move the feature branch to the tip of `main`.

  If this completes successfully, we would then go on to merge the feature with
  main.

  If conflicts occur, the rebase will halt. You fix the conflicts and then run:

  ```
  git rebase --continue
  ```

  Or if you don't want to resolve the conflict (not advised):

  ```
  git rebase --skip
  ```

  Or stop the whole process:

  ```
  git rebase --abort
  ```

  ### Isolate the point at which one branch diverges from another

  This can be useful to check before you create a rebase:

  ```
  git merge-base main feature/some-feature

  c33acc84f06fcb94e0e87d9adb240c038da6d71c
  ```

  ## Golden rule of rebasing

  When we rebase we remove the additional merge commit but we are changing
  history. The commits are still there but the SHAs are changed. For this reason
  **you should not rebase a public branch**, otherwise you will mess up your
  collaborators history. You should do your rebasing locally and then make a pull
  request.

  ## Difference from cherry-picking

  The main difference between the two approaches is that
  [cherry-picking](Cherry_picking_a_branch.md) is a more selective
  process, where you can pick and choose specific commits that you want to include
  in another branch. This can be useful when you only want to apply specific
  changes or fixes from one branch to another, without including all the changes
  made in the original branch.

  On the other hand, rebasing is a more comprehensive process that can include all
  the changes from one branch to another, but it modifies the commit history,
  making it more linear and easier to understand. Unlike rebasing, cherry-picking
  does not modify the commit history of either branch.

  ## Squashing commits

  This is a handy feature of rebasing. It allows you to combine multiple commits
  into one. This is useful when you have been doing a lot of trial and error to
  resolve a bug and you have lots of small commits that are not that important in
  themselves.

  We use the interactive rebase tool for this.

  Checkout the branch where the commit you want to squash is located.

  Then run:

  ```
  git rebase -i HEAD~n
  ```

  Where `n` is the number of commits you want to squash starting from the most
  recent. This will open an interactive rebase window, listing the commits. You
  can then use the keywords to decide what you want to do with them. In our case
  this will be `s` for squash.

  ![](/img/git-interactive-rebase.png)

  ### Example

  I made a small fix commit because something went wrong during build. I do not
  want this commit to be in the history because it is so minor. Instead I want to
  incorporate these changes into the last public commit.

  Trivial commit is `xy` Previous commit is `aa`

  ```
  git rebase -i HEAD~2
  ```

  This displays:

  ```
  pick xy
  pick aa
  ```

  Change this to:

  ```
  pick xy
  squash aa
  ```

  Now `xy == aa`

  ### Another example

  Here we will rebase the interim commits into the last feature commit

  ```sh
  git rebase -i HEAD~10
  ```

  ```
  pick a691a7667 feature (shared-nav) add active app check
  pick 74a07fdb1 interim: add logging
  pick 9af4e2652 interim: more logging
  pick 51b36f667 interim: use href not origin
  pick 33f161198 interim : try catch handler
  pick 41c144ddf interim: remove didLoadCheck
  pick ebd1c9f24 interim: fix flash star
  pick 77af452e7 interim: rm redundant active app check
  pick 79a9b737a iterim: test ternary
  pick a90d23223 interim: further tidying
  ```

  ```
  pick a691a7667 feature (shared-nav) add active app check
  squash 74a07fdb1 interim: add logging
  squash 9af4e2652 interim: more logging
  squash 51b36f667 interim: use href not origin
  squash 33f161198 interim : try catch handler
  squash 41c144ddf interim: remove didLoadCheck
  squash ebd1c9f24 interim: fix flash star
  squash 77af452e7 interim: rm redundant active app check
  squash 79a9b737a iterim: test ternary
  squash a90d23223 interim: further tidying
  ```

  ## `git pull rebase`

  We can use this command to combine a rebase with a pull. This way we silently
  update our local version of a branch with the remote, without adding a merge
  commit.

  ```
  git pull --rebase
  ```

  Add `--rebase=preserve` to keep merges that have been done _locally_. It is
  generally a good idea to do this.

raw_content:
  ---
  tags:
    - git
  ---

  # Rebasing

  Rebasing is a way to integrate changes from one branch into another. In this
  regarding it is like merging a branch B into another branch A. However rebasing
  differs from normal merging in the way in which it modifies the Git history.

  In a normal merge of branch B into branch A, Git creates a new commit that
  combines the changes of branches B and A. This is known as the merge commit and
  is evident from the following automatic commit message that is generated:

  ```
  Merge branch B of github.com:thomasabishop/remote-repository into A
  ```

  ![](/img/normal-merge-again.svg)

  In this scenario the merge commit has two or more parent commits each
  representing the history of the merged branches. The resulting history of A will
  include the commits of B. Basically the two histories are combined.

  This would give us a history that looks like the following, with different
  colours for the separate SHAs of each merged branch:

  ![](/img/combined-merge-hist.svg)

  If we were to create a rebase branch of A from B, there would be a new singular
  history without distinguishing multiple parents that combines the commits of A
  and B. The rebased branch's commits are recreated with new commit IDs and new
  timestamps, based on the current state of the branch that you are rebasing onto.
  This is obviously potentially destructive because it does not preserve the
  respective branch's separate history. It rewrites history as a continuous stream
  of commits in a single branch.

  When a rebase is applied, it will put the diverging B commits at the tip of A
  like so:

  ![](/img/rebase-tip-chage.svg)

  And then rebrand the previous A commits to be continuous with B presenting a
  flat and linear Git history like the following:

  ![](/img/single-git-history-rebase.svg)

  ## Benefits, use-cases

  > The purpose of rebasing is to ensure that the commit history of a project is
  > as linear and simple as possible, by incorporating changes from one branch
  > into another, as if the changes had been made on the other branch all along.

  A common use-case is with feature branches as it makes the features fit more
  seamlessly with the `main` or `develop` branch.

  It is also a technique that can be used to integrate recent commits without
  merging.

  When to use one over the other:

  - **Merge** to allow commits to stand out or to be clearly grouped
  - **Merge** to bring large feature branches back into main
  - **Rebase** to add minor commits in a main branch into a feature branch
  - **Rebase** when you need to move commits from one branch to another

  So in terms of my personal workflow, I should be using

  - Merge when I want to bring a feature into develop
  - Merge when I want to bring develop into main for a release
  - Merge any time the feature branch is already public and being used by others.
  - Rebase to capture small changes in the parent branch
  - Rebase when I have branched from a branch because a refactor or something has
    become more complex than originally anticipated.

  ## Syntax

  For example if we are working on a feature branch off of `main`, we would run
  the following from this feature branch:

  ```
  git rebase main
  ```

  This will move the feature branch to the tip of `main`.

  If this completes successfully, we would then go on to merge the feature with
  main.

  If conflicts occur, the rebase will halt. You fix the conflicts and then run:

  ```
  git rebase --continue
  ```

  Or if you don't want to resolve the conflict (not advised):

  ```
  git rebase --skip
  ```

  Or stop the whole process:

  ```
  git rebase --abort
  ```

  ### Isolate the point at which one branch diverges from another

  This can be useful to check before you create a rebase:

  ```
  git merge-base main feature/some-feature

  c33acc84f06fcb94e0e87d9adb240c038da6d71c
  ```

  ## Golden rule of rebasing

  When we rebase we remove the additional merge commit but we are changing
  history. The commits are still there but the SHAs are changed. For this reason
  **you should not rebase a public branch**, otherwise you will mess up your
  collaborators history. You should do your rebasing locally and then make a pull
  request.

  ## Difference from cherry-picking

  The main difference between the two approaches is that
  [cherry-picking](Cherry_picking_a_branch.md) is a more selective
  process, where you can pick and choose specific commits that you want to include
  in another branch. This can be useful when you only want to apply specific
  changes or fixes from one branch to another, without including all the changes
  made in the original branch.

  On the other hand, rebasing is a more comprehensive process that can include all
  the changes from one branch to another, but it modifies the commit history,
  making it more linear and easier to understand. Unlike rebasing, cherry-picking
  does not modify the commit history of either branch.

  ## Squashing commits

  This is a handy feature of rebasing. It allows you to combine multiple commits
  into one. This is useful when you have been doing a lot of trial and error to
  resolve a bug and you have lots of small commits that are not that important in
  themselves.

  We use the interactive rebase tool for this.

  Checkout the branch where the commit you want to squash is located.

  Then run:

  ```
  git rebase -i HEAD~n
  ```

  Where `n` is the number of commits you want to squash starting from the most
  recent. This will open an interactive rebase window, listing the commits. You
  can then use the keywords to decide what you want to do with them. In our case
  this will be `s` for squash.

  ![](/img/git-interactive-rebase.png)

  ### Example

  I made a small fix commit because something went wrong during build. I do not
  want this commit to be in the history because it is so minor. Instead I want to
  incorporate these changes into the last public commit.

  Trivial commit is `xy` Previous commit is `aa`

  ```
  git rebase -i HEAD~2
  ```

  This displays:

  ```
  pick xy
  pick aa
  ```

  Change this to:

  ```
  pick xy
  squash aa
  ```

  Now `xy == aa`

  ### Another example

  Here we will rebase the interim commits into the last feature commit

  ```sh
  git rebase -i HEAD~10
  ```

  ```
  pick a691a7667 feature (shared-nav) add active app check
  pick 74a07fdb1 interim: add logging
  pick 9af4e2652 interim: more logging
  pick 51b36f667 interim: use href not origin
  pick 33f161198 interim : try catch handler
  pick 41c144ddf interim: remove didLoadCheck
  pick ebd1c9f24 interim: fix flash star
  pick 77af452e7 interim: rm redundant active app check
  pick 79a9b737a iterim: test ternary
  pick a90d23223 interim: further tidying
  ```

  ```
  pick a691a7667 feature (shared-nav) add active app check
  squash 74a07fdb1 interim: add logging
  squash 9af4e2652 interim: more logging
  squash 51b36f667 interim: use href not origin
  squash 33f161198 interim : try catch handler
  squash 41c144ddf interim: remove didLoadCheck
  squash ebd1c9f24 interim: fix flash star
  squash 77af452e7 interim: rm redundant active app check
  squash 79a9b737a iterim: test ternary
  squash a90d23223 interim: further tidying
  ```

  ## `git pull rebase`

  We can use this command to combine a rebase with a pull. This way we silently
  update our local version of a branch with the remote, without adding a merge
  commit.

  ```
  git pull --rebase
  ```

  Add `--rebase=preserve` to keep merges that have been done _locally_. It is
  generally a good idea to do this.


path: Global_object_in_NodeJS.md
sortable_path: Global_object_in_NodeJS.md
title: Global object
checksum: a833275912428951e9737156c72959670ed03b91fdbb78dac2aeb1cf52690ac3
created: 2024-10-22 16:52:56.996600909+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["node-js"]}
lead:
  > In Node every function and variable should be scoped to a module. We should
  > not define functions and variables within the global scope.

body:
  > In Node every function and variable should be scoped to a module. We should
  > not define functions and variables within the global scope.

  - In Node the equivalent to the browser's `Window` object is `global`. The
    properties and methods that belong to this method are available anywhere in a
    program.

  - Just as we can technically write `Window.console.log()`, we can write
    `global.console.log()` however in both cases it is more sane to use the
    shorthand.

  - However if we declare a variable in this scope in browser-based JavaScript,
    this variable becomes accessible via the `Window` object and thus is
    accessible in global scope. The same is not true for Node. If you declare a
    variable at this level it will return undefined.

  - This is because of Node's modular nature. If you were to define a function
    `foo` in a module and then also define it in the global scope, when you call
    `foo`, the Node interpreter would not know which function to call. Hence it
    chooses not to recognise the global `foo`, returning undefined.

raw_content:
  ---
  tags:
    - node-js
  ---

  # Global object

  > In Node every function and variable should be scoped to a module. We should
  > not define functions and variables within the global scope.

  - In Node the equivalent to the browser's `Window` object is `global`. The
    properties and methods that belong to this method are available anywhere in a
    program.

  - Just as we can technically write `Window.console.log()`, we can write
    `global.console.log()` however in both cases it is more sane to use the
    shorthand.

  - However if we declare a variable in this scope in browser-based JavaScript,
    this variable becomes accessible via the `Window` object and thus is
    accessible in global scope. The same is not true for Node. If you declare a
    variable at this level it will return undefined.

  - This is because of Node's modular nature. If you were to define a function
    `foo` in a module and then also define it in the global scope, when you call
    `foo`, the Node interpreter would not know which function to call. Hence it
    chooses not to recognise the global `foo`, returning undefined.


path: Grep.md
sortable_path: Grep.md
title: Grep
lead: ## Purpose of `grep`
checksum: be1a28cd33fe7976d80627887f90d029ed84591c7e294bc75165973cc3f70c59
created: 2024-10-22 16:52:56.999727545+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["shell"]}
body:
  ## Purpose of `grep`

  `grep` stands for “global regular expression print”. It allows you to search
  plain text data sets for strings which match a regular expression or pattern.

  ## Syntax

  ### Schematic

  ```bash
  grep [options] [pattern] [source file] > [output file]
  ```

  Note that above we redirect the file matches to a new file. You don't have to do
  this. If you omit the redirection, `grep` will output to standard output.

  ### Applied

  ```bash
  grep -i -n "banana" fruits.txt > banana.txt
  ```

  The above example searches, using regex, for strings matching the pattern
  “banana” in the file `fruits.txt` regardless of the character case (`-i` ensures
  this) and outputs its findings to the file `banana.txt`, with the line number
  where the match occurs appended to the output (`-n` takes care of this).

  Note that for simplicity, you can chain optional values together, i.e. the
  options in the above example could be input as `-in`.

  ## Useful options

  - ignore case: `i`
  - count matches instead of returning actual match: `-c`
  - precede each match with the line number where it occurs: `-n`
  - invert the match (show everything that doesn't match the expression): `-v`
  - search entire directories recursively: `-r`
  - list file names where matches occur (in the scenario of a recursive match):
    `-l`

  ## `ripgrep`

  `ripgrep` is generally faster however it does not come as default with Unix and
  only works recursively, i.e. it is designed to find strings within files within
  multiple directories not just single files or piped streams.

  It also respects `.gitignore` files that it finds within directories by default
  and `node_modules` which is really handy.

  Most of the standard `grep` options transfer over.

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

  # Grep

  ## Purpose of `grep`

  `grep` stands for “global regular expression print”. It allows you to search
  plain text data sets for strings which match a regular expression or pattern.

  ## Syntax

  ### Schematic

  ```bash
  grep [options] [pattern] [source file] > [output file]
  ```

  Note that above we redirect the file matches to a new file. You don't have to do
  this. If you omit the redirection, `grep` will output to standard output.

  ### Applied

  ```bash
  grep -i -n "banana" fruits.txt > banana.txt
  ```

  The above example searches, using regex, for strings matching the pattern
  “banana” in the file `fruits.txt` regardless of the character case (`-i` ensures
  this) and outputs its findings to the file `banana.txt`, with the line number
  where the match occurs appended to the output (`-n` takes care of this).

  Note that for simplicity, you can chain optional values together, i.e. the
  options in the above example could be input as `-in`.

  ## Useful options

  - ignore case: `i`
  - count matches instead of returning actual match: `-c`
  - precede each match with the line number where it occurs: `-n`
  - invert the match (show everything that doesn't match the expression): `-v`
  - search entire directories recursively: `-r`
  - list file names where matches occur (in the scenario of a recursive match):
    `-l`

  ## `ripgrep`

  `ripgrep` is generally faster however it does not come as default with Unix and
  only works recursively, i.e. it is designed to find strings within files within
  multiple directories not just single files or piped streams.

  It also respects `.gitignore` files that it finds within directories by default
  and `node_modules` which is really handy.

  Most of the standard `grep` options transfer over.


path: Ground.md
sortable_path: Ground.md
title: Ground
checksum: 06d6a82b347c9527a50fa40afccff167a415ff9ab7bfed33ff87c58dc8ae473d
created: 2024-10-22 16:52:57.002909926+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["physics","electricity"]}
lead:
  The term 'ground' is used as a general reference for 'zero potential': the point
  in an electrical circuit where there is no potential difference between the
  positive and negative terminals of the voltage source.

body:
  The term 'ground' is used as a general reference for 'zero potential': the point
  in an electrical circuit where there is no potential difference between the
  positive and negative terminals of the voltage source.

  As a consequence of the lack of potential, ground is a point at which no current
  can be generated.

  The term originates from domestic electrical systems where the circuit box is
  literally attached to the ground (represented by the green and yellow ribboned
  wires in plug sockets and light fixtures etc) where current is discharged to
  prevent appliances emitting electrical shocks.

  In circuit diagrams with simple DC current, ground is taken to be the negative
  terminal of the voltage source.

  The symbol for ground in circuit diagrams:

  <img src="/home/thomas/repos/computer_science/img/ground-symbol.png" width="300px">

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

  # Ground

  The term 'ground' is used as a general reference for 'zero potential': the point
  in an electrical circuit where there is no potential difference between the
  positive and negative terminals of the voltage source.

  As a consequence of the lack of potential, ground is a point at which no current
  can be generated.

  The term originates from domestic electrical systems where the circuit box is
  literally attached to the ground (represented by the green and yellow ribboned
  wires in plug sockets and light fixtures etc) where current is discharged to
  prevent appliances emitting electrical shocks.

  In circuit diagrams with simple DC current, ground is taken to be the negative
  terminal of the voltage source.

  The symbol for ground in circuit diagrams:

  <img src="/home/thomas/repos/computer_science/img/ground-symbol.png" width="300px">


path: Guide_to_YAML.md
sortable_path: Guide_to_YAML.md
title: Guide to YAML
checksum: 22c8571439ba88961c2f29fa85f550ded8ffcbe85324bac952d3ee59bf419992
created: 2024-10-22 16:52:57.006500637+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["yaml"]}
lead:
  - Data description language
  - Alternative to XML and JSON
  - Easy to read by humans and process by code

body:
  - Data description language
  - Alternative to XML and JSON
  - Easy to read by humans and process by code

  ## Main rule

  > Never use tabs for indentation, use two spaces

  ## Available data types

  - strings
  - numbers
  - booleans
  - arrays
  - maps (key, value pairs)

  ### Example

  ````yaml
  # Basic scalar data types
  age: 51
  greeting: Hello, world
  length: 182.3
  happy: true
  ``

  ```yaml
  # array
  pets:
    - cat
    - dog

  # nested array
  pets:
    - cat
    - dog
      - staffy
      - pitty
  ````

  ```yaml
  # map
  languages:
    - java: advanced
    - python: average
    - javascript: basic
  ```

  ### Strings

  - Don't have to use quotes unless string contains reserved YAML characters
    (dashes, colons etc)
  - Can use single or double quotes

  ### Multi-line text

  Two methods:

  ```yaml
  # Literal block preserves new line characters
  literal_block: |
    Lorem ipsum dolar sit avcc lorem dolar 
    Blah blah
    Blah
  ```

  ```yaml
  folded_block: >
    Each different line will be folded back on itself so you don't have to keep
    scrolling to the right
  ```

raw_content:
  ---
  tags:
    - yaml
  ---

  # Guide to YAML

  - Data description language
  - Alternative to XML and JSON
  - Easy to read by humans and process by code

  ## Main rule

  > Never use tabs for indentation, use two spaces

  ## Available data types

  - strings
  - numbers
  - booleans
  - arrays
  - maps (key, value pairs)

  ### Example

  ````yaml
  # Basic scalar data types
  age: 51
  greeting: Hello, world
  length: 182.3
  happy: true
  ``

  ```yaml
  # array
  pets:
    - cat
    - dog

  # nested array
  pets:
    - cat
    - dog
      - staffy
      - pitty
  ````

  ```yaml
  # map
  languages:
    - java: advanced
    - python: average
    - javascript: basic
  ```

  ### Strings

  - Don't have to use quotes unless string contains reserved YAML characters
    (dashes, colons etc)
  - Can use single or double quotes

  ### Multi-line text

  Two methods:

  ```yaml
  # Literal block preserves new line characters
  literal_block: |
    Lorem ipsum dolar sit avcc lorem dolar 
    Blah blah
    Blah
  ```

  ```yaml
  folded_block: >
    Each different line will be folded back on itself so you don't have to keep
    scrolling to the right
  ```


path: HTTP_request_types.md
sortable_path: HTTP_request_types.md
title: HTTP request types
lead: ## GET
body:
  ## GET

  - Get data

  ## POST

  - Create data

  ## PUT

  - Update data

  ## DELETE

  - Remove data

checksum: e2e8f9de5eeea50e69c7e5a4b92f8917d48f0ca9b47c70024d8ded12ec067f65
created: 2024-10-22 16:52:57.011129121+00:00
modified: 2024-10-18 18:14:47.164002558+00:00
metadata: {"tags":["REST"]}
raw_content:
  ---
  tags:
    - REST
  ---

  # HTTP request types

  ## GET

  - Get data

  ## POST

  - Create data

  ## PUT

  - Update data

  ## DELETE

  - Remove data


path: Half_adder_and_full_adder.md
sortable_path: Half_adder_and_full_adder.md
title: The half adder and full adder
lead: ## Binary arithmetic
checksum: 6c57575017ae451e8a8b357b8e8a6c8cdab2de4305b514ab62eaa9bfeb654556
created: 2024-10-22 16:52:57.017493126+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["logic-gates","binary"]}
body:
  ## Binary arithmetic

  The half adder and full adder are components of digital circuits that enable us
  to carry out binary addition. Using adders and half adders we can add two binary
  numbers together. Adders are a type of [integrated circuit]() comprising certain
  [logic gates](Logic_gates.md) where the arrangement allows for the
  representation of the addition of bits.

  ### Example addition

  Consider the following binary addition:

  ```
    c_1
  0   0   1   0
  0   0   1   1
  _____________
  0   1   0   1
  ```

  How does the calculation proceed? Moving from the right-most column:

  - $1 + 0 = 1$ hence the sum of the first column (the binary 1 coumn) is $1$ with
    no carry
  - Moving to the second column (the binary 2 column), $1 + 1 = 2$ however we do
    not have the digit "2" in binary, therefore we must bump the place value by
    one place and carry this sum as a $1$ leaving a zero to mark the place value
  - Thus the third column (the binary 4 column) has the calculation:
    $1 + 0 + 0 = 1$ with no carry.

  ### Bits

  From the previous example we can identified the following repeated actions:

  - For calculations of the form $1 + 0$ or $0 + 0$ there will be no carried digit
  - For calculations of the form $1 + 1$ there will be a carried digit

  In the context of adders the digits are bits. We distinguish three core bits by
  their role in the calculation: the carry, the **least significant bit** and the
  **most significant bit**. The least significant bit is the right-most digit in
  the calculation. The most significant bit is the leftmost.

  The two types of adders are distinguished by which bits of the calculation they
  operate on. The half adder focuses on the least significant bit whereas the full
  adder focuses on the most significant bit.

  ## Half adder

  The half adder receives two bits (A and B) which are to be added together. It
  outputs this value as the **sum bit**. If there is a bit to be carried to the
  next column in the binary calculation this will be output as the **carry-out**
  bit.

  | A                            | B                             | S           | C_out             |
  | ---------------------------- | ----------------------------- | ----------- | ----------------- |
  | The first number to be added | The second number to be added | The sum bit | The carry-out bit |

  The diagram below shows the circuit representation of a half-adder and an
  example calculation. This calculation matches the ones column of the earlier
  binary addition example: $0011 + 0010$.

  ![](/img/half-adder-new.png)

  ### Implementation with logic gates

  If we think about it, the possible inputs and outputs of a half adder are highly
  circumscribed:

  - If the sum exceeds $1$, the sum bit will be $0$ and the carry-out bit will be
    $1$
  - In all other cases the carry-out bit will be $0$. These other cases are when
    the sum bit is either $0$ or $1$, e.g: $1 + 0$ or $0 + 0$.

  We can represent this with a simple truth-table:

  | A   | B   | S   | C_out |
  | --- | --- | --- | ----- |
  | 0   | 0   | 0   | 0     |
  | 0   | 1   | 1   | 0     |
  | 1   | 0   | 1   | 0     |
  | 1   | 1   | 0   | 1     |

  We can see that the sum bit column replicates the truth-conditions of
  [XOR](Logic_gates.md#xor-gate):

  | P   | Q   | P V Q |
  | --- | --- | ----- |
  | T   | T   | F     |
  | T   | F   | T     |
  | F   | T   | T     |
  | F   | F   | F     |

  And the carry-out bit replicates the truth conditions of
  [AND](Logic_gates.md#and-gate):

  | P   | Q   | P & Q |
  | --- | --- | ----- |
  | T   | T   | F     |
  | T   | F   | F     |
  | F   | T   | F     |
  | F   | F   | T     |

  It is therefore possible to implement a half-adder with just these two logic
  gates:

  ![](/img/half-adder-gates-three.png)

  The digital circuit above has the same inputs and outputs as the half adder
  diagram above.

  <iframe src="https://circuitverse.org/simulator/embed/half-adder-67b14a9f-d1ed-4240-ab19-1d753f18a40d" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="400" width="600" allowFullScreen></iframe>

  ## Full adder

  As the half adder only calculates the least significant bit, it is not
  sufficient by itself to complete a binary addition; it cannot account for
  movements in binary place value. To carry out full calculations it must be
  supplemented with the full adder.

  The full adder takes in three inputs and has two outputs. It is identical to the
  half-adder apart from the fact that one of its inputs is **carry-in**. This is
  equivalent to the value that is designated as **carry-out** in a half adder. It
  is an incoming value that is the product of a previous operation that resulted
  in a carry-out. This is added together with the full adders own two inputs (A
  and B) and like the half adder, generates a sum bit and a carry-out bit.

  | A                            | B                             | C_in                     | S                      | C_out                        |
  | ---------------------------- | ----------------------------- | ------------------------ | ---------------------- | ---------------------------- |
  | The first number to be added | The second number to be added | The incoming carried bit | The sum bit (A+B+C_in) | The carry-out bit (A+B+C_in) |

  ![](/img/full-adder-new.png)

  The diagram above is equivalent to the calculation taking place in the fours
  column. It has received a carry from the twos column ($1 + 1$ results in $1$ as
  a carry) and then adds this together with its own inputs ($0$ and $0$).

  ```
    c_1
  0   0   1   0
  0   0   1   1
  _____________
  0   1   0   1
  ```

  ### Implementation with logic gates

  When it comes to implementing the full adder with logic gates we can allow
  ourselves greater abstraction as we already have the half adder to work with. We
  don't need to create a half adder from scratch, we can reuse it.

  We are adding three bits: $1$, $0$ and $0$. This can be achieved with two half
  adders:

  - One half adder (HA1) for the sum of $0 + 0$
  - One half adder (HA2) for the sum of $(0 + 0) + 1$ (the previous sum plus the
    third bit)

  The sum of HA1 ($0 + 0$) is passed in to the B input on HA2 and the $1$ is
  passed in as the A input of HA2. This gives us $1$ as the sum bit of HA2.

  At this point we have completed the addition and have successfully added the
  three bits: $1$, $0$, and $0$ to get $1$ as ths sum. However we also have to
  account for the fact that the addition may result in its own carry-out bit. What
  if the inputs were $1, 1, 0$ for example?

  <iframe src="https://circuitverse.org/simulator/embed/full-adder-674f8ddf-c5aa-42fa-9c0f-4349ffabe723" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="400" width="700" allowFullScreen></iframe>

raw_content:
  ---
  tags: [logic-gates, binary]
  ---

  # The half adder and full adder

  ## Binary arithmetic

  The half adder and full adder are components of digital circuits that enable us
  to carry out binary addition. Using adders and half adders we can add two binary
  numbers together. Adders are a type of [integrated circuit]() comprising certain
  [logic gates](Logic_gates.md) where the arrangement allows for the
  representation of the addition of bits.

  ### Example addition

  Consider the following binary addition:

  ```
    c_1
  0   0   1   0
  0   0   1   1
  _____________
  0   1   0   1
  ```

  How does the calculation proceed? Moving from the right-most column:

  - $1 + 0 = 1$ hence the sum of the first column (the binary 1 coumn) is $1$ with
    no carry
  - Moving to the second column (the binary 2 column), $1 + 1 = 2$ however we do
    not have the digit "2" in binary, therefore we must bump the place value by
    one place and carry this sum as a $1$ leaving a zero to mark the place value
  - Thus the third column (the binary 4 column) has the calculation:
    $1 + 0 + 0 = 1$ with no carry.

  ### Bits

  From the previous example we can identified the following repeated actions:

  - For calculations of the form $1 + 0$ or $0 + 0$ there will be no carried digit
  - For calculations of the form $1 + 1$ there will be a carried digit

  In the context of adders the digits are bits. We distinguish three core bits by
  their role in the calculation: the carry, the **least significant bit** and the
  **most significant bit**. The least significant bit is the right-most digit in
  the calculation. The most significant bit is the leftmost.

  The two types of adders are distinguished by which bits of the calculation they
  operate on. The half adder focuses on the least significant bit whereas the full
  adder focuses on the most significant bit.

  ## Half adder

  The half adder receives two bits (A and B) which are to be added together. It
  outputs this value as the **sum bit**. If there is a bit to be carried to the
  next column in the binary calculation this will be output as the **carry-out**
  bit.

  | A                            | B                             | S           | C_out             |
  | ---------------------------- | ----------------------------- | ----------- | ----------------- |
  | The first number to be added | The second number to be added | The sum bit | The carry-out bit |

  The diagram below shows the circuit representation of a half-adder and an
  example calculation. This calculation matches the ones column of the earlier
  binary addition example: $0011 + 0010$.

  ![](/img/half-adder-new.png)

  ### Implementation with logic gates

  If we think about it, the possible inputs and outputs of a half adder are highly
  circumscribed:

  - If the sum exceeds $1$, the sum bit will be $0$ and the carry-out bit will be
    $1$
  - In all other cases the carry-out bit will be $0$. These other cases are when
    the sum bit is either $0$ or $1$, e.g: $1 + 0$ or $0 + 0$.

  We can represent this with a simple truth-table:

  | A   | B   | S   | C_out |
  | --- | --- | --- | ----- |
  | 0   | 0   | 0   | 0     |
  | 0   | 1   | 1   | 0     |
  | 1   | 0   | 1   | 0     |
  | 1   | 1   | 0   | 1     |

  We can see that the sum bit column replicates the truth-conditions of
  [XOR](Logic_gates.md#xor-gate):

  | P   | Q   | P V Q |
  | --- | --- | ----- |
  | T   | T   | F     |
  | T   | F   | T     |
  | F   | T   | T     |
  | F   | F   | F     |

  And the carry-out bit replicates the truth conditions of
  [AND](Logic_gates.md#and-gate):

  | P   | Q   | P & Q |
  | --- | --- | ----- |
  | T   | T   | F     |
  | T   | F   | F     |
  | F   | T   | F     |
  | F   | F   | T     |

  It is therefore possible to implement a half-adder with just these two logic
  gates:

  ![](/img/half-adder-gates-three.png)

  The digital circuit above has the same inputs and outputs as the half adder
  diagram above.

  <iframe src="https://circuitverse.org/simulator/embed/half-adder-67b14a9f-d1ed-4240-ab19-1d753f18a40d" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="400" width="600" allowFullScreen></iframe>

  ## Full adder

  As the half adder only calculates the least significant bit, it is not
  sufficient by itself to complete a binary addition; it cannot account for
  movements in binary place value. To carry out full calculations it must be
  supplemented with the full adder.

  The full adder takes in three inputs and has two outputs. It is identical to the
  half-adder apart from the fact that one of its inputs is **carry-in**. This is
  equivalent to the value that is designated as **carry-out** in a half adder. It
  is an incoming value that is the product of a previous operation that resulted
  in a carry-out. This is added together with the full adders own two inputs (A
  and B) and like the half adder, generates a sum bit and a carry-out bit.

  | A                            | B                             | C_in                     | S                      | C_out                        |
  | ---------------------------- | ----------------------------- | ------------------------ | ---------------------- | ---------------------------- |
  | The first number to be added | The second number to be added | The incoming carried bit | The sum bit (A+B+C_in) | The carry-out bit (A+B+C_in) |

  ![](/img/full-adder-new.png)

  The diagram above is equivalent to the calculation taking place in the fours
  column. It has received a carry from the twos column ($1 + 1$ results in $1$ as
  a carry) and then adds this together with its own inputs ($0$ and $0$).

  ```
    c_1
  0   0   1   0
  0   0   1   1
  _____________
  0   1   0   1
  ```

  ### Implementation with logic gates

  When it comes to implementing the full adder with logic gates we can allow
  ourselves greater abstraction as we already have the half adder to work with. We
  don't need to create a half adder from scratch, we can reuse it.

  We are adding three bits: $1$, $0$ and $0$. This can be achieved with two half
  adders:

  - One half adder (HA1) for the sum of $0 + 0$
  - One half adder (HA2) for the sum of $(0 + 0) + 1$ (the previous sum plus the
    third bit)

  The sum of HA1 ($0 + 0$) is passed in to the B input on HA2 and the $1$ is
  passed in as the A input of HA2. This gives us $1$ as the sum bit of HA2.

  At this point we have completed the addition and have successfully added the
  three bits: $1$, $0$, and $0$ to get $1$ as ths sum. However we also have to
  account for the fact that the addition may result in its own carry-out bit. What
  if the inputs were $1, 1, 0$ for example?

  <iframe src="https://circuitverse.org/simulator/embed/full-adder-674f8ddf-c5aa-42fa-9c0f-4349ffabe723" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="400" width="700" allowFullScreen></iframe>


path: Handle_multiple_endpoints_single_lambda.md
sortable_path: Handle_multiple_endpoints_single_lambda.md
title: Handle multiple API Gateway endpoints in a single lambda
lead:
  With the following config we have a single lambda (`/query-waktime`) with
  multiple endpoints:

checksum: 64aa42198251e03ef3600ba126849e631e989fd286ce097a316ad6fe91a04379
created: 2024-10-22 16:52:57.035124295+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["AWS","aws-lambda"]}
body:
  With the following config we have a single lambda (`/query-waktime`) with
  multiple endpoints:

  ```yml
  Resources:
    QueryWakatimeFunction:
      Type: AWS::Serverless::Function
      Properties:
        CodeUri: src/query-wakatime/
        Handler: index.handler
        Events:
          MainMetrics:
            Type: Api
            Properties:
              Path: /query-wakatime/main-metrics
              Method: get
              Auth:
                Authorizer: AWS_IAM
          Durations:
            Type: Api
            Properties:
              Path: /query-wakatime/durations
              Method: get
              Auth:
                Authorizer: AWS_IAM
          TodayOnly:
            Type: Api
            Properties:
              Path: /query-wakatime/today-only
              Method: get
              Auth:
                Authorizer: AWS_IAM
  ```

  Our endpoints are as follows:

  - `/query-wakatime/main-metrics`
  - `/query-wakatime/durations`
  - `/query-wakatime/today-only`

  Each returns a different dataset from an external API. The lambda function makes
  a different request to the API based on the endpoint it receives and the time
  period that is passed as a query string parameter, e.g.
  `/query-wakatime?range=last_7_days`.

  Below is a template for concisely handling requests to the different endpoints
  in the single lambda function wihout excessive repetition.

  ```js
  const getEndpoint = (path: string, timePeriod?: string | undefined) => {
    const basePath = "/users/current";
    switch (path) {
      case "/query-wakatime/main-metrics":
        return `${basePath}/stats/${timePeriod}`;
      case "/query-wakatime/durations":
        return `${basePath}/summaries?range=${timePeriod}`;
      case "/query-wakatime/today-only":
        return `${basePath}/status_bar/today`;
    }
  };

  export const handler = async (event) => {
    const fetchClient = new FetchClient("https://wakatime.com/api/v1/");
    const path = event.path;
    const timePeriod = event.queryStringParameters?.timePeriod;
    const endpoint = getEndpoint(path, timePeriod);

    const init: RequestInit = {
      headers: {
        Authorization: `Basic ${encodedApiKey}`,
      },
    };

    if (!endpoint) {
      throw new Error("Endpoint is undefined")
    }

   let data = await fetchClient.get(endpoint, init)
   let dataJson = (await data.json()) as string

   response = {
      statusCode: 200,
      body: JSON.stringify(dataJson),
      headers: responseHeaders,
      }
  };



  ```

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

  # Handle multiple API Gateway endpoints in a single lambda

  With the following config we have a single lambda (`/query-waktime`) with
  multiple endpoints:

  ```yml
  Resources:
    QueryWakatimeFunction:
      Type: AWS::Serverless::Function
      Properties:
        CodeUri: src/query-wakatime/
        Handler: index.handler
        Events:
          MainMetrics:
            Type: Api
            Properties:
              Path: /query-wakatime/main-metrics
              Method: get
              Auth:
                Authorizer: AWS_IAM
          Durations:
            Type: Api
            Properties:
              Path: /query-wakatime/durations
              Method: get
              Auth:
                Authorizer: AWS_IAM
          TodayOnly:
            Type: Api
            Properties:
              Path: /query-wakatime/today-only
              Method: get
              Auth:
                Authorizer: AWS_IAM
  ```

  Our endpoints are as follows:

  - `/query-wakatime/main-metrics`
  - `/query-wakatime/durations`
  - `/query-wakatime/today-only`

  Each returns a different dataset from an external API. The lambda function makes
  a different request to the API based on the endpoint it receives and the time
  period that is passed as a query string parameter, e.g.
  `/query-wakatime?range=last_7_days`.

  Below is a template for concisely handling requests to the different endpoints
  in the single lambda function wihout excessive repetition.

  ```js
  const getEndpoint = (path: string, timePeriod?: string | undefined) => {
    const basePath = "/users/current";
    switch (path) {
      case "/query-wakatime/main-metrics":
        return `${basePath}/stats/${timePeriod}`;
      case "/query-wakatime/durations":
        return `${basePath}/summaries?range=${timePeriod}`;
      case "/query-wakatime/today-only":
        return `${basePath}/status_bar/today`;
    }
  };

  export const handler = async (event) => {
    const fetchClient = new FetchClient("https://wakatime.com/api/v1/");
    const path = event.path;
    const timePeriod = event.queryStringParameters?.timePeriod;
    const endpoint = getEndpoint(path, timePeriod);

    const init: RequestInit = {
      headers: {
        Authorization: `Basic ${encodedApiKey}`,
      },
    };

    if (!endpoint) {
      throw new Error("Endpoint is undefined")
    }

   let data = await fetchClient.get(endpoint, init)
   let dataJson = (await data.json()) as string

   response = {
      statusCode: 200,
      body: JSON.stringify(dataJson),
      headers: responseHeaders,
      }
  };



  ```


path: Handling_negative_fractions.md
sortable_path: Handling_negative_fractions.md
title: Negative fractions
checksum: 134790bda657057e507a4087eb4d4ce42197cdcf7f3eb41586397ff6af015de4
created: 2024-10-22 16:52:57.0391904+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["prealgebra","fractions"]}
lead:
  To work with negative fractions we draw on the
  [Rules for operations on like and unlike terms](Rules%20for%20operations%20on%20like%20and%20unlike%20terms.md).

body:
  To work with negative fractions we draw on the
  [Rules for operations on like and unlike terms](Rules%20for%20operations%20on%20like%20and%20unlike%20terms.md).

  ## Fractions with unlike terms

  - A fraction is just one number divided by another. $\frac{5}{10}$ is just ten
    divided by 5.

  - A positive integer divided by a negative or vice versa will always result in a
    negative. Thus $\frac{5}{-15}$ is equal to $-3$.

  - We can therefore express the whole fraction as a negative:

    $$
    -	\frac{5}{15}
    $$

  - Or we could apply the negative symbol to the numerator. It would stand for the
    same value:
    $$
    \\frac{-5}{15}
    $$

  Therefore:

  > Let $a,b$ be any integers. The following three fractions are
  > [equivalent](Equivalent%20fractions.md):
  > $$\frac{-5}{15}, \frac{5}{-15}, - \frac{5}{15}$$

  ## Fractions with like terms

  - In cases where both the numerator and denominator are both negative, the value
    that the fraction represents will be positive overall. This is because the
    quotient of a negative integer divided by a negative integer will always be
    positive.

  - Thus: $$ \frac{- 12xy^2}{ - 18xy^2} = \frac{12xy^2}{18xy^2}$$

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

  # Negative fractions

  To work with negative fractions we draw on the
  [Rules for operations on like and unlike terms](Rules%20for%20operations%20on%20like%20and%20unlike%20terms.md).

  ## Fractions with unlike terms

  - A fraction is just one number divided by another. $\frac{5}{10}$ is just ten
    divided by 5.

  - A positive integer divided by a negative or vice versa will always result in a
    negative. Thus $\frac{5}{-15}$ is equal to $-3$.

  - We can therefore express the whole fraction as a negative:

    $$
    -	\frac{5}{15}
    $$

  - Or we could apply the negative symbol to the numerator. It would stand for the
    same value:
    $$
    \\frac{-5}{15}
    $$

  Therefore:

  > Let $a,b$ be any integers. The following three fractions are
  > [equivalent](Equivalent%20fractions.md):
  > $$\frac{-5}{15}, \frac{5}{-15}, - \frac{5}{15}$$

  ## Fractions with like terms

  - In cases where both the numerator and denominator are both negative, the value
    that the fraction represents will be positive overall. This is because the
    quotient of a negative integer divided by a negative integer will always be
    positive.

  - Thus: $$ \frac{- 12xy^2}{ - 18xy^2} = \frac{12xy^2}{18xy^2}$$


path: Hardware_Description_Language.md
sortable_path: Hardware_Description_Language.md
title: Hardware Description Language
checksum: 165dfe31b4879e4bb4e7cb751f68c3732809d918fb7c78529bc82727f3eadbcb
created: 2024-10-22 16:52:57.057304241+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["nand-to-tetris"]}
lead:
  An HDL is a declarative programming language used to describe the behaviour or
  structure of
  [digital circuits](Integrated_circuits.md).
  They are used to simulate the circuit and check its response.

body:
  An HDL is a declarative programming language used to describe the behaviour or
  structure of
  [digital circuits](Integrated_circuits.md).
  They are used to simulate the circuit and check its response.

  The hardware designer specifies a chip's logic by writing an HDL program which
  is then rigorously tested. At this stage, a
  [hardware simulator](Hardware_simulation.md) takes the
  HDL program as input and creates a software representation of the chip logic.
  The designer can instruct the simulator to test the virtual chip on various sets
  of inputs. This is done to check the chip's functionality but also to benchmark
  a variety of other parameters such as speed of computation and energy
  consumption.

  There are many HDLs but the most popular are VHDL ("very high speed
  integrated-circuit description language") and Verilog.

  ## Usage in _NAND to Tetris_

  We won't use an actual HDL language, instead we will use a simplified toy
  language called HDL that is simple enough that when it is used with a simulator,
  we can learn the main facets of chip design. Its syntax is very similar to VHDL.

  ## Demonstration of HDL program

  ### Boolean function to enact

  We will create an HDL program for an XOR gate that is implemented through the
  following arrangement of NOT, AND, and OR gates:

  ![](/img/xor-hdl.png)

  ### HDL file (`Xor.hdl`):

  Here is our HDL file:

  ```
  /* Xor gate
     If a!=b out=1 else out=0
  */
  CHIP Xor {
    IN a, b;
    OUT out;
    PARTS:
    Not (in=a, out=nota);
    Not (in=b, out=notb);
    And (a=a, b=notb, out=w1);
    And (a=nota, b=b, out=w2);
    Or  (a=w1, b=w2, out=out)
  }
  ```

  #### Interface (`CHIP, IN, OUT`)

  At the top level of the HDL program, the `CHIP` name and `IN`/`OUT` declaration
  is the _interface_ of the chip. Here we specify our naming convention for the
  `IN` and `OUT` values which we will refer to in the implementation declaration
  in `PARTS`.

  #### Implementation (`PARTS`)

  Everything under the `PARTS` section is the chip _implementation_. We can draw
  on composite gates in the `PARTS` declaration (e.g. `Not`, `And`, `Or`). The
  convention is to work from left to right when transcribing from a digital
  circuit diagram

  #### Pins

  In an HDL program we distinguish internal pins along with the standard
  [input and output pins](Integrated_circuits.md).
  At the level of the interface, we are concerned only with input and output pins
  (in the example program these are `a`, `b` and `out`). It is at the level of the
  implementation that internal pins are encountered. In the example these are the
  connections between, e.g. the AND and NOT gates such as
  `And (a=a, b-notb, out=w1)`. This means the AND gate is receiving through its
  `a` pin the input `a` value and through its `b` pin the value of `b` inverted by
  a NOT. `out` is the value that is computed based on the input pins of `a` and
  `b`.

  ### Test file (`Xor.tst`)

  Along with the HDL file we also create a test file. This runs the chip against
  the inputs we supply, these will typically be equivalent to the (left-hand)
  truth-values column in a truth table which is the same as the parameters passed
  to a [Boolean function](Boolean_functions.md), for
  example:

  ```vhdl
  load Xor.hdl
  output-list a, b, out;
  set a 0, set b 0, eval, output;
  set a 0, set b 1, eval, output;
  set a 1, set b 0, eval, output;
  set a 1, set b =, eval, output;
  ```

  ### Output file (`Xor.out`)

  When the test file is run against the HDL file it will generate an output file.
  This is effectively the result of the unit test. And will take the form of a
  truth table:

  ```
  a | b | out
  -----------
  0 | 0 | 0
  0 | 1 | 1
  1 | 0 | 1
  1 | 1 | 0
  ```

raw_content:
  ---
  tags: [nand-to-tetris]
  ---

  # Hardware Description Language

  An HDL is a declarative programming language used to describe the behaviour or
  structure of
  [digital circuits](Integrated_circuits.md).
  They are used to simulate the circuit and check its response.

  The hardware designer specifies a chip's logic by writing an HDL program which
  is then rigorously tested. At this stage, a
  [hardware simulator](Hardware_simulation.md) takes the
  HDL program as input and creates a software representation of the chip logic.
  The designer can instruct the simulator to test the virtual chip on various sets
  of inputs. This is done to check the chip's functionality but also to benchmark
  a variety of other parameters such as speed of computation and energy
  consumption.

  There are many HDLs but the most popular are VHDL ("very high speed
  integrated-circuit description language") and Verilog.

  ## Usage in _NAND to Tetris_

  We won't use an actual HDL language, instead we will use a simplified toy
  language called HDL that is simple enough that when it is used with a simulator,
  we can learn the main facets of chip design. Its syntax is very similar to VHDL.

  ## Demonstration of HDL program

  ### Boolean function to enact

  We will create an HDL program for an XOR gate that is implemented through the
  following arrangement of NOT, AND, and OR gates:

  ![](/img/xor-hdl.png)

  ### HDL file (`Xor.hdl`):

  Here is our HDL file:

  ```
  /* Xor gate
     If a!=b out=1 else out=0
  */
  CHIP Xor {
    IN a, b;
    OUT out;
    PARTS:
    Not (in=a, out=nota);
    Not (in=b, out=notb);
    And (a=a, b=notb, out=w1);
    And (a=nota, b=b, out=w2);
    Or  (a=w1, b=w2, out=out)
  }
  ```

  #### Interface (`CHIP, IN, OUT`)

  At the top level of the HDL program, the `CHIP` name and `IN`/`OUT` declaration
  is the _interface_ of the chip. Here we specify our naming convention for the
  `IN` and `OUT` values which we will refer to in the implementation declaration
  in `PARTS`.

  #### Implementation (`PARTS`)

  Everything under the `PARTS` section is the chip _implementation_. We can draw
  on composite gates in the `PARTS` declaration (e.g. `Not`, `And`, `Or`). The
  convention is to work from left to right when transcribing from a digital
  circuit diagram

  #### Pins

  In an HDL program we distinguish internal pins along with the standard
  [input and output pins](Integrated_circuits.md).
  At the level of the interface, we are concerned only with input and output pins
  (in the example program these are `a`, `b` and `out`). It is at the level of the
  implementation that internal pins are encountered. In the example these are the
  connections between, e.g. the AND and NOT gates such as
  `And (a=a, b-notb, out=w1)`. This means the AND gate is receiving through its
  `a` pin the input `a` value and through its `b` pin the value of `b` inverted by
  a NOT. `out` is the value that is computed based on the input pins of `a` and
  `b`.

  ### Test file (`Xor.tst`)

  Along with the HDL file we also create a test file. This runs the chip against
  the inputs we supply, these will typically be equivalent to the (left-hand)
  truth-values column in a truth table which is the same as the parameters passed
  to a [Boolean function](Boolean_functions.md), for
  example:

  ```vhdl
  load Xor.hdl
  output-list a, b, out;
  set a 0, set b 0, eval, output;
  set a 0, set b 1, eval, output;
  set a 1, set b 0, eval, output;
  set a 1, set b =, eval, output;
  ```

  ### Output file (`Xor.out`)

  When the test file is run against the HDL file it will generate an output file.
  This is effectively the result of the unit test. And will take the form of a
  truth table:

  ```
  a | b | out
  -----------
  0 | 0 | 0
  0 | 1 | 1
  1 | 0 | 1
  1 | 1 | 0
  ```


path: Hardware_abstraction_and_modularity.md
sortable_path: Hardware_abstraction_and_modularity.md
title: Hardware abstraction and modularity
checksum: 6d101cf97794548ab23f8a70cf416e70d8642d888d2ba82ecbbe32ac1327bc4a
created: 2024-10-22 16:52:57.073942054+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["nand-to-tetris"]}
lead:
  In computer architecture we deal with complexity by breaking the system into
  **modules**. For each module we distinguish **_abstraction_** from
  **_implementation_**.

body:
  In computer architecture we deal with complexity by breaking the system into
  **modules**. For each module we distinguish **_abstraction_** from
  **_implementation_**.

  <dl>
    <dt>abstraction</dt>
    <dd>what the module does</dd>
    <dt>implementation</dt>
    <dd>how it does it</dd>
  </dl>

  When using a module as a building block you are to focus exclusively on the
  module's abstraction, ignoring completely its implementation details.

  > The abstraction-implementation paradigm helps developers manage complexity and
  > maintain sanity: by dividing an overwhelming system into well-defined modules
  > we create manageable chunks of implementation work and localize error
  > detection and correction.

  [N.Nisan, S.Schoken. 2021. **The Elements of Computing Systems** (Second
  Edition)]

  The design of the diagram below emphasises the role of abstraction and
  modularity in the movement from transistors to chips:

  ![](/img/hardware-abstraction-hierarchy.png)

raw_content:
  ---
  tags: [nand-to-tetris]
  ---

  # Hardware abstraction and modularity

  In computer architecture we deal with complexity by breaking the system into
  **modules**. For each module we distinguish **_abstraction_** from
  **_implementation_**.

  <dl>
    <dt>abstraction</dt>
    <dd>what the module does</dd>
    <dt>implementation</dt>
    <dd>how it does it</dd>
  </dl>

  When using a module as a building block you are to focus exclusively on the
  module's abstraction, ignoring completely its implementation details.

  > The abstraction-implementation paradigm helps developers manage complexity and
  > maintain sanity: by dividing an overwhelming system into well-defined modules
  > we create manageable chunks of implementation work and localize error
  > detection and correction.

  [N.Nisan, S.Schoken. 2021. **The Elements of Computing Systems** (Second
  Edition)]

  The design of the diagram below emphasises the role of abstraction and
  modularity in the movement from transistors to chips:

  ![](/img/hardware-abstraction-hierarchy.png)


path: Hardware_simulation.md
sortable_path: Hardware_simulation.md
title: Hardware simulation
checksum: 81eaf5ae524f90bc704e6b23c5218acb3c5324da1ad875beec4ae07caba430d7
created: 2024-10-22 16:52:57.076856387+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["nand-to-tetris"]}
lead:
  In order to test our
  [HDL](Hardware_Description_Language.md) files we load
  them into the hardware simulator program. We will demonstrate this with the
  following XOR implementation:

body:
  In order to test our
  [HDL](Hardware_Description_Language.md) files we load
  them into the hardware simulator program. We will demonstrate this with the
  following XOR implementation:

  ![](/img/xor-addition-four.png)

  There are several simulation options:

  - interactive
  - script-based (where we load a test script into the simulator along with the
    HDL file
  - comparative (running the HDL program against our intended output specified in
    the `.cmp` file)

  The use-cases for each mode are based on the complexity of the chip you are
  evaluating. For a small chip, interactive and script-based testing would be
  sufficient but for much larger components like an ALU a comparative approach
  would be more manageable and efficient.

  ## Interactive

  The image below shows a basic interactive usage of the simulator. We have
  uploaded the `Xor.hdl` file into the simulator and changed the input pins to
  `a=1, b=0` and clicked the calculator icon (representing "evaluation"). This
  then shows the output and internal pin values for these inputs.

  <img src="/home/thomas/repos/computer_science/img/hardware-sim-basic.png" >

  ## Script-based

  This time we have clicked the script icon to load `Xor.tst`. This loads the test
  script into the main GUI panel on the left. We can step through each line of the
  test file and we will see the pin values update in accordance with the test.

  When this is run it automatically generates an output file in the source
  directory at `Xor.out`. This can be viewed within the simulator via the 'View'
  drop down.

  ![](/img/hd-sim-test.png)

  ## Comparison-based

  With a comparison-based approach to chip testing we run a comparison against the
  `.out` file that the simulator generates when running the HDL program against a
  `.cmp` comparison file that we provide. Both are simply truth-tables. For XOR if
  the program matched the comparison specification both `Xor.out` and `Xor.cmp`
  would look like the following:

  ```
  a | b | out
  -----------
  0 | 0 | 0
  0 | 1 | 1
  1 | 0 | 1
  1 | 1 | 0
  ```

  You don't have to do anything to apply the comparison since the compare file
  will already be loaded as part of the test file's set up:

  ```vhdl
  load Xor.hdl
  output file Xor.out
  compare-to Xor.cmp
  output-list a, b, out;
  set ...
  ```

raw_content:
  ---
  tags: [nand-to-tetris]
  ---

  # Hardware simulation

  In order to test our
  [HDL](Hardware_Description_Language.md) files we load
  them into the hardware simulator program. We will demonstrate this with the
  following XOR implementation:

  ![](/img/xor-addition-four.png)

  There are several simulation options:

  - interactive
  - script-based (where we load a test script into the simulator along with the
    HDL file
  - comparative (running the HDL program against our intended output specified in
    the `.cmp` file)

  The use-cases for each mode are based on the complexity of the chip you are
  evaluating. For a small chip, interactive and script-based testing would be
  sufficient but for much larger components like an ALU a comparative approach
  would be more manageable and efficient.

  ## Interactive

  The image below shows a basic interactive usage of the simulator. We have
  uploaded the `Xor.hdl` file into the simulator and changed the input pins to
  `a=1, b=0` and clicked the calculator icon (representing "evaluation"). This
  then shows the output and internal pin values for these inputs.

  <img src="/home/thomas/repos/computer_science/img/hardware-sim-basic.png" >

  ## Script-based

  This time we have clicked the script icon to load `Xor.tst`. This loads the test
  script into the main GUI panel on the left. We can step through each line of the
  test file and we will see the pin values update in accordance with the test.

  When this is run it automatically generates an output file in the source
  directory at `Xor.out`. This can be viewed within the simulator via the 'View'
  drop down.

  ![](/img/hd-sim-test.png)

  ## Comparison-based

  With a comparison-based approach to chip testing we run a comparison against the
  `.out` file that the simulator generates when running the HDL program against a
  `.cmp` comparison file that we provide. Both are simply truth-tables. For XOR if
  the program matched the comparison specification both `Xor.out` and `Xor.cmp`
  would look like the following:

  ```
  a | b | out
  -----------
  0 | 0 | 0
  0 | 1 | 1
  1 | 0 | 1
  1 | 1 | 0
  ```

  You don't have to do anything to apply the comparison since the compare file
  will already be loaded as part of the test file's set up:

  ```vhdl
  load Xor.hdl
  output file Xor.out
  compare-to Xor.cmp
  output-list a, b, out;
  set ...
  ```


path: Headless_Raspi_network_setup.md
sortable_path: Headless_Raspi_network_setup.md
title: Headless network setup
checksum: 1130cb421dc289d445aaedfbcfa79cac0079753aa0c992958503737758883f4b
created: 2024-10-22 16:52:57.082466722+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
lead:
  If you are using a headless system and you aren't using an ethernet connection,
  you face a chicken-egg issue of how to connect the device to WiFi via `ssh` when
  the device isn't yet on the network.

body:
  If you are using a headless system and you aren't using an ethernet connection,
  you face a chicken-egg issue of how to connect the device to WiFi via `ssh` when
  the device isn't yet on the network.

  You can get around this by saving a file called `wp_supplicant.conf` to the
  `/boot` directory of the device. This specifies the SSID name and password and
  will be used to auto-connect when the device boots.

  For example:

  ```
  country=GB
  ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
  update_config=1
  network={
       ssid="SKYVL7XP"
       scan_ssid=1
       psk="WIFI_PASSWORD"
       key_mgmt=WPA-PSK
  }
  ```

  ## Related notes

raw_content:
  ---
  id: hb10
  tags:
    - networks
    - Linux
    - raspberry-pi
    - procedural
  created: Wednesday, June 12, 2024
  ---

  # Headless network setup

  If you are using a headless system and you aren't using an ethernet connection,
  you face a chicken-egg issue of how to connect the device to WiFi via `ssh` when
  the device isn't yet on the network.

  You can get around this by saving a file called `wp_supplicant.conf` to the
  `/boot` directory of the device. This specifies the SSID name and password and
  will be used to auto-connect when the device boots.

  For example:

  ```
  country=GB
  ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
  update_config=1
  network={
       ssid="SKYVL7XP"
       scan_ssid=1
       psk="WIFI_PASSWORD"
       key_mgmt=WPA-PSK
  }
  ```

  ## Related notes

metadata:
  {"created":"Wednesday, June 12, 2024","id":"hb10","tags":["networks","Linux","raspberry-pi","procedural"]}


path: Heap_memory.md
sortable_path: Heap_memory.md
title: Heap memory
lead:
  Along with [[Stack_memory|Stack memory]], programs make use of _heap memory_
  during runtime.

checksum: f599daed3b0ffc815cbf770feb48f87ca0f44f85901555625c85c9884270a479
created: 2024-10-22 16:52:57.085725092+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"created":"Saturday, April 20, 2024","id":"18bl","tags":["memory","C"]}
body:
  Along with [[Stack_memory|Stack memory]], programs make use of _heap memory_
  during runtime.

  Heap memory does not use a standardised data structure and can be accessed from
  any point within the program.

  Whereas stack memory with it's LIFO structure has memory management built-in
  when programs allocate memory from the heap they must manually deallocate it
  when it is no longer required. This process of "freeing memory" is known as
  _garbage collection_. In a language like C, this is the explicit concern of the
  programmer and is not abstracted away. Failure to properly manage garbage
  collection is what causes [[Memory_leaks]].

  Heap memory is used in combination with the stack since a given heap memory
  allocation address is stored as a stack variable during runtime. It points to
  the heap memory address whilst not being that memory itself.

  Here is an example of managing heap memory allocation in C:

  ```c
  void * data;
  data = malloc(512)
  ```

  The first line assigns a special _pointer_ variable (indicated by `void *`
  rather than `int` or `str`) . This is a variable only holds a memory address.
  The `malloc` method requests 512 bytes that it wants to assign to the `data`
  variable. It will return the address of the first byte in the newly allocated
  memory. `data` will then refer to the address on the stack that holds the
  address allocation on the heap.

  ## Related notes

raw_content:
  ---
  id: 18bl
  tags: [memory, C]
  created: Saturday, April 20, 2024
  ---

  # Heap memory

  Along with [[Stack_memory|Stack memory]], programs make use of _heap memory_
  during runtime.

  Heap memory does not use a standardised data structure and can be accessed from
  any point within the program.

  Whereas stack memory with it's LIFO structure has memory management built-in
  when programs allocate memory from the heap they must manually deallocate it
  when it is no longer required. This process of "freeing memory" is known as
  _garbage collection_. In a language like C, this is the explicit concern of the
  programmer and is not abstracted away. Failure to properly manage garbage
  collection is what causes [[Memory_leaks]].

  Heap memory is used in combination with the stack since a given heap memory
  allocation address is stored as a stack variable during runtime. It points to
  the heap memory address whilst not being that memory itself.

  Here is an example of managing heap memory allocation in C:

  ```c
  void * data;
  data = malloc(512)
  ```

  The first line assigns a special _pointer_ variable (indicated by `void *`
  rather than `int` or `str`) . This is a variable only holds a memory address.
  The `malloc` method requests 512 bytes that it wants to assign to the `data`
  variable. It will return the address of the first byte in the newly allocated
  memory. `data` will then refer to the address on the stack that holds the
  address allocation on the heap.

  ## Related notes


path: Hexadecimal_number_system.md
sortable_path: Hexadecimal_number_system.md
title: Hexadecimal number system
checksum: 964ff70772f2ed1f0a21e567fb11783b90624a99c3327a8257a9b75bf82fa805
created: 2024-10-22 16:52:57.102595523+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["number-systems","computer-architecture"]}
lead:
  Hexadecimal is the other main number system used in computing. It works in
  tandem with the [binary number system](Binary_number_system.md) and provides an
  easier and more accessible means of working with long sequences of binary
  numbers.

body:
  Hexadecimal is the other main number system used in computing. It works in
  tandem with the [binary number system](Binary_number_system.md) and provides an
  easier and more accessible means of working with long sequences of binary
  numbers.

  ## Hexadecimal place value

  Unlike denary which uses base ten and binary which uses base two, hexadecimal
  uses base 16 as its place value.

  > Each place in a hexadecimal number represents a power of 16 and each place can
  > be one of 16 symbols.

  ## Hexadecimal values

  The table below shows the symbols comprising hexadecimal alongside their denary
  and binary equivalents:

  | Hexadecimal | Decimal | Binary |
  | ----------- | ------- | ------ |
  | 0           | 0       | 0000   |
  | 1           | 1       | 0001   |
  | 2           | 2       | 0010   |
  | 3           | 3       | 0011   |
  | 4           | 4       | 0100   |
  | 5           | 5       | 0101   |
  | 6           | 6       | 0110   |
  | 7           | 7       | 0111   |
  | 8           | 8       | 1000   |
  | 9           | 9       | 1001   |
  | A           | 10      | 1010   |
  | B           | 11      | 1011   |
  | C           | 12      | 1100   |
  | D           | 13      | 1101   |
  | E           | 14      | 1110   |
  | F           | 15      | 1111   |

  This table shows the raw value of each hexadecimal place value:

  | $16^{3}$ | $16^{2}$ | $16^{1}$ | $16^{0}$ |
  | -------- | -------- | -------- | -------- |
  | 4096     | 256      | 16       | 1        |

  ## Hexadecimal prefix

  The custom is to prefix a hexadecimal number with `0x` to indicate that the
  number is hexadecimal.

  ## Converting hexadecimal numbers

  Using the previous table we can convert hexadecimal values to decimal.

  For example we can convert `1A5` as follows, working from right to left:

  $(5 \cdot 1 = 5) + (A \cdot 16 = 160) + (1 \cdot 256 = 256) = 421$

  The process is quite easy: we get the n from $16^{n}$ based on the position of
  the digit and then multiply this by the value of the symbol (1,2,...F):

  $$
    16^{n} \cdot 1,2,...F
  $$

  As applied to `0x1A5`:

  | $16^{3}$               | $16^{2}$              | $16^{1}$                   | $16^{0}$            |
  | ---------------------- | --------------------- | -------------------------- | ------------------- |
  | $1\cdot 16^{3} = 4096$ | $1\cdot 16^{2} = 256$ | $A (10)\cdot 16^{1} = 160$ | $5\cdot 16^{0} = 5$ |

  Another example for `0xF00F`:

  $(15 \cdot 4096 = 61440) + (0 \cdot 256 = 0) + (0 \cdot 16 = 0) + (15 \cdot 1 = 15) = 61455$

  ## Using hexadecimal to simplify binary numbers

  Whilst computers themselves do not use the hexadecimal number system (everything
  is binary), hexadecimal offers advantages for humans who must work with binary:

  1. It is much easier to read a hexadecimal number than long sequences of binary
     numbers
  2. It is easier to quickly convert binary numbers to hexadecimal than to convert
     binary numbers to decimal

  Look at the following equivalences

  | Number system   | Example 1           | Example 2           |
  | --------------- | ------------------- | ------------------- |
  | **Binary**      | 1111 0000 0000 1111 | 1000 1000 1000 0001 |
  | **Hexadecimal** | F00F                | 8881                |
  | **Decimal**     | 61,455              | 34,945              |

  It is obvious that a pattern is maintained between the hexadecimal and binary
  numbers and that this pattern is obscured by the decimal conversion. In the
  first example the binary half-byte `1111` is matched by the hexadecimal `F00F`.

  Mathematically comparing hex `F` and binary `1111`:

  $$
    \textsf{1111} = ((1 \cdot 2^{3}) + (1 \cdot 2^{2}) + (1 \cdot 2^{1}) + (1 \cdot 2^{0})) \\
    = 8 + 4 + 2 + 1 \\
    = 15
  $$

  $$
    \textsf{F} = 15 \cdot 16^{0}  \\
    = 15
  $$

  ![](/img/hexadecimal-to-bytes.svg)

  > Every four bits (or half byte) in binary corresponds to one symbol in
  > hexadecimal. Therefore **a byte can be easily represented with two hexadecimal
  > symbols, a 16-bit number can be represented with four hex symbols, a 32-bit
  > number can represented with eight hex symbols and so on.**

raw_content:
  ---
  tags:
    - number-systems
    - computer-architecture
  ---

  # Hexadecimal number system

  Hexadecimal is the other main number system used in computing. It works in
  tandem with the [binary number system](Binary_number_system.md) and provides an
  easier and more accessible means of working with long sequences of binary
  numbers.

  ## Hexadecimal place value

  Unlike denary which uses base ten and binary which uses base two, hexadecimal
  uses base 16 as its place value.

  > Each place in a hexadecimal number represents a power of 16 and each place can
  > be one of 16 symbols.

  ## Hexadecimal values

  The table below shows the symbols comprising hexadecimal alongside their denary
  and binary equivalents:

  | Hexadecimal | Decimal | Binary |
  | ----------- | ------- | ------ |
  | 0           | 0       | 0000   |
  | 1           | 1       | 0001   |
  | 2           | 2       | 0010   |
  | 3           | 3       | 0011   |
  | 4           | 4       | 0100   |
  | 5           | 5       | 0101   |
  | 6           | 6       | 0110   |
  | 7           | 7       | 0111   |
  | 8           | 8       | 1000   |
  | 9           | 9       | 1001   |
  | A           | 10      | 1010   |
  | B           | 11      | 1011   |
  | C           | 12      | 1100   |
  | D           | 13      | 1101   |
  | E           | 14      | 1110   |
  | F           | 15      | 1111   |

  This table shows the raw value of each hexadecimal place value:

  | $16^{3}$ | $16^{2}$ | $16^{1}$ | $16^{0}$ |
  | -------- | -------- | -------- | -------- |
  | 4096     | 256      | 16       | 1        |

  ## Hexadecimal prefix

  The custom is to prefix a hexadecimal number with `0x` to indicate that the
  number is hexadecimal.

  ## Converting hexadecimal numbers

  Using the previous table we can convert hexadecimal values to decimal.

  For example we can convert `1A5` as follows, working from right to left:

  $(5 \cdot 1 = 5) + (A \cdot 16 = 160) + (1 \cdot 256 = 256) = 421$

  The process is quite easy: we get the n from $16^{n}$ based on the position of
  the digit and then multiply this by the value of the symbol (1,2,...F):

  $$
    16^{n} \cdot 1,2,...F
  $$

  As applied to `0x1A5`:

  | $16^{3}$               | $16^{2}$              | $16^{1}$                   | $16^{0}$            |
  | ---------------------- | --------------------- | -------------------------- | ------------------- |
  | $1\cdot 16^{3} = 4096$ | $1\cdot 16^{2} = 256$ | $A (10)\cdot 16^{1} = 160$ | $5\cdot 16^{0} = 5$ |

  Another example for `0xF00F`:

  $(15 \cdot 4096 = 61440) + (0 \cdot 256 = 0) + (0 \cdot 16 = 0) + (15 \cdot 1 = 15) = 61455$

  ## Using hexadecimal to simplify binary numbers

  Whilst computers themselves do not use the hexadecimal number system (everything
  is binary), hexadecimal offers advantages for humans who must work with binary:

  1. It is much easier to read a hexadecimal number than long sequences of binary
     numbers
  2. It is easier to quickly convert binary numbers to hexadecimal than to convert
     binary numbers to decimal

  Look at the following equivalences

  | Number system   | Example 1           | Example 2           |
  | --------------- | ------------------- | ------------------- |
  | **Binary**      | 1111 0000 0000 1111 | 1000 1000 1000 0001 |
  | **Hexadecimal** | F00F                | 8881                |
  | **Decimal**     | 61,455              | 34,945              |

  It is obvious that a pattern is maintained between the hexadecimal and binary
  numbers and that this pattern is obscured by the decimal conversion. In the
  first example the binary half-byte `1111` is matched by the hexadecimal `F00F`.

  Mathematically comparing hex `F` and binary `1111`:

  $$
    \textsf{1111} = ((1 \cdot 2^{3}) + (1 \cdot 2^{2}) + (1 \cdot 2^{1}) + (1 \cdot 2^{0})) \\
    = 8 + 4 + 2 + 1 \\
    = 15
  $$

  $$
    \textsf{F} = 15 \cdot 16^{0}  \\
    = 15
  $$

  ![](/img/hexadecimal-to-bytes.svg)

  > Every four bits (or half byte) in binary corresponds to one symbol in
  > hexadecimal. Therefore **a byte can be easily represented with two hexadecimal
  > symbols, a 16-bit number can be represented with four hex symbols, a 32-bit
  > number can represented with eight hex symbols and so on.**


path: IP_addresses.md
sortable_path: IP_addresses.md
title: IP addresses
lead: ## IPv4 vs IPv6
checksum: 0f01198098c3d02c6940f70369dd6d8e152f046585adc2f39f3fc788135e834a
created: 2024-10-22 16:52:57.117946015+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"created":"Friday, August 16, 2024","tags":["networks","internet"]}
body:
  ## IPv4 vs IPv6

  IP addresses come in two formats and bit-lengths depending on which version of
  the [Internet Protocol](Internet_Layer_of_Internet_Protocol.md) is being used.

  - IPv4 addresses are 32-bits in length
  - IPv6 addresses are 128-bits in length

  The larger the bit-length, the more unique addresses that can be generated. Thus
  IPv6 can create more addresses that IPv4.

  This entry focuses on IPv4 addresses.

  ## Anatomy of an IPv4 address

  Each of the 32 bits are divided into four groups which are each 8 bits in
  length. Each group therefore comprises an octet and is separated by dots.

  The decimal number system, rather than binary is used to represent the values.
  This means each binary octet is three decimal digits in length.

  The 32 bits comprise two major groupings

  - the **network prefix**
  - the **host**

  ![Different sections of IP address](../img/ip-address-anatomy.png)

  Devices connected to the same network share the same network prefix. They are
  said to be on the same **subnet**. However each device will have a unique value
  for the host octet.

  (This account is somewhat idealised. The length of the prefix does not always
  align with the octet boundary. For instance, the prefix could be 25 bits in
  length, rather than 24. In this case it would "steal" one bit from the host
  section making it 23 bits in length.)

  Any changes to the length of the network prefix change the number of hosts
  available on the subnet. If the network prefix is longer, the number of unique
  hosts is reduced. If the network prefix is shorted, the number of unique hosts
  is increased.

  ## Identifying the network and host groupings

  Given that the network prefix and host identifier do not always stick to set
  boundaries, we require a way of distinguishing the two values. There are two
  dominant methods.

  ### CIDR notation

  This stands for _Classless Inter-Domain Routing_. An example:

  > 192.168.1.23/24

  The value after the forward slash is the number of bits that specify the network
  address. In the example, it is the standard 24-bits. Thus 192.168.1.23 stands
  for the network.

  ### Subnet masking

  The subnet mask is another number _in addition to_ the network address. It is a
  kind of superimposed map on top of the address.

  Here is an example of a subnet mask:

  > 11111111.11111111.11111111.00000000

  The denary form would be as follows (as 255 is the maximal decimal number that
  can be represented with a single 8-bit number and 0 is the smallest):

  > 255.255.255.0

  In the binary form, the 1 values represent the bits which designate the network
  address and the 0 values represent the bits that designate the host.

  In the example above this corresponds to the idealised 32-bit:8-bit ratio of
  CIDR /24.

  There is a clever consequence of the subnet mask: if you apply a bitwise AND
  operator against the IP address and mask (both in their binary form) you can
  determine whether two addresses are on the same network.

  To compare the IP address 192.168.1.23 against 192.168.1.100 to demonstrate:

  ```
  192.168.1.23:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ___________________________________

  192.168.1.100:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ```

  After applying the bitwise AND logic we see that the result is identical for
  both IPs indicating they are on the same network (share the same network
  prefix), whilst the host value is "masked".

  Here is a scenario where the subnet mask indicates that two IPs are not on the
  shared network:

  ```
  192.168.1.23:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ___________________________________

  192.168.2.1
  IP:     11000000.10101000.00000010.00000001
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000010.00000000

  ```

  255.255.255.0 is not the only possible subnet mask. There are masks
  corresponding to ratios other than CIDR /24. For example:

  - 255.0.0.0 (/8)
  - 255.255.0.0 (/8)
  - 255.255.255.192 (/26)
  - 255.255.255.240 (/28)

  As noted previously, the different ratios will obviously affect the number of
  unique hosts available on the subnet.

  ## Determining the range of hosts: practical example

  Let's say we have the following IP address expressed in CIDR: 192.168.0.133/27.
  In binary this would be:

  ```
  110000000.10101000.00000000.100--00101
  ```

  The `--` indicates the demarcation point between the network prefix and the bits
  designated for the host. 32 - 27 leaves us 5 bits for our range of hosts. This
  gives us 2^5 = 32 unique host values.

  In fact it will actually be 30 values since the first value (`00000`) will be
  used to identify the network itself and the last value (`11111`) will be the
  broadcast address - the address used when a message needs to be sent to all
  hosts on the network.

  An actual host address in this range is included in the example: `00101`.

raw_content:
  ---
  tags: [networks, internet]
  created: Friday, August 16, 2024
  ---

  # IP addresses

  ## IPv4 vs IPv6

  IP addresses come in two formats and bit-lengths depending on which version of
  the [Internet Protocol](Internet_Layer_of_Internet_Protocol.md) is being used.

  - IPv4 addresses are 32-bits in length
  - IPv6 addresses are 128-bits in length

  The larger the bit-length, the more unique addresses that can be generated. Thus
  IPv6 can create more addresses that IPv4.

  This entry focuses on IPv4 addresses.

  ## Anatomy of an IPv4 address

  Each of the 32 bits are divided into four groups which are each 8 bits in
  length. Each group therefore comprises an octet and is separated by dots.

  The decimal number system, rather than binary is used to represent the values.
  This means each binary octet is three decimal digits in length.

  The 32 bits comprise two major groupings

  - the **network prefix**
  - the **host**

  ![Different sections of IP address](../img/ip-address-anatomy.png)

  Devices connected to the same network share the same network prefix. They are
  said to be on the same **subnet**. However each device will have a unique value
  for the host octet.

  (This account is somewhat idealised. The length of the prefix does not always
  align with the octet boundary. For instance, the prefix could be 25 bits in
  length, rather than 24. In this case it would "steal" one bit from the host
  section making it 23 bits in length.)

  Any changes to the length of the network prefix change the number of hosts
  available on the subnet. If the network prefix is longer, the number of unique
  hosts is reduced. If the network prefix is shorted, the number of unique hosts
  is increased.

  ## Identifying the network and host groupings

  Given that the network prefix and host identifier do not always stick to set
  boundaries, we require a way of distinguishing the two values. There are two
  dominant methods.

  ### CIDR notation

  This stands for _Classless Inter-Domain Routing_. An example:

  > 192.168.1.23/24

  The value after the forward slash is the number of bits that specify the network
  address. In the example, it is the standard 24-bits. Thus 192.168.1.23 stands
  for the network.

  ### Subnet masking

  The subnet mask is another number _in addition to_ the network address. It is a
  kind of superimposed map on top of the address.

  Here is an example of a subnet mask:

  > 11111111.11111111.11111111.00000000

  The denary form would be as follows (as 255 is the maximal decimal number that
  can be represented with a single 8-bit number and 0 is the smallest):

  > 255.255.255.0

  In the binary form, the 1 values represent the bits which designate the network
  address and the 0 values represent the bits that designate the host.

  In the example above this corresponds to the idealised 32-bit:8-bit ratio of
  CIDR /24.

  There is a clever consequence of the subnet mask: if you apply a bitwise AND
  operator against the IP address and mask (both in their binary form) you can
  determine whether two addresses are on the same network.

  To compare the IP address 192.168.1.23 against 192.168.1.100 to demonstrate:

  ```
  192.168.1.23:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ___________________________________

  192.168.1.100:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ```

  After applying the bitwise AND logic we see that the result is identical for
  both IPs indicating they are on the same network (share the same network
  prefix), whilst the host value is "masked".

  Here is a scenario where the subnet mask indicates that two IPs are not on the
  shared network:

  ```
  192.168.1.23:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ___________________________________

  192.168.2.1
  IP:     11000000.10101000.00000010.00000001
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000010.00000000

  ```

  255.255.255.0 is not the only possible subnet mask. There are masks
  corresponding to ratios other than CIDR /24. For example:

  - 255.0.0.0 (/8)
  - 255.255.0.0 (/8)
  - 255.255.255.192 (/26)
  - 255.255.255.240 (/28)

  As noted previously, the different ratios will obviously affect the number of
  unique hosts available on the subnet.

  ## Determining the range of hosts: practical example

  Let's say we have the following IP address expressed in CIDR: 192.168.0.133/27.
  In binary this would be:

  ```
  110000000.10101000.00000000.100--00101
  ```

  The `--` indicates the demarcation point between the network prefix and the bits
  designated for the host. 32 - 27 leaves us 5 bits for our range of hosts. This
  gives us 2^5 = 32 unique host values.

  In fact it will actually be 30 values since the first value (`00000`) will be
  used to identify the network itself and the last value (`11111`) will be the
  broadcast address - the address used when a message needs to be sent to all
  hosts on the network.

  An actual host address in this range is included in the example: `00101`.


path: Identify_merged_branches.md
sortable_path: Identify_merged_branches.md
title: Identify merged branches
lead:
  ```sh
  # View merged
  git branch --merged

checksum: 4276aeff9c2f164c65c35fb617983aa2739633e1617b080b74ae32b35baacc1a
created: 2024-10-22 16:52:57.134182357+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["git"]}
body:
  ```sh
  # View merged
  git branch --merged

  # View merged remote
  git branch -r --merged

  # View unmerged
  git branch --no-merged

  # View unmerged remote

  git branch -r --no-merged
  ```

  The above cases run the merge check from the point of view of HEAD as this is
  the default.

  But we can also run it from any branch.

  ```
  git branch --merged non_head_branch
  ```

  ## Demonstration

  We have the following branches

  ```
  git branch

  * main
  key_feature
  another_feature
  ```

  The branches other than `main`, have not been merged:

  ```
  git branch --merged

  * main
  ```

  Just to confirm:

  ```
  git branch --no-merged

  key_feature
  another_feature
  ```

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

  # Identify merged branches

  ```sh
  # View merged
  git branch --merged

  # View merged remote
  git branch -r --merged

  # View unmerged
  git branch --no-merged

  # View unmerged remote

  git branch -r --no-merged
  ```

  The above cases run the merge check from the point of view of HEAD as this is
  the default.

  But we can also run it from any branch.

  ```
  git branch --merged non_head_branch
  ```

  ## Demonstration

  We have the following branches

  ```
  git branch

  * main
  key_feature
  another_feature
  ```

  The branches other than `main`, have not been merged:

  ```
  git branch --merged

  * main
  ```

  Just to confirm:

  ```
  git branch --no-merged

  key_feature
  another_feature
  ```


path: Importing_MongoDB_data.md
sortable_path: Importing_MongoDB_data.md
title: Importing data to MongoDB
checksum: 677b579e14fd2d752e80d93d1b76f3f2d113a7454f3689917dc8a11c9b4564cd
created: 2024-10-22 16:52:57.13720786+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["mongo-db","node-js","databases"]}
lead:
  We can use the command line to import data into a Mongo database. We can also
  combine the importing of data with the creation of a database:

body:
  We can use the command line to import data into a Mongo database. We can also
  combine the importing of data with the creation of a database:

  ```bash
  mongoimport --db [preexisting_or_new_database name] --collection [collection_name] --file sourcedata.json --jsonArray
  ```

  We use `--jsonArray` to indicate the format of the data we are importing.

  Once this has executed, the data will be visible in Compass.

raw_content:
  ---
  tags:
    - mongo-db
    - node-js
    - databases
  ---

  # Importing data to MongoDB

  We can use the command line to import data into a Mongo database. We can also
  combine the importing of data with the creation of a database:

  ```bash
  mongoimport --db [preexisting_or_new_database name] --collection [collection_name] --file sourcedata.json --jsonArray
  ```

  We use `--jsonArray` to indicate the format of the data we are importing.

  Once this has executed, the data will be visible in Compass.


path: Increasing_fractions_to_their_highest_terms.md
sortable_path: Increasing_fractions_to_their_highest_terms.md
title: Increasing fractions to their highest terms
checksum: 29fe4d70e55797777968ba4e5bf1a14774a0ffe5cad62973beab7c005bc209f3
created: 2024-10-22 16:52:57.14035676+00:00
modified: 2024-10-18 18:15:04.260990305+00:00
metadata: {"tags":["prealgebra","fractions"]}
lead:
  Given the equivalence between factors and divisors we can increase fractions to
  higher terms in a very similar way to when we reduce fractions. In the latter
  case we are dividing by divisors to reduce. In the former, we are multiplying by
  factors to increase.

body:
  Given the equivalence between factors and divisors we can increase fractions to
  higher terms in a very similar way to when we reduce fractions. In the latter
  case we are dividing by divisors to reduce. In the former, we are multiplying by
  factors to increase.

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

  ## Demonstration

  _Express $\frac{3}{4}$ as an equivalent fraction having the denominator 20_

  $$
  \\frac{3 \cdot 4}{5 \cdot 4} = \frac{12}{20}
  $$

  _Express $\frac{2}{3}$ as an equivalent fraction having the denominator 21_

  $$
  \\frac{2 \cdot 7}{3 \cdot 7} = \frac{14}{21}
  $$

  ## Increasing fractions with variables to higher terms

  _Express $\frac{2}{9}$ as an equivalent fraction having the denominator 18a_

  In these cases, just append the variable to the factor:

  $$
  \\frac{2 \cdot 2a}{9 \cdot 2a} = \frac{4a}{18a}
  $$

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

  # Increasing fractions to their highest terms

  Given the equivalence between factors and divisors we can increase fractions to
  higher terms in a very similar way to when we reduce fractions. In the latter
  case we are dividing by divisors to reduce. In the former, we are multiplying by
  factors to increase.

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

  ## Demonstration

  _Express $\frac{3}{4}$ as an equivalent fraction having the denominator 20_

  $$
  \\frac{3 \cdot 4}{5 \cdot 4} = \frac{12}{20}
  $$

  _Express $\frac{2}{3}$ as an equivalent fraction having the denominator 21_

  $$
  \\frac{2 \cdot 7}{3 \cdot 7} = \frac{14}{21}
  $$

  ## Increasing fractions with variables to higher terms

  _Express $\frac{2}{9}$ as an equivalent fraction having the denominator 18a_

  In these cases, just append the variable to the factor:

  $$
  \\frac{2 \cdot 2a}{9 \cdot 2a} = \frac{4a}{18a}
  $$


path: Insert_data_into_SQL_table.md
sortable_path: Insert_data_into_SQL_table.md
title: Insert data into table with SQL INSERT statement
lead: _A table named `sales`:_
checksum: c665728b91472161cc4b0d63c8626aec625bba9226159c46e3f801e7b51242e0
created: 2024-10-22 16:52:57.14754262+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["SQL","databases"]}
body:
  _A table named `sales`:_

  | saleId | modelId | date       | salesPerson |
  | ------ | ------- | ---------- | ----------- |
  | 223    | 2       | 2020-01-09 | tgomay      |
  | 321    | 3       | 2020-03-14 | snedgamon   |
  | 491    | 4       | 2021-08-06 | tricksahoy  |

  ```sql
  INSERT INTO sales
  VALUES (1, 11, '2020-01-01','mhogan');
  ```

  > If you intend to miss out a value, you shouldn't leave it blank, you should
  > instead use `NULL` :

  ```sql
  INSERT INTO sales
  VALUES (1, 11, '2020-01-01', NULL);
  ```

  > The approach above works well if the order of entry matches the order of the
  > table fields. If this is not the case you can specify the order as part of the
  > `INSERT` statement:

  ```sql
  INSERT INTO sales**(employee_id, sale_id, model_id, sale_date)**
  VALUES ('mhogan', 1, 11, '2020-01-01',);
  ```

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

  # Insert data into table with SQL `INSERT` statement

  _A table named `sales`:_

  | saleId | modelId | date       | salesPerson |
  | ------ | ------- | ---------- | ----------- |
  | 223    | 2       | 2020-01-09 | tgomay      |
  | 321    | 3       | 2020-03-14 | snedgamon   |
  | 491    | 4       | 2021-08-06 | tricksahoy  |

  ```sql
  INSERT INTO sales
  VALUES (1, 11, '2020-01-01','mhogan');
  ```

  > If you intend to miss out a value, you shouldn't leave it blank, you should
  > instead use `NULL` :

  ```sql
  INSERT INTO sales
  VALUES (1, 11, '2020-01-01', NULL);
  ```

  > The approach above works well if the order of entry matches the order of the
  > table fields. If this is not the case you can specify the order as part of the
  > `INSERT` statement:

  ```sql
  INSERT INTO sales**(employee_id, sale_id, model_id, sale_date)**
  VALUES ('mhogan', 1, 11, '2020-01-01',);
  ```


path: Instruction_set_architectures.md
sortable_path: Instruction_set_architectures.md
title: Instruction Set Architectures
lead: ## Summary
checksum: 38ccb65b129d4cbcb060765e9e148bd9527299c2ec783167fa140f86880ee1db
created: 2024-10-22 16:52:57.150083026+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["CPU"]}
body:
  ## Summary

  - Computers with different hardware execute machine code instructions (e.g
    _add_) in different ways. They have different _instruction set architectures_
    (ISAs).

  - A family of CPUs that implement the core instructions of the
    [[Arithmetic_Logic_Unit]] in the same way share an ISA. Any software built for
    that ISA will work on any CPU that implements the ISA

  - The two main CPU architectures are ARM and x86

  ## Architecture is logical not physical

  The "architecture" of an ISA is logical rather than physical, i.e processors
  that share an ISA may work very differently on the hardware level whilst still
  adhering to the same instruction set.

  ## ARM and x86

  The two main processor architectures are ARM and x86. x86 processors were first
  introduced by Intel but are also made by AMD.

  ### x86

  - First introduced by Intel but also made by AMD.

  - Name stems from Intel's internal naming conventions - every new processor they
    introduced would have a letter designation ending with "86". Later they would
    adopt names such as "Pentium", "Celeron" etc. Regardless, these processors
    still fall under the "x86" family.

  - Over time, new instructions have been added to the x86 architecture but they
    all maintain backwards compatibility with preceding generations.

  - There have been different, successive generations of x86 corresponding to
    their [word-size](Signed_and_unsigned_numbers.md): 16-bit, 32-bit, 64-bit.
    Word size here just means how many bits the processor can work with at a time.
    A 34-bit processor can operate on values that are 32 bits in length. This
    means it has 32bit [[CPU_architecture#registers|registers]], a 32-bit data
    [[Bus|bus]], a 32-bit address bus and so on.

  - AMD introduced the first 64-bit processor, not Intel but they later caught up.
    A 64-bit x86 processor is often referred to as _x64_ or _x86-64_.

  ### ARM

  - x86 processors dominate the personal computer and server space

  - On movile devices ARM ISA predominates

  - The ARM company develops the architecture and standard which it licenses it to
    other companies to implement.

  - Preferred on mobile because they have reduced power consumption and lower
    costs compared to x86.

  - The processor is on a single chip that contains the CPU as well as memory and
    other hardware.

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

  # Instruction Set Architectures

  ## Summary

  - Computers with different hardware execute machine code instructions (e.g
    _add_) in different ways. They have different _instruction set architectures_
    (ISAs).

  - A family of CPUs that implement the core instructions of the
    [[Arithmetic_Logic_Unit]] in the same way share an ISA. Any software built for
    that ISA will work on any CPU that implements the ISA

  - The two main CPU architectures are ARM and x86

  ## Architecture is logical not physical

  The "architecture" of an ISA is logical rather than physical, i.e processors
  that share an ISA may work very differently on the hardware level whilst still
  adhering to the same instruction set.

  ## ARM and x86

  The two main processor architectures are ARM and x86. x86 processors were first
  introduced by Intel but are also made by AMD.

  ### x86

  - First introduced by Intel but also made by AMD.

  - Name stems from Intel's internal naming conventions - every new processor they
    introduced would have a letter designation ending with "86". Later they would
    adopt names such as "Pentium", "Celeron" etc. Regardless, these processors
    still fall under the "x86" family.

  - Over time, new instructions have been added to the x86 architecture but they
    all maintain backwards compatibility with preceding generations.

  - There have been different, successive generations of x86 corresponding to
    their [word-size](Signed_and_unsigned_numbers.md): 16-bit, 32-bit, 64-bit.
    Word size here just means how many bits the processor can work with at a time.
    A 34-bit processor can operate on values that are 32 bits in length. This
    means it has 32bit [[CPU_architecture#registers|registers]], a 32-bit data
    [[Bus|bus]], a 32-bit address bus and so on.

  - AMD introduced the first 64-bit processor, not Intel but they later caught up.
    A 64-bit x86 processor is often referred to as _x64_ or _x86-64_.

  ### ARM

  - x86 processors dominate the personal computer and server space

  - On movile devices ARM ISA predominates

  - The ARM company develops the architecture and standard which it licenses it to
    other companies to implement.

  - Preferred on mobile because they have reduced power consumption and lower
    costs compared to x86.

  - The processor is on a single chip that contains the CPU as well as memory and
    other hardware.


path: Integrated_circuits.md
sortable_path: Integrated_circuits.md
title: Integrated circuits
checksum: 0dc9e1f489b2886878f382b4fe89e0ac58430d1e5c1c38a850e9bbe4f5c96aab
created: 2024-10-22 16:52:57.167943835+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["logic-gates","electronics"]}
lead:
  An integrated circuit (IC) is a single unit that comprises several logic gates
  designed for the easy construction of [digital circuits](Digital_circuits.md).
  The terms "integrated circuit" and "chip" are often used interchangeably.

body:
  An integrated circuit (IC) is a single unit that comprises several logic gates
  designed for the easy construction of [digital circuits](Digital_circuits.md).
  The terms "integrated circuit" and "chip" are often used interchangeably.

  An IC puts the gates on a single piece of silicon that has electrical contact
  points called pins. There are two types of pins on an IC: input pins and output
  pins. Input pins are used to receive signals from other components in the
  circuit, while output pins are used to send signals to other components. The
  function of a pin is determined by the connection it has to the rest of the
  circuit and the type of signal it is intended to transmit or receive.

  The type we will look at are called **dual in-line packages** (DIPs). They are
  rectangular wth two parallel rows of pins. The pins make it easy to connect DIPs
  to a breadboard.

  _An integrated circuit and its use on a breadboard:_

  ![](../img/integrated-circuit.jpeg)

  ![](../img/breadboard-DIP.jpg)

raw_content:
  ---
  tags:
    - logic-gates
    - electronics
  ---

  # Integrated circuits

  An integrated circuit (IC) is a single unit that comprises several logic gates
  designed for the easy construction of [digital circuits](Digital_circuits.md).
  The terms "integrated circuit" and "chip" are often used interchangeably.

  An IC puts the gates on a single piece of silicon that has electrical contact
  points called pins. There are two types of pins on an IC: input pins and output
  pins. Input pins are used to receive signals from other components in the
  circuit, while output pins are used to send signals to other components. The
  function of a pin is determined by the connection it has to the rest of the
  circuit and the type of signal it is intended to transmit or receive.

  The type we will look at are called **dual in-line packages** (DIPs). They are
  rectangular wth two parallel rows of pins. The pins make it easy to connect DIPs
  to a breadboard.

  _An integrated circuit and its use on a breadboard:_

  ![](../img/integrated-circuit.jpeg)

  ![](../img/breadboard-DIP.jpg)


path: Interactive_staging.md
sortable_path: Interactive_staging.md
title: Interactive staging
checksum: fdef4d4ff9933facb66f0b9ca70df3e69d95d9376ecb3748f72eb15d9a91a064
created: 2024-10-22 16:52:57.175418247+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["git"]}
lead:
  With interactive staging we can stage sub-portions of files rather than the
  whole file. This makes commits much more targetted and atomic.

body:
  With interactive staging we can stage sub-portions of files rather than the
  whole file. This makes commits much more targetted and atomic.

  ## Basic use

  We enter interactive mode with:

  ```
  git add -i
  ```

  This opens an interface:

  ![](/img/git-interactive-mode-2.png)

  We select 2 and it lets us stage by number. If I enter 1, it will stage the
  first change.

  If we then go into 3 for revert, we can undo what we have staged.

  > Note that untracked files are not automatically added to the changes. We have
  > to manually add them with 4 ("add untracked").

  > Interactive mode doesn't commit anything, it is only for staging.

  ## Working with patches and hunks

  There's not much utility in interactive mode if you are commiting whole files.
  You can just use `git add [file]` and not bother with it.

  But it is useful for staging sub-portions of a file, which are called **hunks**.

  We access these via **patch mode**:

  ![](/img/git-patch-mode.png)

  ### Splitting hunks

  Git will try to suggest the best hunks it can but these will sometimes not
  reflect the changes you want. Sometimes they will reflect multiple changes
  rather than a single change.

  So long as there is an unchanged line between the different changes that it has
  selected as a hunk you can split the hunk further. You just hit `s` in patch
  mode, and it will repeatedly split the hunk each time until you get what you
  want.

  This is still an automated process and you may not find that the splitting
  results in a hunk you want to commit. In this case you can use manual splitting
  mode. We enter this with `e`.

  This will oped up Vim for the manual work to be done:

  ![](/img/git-manual-hunk.png)

  We use the diff symbols `+`, `-` and space to do this.

  ### Accessing patch mode directly

  If we just want to go straight to patch mode, without anything else:

  ```
  git add -p
  ```

  We can also work with patches in the following commands:

  ```
  git stash -p
  git reset -p
  git restore -p
  git commit -p
  ```

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

  # Interactive staging

  With interactive staging we can stage sub-portions of files rather than the
  whole file. This makes commits much more targetted and atomic.

  ## Basic use

  We enter interactive mode with:

  ```
  git add -i
  ```

  This opens an interface:

  ![](/img/git-interactive-mode-2.png)

  We select 2 and it lets us stage by number. If I enter 1, it will stage the
  first change.

  If we then go into 3 for revert, we can undo what we have staged.

  > Note that untracked files are not automatically added to the changes. We have
  > to manually add them with 4 ("add untracked").

  > Interactive mode doesn't commit anything, it is only for staging.

  ## Working with patches and hunks

  There's not much utility in interactive mode if you are commiting whole files.
  You can just use `git add [file]` and not bother with it.

  But it is useful for staging sub-portions of a file, which are called **hunks**.

  We access these via **patch mode**:

  ![](/img/git-patch-mode.png)

  ### Splitting hunks

  Git will try to suggest the best hunks it can but these will sometimes not
  reflect the changes you want. Sometimes they will reflect multiple changes
  rather than a single change.

  So long as there is an unchanged line between the different changes that it has
  selected as a hunk you can split the hunk further. You just hit `s` in patch
  mode, and it will repeatedly split the hunk each time until you get what you
  want.

  This is still an automated process and you may not find that the splitting
  results in a hunk you want to commit. In this case you can use manual splitting
  mode. We enter this with `e`.

  This will oped up Vim for the manual work to be done:

  ![](/img/git-manual-hunk.png)

  We use the diff symbols `+`, `-` and space to do this.

  ### Accessing patch mode directly

  If we just want to go straight to patch mode, without anything else:

  ```
  git add -p
  ```

  We can also work with patches in the following commands:

  ```
  git stash -p
  git reset -p
  git restore -p
  git commit -p
  ```


path: Internet_Layer_of_Internet_Protocol.md
sortable_path: Internet_Layer_of_Internet_Protocol.md
title: Internet Layer of the Internet Protocol Suite
checksum: fd97eae4f1441c51acff3d52fc5d636be1e7c59d68b435c45a6495faca4fc21e
created: 2024-10-22 16:52:57.178934665+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"created":"Thursday, August 15, 2024","tags":["internet","networks"]}
lead:
  The Internet Layer is concerned with establishing the basis for the connection
  and communication between hosts accross the internet.

body:
  The Internet Layer is concerned with establishing the basis for the connection
  and communication between hosts accross the internet.

  In order for the data to get from a machine on one local network to a machine on
  another local network, each machine must have a way of identifying and
  interfacing with the other.

  The Internet Layer utilises the **Internet Protocol** to determine how devices
  will be publicly identified to devices on other networks and how different
  devices on the same local network will be distinguished from outside of this
  network. This is achieved through
  [Internet Protocol (IP) addresses](./IP_addresses.md).

  ## Packets

  Data is sent accross the internet between hosts as _packets_. These packets,
  when sent and received within the
  [Link Layer](Link_Layer_of_Internet_Protocol.md) on the local network, are
  enclosed within a Link Layer frame (in its payload section):

  ![Internet Layer packet](../img/internet-layer-packet-revised.png)

  Like a frame, an IP packet has a header and a payload. The payload comprises the
  data between sent between hosts. The header contains a source IP address and a
  destination IP address and also specifies which version of the Internet Protocol
  is being used.

  // Need to find out more about packets work.

raw_content:
  ---
  tags:
    - internet
    - networks
  created: Thursday, August 15, 2024
  ---

  # Internet Layer of the Internet Protocol Suite

  The Internet Layer is concerned with establishing the basis for the connection
  and communication between hosts accross the internet.

  In order for the data to get from a machine on one local network to a machine on
  another local network, each machine must have a way of identifying and
  interfacing with the other.

  The Internet Layer utilises the **Internet Protocol** to determine how devices
  will be publicly identified to devices on other networks and how different
  devices on the same local network will be distinguished from outside of this
  network. This is achieved through
  [Internet Protocol (IP) addresses](./IP_addresses.md).

  ## Packets

  Data is sent accross the internet between hosts as _packets_. These packets,
  when sent and received within the
  [Link Layer](Link_Layer_of_Internet_Protocol.md) on the local network, are
  enclosed within a Link Layer frame (in its payload section):

  ![Internet Layer packet](../img/internet-layer-packet-revised.png)

  Like a frame, an IP packet has a header and a payload. The payload comprises the
  data between sent between hosts. The header contains a source IP address and a
  destination IP address and also specifies which version of the Internet Protocol
  is being used.

  // Need to find out more about packets work.


path: Internet_fundamentals.md
sortable_path: Internet_fundamentals.md
title: Internet fundamentals
checksum: 74fbd8a154de3ccf6b6fc4bba7e3b09e2c7c260d38dd2e21d11b735908d767e2
created: 2024-10-22 16:52:57.194431147+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"created":"Saturday, August 03, 2024","tags":["networks"]}
lead:
  The internet is a _[network](Network_fundamentals.md) of networks_. A globally
  connected set of computer networks that all use a suite of common
  [protocols](Network_protocols.md).

body:
  The internet is a _[network](Network_fundamentals.md) of networks_. A globally
  connected set of computer networks that all use a suite of common
  [protocols](Network_protocols.md).

  The protocols governing the communication between networks on the internet are
  specified in the **Internet Protocol Suite**.

  This combines two protocols:

  - **TCP**
    - The Transmission Control Protocol
  - **IP**
    - The Internet Protocol

  Hence the Internet Protocol Suite is also known as "TCP/IP".

  The TCP/IP is a network stack comprising four layers, each with its own
  protocols. From the top:

  ![TCP/IP stack diagram](../img/TCP_IP.png)

  - The [Link Layer](Link_Layer_of_Internet_Protocol.md) concerns communication
    between devices on the same local network. The transfer of information once it
    has been received from the internet or its transfer between devices before it
    sent out into the internet. Ethernet and WiFi are two core protocols utilised
    at this layer.

  - The [Internet Layer](Internet_Layer_of_Internet_Protocol.md) is concerned with
    communication accross networks. It is responsible for identifying hosts on the
    inter-network by their addresses and routing data from network to network.

  - The [Transport Layer](Transport_Layer_of_Internet_Protocol.md) creates a
    commmunication channel between two hosts using ports and sockets.

  - The [Application Layer](Application_Layer_of_Internet_Protocol.md) concerned
    with software that utilises information received and sent over the internet,
    for instance web pages and emails. This is the highest level of abstraction
    for which all the lower levels serve as "plumbing". Examples of protocols
    operative at the Application Layer include HTTP and SSH.

  See:
  [Example_scenario_internet_data_transfer](Example_scenario_internet_data_transfer.md)

raw_content:
  ---
  tags: [networks]
  created: Saturday, August 03, 2024
  ---

  # Internet fundamentals

  The internet is a _[network](Network_fundamentals.md) of networks_. A globally
  connected set of computer networks that all use a suite of common
  [protocols](Network_protocols.md).

  The protocols governing the communication between networks on the internet are
  specified in the **Internet Protocol Suite**.

  This combines two protocols:

  - **TCP**
    - The Transmission Control Protocol
  - **IP**
    - The Internet Protocol

  Hence the Internet Protocol Suite is also known as "TCP/IP".

  The TCP/IP is a network stack comprising four layers, each with its own
  protocols. From the top:

  ![TCP/IP stack diagram](../img/TCP_IP.png)

  - The [Link Layer](Link_Layer_of_Internet_Protocol.md) concerns communication
    between devices on the same local network. The transfer of information once it
    has been received from the internet or its transfer between devices before it
    sent out into the internet. Ethernet and WiFi are two core protocols utilised
    at this layer.

  - The [Internet Layer](Internet_Layer_of_Internet_Protocol.md) is concerned with
    communication accross networks. It is responsible for identifying hosts on the
    inter-network by their addresses and routing data from network to network.

  - The [Transport Layer](Transport_Layer_of_Internet_Protocol.md) creates a
    commmunication channel between two hosts using ports and sockets.

  - The [Application Layer](Application_Layer_of_Internet_Protocol.md) concerned
    with software that utilises information received and sent over the internet,
    for instance web pages and emails. This is the highest level of abstraction
    for which all the lower levels serve as "plumbing". Examples of protocols
    operative at the Application Layer include HTTP and SSH.

  See:
  [Example_scenario_internet_data_transfer](Example_scenario_internet_data_transfer.md)


path: Intersection_types_in_TypeScript.md
sortable_path: Intersection_types_in_TypeScript.md
title: Intersection types
checksum: f5d651d47122ae6fa8ecc204ac6dad3d06c12d1531df51dbfcbaafb1aaea5b91
created: 2024-10-22 16:52:57.226499738+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["typescript"]}
lead:
  Like union types, intersection types allow us to combine the properties of
  existing primitive and custom types to make composites.

body:
  Like union types, intersection types allow us to combine the properties of
  existing primitive and custom types to make composites.

  ### Creating an intersection

  Say you have the following three types:

  ```ts
  type IConcert = {
    date: number;
    location: string;
    band: string;
  };

  type IConference = {
    date: number;
    location: string;
    keynoteSpeaker: string;
  };

  type IMeeting = {
    date: number;
    location: string;
    agenda: string[];
  };
  ```

  Each type corresponds to a different event and has two properties in common with
  the other events: `date` and `location`.

  Rather than repeat these properties for each individual type, we could create a
  general `IEvent` type and apply this as an intersection to each of the
  individual event types:

  ```ts
  // The common event type
  type IEvent = {
    date: number;
    location: string;
  };
  ```

  ```ts
  // The intersection

  type IConcert = IEvent & {
    band: string;
  };

  type IConference = IEvent & {
    keynoteSpeaker: string;
  };
  ```

  > In the case of interfaces, instead of using `&` we would write
  > `interface IConcert extends IEvent`

  ### Alternative method

  There is another equivalent way of intersecting the types. We could first define
  `IConcert` and `IConference` as their own types (without `extends` or `&`) and
  then intersect with, e.g., type IConcert = SelfStandingConcertType & IEvent.

  ### Benefits of intersections

  - It is more concise and we do not repeat ourselves needlessly
  - Common properties can be modelled **in one place** which makes updates and
    changes much easier: we do not have to change multiple type declarations, just
    change the 'master' type that is intersected with the variants.
  - It is more readable: we can see which properties are unique to the type
    without having to parse repeated properties.

  ## Set theory: understanding errors

  With intersections we literally use the & operator which discloses that the
  logic is equivalent to logical AND: a member of the intersection must have the
  properties of both types. In the case of primitive values this is impossible or,
  more accurately, it results in an empty set because, for example, `string` and
  `boolean` do not share any common values. This means you will get an error if
  you assign an intersection type to a value that does not have all the features
  of each custom type in the intersection.

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

  # Intersection types

  Like union types, intersection types allow us to combine the properties of
  existing primitive and custom types to make composites.

  ### Creating an intersection

  Say you have the following three types:

  ```ts
  type IConcert = {
    date: number;
    location: string;
    band: string;
  };

  type IConference = {
    date: number;
    location: string;
    keynoteSpeaker: string;
  };

  type IMeeting = {
    date: number;
    location: string;
    agenda: string[];
  };
  ```

  Each type corresponds to a different event and has two properties in common with
  the other events: `date` and `location`.

  Rather than repeat these properties for each individual type, we could create a
  general `IEvent` type and apply this as an intersection to each of the
  individual event types:

  ```ts
  // The common event type
  type IEvent = {
    date: number;
    location: string;
  };
  ```

  ```ts
  // The intersection

  type IConcert = IEvent & {
    band: string;
  };

  type IConference = IEvent & {
    keynoteSpeaker: string;
  };
  ```

  > In the case of interfaces, instead of using `&` we would write
  > `interface IConcert extends IEvent`

  ### Alternative method

  There is another equivalent way of intersecting the types. We could first define
  `IConcert` and `IConference` as their own types (without `extends` or `&`) and
  then intersect with, e.g., type IConcert = SelfStandingConcertType & IEvent.

  ### Benefits of intersections

  - It is more concise and we do not repeat ourselves needlessly
  - Common properties can be modelled **in one place** which makes updates and
    changes much easier: we do not have to change multiple type declarations, just
    change the 'master' type that is intersected with the variants.
  - It is more readable: we can see which properties are unique to the type
    without having to parse repeated properties.

  ## Set theory: understanding errors

  With intersections we literally use the & operator which discloses that the
  logic is equivalent to logical AND: a member of the intersection must have the
  properties of both types. In the case of primitive values this is impossible or,
  more accurately, it results in an empty set because, for example, `string` and
  `boolean` do not share any common values. This means you will get an error if
  you assign an intersection type to a value that does not have all the features
  of each custom type in the intersection.


path: Intro_to_the_Unix_shell.md
sortable_path: Intro_to_the_Unix_shell.md
title: Introduction to the Unix shell
lead: ## Unix based systems
checksum: 729368e67cdd533f552430cf7186e4e791cbdefc0b45b3909482857a0fba8ada
created: 2024-10-22 16:52:57.229874248+00:00
modified: 2024-10-18 18:15:25.351386131+00:00
metadata: {"tags":["shell"]}
body:
  ## Unix based systems

  Many operating systems are based on the UNIX software architecture. macOS/OSX
  (Darwin) and GNU/Linux are two very popular examples. Most web servers run a
  version of Linux as their native OS thus a knowledge of command line for UNIX
  systems is invaluable for web developers. Windows systems are not UNIX based so
  everything that is written here is not applicable to the Windows command line
  program Command Prompt although there are obvious conceptual overlaps.

  UNIX was initially developed by AT&T and is still owned by them. GNU (the basis
  for Linux) is a recursive acronym for ’GNU is not UNIX’. Functionally it is the
  same as Unix however, it just doesn’t contain the proprietary code.

  ## Key terms

  ### Kernal

  The kernel is the central part of an operating system. It manages the operations
  of the computer and the hardware; most notably memory and CPU time. There are
  two types of kernels: a **micro-kernel**, which only contains basic
  functionality; and a **monolithic kernel**, which contains many device drivers.

  ### Shell

  A shell is a user interface for access to an operating system’s services. In
  general, operating system shells use either a command-line interface or GUI,
  depending on a computer’s role and particular operation. It is named a shell
  because it is the outermost layer around the operating system kernel

  ### Bash

  Bash is a Unix shell and command language written by Brian Fox for the GNU
  Project as a free software replacement for the Bourne shell. First released in
  1989, it has been distributed widely as the default login shell for most Linux
  distributions and Apple’s macOS.

  ## What is the command line?

  Command line is both a general computing concept and a specific utility. In its
  general sense, engaging with a computer via command line means inputting
  abbreviated written commands into a console or terminal window on a computer.
  The computer then runs these commands on the basis of the instructions you have
  given.

  You can get the computer to ‘do things’ (make files, connect to networks etc)
  via command line and you can use it to check the status of systems within your
  computer.

  This mode of engagement contrasts with the engagement facilitated by modern
  graphical user interfaces (GUIs) where the user navigates via a combination of
  keyboard and mouse inputs and interacts with windows whose functionality is
  usually self-explanatory and therefore more user-friendly. Command line predates
  GUIs but it is considered a more efficient and expansive means of interacting
  with the whole of your computer especially if you are a developer or programmer.

  In the more specific sense, the ‘command line’ refers to the location in the
  console window where you input your commands. The command line consists in a
  user name and the current directory you are in followed by a dollar sign and a
  cursor waiting for input.

  ## Basic orientation

  When you are using command line you are always situated somewhere within a file
  system. Typically this will be your own user files. directory structure By
  default you are always in your home directory, this is represented by the tilda
  symbol: `~`. This is why you see the tilda symbol as part of the command line.
  In command line, we do not use the term ‘folders’, instead they are called
  directories. (I will follow this convention from hereon.) You do not have to
  worry too much about remembering the names of specific folders and files you can
  always ask the computer to display them to you by using `ls` argument or `pwd`.

  ## Command line syntax

  The syntax of the language used to input commands is analogous to the grammar of
  natural languages. We have a verb that is operative on an object/noun and which
  can be modified through adverbs. The syntax of a command sequence is as follows:

  1. Command (verb): what we want to do
  1. Option (adverb): modifying the command - always starts with a hyphen
  1. Argument (noun/object): what we want our command to operate on

  We will see that not all commands require arguments, but this is the general
  structure

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

  # Introduction to the Unix shell

  ## Unix based systems

  Many operating systems are based on the UNIX software architecture. macOS/OSX
  (Darwin) and GNU/Linux are two very popular examples. Most web servers run a
  version of Linux as their native OS thus a knowledge of command line for UNIX
  systems is invaluable for web developers. Windows systems are not UNIX based so
  everything that is written here is not applicable to the Windows command line
  program Command Prompt although there are obvious conceptual overlaps.

  UNIX was initially developed by AT&T and is still owned by them. GNU (the basis
  for Linux) is a recursive acronym for ’GNU is not UNIX’. Functionally it is the
  same as Unix however, it just doesn’t contain the proprietary code.

  ## Key terms

  ### Kernal

  The kernel is the central part of an operating system. It manages the operations
  of the computer and the hardware; most notably memory and CPU time. There are
  two types of kernels: a **micro-kernel**, which only contains basic
  functionality; and a **monolithic kernel**, which contains many device drivers.

  ### Shell

  A shell is a user interface for access to an operating system’s services. In
  general, operating system shells use either a command-line interface or GUI,
  depending on a computer’s role and particular operation. It is named a shell
  because it is the outermost layer around the operating system kernel

  ### Bash

  Bash is a Unix shell and command language written by Brian Fox for the GNU
  Project as a free software replacement for the Bourne shell. First released in
  1989, it has been distributed widely as the default login shell for most Linux
  distributions and Apple’s macOS.

  ## What is the command line?

  Command line is both a general computing concept and a specific utility. In its
  general sense, engaging with a computer via command line means inputting
  abbreviated written commands into a console or terminal window on a computer.
  The computer then runs these commands on the basis of the instructions you have
  given.

  You can get the computer to ‘do things’ (make files, connect to networks etc)
  via command line and you can use it to check the status of systems within your
  computer.

  This mode of engagement contrasts with the engagement facilitated by modern
  graphical user interfaces (GUIs) where the user navigates via a combination of
  keyboard and mouse inputs and interacts with windows whose functionality is
  usually self-explanatory and therefore more user-friendly. Command line predates
  GUIs but it is considered a more efficient and expansive means of interacting
  with the whole of your computer especially if you are a developer or programmer.

  In the more specific sense, the ‘command line’ refers to the location in the
  console window where you input your commands. The command line consists in a
  user name and the current directory you are in followed by a dollar sign and a
  cursor waiting for input.

  ## Basic orientation

  When you are using command line you are always situated somewhere within a file
  system. Typically this will be your own user files. directory structure By
  default you are always in your home directory, this is represented by the tilda
  symbol: `~`. This is why you see the tilda symbol as part of the command line.
  In command line, we do not use the term ‘folders’, instead they are called
  directories. (I will follow this convention from hereon.) You do not have to
  worry too much about remembering the names of specific folders and files you can
  always ask the computer to display them to you by using `ls` argument or `pwd`.

  ## Command line syntax

  The syntax of the language used to input commands is analogous to the grammar of
  natural languages. We have a verb that is operative on an object/noun and which
  can be modified through adverbs. The syntax of a command sequence is as follows:

  1. Command (verb): what we want to do
  1. Option (adverb): modifying the command - always starts with a hyphen
  1. Argument (noun/object): what we want our command to operate on

  We will see that not all commands require arguments, but this is the general
  structure


path: Inversion_of_operators.md
sortable_path: Inversion_of_operators.md
title: Inversion of operators
lead: ## Addition, subtraction
checksum: 1a1ebe83392d9fc836df388f84a67c4305490a99f31da6c5b6c3df299d15b685
created: 2024-10-22 16:52:57.233503648+00:00
modified: 2024-10-18 18:15:36.278257775+00:00
metadata: {"tags":["prealgebra"]}
body:
  ## Addition, subtraction

  Addition is the inverse of subtraction: $$(x - a) + a = x$$ $$ (6 - 2) + 2 = 6

  $$

  Subtraction is the inverse of addition: $$(x + a) - a = x$$ $$ (3 + 2) - 2 = 3$$

  Division is the inverse of multiplication $$ \frac{a \cdot x}{a} = x$$
  $$ \frac{6
  \cdot 3}{6} = 3
  $$

  Multiplication is the inverse of division $$ a \cdot \frac{x}{a} = x$$

  $$
  2 \cdot
  \frac{8}{2} = 8
  $$

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

  # Inversion of operators

  ## Addition, subtraction

  Addition is the inverse of subtraction: $$(x - a) + a = x$$ $$ (6 - 2) + 2 = 6

  $$

  Subtraction is the inverse of addition: $$(x + a) - a = x$$ $$ (3 + 2) - 2 = 3$$

  Division is the inverse of multiplication $$ \frac{a \cdot x}{a} = x$$
  $$ \frac{6
  \cdot 3}{6} = 3
  $$

  Multiplication is the inverse of division $$ a \cdot \frac{x}{a} = x$$

  $$
  2 \cdot
  \frac{8}{2} = 8
  $$


path: Invoking_the_shell_in_Python.md
sortable_path: Invoking_the_shell_in_Python.md
title: Invoking the shell in Python
lead:
  ```py
  import subprocess

checksum: e86663be11247bed326351c2daf35c5a26ad5a2c1b9fb0e2d898f860bba9d678
created: 2024-10-22 16:52:57.236714108+00:00
modified: 2024-10-18 18:15:48.06514547+00:00
metadata: {"created":"Monday, April 29, 2024","id":"cfr4","tags":["python","shell"]}
body:
  ```py
  import subprocess

  try:
      process = subprocess.run(
          ["ls", "-la"],
          shell=True,
          stdout=subprocess.PIPE,
          stderr=subprocess.PIPE,
          text=True,
      )
      return process.stdout

  except subprocess.CalledProcessError as e:
          return e.stderr.strip()
  ```

raw_content:
  ---
  id: cfr4
  tags: [python, shell]
  created: Monday, April 29, 2024
  ---

  # Invoking the shell in Python

  ```py
  import subprocess

  try:
      process = subprocess.run(
          ["ls", "-la"],
          shell=True,
          stdout=subprocess.PIPE,
          stderr=subprocess.PIPE,
          text=True,
      )
      return process.stdout

  except subprocess.CalledProcessError as e:
          return e.stderr.strip()
  ```


path: Iterating_in_React.md
sortable_path: Iterating_in_React.md
title: Iterating through data
checksum: a417bdd27a8eaa5408dcb27982df7ef2466161fa88eeacb315e39b99b39ad4d2
created: 2024-10-22 16:52:57.239286061+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["javascript","react"]}
lead:
  As React follows the **functional programming paradigm** and state versioning,
  looping is not the recommended way of engineering repeated processes since it
  presupposes pre-existing state. Instead React relies on the functional methods
  of modern JavaScript such as `map` , `filter` , `reduce` to output the values of
  iterative data structures.

body:
  As React follows the **functional programming paradigm** and state versioning,
  looping is not the recommended way of engineering repeated processes since it
  presupposes pre-existing state. Instead React relies on the functional methods
  of modern JavaScript such as `map` , `filter` , `reduce` to output the values of
  iterative data structures.

  Instead of mutating the state of a reference type, these methods are
  'non-destructive': they create a new array that meets the requirements set in
  the particular method. These newly created arrays and objects therefore form the
  basis of the state versioning, because each new array that is engendered, is a
  new state configuration.

  ## The `key` attribute

  The `key` attribute is a special React attribute that should be added to the
  elements of any listed content (i.e numbered lists, unordered lists, `select`
  options etc; basically anything rendered that comes about through mapping arrays
  and objects).

  This enables React to monitor the state of the list: which items have changed,
  been added, or removed.

  The best way to pick a key is to use a string that uniquely identifies a list
  item among its siblings.

  For instance, using our earlier `numbers` array, assuming each number is unique,
  we could use the number itself as the key:

  ```jsx
  const numbers = [1, 2, 3, 4, 5];
  const listItems = numbers.map((number) => (
    <li key={number.toString()}> {number}</li>
  ));
  ```

  > Keys have to be unique to the individual element and not shared by any other
  > sibling. But they do not need to be globally unique. You could use the same
  > `key` name in another array list.

  > Keys should be string types, not other data types. You can use the
  > `.toString()` method to ensure this is the case.

  If you can't find a unique basis for naming a series of keys, you can use the
  `index` value of the `Array.map` function. This should only be used if the order
  of the items doesn't change. An example:

  ```jsx
  class List extends React.Component {
    render() {
      const demoArr = ["cat", "dog", "mouse"];
      return (
          <ul>
            {demoArr.map((x, index) => (
              <li key={index}>{x}</li>
            ))}
          </ul>
        </div>
      );
    }
  }
  ```

  ## Demonstration

  ### Source data

  ```jsx
  const blogData = [
    {
      id: 1,
      date: "2020-11-15",
          shortByline:
        "Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. ",
      longByline:
        "Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Morbi lacinia molestie dui. Praesent blandit dolor.",
      image: blogImage,
    },
    {
      id: 2,
      date: "2020-08-11",
          shortByline:
        "Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. ",
      longByline:
        "Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Morbi lacinia molestie dui. Praesent blandit dolor.",
      image: blogImage,
    },
  ];
  ```

  ### Child component

  ```jsx
  export default function BlogPreviewCard(props) {
    return (
      <Card>
        <CardMedia image={props.image} />
        <CardContent>
          <Typography>{props.date}</Typography>
          <Typography>{props.title}</Typography>
          <Typography>{props.shortByline}</Typography>
        </CardContent>
        <CardActions>
          <Button>Read</Button>
        </CardActions>
      </Card>
    );
  }
  ```

  ### Iteration

  ```jsx
  // This uses the Material-UI grid component

  import BlogPreviewCard from "../components/BlogPreviewCard"
  import blogData from "../data/blogData

  export default function BlogPreviewSet() {
    return (
      <Grid>
        {blogDummyData.map((data, index) => (
          <Grid key={index}>
            <BlogPreviewCard key={index} {...data} />
          </Grid>
        ))}
      </Grid>
    )
  }
  ```

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

  # Iterating through data

  As React follows the **functional programming paradigm** and state versioning,
  looping is not the recommended way of engineering repeated processes since it
  presupposes pre-existing state. Instead React relies on the functional methods
  of modern JavaScript such as `map` , `filter` , `reduce` to output the values of
  iterative data structures.

  Instead of mutating the state of a reference type, these methods are
  'non-destructive': they create a new array that meets the requirements set in
  the particular method. These newly created arrays and objects therefore form the
  basis of the state versioning, because each new array that is engendered, is a
  new state configuration.

  ## The `key` attribute

  The `key` attribute is a special React attribute that should be added to the
  elements of any listed content (i.e numbered lists, unordered lists, `select`
  options etc; basically anything rendered that comes about through mapping arrays
  and objects).

  This enables React to monitor the state of the list: which items have changed,
  been added, or removed.

  The best way to pick a key is to use a string that uniquely identifies a list
  item among its siblings.

  For instance, using our earlier `numbers` array, assuming each number is unique,
  we could use the number itself as the key:

  ```jsx
  const numbers = [1, 2, 3, 4, 5];
  const listItems = numbers.map((number) => (
    <li key={number.toString()}> {number}</li>
  ));
  ```

  > Keys have to be unique to the individual element and not shared by any other
  > sibling. But they do not need to be globally unique. You could use the same
  > `key` name in another array list.

  > Keys should be string types, not other data types. You can use the
  > `.toString()` method to ensure this is the case.

  If you can't find a unique basis for naming a series of keys, you can use the
  `index` value of the `Array.map` function. This should only be used if the order
  of the items doesn't change. An example:

  ```jsx
  class List extends React.Component {
    render() {
      const demoArr = ["cat", "dog", "mouse"];
      return (
          <ul>
            {demoArr.map((x, index) => (
              <li key={index}>{x}</li>
            ))}
          </ul>
        </div>
      );
    }
  }
  ```

  ## Demonstration

  ### Source data

  ```jsx
  const blogData = [
    {
      id: 1,
      date: "2020-11-15",
          shortByline:
        "Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. ",
      longByline:
        "Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Morbi lacinia molestie dui. Praesent blandit dolor.",
      image: blogImage,
    },
    {
      id: 2,
      date: "2020-08-11",
          shortByline:
        "Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. ",
      longByline:
        "Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Morbi lacinia molestie dui. Praesent blandit dolor.",
      image: blogImage,
    },
  ];
  ```

  ### Child component

  ```jsx
  export default function BlogPreviewCard(props) {
    return (
      <Card>
        <CardMedia image={props.image} />
        <CardContent>
          <Typography>{props.date}</Typography>
          <Typography>{props.title}</Typography>
          <Typography>{props.shortByline}</Typography>
        </CardContent>
        <CardActions>
          <Button>Read</Button>
        </CardActions>
      </Card>
    );
  }
  ```

  ### Iteration

  ```jsx
  // This uses the Material-UI grid component

  import BlogPreviewCard from "../components/BlogPreviewCard"
  import blogData from "../data/blogData

  export default function BlogPreviewSet() {
    return (
      <Grid>
        {blogDummyData.map((data, index) => (
          <Grid key={index}>
            <BlogPreviewCard key={index} {...data} />
          </Grid>
        ))}
      </Grid>
    )
  }
  ```


path: Iterating_through_complex_data_structures_in_Python.md
sortable_path: Iterating_through_complex_data_structures_in_Python.md
title: Iterating through complex data structures in Python
lead: // TODO: sections :
checksum: ec3567e218e2db403c03023983dc935a585c61867d668b1797e5faf97b08aee0
created: 2024-10-22 16:52:57.242803733+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["python"]}
body:
  // TODO: sections :

  - Looping, mapping, filtering lists
  - Looping, mapping, filtering dictionaries
  - As above with multi-dimensional lists
  - As above with list of dictionaries

  ## Complex data structures

  In JavaScript when we return data from an API we tend to use an array of objects
  as the canonical form of a repeating iterable, e.g:

  ```js
  const data = [
    {
      name: "Thomas",
      age: 35,
    },
    {
      name: "Joe",
      age: 75,
    },
  ];
  ```

  In Python there are two common ways to handle similar data structures:

  - A list of lists:
    ```py
    data = [
      ["Thomas", 35],
      ["Joe", 75]
    ]
    ```
  - A list of dictionaries:
    ```py
    data = [
       {"name": "Thomas", "age": 35},
       {"name": "Joe", "age": 75},
    ]
    ```

  ## Multi-dimensional lists (list of lists)

  ### Sorting by common property

  Assuming the sub-lists have an identical structure, you can
  [sort](Sorting_lists_in_Python.md) them by
  a common property by passing a
  [lambda function](Lambdas_in_Python.md) to
  the `key` value of `sorted()` and `.sort()`.

  For example, to sort the following list of lists by the second `age` property:

  ```python
  people = [["Alice", 30], ["Bob", 25], ["Clare", 35], ["Dave", 28]]
  ```

  Using `sorted()`:

  ```py
  sorted_people = sorted(people, key=lambda x: x[1])
  print(sorted_people)
  # Output: [['Bob', 25], ['Dave', 28], ['Alice', 30], ['Clare', 35]]
  ```

  Using `.sort`:

  ```py
  people.sort(key=lambda x: x[1])
  print(people)
  # Output: [['Bob', 25], ['Dave', 28], ['Alice', 30], ['Clare', 35]]
  ```

  If you want to sort by name instead, you could change the lambda function to
  `lambda x: x[0]`:

  ```python
  # Using sorted()
  sorted_people = sorted(people, key=lambda x: x[0])
  print(sorted_people)
  # Output: [['Alice', 30], ['Bob', 25], ['Clare', 35], ['Dave', 28]]

  # Using .sort()
  people.sort(key=lambda x: x[0])
  print(people)
  # Output: [['Alice', 30], ['Bob', 25], ['Clare', 35], ['Dave', 28]]
  ```

  ### Updating an inner value within a multidimensional list

  In the following example we have a list of the following structure:

  ```py
  data = [
      ["1688582410", "Article One"],
      ["1688647447", "Article Two"],
      ["1689023491", "Article Three"],
  ]
  ```

  Below we use
  [map](Map_and_filter_in_Python.md) and a
  [lambda function](Lambdas_in_Python.md) to
  convert the first element of each iner list from a Unix timestamp to a readable
  string:

  ```py
   readable_date = list(map(lambda i: [convert_timestamp(i[0])] + i[1:], date))
  ```

  We could also use
  [list comprehension](List_comprehension_in_Python.md)
  to achieve the same outcome:

  ```py
  readable_date = [[convert_timestamp(i[0])] + i[1:] for i in data]
  ```

  ### Filter elements in a multidimensional list

  Say we have the following data structure:

  ```py
  name_age = [ ["Anthony", 16], ["Christopher", 22], ["James", 6] ]
  ```

  We can return only the people who are older than 18 with the following `filter`
  function and `lambda`:

  ```py
  filtered_ages = list(filter(lambda person: person[1] > 18, name_age))
  ```

  ### Remove duplicate entries from multidimensional list

  If we are just working with a normal list, a quick way to remove duplicates is
  just:

  ```py
  our_list = [9, 9, 3, 2]
  unique = list(set(our_list))
  ```

  This won't work with a list of lists because lists are not hashable.

  There are different approaches. Let's say we have the following multidimensional
  list:

  ```py
  dummy_data = [
      ["1689023491", "Article Three", "Lorem ipsum...", "https://example.com"],
      ["1688582410", "Article One", "Lorem ipsum...", "https://example.com"],
      ["1688647447", "Article Two", "Lorem ipsum...", "https://example.com"],
      ["1689023491", "Article Three", "Lorem ipsum...", "https://example.com"],
  ]
  ```

  Here is one method:

  ```py
  unique = []
  seen = set()
  for element in dummy_data:
      element_to_check = element[0]
      if element_to_check not in seen:
          unique.append(element)
          seen.add(element_to_check)
  print(unique)
  ```

  We designate a unique property in the inner lists and then store a unique record
  of each. If the seen set doesn't have the incoming element, we add it to the
  unique array.

  It's more efficient to use a dictionary because it allows us to do this in 0(1)
  time rather than 0(n):

  ```py
  unique_dict = {x[0]: x for x in dummy_data}
  unique_data = list(unique_dict.values())
  ```

  This approach leverages the fact that a dictionary cannot have duplicate keys:
  if you try to insert an element with a key that already exists, the new value
  will simply overwrite the old value for that key.

  Accordingly, we create a dictionary which uses the unique key in each list as
  the key of each dictionary entry via
  [dictionary comprehension](List_comprehension_in_Python.md#dictionary-comprehension)
  that loops through each value in the inner lists of the multidimensional array.
  We then parse the values of the dictionary into a list.

  ## List of dictionaries

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

  # Iterating through complex data structures in Python

  // TODO: sections :

  - Looping, mapping, filtering lists
  - Looping, mapping, filtering dictionaries
  - As above with multi-dimensional lists
  - As above with list of dictionaries

  ## Complex data structures

  In JavaScript when we return data from an API we tend to use an array of objects
  as the canonical form of a repeating iterable, e.g:

  ```js
  const data = [
    {
      name: "Thomas",
      age: 35,
    },
    {
      name: "Joe",
      age: 75,
    },
  ];
  ```

  In Python there are two common ways to handle similar data structures:

  - A list of lists:
    ```py
    data = [
      ["Thomas", 35],
      ["Joe", 75]
    ]
    ```
  - A list of dictionaries:
    ```py
    data = [
       {"name": "Thomas", "age": 35},
       {"name": "Joe", "age": 75},
    ]
    ```

  ## Multi-dimensional lists (list of lists)

  ### Sorting by common property

  Assuming the sub-lists have an identical structure, you can
  [sort](Sorting_lists_in_Python.md) them by
  a common property by passing a
  [lambda function](Lambdas_in_Python.md) to
  the `key` value of `sorted()` and `.sort()`.

  For example, to sort the following list of lists by the second `age` property:

  ```python
  people = [["Alice", 30], ["Bob", 25], ["Clare", 35], ["Dave", 28]]
  ```

  Using `sorted()`:

  ```py
  sorted_people = sorted(people, key=lambda x: x[1])
  print(sorted_people)
  # Output: [['Bob', 25], ['Dave', 28], ['Alice', 30], ['Clare', 35]]
  ```

  Using `.sort`:

  ```py
  people.sort(key=lambda x: x[1])
  print(people)
  # Output: [['Bob', 25], ['Dave', 28], ['Alice', 30], ['Clare', 35]]
  ```

  If you want to sort by name instead, you could change the lambda function to
  `lambda x: x[0]`:

  ```python
  # Using sorted()
  sorted_people = sorted(people, key=lambda x: x[0])
  print(sorted_people)
  # Output: [['Alice', 30], ['Bob', 25], ['Clare', 35], ['Dave', 28]]

  # Using .sort()
  people.sort(key=lambda x: x[0])
  print(people)
  # Output: [['Alice', 30], ['Bob', 25], ['Clare', 35], ['Dave', 28]]
  ```

  ### Updating an inner value within a multidimensional list

  In the following example we have a list of the following structure:

  ```py
  data = [
      ["1688582410", "Article One"],
      ["1688647447", "Article Two"],
      ["1689023491", "Article Three"],
  ]
  ```

  Below we use
  [map](Map_and_filter_in_Python.md) and a
  [lambda function](Lambdas_in_Python.md) to
  convert the first element of each iner list from a Unix timestamp to a readable
  string:

  ```py
   readable_date = list(map(lambda i: [convert_timestamp(i[0])] + i[1:], date))
  ```

  We could also use
  [list comprehension](List_comprehension_in_Python.md)
  to achieve the same outcome:

  ```py
  readable_date = [[convert_timestamp(i[0])] + i[1:] for i in data]
  ```

  ### Filter elements in a multidimensional list

  Say we have the following data structure:

  ```py
  name_age = [ ["Anthony", 16], ["Christopher", 22], ["James", 6] ]
  ```

  We can return only the people who are older than 18 with the following `filter`
  function and `lambda`:

  ```py
  filtered_ages = list(filter(lambda person: person[1] > 18, name_age))
  ```

  ### Remove duplicate entries from multidimensional list

  If we are just working with a normal list, a quick way to remove duplicates is
  just:

  ```py
  our_list = [9, 9, 3, 2]
  unique = list(set(our_list))
  ```

  This won't work with a list of lists because lists are not hashable.

  There are different approaches. Let's say we have the following multidimensional
  list:

  ```py
  dummy_data = [
      ["1689023491", "Article Three", "Lorem ipsum...", "https://example.com"],
      ["1688582410", "Article One", "Lorem ipsum...", "https://example.com"],
      ["1688647447", "Article Two", "Lorem ipsum...", "https://example.com"],
      ["1689023491", "Article Three", "Lorem ipsum...", "https://example.com"],
  ]
  ```

  Here is one method:

  ```py
  unique = []
  seen = set()
  for element in dummy_data:
      element_to_check = element[0]
      if element_to_check not in seen:
          unique.append(element)
          seen.add(element_to_check)
  print(unique)
  ```

  We designate a unique property in the inner lists and then store a unique record
  of each. If the seen set doesn't have the incoming element, we add it to the
  unique array.

  It's more efficient to use a dictionary because it allows us to do this in 0(1)
  time rather than 0(n):

  ```py
  unique_dict = {x[0]: x for x in dummy_data}
  unique_data = list(unique_dict.values())
  ```

  This approach leverages the fact that a dictionary cannot have duplicate keys:
  if you try to insert an element with a key that already exists, the new value
  will simply overwrite the old value for that key.

  Accordingly, we create a dictionary which uses the unique key in each list as
  the key of each dictionary entry via
  [dictionary comprehension](List_comprehension_in_Python.md#dictionary-comprehension)
  that loops through each value in the inner lists of the multidimensional array.
  We then parse the values of the dictionary into a list.

  ## List of dictionaries


path: Joins_in_SQL.md
sortable_path: Joins_in_SQL.md
title: Joins in SQL
checksum: 6a774fe4238d77ff4c2738c5352ec7e5df7b5f564a96348deaa82f6c931f261f
created: 2024-10-22 16:52:57.275996763+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["SQL","databases"]}
lead:
  <!-- Once a relationship has been created using primary and foreign keys (as detailed in the previous section), you are able to combine and integrate data from the different tables. This is known as performing **joins.** -->

body:
  <!-- Once a relationship has been created using primary and foreign keys (as detailed in the previous section), you are able to combine and integrate data from the different tables. This is known as performing **joins.** -->

  We perform joins in SQL when we combine and integrate data from multiple tables.
  In order for a join to be possible, **the tables you want to join must have rows
  that share the same fields**.

  The output will be represented as a table but it is virtual, not an actual
  table. (If you wish to actually create a table or a view off the back of a join
  operation you should combine the join with the
  [`CREATE TABLE`](Create_an_SQL_table.md) command etc.)

  ## Inner joins

  > An inner join combines rows that have matching values in two or more tables.
  > This join is based on the existence of a common field between the tables and
  > is used to retrieve data that appears in both tables.

  The following is a good scenario for creating an inner join:

  > We want to create a list of the name of all computers that have been sold and
  > when they were sold.

  We will be working from the following tables.

  _The `sales` table_:

  | saleId | modelId | saleDate   | employeeId | price   |
  | ------ | ------- | ---------- | ---------- | ------- |
  | 1      | 44      | 2020-07-27 | tbishop    | 40.99   |
  | 2      | 22      | 2021-02-05 | tbishop    | 986.99  |
  | 3      | 14      | 2022-11-16 | tgnomay    | 1248.99 |

  _The `model` table_:

  | modelId | modelName            |
  | ------- | -------------------- |
  | 44      | Raspberry Pi Model A |
  | 22      | Apple MacBook Air    |
  | 14      | Lenovo ThinkPad P15  |

  The data points we need are not provided in a single table. `sales` gives us the
  `saleDate` but not the `modelName`. In order to meet our requirements we must
  perform a join that combines `modelName` from the `model` table and `saleDate`
  from the `sale` table to give us a table like the following:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |

  We can do this by utilising the field they have in common: `modelId`.

  ### Implementation

  The general syntax for creating an inner join from two tables that share a
  common field is as follows:

  ```sql
  SELECT column_name(s)
  FROM table1
  INNER JOIN table2
  ON table1.columnName = table2.columnName
  ```

  So basically, look at `table1` and `table2` return the rows where the fields for
  `table1` and `table2` match, where these fields are the ones specified in the
  `SELECT`.

  Applied to our example:

  ```sql
  SELECT model.modelName, sales.saleDate -- Name the fields
  FROM model -- Specify the first table
  INNER JOIN sales -- Specify the second table
  ON model.model_id = sales.model_id; -- Specify the match criteria
  ```

  > It is important to note that the match must _be_ a match. Both tables must
  > have a row with the same value for `modelId` for the join to be applied. If
  > there is a `modelId` of X in `sales` but no corresponding X for `modelId` in
  > `model`, nothing will be returned for that row.

  We can represent the logical relationship that obtains between the `sales` and
  `model` tables as follows:

  ![](/img/sql-inner-join.png)

  ## Outer joins

  Outer joins are joins that return matched values **and unmatched** values from
  either or both tables.

  To explain outer joins we need to expand our data set:

  _The `sales` table_:

  | saleId | modelId | saleDate   | employeeId | price   |
  | ------ | ------- | ---------- | ---------- | ------- |
  | 1      | 44      | 2020-07-27 | tbishop    | 40.99   |
  | 2      | 22      | 2021-02-05 | tbishop    | 986.99  |
  | 3      | 14      | 2022-11-16 | tgnomay    | 1248.99 |
  | 4      |         | 2020-03-01 | msmelk     | 699.99  |

  _The `model` table_:

  | modelId | modelName            |
  | ------- | -------------------- |
  | 44      | Raspberry Pi Model A |
  | 22      | Apple MacBook Air    |
  | 14      | Lenovo ThinkPad P15  |
  | 66      | Google Chromebook    |

  > We will assume that `sales` is our left table and `model` our right.

  ### Left outer join

  A left outer join returns all records from the left or first table and the
  matching records from the right or second table. If there are not matches on the
  right then only the left data is returned.

  In the context of our dataset, in the _sales_ database we have a row where there
  is a sale with no corresponding `modelId`. If we performed a left outer join
  this would give us the following table in return:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  |                      | 2020-03-01 |

  The logical relationship sustained between `sales` and `model` by a left inner
  join is represented in the following diagram:

  ![](/img/sql-left-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  LEFT JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  LEFT JOIN model
  ON sales.modelId = model.modelId
  ```

  ### Right outer join

  This is the inverse of the right outer join. A right outer join returns all
  records from the right or second table and the matching records from the left or
  first table. If there are not matches on the left then only the right data is
  returned.

  In the context of our dataset, in the _model_ database we have a row where there
  is a `modelId` without a corresponding entry in the `sales` table. If we
  performed a right outer join this would give us the following table in return:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  | Google Chromebook    |            |

  The logical relationship sustained between `sales` and `model` by a right inner
  join is represented in the following diagram:

  ![](/img/sql-right-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  RIGHT JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  RIGHT JOIN model
  ON sales.modelId = model.modelId
  ```

  ### Full outer join

  The full outer join returns all rows when there is a match in either the left or
  the right table. So, for table A and table B the full join returns all rows in A
  and all rows in B including those where there is a match between A and B. This
  is the broadest type of join and can often result in large data sets.

  In the context of our dataset it would result in the following table being
  generated:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  |                      | 2020-03-01 |
  | Google Chromebook    |            |

  Represented by the following diagram:

  ![](/img/sql-full-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  FULL OUTER JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  FULL OUTER JOIN model
  ON sales.modelId = model.modelId
  ```

  This type of join is used when you want to discern when there is _not_ a match
  between two fields across tables. For example: imagine that you wanted a list of
  computers that had never been sold. In this case, you would be interested in
  rows where there is a `model_id` without a corresponding `sales_id` .

  In SQL this would be achieved with:

  ```sql
  SELECT model.name, sales.sale_date
  FROM model
  LEFT JOIN sales on model.model_id = sales.model_id;
  ```

  Note that this would return all the model names but where there isn't a sale
  data, `NULL` would be returned. This is an **important distinction :** the outer
  join method doesn't just return the rows with a `NULL` value for `sale_date` as
  we might expect. It returns all models along with those that have not been sold.
  This is because it is oriented to the "left" table, equivalent to the table in
  the SQL that we cited first with the `on` keyword.

  > A left outer join returns all the records from the left (model) table and
  > those that match in the right (sales) table. Where there are no matching
  > records in the right (sales) table, a `NULL` value is returned.

  A **right outer join**, often referred to as a right join, is the opposite of a
  left outer; it returns all the records from the right table and those that match
  in the left table. In our scenario this would be all the models that had a
  `sale_date` including models that didn't have a `sale_date` , i.e which returned
  `NULL`

  Finally, a **full outer join** returns all the records from both tables, and
  where a record cannot be matched, a NULL value is returned. So this would mean
  there could be `NULL`s in both fields of the returned rows.

  We can combine multiple types of join in the same SQL query:

  ```sql
  SELECT model.name, sales.sale_date, manufacturer.url
  FROM model
  LEFT JOIN sales on model.model_id = sales.model_id
  INNER JOIN manufacturer on model.manufacturer_id = manufacturer.manufacturer_id;
  ```

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

  # Joins in SQL

  <!-- Once a relationship has been created using primary and foreign keys (as detailed in the previous section), you are able to combine and integrate data from the different tables. This is known as performing **joins.** -->

  We perform joins in SQL when we combine and integrate data from multiple tables.
  In order for a join to be possible, **the tables you want to join must have rows
  that share the same fields**.

  The output will be represented as a table but it is virtual, not an actual
  table. (If you wish to actually create a table or a view off the back of a join
  operation you should combine the join with the
  [`CREATE TABLE`](Create_an_SQL_table.md) command etc.)

  ## Inner joins

  > An inner join combines rows that have matching values in two or more tables.
  > This join is based on the existence of a common field between the tables and
  > is used to retrieve data that appears in both tables.

  The following is a good scenario for creating an inner join:

  > We want to create a list of the name of all computers that have been sold and
  > when they were sold.

  We will be working from the following tables.

  _The `sales` table_:

  | saleId | modelId | saleDate   | employeeId | price   |
  | ------ | ------- | ---------- | ---------- | ------- |
  | 1      | 44      | 2020-07-27 | tbishop    | 40.99   |
  | 2      | 22      | 2021-02-05 | tbishop    | 986.99  |
  | 3      | 14      | 2022-11-16 | tgnomay    | 1248.99 |

  _The `model` table_:

  | modelId | modelName            |
  | ------- | -------------------- |
  | 44      | Raspberry Pi Model A |
  | 22      | Apple MacBook Air    |
  | 14      | Lenovo ThinkPad P15  |

  The data points we need are not provided in a single table. `sales` gives us the
  `saleDate` but not the `modelName`. In order to meet our requirements we must
  perform a join that combines `modelName` from the `model` table and `saleDate`
  from the `sale` table to give us a table like the following:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |

  We can do this by utilising the field they have in common: `modelId`.

  ### Implementation

  The general syntax for creating an inner join from two tables that share a
  common field is as follows:

  ```sql
  SELECT column_name(s)
  FROM table1
  INNER JOIN table2
  ON table1.columnName = table2.columnName
  ```

  So basically, look at `table1` and `table2` return the rows where the fields for
  `table1` and `table2` match, where these fields are the ones specified in the
  `SELECT`.

  Applied to our example:

  ```sql
  SELECT model.modelName, sales.saleDate -- Name the fields
  FROM model -- Specify the first table
  INNER JOIN sales -- Specify the second table
  ON model.model_id = sales.model_id; -- Specify the match criteria
  ```

  > It is important to note that the match must _be_ a match. Both tables must
  > have a row with the same value for `modelId` for the join to be applied. If
  > there is a `modelId` of X in `sales` but no corresponding X for `modelId` in
  > `model`, nothing will be returned for that row.

  We can represent the logical relationship that obtains between the `sales` and
  `model` tables as follows:

  ![](/img/sql-inner-join.png)

  ## Outer joins

  Outer joins are joins that return matched values **and unmatched** values from
  either or both tables.

  To explain outer joins we need to expand our data set:

  _The `sales` table_:

  | saleId | modelId | saleDate   | employeeId | price   |
  | ------ | ------- | ---------- | ---------- | ------- |
  | 1      | 44      | 2020-07-27 | tbishop    | 40.99   |
  | 2      | 22      | 2021-02-05 | tbishop    | 986.99  |
  | 3      | 14      | 2022-11-16 | tgnomay    | 1248.99 |
  | 4      |         | 2020-03-01 | msmelk     | 699.99  |

  _The `model` table_:

  | modelId | modelName            |
  | ------- | -------------------- |
  | 44      | Raspberry Pi Model A |
  | 22      | Apple MacBook Air    |
  | 14      | Lenovo ThinkPad P15  |
  | 66      | Google Chromebook    |

  > We will assume that `sales` is our left table and `model` our right.

  ### Left outer join

  A left outer join returns all records from the left or first table and the
  matching records from the right or second table. If there are not matches on the
  right then only the left data is returned.

  In the context of our dataset, in the _sales_ database we have a row where there
  is a sale with no corresponding `modelId`. If we performed a left outer join
  this would give us the following table in return:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  |                      | 2020-03-01 |

  The logical relationship sustained between `sales` and `model` by a left inner
  join is represented in the following diagram:

  ![](/img/sql-left-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  LEFT JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  LEFT JOIN model
  ON sales.modelId = model.modelId
  ```

  ### Right outer join

  This is the inverse of the right outer join. A right outer join returns all
  records from the right or second table and the matching records from the left or
  first table. If there are not matches on the left then only the right data is
  returned.

  In the context of our dataset, in the _model_ database we have a row where there
  is a `modelId` without a corresponding entry in the `sales` table. If we
  performed a right outer join this would give us the following table in return:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  | Google Chromebook    |            |

  The logical relationship sustained between `sales` and `model` by a right inner
  join is represented in the following diagram:

  ![](/img/sql-right-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  RIGHT JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  RIGHT JOIN model
  ON sales.modelId = model.modelId
  ```

  ### Full outer join

  The full outer join returns all rows when there is a match in either the left or
  the right table. So, for table A and table B the full join returns all rows in A
  and all rows in B including those where there is a match between A and B. This
  is the broadest type of join and can often result in large data sets.

  In the context of our dataset it would result in the following table being
  generated:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  |                      | 2020-03-01 |
  | Google Chromebook    |            |

  Represented by the following diagram:

  ![](/img/sql-full-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  FULL OUTER JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  FULL OUTER JOIN model
  ON sales.modelId = model.modelId
  ```

  This type of join is used when you want to discern when there is _not_ a match
  between two fields across tables. For example: imagine that you wanted a list of
  computers that had never been sold. In this case, you would be interested in
  rows where there is a `model_id` without a corresponding `sales_id` .

  In SQL this would be achieved with:

  ```sql
  SELECT model.name, sales.sale_date
  FROM model
  LEFT JOIN sales on model.model_id = sales.model_id;
  ```

  Note that this would return all the model names but where there isn't a sale
  data, `NULL` would be returned. This is an **important distinction :** the outer
  join method doesn't just return the rows with a `NULL` value for `sale_date` as
  we might expect. It returns all models along with those that have not been sold.
  This is because it is oriented to the "left" table, equivalent to the table in
  the SQL that we cited first with the `on` keyword.

  > A left outer join returns all the records from the left (model) table and
  > those that match in the right (sales) table. Where there are no matching
  > records in the right (sales) table, a `NULL` value is returned.

  A **right outer join**, often referred to as a right join, is the opposite of a
  left outer; it returns all the records from the right table and those that match
  in the left table. In our scenario this would be all the models that had a
  `sale_date` including models that didn't have a `sale_date` , i.e which returned
  `NULL`

  Finally, a **full outer join** returns all the records from both tables, and
  where a record cannot be matched, a NULL value is returned. So this would mean
  there could be `NULL`s in both fields of the returned rows.

  We can combine multiple types of join in the same SQL query:

  ```sql
  SELECT model.name, sales.sale_date, manufacturer.url
  FROM model
  LEFT JOIN sales on model.model_id = sales.model_id
  INNER JOIN manufacturer on model.manufacturer_id = manufacturer.manufacturer_id;
  ```


path: Key_characteristics_of_GraphQL.md
sortable_path: Key_characteristics_of_GraphQL.md
title: Key characteristics of GraphQL
checksum: 67f1011150d2c40caae68f5b96c1c03339c9a9b90925c06b8b05534ca54fe2e9
created: 2024-10-22 16:52:57.287126023+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["graphql","APIs"]}
lead:
  > In a nutshell, GraphQL is all about optimizing data communication between a
  > client and a server. This includes the client asking for needed data and
  > communicating that need to the server, and the server preparing a fulfillment
  > for that need and communicating the fulfillment back to the client. GraphQL
  > allows clients to ask for the exact data they need and makes it easier for
  > servers to aggregate data from multiple data storage resources.

body:
  > In a nutshell, GraphQL is all about optimizing data communication between a
  > client and a server. This includes the client asking for needed data and
  > communicating that need to the server, and the server preparing a fulfillment
  > for that need and communicating the fulfillment back to the client. GraphQL
  > allows clients to ask for the exact data they need and makes it easier for
  > servers to aggregate data from multiple data storage resources.

  - GraphQL is a **query language** from which you can build APIs. It exists as an
    abstraction layer between a client that issues requests and a backend
    comprising any number of databases that responds to queries.
  - Crucially, with applications that utilise GraphQL, both the backend and the
    frontend use the same GraphQL syntax. They speak the same language. This means
    that neither needs to take account of the way in which the other is
    implemented. For example, a client could request data from both a relational
    database and an object database without knowing there is any difference from
    its point of view. Conversely, client requests could be made through
    JavaScript or Python and the structure of the request received by the backend
    would be the same.
  - The main benefit is its interoperability and its network economy when compared
    to REST APIs: you ask only for what you want and you get only that; there is
    no surplus data, increasing efficiency, reducing the number of network
    requests and reducing the size of the payload.

  > The best way to think of GraphQL architecturally is to think of it as a
  > different technique for instantiating the _uniform interface_, _client/server
  > decoupling_, and _layered-system architecture_ contraints of traditional
  > [REST APIs](RESTful_APIs.md#rest). However, instead of making
  > GET or POST requests to a REST endpoint URL and passing parameters, you send a
  > structured query object.

  ## Architecture

  From the point of view of the frontend, GraphQL is a query language that may be
  used via wrappers for a given framework or programming language (such as Apollo)
  or directly simply by sending requests over HTTPS to a single URL.

  From the point of view of the backend, GraphQL is a **runtime** that provides a
  structure for servers to describe the data to be exposed in their APIs. We call
  this structure the **schema**. For example, the GraphQL server might translate
  the query into SQL statements for a relational database then take what the
  storage engine responds with, translate it into JSON and send it back to the
  client application.

  > The backend implementation of GraphQL is known as the **GraphQL server**. This
  > server is distinct from any of the physical servers that the backend may rely
  > on. The client queries the GraphQL server.

  Client requests are sent over HTTPS and the data is typically returned in the
  form of JSON:

  ![](/img/graphql-architecture.png)

  ## Implementation overview

  ### Queries, mutations, subscriptions

  Queries can be used for **both** reading and modifying data.

  - When you need to read data with GraphQL you use **queries**
  - When you need to modify data, you use **mutations**

  So, for comparison, a query would be akin to a `READ` instruction in SQL or a
  `GET` request send to a REST API. A mutation would be akin to `WRITE` then
  `READ` instruction in SQL or a `POST` request to a REST API.

  There is a third request type called a **subscription**. This is used for
  real-time data monitoring requests, like a data stream, similar to a continuous
  `READ` process. Mutations typically trigger events that can be subscribed to.

  ### Frontend

  Below is an example of a request that would be made to a GraphQL server from the
  frontend:

  ```graphql
  query {
    employee(id: 42) {
      name
      email
    }
  }
  ```

  ### Backend: structure and behaviour

  We define the structure of a GraphQL API on the backend through the schema. A
  schema is strongly typed and is basically a graph of fields that have types, e.g

  ```graphql
  type Employee(id: Int!) {
    name: String!
    email: String!
  }
  ```

  `!` stands for required. In addition to the standard primitive data-types you
  can also have custom types.

  We implement the behaviour of the API through functions called **resolver
  functions**. Each field in a GraphQL schema is backed by a resolver function. A
  resolver function defines what data to fetch for its field.

  > A resolver function represents the instructions on how and where to access raw
  > data. For example, a resolver function might issue a SQL statement to a
  > relational database, read a file’s data directly from the operating system, or
  > update some cached data in a document database. A resolver function is
  > directly related to a field in a GraphQL request, and it can represent a
  > single primitive value, an object, or a list of values or objects.

  ### GraphQL endpoint

  The GraphQL server exposes a single endpoint as a URL. This is in contrast to a
  REST API where there are multiple endpoints, each corresponding to a different
  resource. With GraphQL, the request is not specified by the URL and the payload
  but rather in the GraphQL string that you send to the endpoint, like the
  `employee` query above.

  ## Benefits

  ### Single requests

  With a REST API if you require multiple resources you have to make multiple
  requests to different endpoints and then once they resolve, synthesise them on
  the client side. With GraphQL you can send a single request that encompasses
  each individual resource:

  The REST scenario:

  ![](/img/REST_request-load.png)

  The GraphQL scenario:

  ![](/img/graphQL_request_load.png)

  ### Abstraction of multiple services

  ![](/img/graphql_multiple_resources.png)

  ### Stops overfetching

  In a strict REST API you must request all resources from a given resource and
  then filter on the client. With GraphQL you request only the resource you need
  at the client-level.

  ### Single endpoint

  In GraphQL you have a single endpoint and it always remains the same. This makes
  updates easier to manage, since you don't need to broadcast new endpoints as the
  API grows. It also simplifies frontend parsing, since you don't need to account
  for, or interpolate different endpoints in your request URL.

raw_content:
  ---
  tags: [graphql, APIs]
  ---

  # Key characteristics of GraphQL

  > In a nutshell, GraphQL is all about optimizing data communication between a
  > client and a server. This includes the client asking for needed data and
  > communicating that need to the server, and the server preparing a fulfillment
  > for that need and communicating the fulfillment back to the client. GraphQL
  > allows clients to ask for the exact data they need and makes it easier for
  > servers to aggregate data from multiple data storage resources.

  - GraphQL is a **query language** from which you can build APIs. It exists as an
    abstraction layer between a client that issues requests and a backend
    comprising any number of databases that responds to queries.
  - Crucially, with applications that utilise GraphQL, both the backend and the
    frontend use the same GraphQL syntax. They speak the same language. This means
    that neither needs to take account of the way in which the other is
    implemented. For example, a client could request data from both a relational
    database and an object database without knowing there is any difference from
    its point of view. Conversely, client requests could be made through
    JavaScript or Python and the structure of the request received by the backend
    would be the same.
  - The main benefit is its interoperability and its network economy when compared
    to REST APIs: you ask only for what you want and you get only that; there is
    no surplus data, increasing efficiency, reducing the number of network
    requests and reducing the size of the payload.

  > The best way to think of GraphQL architecturally is to think of it as a
  > different technique for instantiating the _uniform interface_, _client/server
  > decoupling_, and _layered-system architecture_ contraints of traditional
  > [REST APIs](RESTful_APIs.md#rest). However, instead of making
  > GET or POST requests to a REST endpoint URL and passing parameters, you send a
  > structured query object.

  ## Architecture

  From the point of view of the frontend, GraphQL is a query language that may be
  used via wrappers for a given framework or programming language (such as Apollo)
  or directly simply by sending requests over HTTPS to a single URL.

  From the point of view of the backend, GraphQL is a **runtime** that provides a
  structure for servers to describe the data to be exposed in their APIs. We call
  this structure the **schema**. For example, the GraphQL server might translate
  the query into SQL statements for a relational database then take what the
  storage engine responds with, translate it into JSON and send it back to the
  client application.

  > The backend implementation of GraphQL is known as the **GraphQL server**. This
  > server is distinct from any of the physical servers that the backend may rely
  > on. The client queries the GraphQL server.

  Client requests are sent over HTTPS and the data is typically returned in the
  form of JSON:

  ![](/img/graphql-architecture.png)

  ## Implementation overview

  ### Queries, mutations, subscriptions

  Queries can be used for **both** reading and modifying data.

  - When you need to read data with GraphQL you use **queries**
  - When you need to modify data, you use **mutations**

  So, for comparison, a query would be akin to a `READ` instruction in SQL or a
  `GET` request send to a REST API. A mutation would be akin to `WRITE` then
  `READ` instruction in SQL or a `POST` request to a REST API.

  There is a third request type called a **subscription**. This is used for
  real-time data monitoring requests, like a data stream, similar to a continuous
  `READ` process. Mutations typically trigger events that can be subscribed to.

  ### Frontend

  Below is an example of a request that would be made to a GraphQL server from the
  frontend:

  ```graphql
  query {
    employee(id: 42) {
      name
      email
    }
  }
  ```

  ### Backend: structure and behaviour

  We define the structure of a GraphQL API on the backend through the schema. A
  schema is strongly typed and is basically a graph of fields that have types, e.g

  ```graphql
  type Employee(id: Int!) {
    name: String!
    email: String!
  }
  ```

  `!` stands for required. In addition to the standard primitive data-types you
  can also have custom types.

  We implement the behaviour of the API through functions called **resolver
  functions**. Each field in a GraphQL schema is backed by a resolver function. A
  resolver function defines what data to fetch for its field.

  > A resolver function represents the instructions on how and where to access raw
  > data. For example, a resolver function might issue a SQL statement to a
  > relational database, read a file’s data directly from the operating system, or
  > update some cached data in a document database. A resolver function is
  > directly related to a field in a GraphQL request, and it can represent a
  > single primitive value, an object, or a list of values or objects.

  ### GraphQL endpoint

  The GraphQL server exposes a single endpoint as a URL. This is in contrast to a
  REST API where there are multiple endpoints, each corresponding to a different
  resource. With GraphQL, the request is not specified by the URL and the payload
  but rather in the GraphQL string that you send to the endpoint, like the
  `employee` query above.

  ## Benefits

  ### Single requests

  With a REST API if you require multiple resources you have to make multiple
  requests to different endpoints and then once they resolve, synthesise them on
  the client side. With GraphQL you can send a single request that encompasses
  each individual resource:

  The REST scenario:

  ![](/img/REST_request-load.png)

  The GraphQL scenario:

  ![](/img/graphQL_request_load.png)

  ### Abstraction of multiple services

  ![](/img/graphql_multiple_resources.png)

  ### Stops overfetching

  In a strict REST API you must request all resources from a given resource and
  then filter on the client. With GraphQL you request only the resource you need
  at the client-level.

  ### Single endpoint

  In GraphQL you have a single endpoint and it always remains the same. This makes
  updates easier to manage, since you don't need to broadcast new endpoints as the
  API grows. It also simplifies frontend parsing, since you don't need to account
  for, or interpolate different endpoints in your request URL.


path: Killing_processes.md
sortable_path: Killing_processes.md
title: Killing processes
lead: ## Kill a process running on a port
checksum: 07218a7413a82d17fabe750d5aae2b74aebb0b518682695a1842695a96dc5f3c
created: 2024-10-22 16:52:57.294602202+00:00
modified: 2024-10-18 18:16:00.905386169+00:00
metadata: {"tags":["shell","procedural"]}
body:
  ## Kill a process running on a port

  For example a local server.

  ```bash
  sudo lsof -t -i:8000
  # List files and proces ID (-t) and internet connections (-i) on port number

  $ 7890
  # Gives you process id

  sudo kill -9 7890
  # Kill the process that is running there
  ```

  ## Alterntive

  ```sh
  ss -ltnp | grep ':8000'
  ```

  Returns:

  ```
  LISTEN 0      511            [::1]:8000          [::]:*    users:(("node",pid=10493,fd=34))
  ```

  Kill with:

  ```
  kill 10493
  ```

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

  # Killing processes

  ## Kill a process running on a port

  For example a local server.

  ```bash
  sudo lsof -t -i:8000
  # List files and proces ID (-t) and internet connections (-i) on port number

  $ 7890
  # Gives you process id

  sudo kill -9 7890
  # Kill the process that is running there
  ```

  ## Alterntive

  ```sh
  ss -ltnp | grep ':8000'
  ```

  Returns:

  ```
  LISTEN 0      511            [::1]:8000          [::]:*    users:(("node",pid=10493,fd=34))
  ```

  Kill with:

  ```
  kill 10493
  ```


path: LEDs.md
sortable_path: LEDs.md
title: LEDs
checksum: 5c8ac42013743c18c7549d23da080ac4901800a4fa11ec252f85f79d3992b0b8
created: 2024-10-22 16:52:57.299705092+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["electronics"]}
lead:
  LED' stands for **Light Emitting Diode**, a [circuit]() component that emits
  light. The symbol for an LED is displayed below:

body:
  LED' stands for **Light Emitting Diode**, a [circuit]() component that emits
  light. The symbol for an LED is displayed below:

  ![](/img/diode-led.png)

  A **diode** is a special kind of component that only permits current to flow
  through it in one direction. To achieve this it has very low resistance in one
  direction to allow current flow and high resistance in the other direction to
  impede current flow. This feature of diodes is clearly represented in the
  generic diode circuit symbol:

  ![](/img/diode.png)

  An LED diode lights up when the right amount of current flows through it. A
  standard LED has a maximum current of 20mA. An appropriate
  [resistor](Resistance.md#resistors)
  must therefore be added to the circuit to ensure the current doesn't exceed this
  amount.

raw_content:
  ---
  tags:
    - electronics
  ---

  # LEDs

  LED' stands for **Light Emitting Diode**, a [circuit]() component that emits
  light. The symbol for an LED is displayed below:

  ![](/img/diode-led.png)

  A **diode** is a special kind of component that only permits current to flow
  through it in one direction. To achieve this it has very low resistance in one
  direction to allow current flow and high resistance in the other direction to
  impede current flow. This feature of diodes is clearly represented in the
  generic diode circuit symbol:

  ![](/img/diode.png)

  An LED diode lights up when the right amount of current flows through it. A
  standard LED has a maximum current of 20mA. An appropriate
  [resistor](Resistance.md#resistors)
  must therefore be added to the circuit to ensure the current doesn't exceed this
  amount.


path: Lambda_directory_structure.md
sortable_path: Lambda_directory_structure.md
title: Lambda directory structure
lead: I am using the following structure for my lambdas:
checksum: 3a91446521948d44f2605951ee5bb5eef2a24d6133d7cd013309cce89c42bdd3
created: 2024-10-22 16:52:57.307383114+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["AWS","aws-lambda"]}
body:
  I am using the following structure for my lambdas:

  ```
  lambda-name/
      events/
      function-name/
          src/
              index.js
          tests/
          local-env.json
          production-env.json
          package.json
      template.yaml
      README.md
  ```

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

  # Lambda directory structure

  I am using the following structure for my lambdas:

  ```
  lambda-name/
      events/
      function-name/
          src/
              index.js
          tests/
          local-env.json
          production-env.json
          package.json
      template.yaml
      README.md
  ```


path: Lambda_handler_function.md
sortable_path: Lambda_handler_function.md
title: AWS Lambda handler function
lead: Every Lambda function begins the same way with a handler function. In NodeJS:
checksum: db7083124ec79bc49cc7e3f28756a97c48e7d950d679c34bcb546b92a6982771
created: 2024-10-22 16:52:57.310396102+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["AWS","aws-lambda"]}
body:
  Every Lambda function begins the same way with a handler function. In NodeJS:

  ```js
  module.exports.handler = function(event, context, callback){...}
  ```

  The handler is the entry point for the Lambda. It is a function that Lambda can
  call to start the execution of your code when an event triggers your Lambda
  function.

  You may put your whole code in the body of the handler, or you may call other
  modules and keep the handler clean and focused on bootstrapping.

  The `module.exports` part is just the standard way of exporting a module in
  Node. `handler` is a property that is being exported.

  You always have access to the parameters `event`, `context` and `callback`.

  - `event`
    - The triggering event of the Lambda function stored as an object. The
      object's contents will be different depending on the trigger. It could be
      any AWS service that serves a trigger (S3, DynamoDB, API Gateway etc).
    - For example, if the triggering function is a request to API Gateway, the
      `event` object will contain information about the HTTP request such as
      headers, path, and the request body.
  - `context`
    - contains information about the runtime environment and the current execution
      of the Lambda function. The context object provides properties and methods
      that you can use to interact with the AWS Lambda service and the current
      invocation.
  - `callback`
    - an optional parameter that represents a callback function provided by AWS
      Lambda. The callback function allows you to signal the completion of your
      Lambda function's execution and return a response or an error. The callback
      function accepts two arguments: error and result. If the first argument is
      not null, AWS Lambda treats it as an error, and the Lambda function
      invocation fails. If the first argument is null, AWS Lambda treats the
      second argument as the result and considers the invocation successful. Using
      the callback function is more common in non-async handler functions. When
      you use async functions, you can return a response or throw an error
      directly, without using the callback function.

  ### Full example:

  In JavaScript:

  ```js
  module.exports.handler = async (event, context) => {
    // Your logic to handle the incoming event
    // For example, you can call other AWS services, process the event data, etc.

    // Return a response
    return {
      statusCode: 200,
      body: JSON.stringify({
        message: "Your Lambda function has successfully executed!",
      }),
    };
  };
  ```

  In Python:

  ```py
  import json

  def handler(event, context):
      # Your logic to handle the incoming event
      # For example, you can call other AWS services, process the event data, etc.

      # Return a response
      response = {
          'statusCode': 200,
          'body': json.dumps({
              'message': 'Your Lambda function has successfully executed!'
          })
      }
      return response
  ```

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

  # AWS Lambda handler function

  Every Lambda function begins the same way with a handler function. In NodeJS:

  ```js
  module.exports.handler = function(event, context, callback){...}
  ```

  The handler is the entry point for the Lambda. It is a function that Lambda can
  call to start the execution of your code when an event triggers your Lambda
  function.

  You may put your whole code in the body of the handler, or you may call other
  modules and keep the handler clean and focused on bootstrapping.

  The `module.exports` part is just the standard way of exporting a module in
  Node. `handler` is a property that is being exported.

  You always have access to the parameters `event`, `context` and `callback`.

  - `event`
    - The triggering event of the Lambda function stored as an object. The
      object's contents will be different depending on the trigger. It could be
      any AWS service that serves a trigger (S3, DynamoDB, API Gateway etc).
    - For example, if the triggering function is a request to API Gateway, the
      `event` object will contain information about the HTTP request such as
      headers, path, and the request body.
  - `context`
    - contains information about the runtime environment and the current execution
      of the Lambda function. The context object provides properties and methods
      that you can use to interact with the AWS Lambda service and the current
      invocation.
  - `callback`
    - an optional parameter that represents a callback function provided by AWS
      Lambda. The callback function allows you to signal the completion of your
      Lambda function's execution and return a response or an error. The callback
      function accepts two arguments: error and result. If the first argument is
      not null, AWS Lambda treats it as an error, and the Lambda function
      invocation fails. If the first argument is null, AWS Lambda treats the
      second argument as the result and considers the invocation successful. Using
      the callback function is more common in non-async handler functions. When
      you use async functions, you can return a response or throw an error
      directly, without using the callback function.

  ### Full example:

  In JavaScript:

  ```js
  module.exports.handler = async (event, context) => {
    // Your logic to handle the incoming event
    // For example, you can call other AWS services, process the event data, etc.

    // Return a response
    return {
      statusCode: 200,
      body: JSON.stringify({
        message: "Your Lambda function has successfully executed!",
      }),
    };
  };
  ```

  In Python:

  ```py
  import json

  def handler(event, context):
      # Your logic to handle the incoming event
      # For example, you can call other AWS services, process the event data, etc.

      # Return a response
      response = {
          'statusCode': 200,
          'body': json.dumps({
              'message': 'Your Lambda function has successfully executed!'
          })
      }
      return response
  ```


path: Lambda_programming_model.md
sortable_path: Lambda_programming_model.md
title: AWS Lambda programming model
lead: The overall architecure consists in the following three processes:
checksum: fc67720166bcee6eceb573ee829de652e833340b4ad16163bb96ba51ee4ca7ad
created: 2024-10-22 16:52:57.314030579+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["AWS","aws-lambda"]}
body:
  The overall architecure consists in the following three processes:

  1. Triggers
  2. Handler function
  3. Code

  ## Triggers

  See [AWS Lambda triggers](Lambda_triggers.md)

  ## Handler function

  See
  [AWS Lambda handler function](Lambda_handler_function.md)

  ## Code

  This is just the stuff that runs in the body of the handler or that is called
  from the handler body.

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

  # AWS Lambda programming model

  The overall architecure consists in the following three processes:

  1. Triggers
  2. Handler function
  3. Code

  ## Triggers

  See [AWS Lambda triggers](Lambda_triggers.md)

  ## Handler function

  See
  [AWS Lambda handler function](Lambda_handler_function.md)

  ## Code

  This is just the stuff that runs in the body of the handler or that is called
  from the handler body.


path: Lambda_triggers.md
sortable_path: Lambda_triggers.md
title: AWS Lambda triggers
lead: Here are some common trigger patterns
checksum: 3586771db03bd5e8ea9e70f94d4be5e2b0015c1b5c44765942f08868468cd483
created: 2024-10-22 16:52:57.324272718+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["AWS","aws-lambda"]}
body:
  Here are some common trigger patterns

  ### API Gateway trigger

  - an HTTP request is sent to AWS API Gateway
  - API Gateway triggers a Lambda function to execute

  ### DynamoDB table change trigger

  - a record in a DynamoDB table is modified
  - DynamoDB triggers a lambda function to execute

  ### S3 trigger

  - a new file is created in S3
  - S3 triggers a Lambda function to execute

  ### SQS trigger

  - a new message is added to an [SQS](AWS_Messaging_services.md#sqs)
    queue
  - SQS triggers a Lambda function to execute

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

  # AWS Lambda triggers

  Here are some common trigger patterns

  ### API Gateway trigger

  - an HTTP request is sent to AWS API Gateway
  - API Gateway triggers a Lambda function to execute

  ### DynamoDB table change trigger

  - a record in a DynamoDB table is modified
  - DynamoDB triggers a lambda function to execute

  ### S3 trigger

  - a new file is created in S3
  - S3 triggers a Lambda function to execute

  ### SQS trigger

  - a new message is added to an [SQS](AWS_Messaging_services.md#sqs)
    queue
  - SQS triggers a Lambda function to execute


path: Lambdas_in_Python.md
sortable_path: Lambdas_in_Python.md
title: Lambdas in Python
checksum: 9fe2f11066968420a8ac1ad471b719570bc546746041668abd6893421ce7c305
created: 2024-10-22 16:52:57.333550768+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["python","aws-lambda","AWS"]}
lead:
  In Python, anonymous functions like arrow-functions in JavaScript (`() => {}`)
  are immediately invoked and unnamed. They are called lambdas.

body:
  In Python, anonymous functions like arrow-functions in JavaScript (`() => {}`)
  are immediately invoked and unnamed. They are called lambdas.

  Whilst they are unnamed, just like JS, the value they return can be stored in a
  variable. They do not require the `return` keyword.

  They are most often used unnamed with the functional methods
  [map, filter](Map_and_filter_in_Python.md)
  and reduce.

  Here is the two syntaxes side by side:

  ```js
  // JavaScript

  const double = (x) => x * x;
  ```

  ```py
  # Python

  double = lambda x: x * x
  ```

  Here is a lambda with multiple parameters:

  ```py
  func = lambda x, y, z: x + y + z
  print(func(2, 3, 4))
  # 9
  ```

  > Lambdas obviously enshrine functional programming paradigms. Therefore they
  > should be pure functions, not mutating values or issueing side effects. For
  > example, it would be improper (though syntactically well-formed) to use a
  > lambda to `print` something

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

  # Lambdas in Python

  In Python, anonymous functions like arrow-functions in JavaScript (`() => {}`)
  are immediately invoked and unnamed. They are called lambdas.

  Whilst they are unnamed, just like JS, the value they return can be stored in a
  variable. They do not require the `return` keyword.

  They are most often used unnamed with the functional methods
  [map, filter](Map_and_filter_in_Python.md)
  and reduce.

  Here is the two syntaxes side by side:

  ```js
  // JavaScript

  const double = (x) => x * x;
  ```

  ```py
  # Python

  double = lambda x: x * x
  ```

  Here is a lambda with multiple parameters:

  ```py
  func = lambda x, y, z: x + y + z
  print(func(2, 3, 4))
  # 9
  ```

  > Lambdas obviously enshrine functional programming paradigms. Therefore they
  > should be pure functions, not mutating values or issueing side effects. For
  > example, it would be improper (though syntactically well-formed) to use a
  > lambda to `print` something


path: Latches.md
sortable_path: Latches.md
title: Latches
checksum: 456a5efe4f003860290d55f3eacc357ef5b2f103f0fb03419c1bb61e014f3cc0
created: 2024-10-22 16:52:57.342831315+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["logic-gates","binary","memory"]}
lead:
  The **combinatorial digital circuits** we have looked at so far have been
  non-sequential. The outcome is a function of its immediate set of inputs and
  everything happens at once: there is no means of storing state for future use.
  In other words there is no _[memory](Memory.md)_.

body:
  The **combinatorial digital circuits** we have looked at so far have been
  non-sequential. The outcome is a function of its immediate set of inputs and
  everything happens at once: there is no means of storing state for future use.
  In other words there is no _[memory](Memory.md)_.

  In contrast, the output of a **sequential digital circuit** depends not only on
  its present set of inputs but also on past inputs to the circuit. It has some
  knowledge of its own previous state through the existence of memory. This can be
  implemented via components that allow for the **storage and retrieval of binary
  data**.

  ## What is a latch?

  A latch is a circuit component that wor

  The **SR Latch** (for "set/reset") has two inputs: _S_ (for "set") and _R_ (for
  "reset") and one output, _Q_. _Q_ stands for the bit that is remembered. (There
  is also _not-Q_ which is the opposite of whatever _Q_ is currently set to.)

  The SR Latch goes through the following state changes:

  - When _S_ is set to 1, output _Q_ becomes 1 also
  - When _S_ goes to 0, _Q_ remains 1
  - When _R_ is set to 1, the memory bit is cleared and _Q_ becomes 0.
  - _Q_ remains at 0 even if _R_ goes back to 0

  This is represented more clearly in the table below:

  | S   | R   | Q   | Operation     |
  | --- | --- | --- | ------------- |
  | 1   | 1   | X   | Invalid, null |

  The most succinct account of a latch:

  > A latch is a 1-bit memory device that has a state _Q_ of either 1 or 0. The
  > _S_ input sets _Q_ to 1 and the _R_ input resets _Q_ to 0.

  _The representation of an SR Latch in a digital circuit diagram_:

  ![](/img/sr_latch_diagram.png)

  ## Creating a latch circuit

  The circuit diagram latch symbol obviously encapsulates more complex
  functionality that occurs at the sub-circuit level. We will demonstrate how this
  functionality can be achieved with two
  [NOR](Logic_gates.md#nor-gate) gates.

  The two gates are in a **cross-coupled configuration**. This basically means
  that the wires are crossed back on themselves such that the output of one is
  also an input of the other at a single stage in the sequence.

  The circuit is created as follows:

  ![](/img/sr_latch_logic_circuit.png)

  Interactive version:

  <iframe src="https://circuitverse.org/simulator/embed/nor-latch-0869192c-7d7b-4161-b13f-3f72c1bce8e9" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="400" width="600" allowFullScreen></iframe>

  <br />
  Let's talk through the logic at each state change:

  - When _S_ is set to 1, output _Q_ becomes 1 also
    - N1 is receiving 1 from S and 0 from R by way of N2. This is the inversion of
      OR so TF equals F. Thus N1 is outputting 0.
    - Thus N2 is receiving 0 from N1 as its top input and 0 from R as its bottom
      input. It is therefore outputting 1 because with NOR, FF equals T. For this
      reason, _Q_ is 1 because is directly connected to _R_.
  - When _S_ goes to 0, _Q_ remains 1
    - N2 is receiving 0 from N1 as the top input and 0 from R as the bottom input
      hence the overall input is FF which means N2 is outputting T and Q remains 1
    - N1 is outputting 0 because it is receiving 0 as its top input and 1 from its
      bottom input
  - When _R_ is set to 1, the memory bit is cleared and _Q_ becomes 0.
    - N2 is receiving 1 from R as its bottom input and 1 from the output of N1 as
      its top input. Therefore it is outputting TT which in NOR evaluates to F
      hence Q is 0
    - N1 is outputting 1 because it is receiving 0 from S as its top input and 0
      from its bottom input coming from N2. FF equals T in NOR therefore 1 is
      outputting 1
  - _Q_ remains at 0 even if _R_ goes back to 0
    - N2 is receiving 0 as its bottom input from R and 1 as its top input from N1.
      TF equals F in NOR hence the output of N2 is 0 and Q remains 0
    - N1 is outputting 1 because it is receiving 0 as its top input from S and 0
      as its bottom input from N2. FF equals T in NOR hence N1 is 1

raw_content:
  ---
  tags: [logic-gates, binary, memory]
  ---

  # Latches

  The **combinatorial digital circuits** we have looked at so far have been
  non-sequential. The outcome is a function of its immediate set of inputs and
  everything happens at once: there is no means of storing state for future use.
  In other words there is no _[memory](Memory.md)_.

  In contrast, the output of a **sequential digital circuit** depends not only on
  its present set of inputs but also on past inputs to the circuit. It has some
  knowledge of its own previous state through the existence of memory. This can be
  implemented via components that allow for the **storage and retrieval of binary
  data**.

  ## What is a latch?

  A latch is a circuit component that wor

  The **SR Latch** (for "set/reset") has two inputs: _S_ (for "set") and _R_ (for
  "reset") and one output, _Q_. _Q_ stands for the bit that is remembered. (There
  is also _not-Q_ which is the opposite of whatever _Q_ is currently set to.)

  The SR Latch goes through the following state changes:

  - When _S_ is set to 1, output _Q_ becomes 1 also
  - When _S_ goes to 0, _Q_ remains 1
  - When _R_ is set to 1, the memory bit is cleared and _Q_ becomes 0.
  - _Q_ remains at 0 even if _R_ goes back to 0

  This is represented more clearly in the table below:

  | S   | R   | Q   | Operation     |
  | --- | --- | --- | ------------- |
  | 1   | 1   | X   | Invalid, null |

  The most succinct account of a latch:

  > A latch is a 1-bit memory device that has a state _Q_ of either 1 or 0. The
  > _S_ input sets _Q_ to 1 and the _R_ input resets _Q_ to 0.

  _The representation of an SR Latch in a digital circuit diagram_:

  ![](/img/sr_latch_diagram.png)

  ## Creating a latch circuit

  The circuit diagram latch symbol obviously encapsulates more complex
  functionality that occurs at the sub-circuit level. We will demonstrate how this
  functionality can be achieved with two
  [NOR](Logic_gates.md#nor-gate) gates.

  The two gates are in a **cross-coupled configuration**. This basically means
  that the wires are crossed back on themselves such that the output of one is
  also an input of the other at a single stage in the sequence.

  The circuit is created as follows:

  ![](/img/sr_latch_logic_circuit.png)

  Interactive version:

  <iframe src="https://circuitverse.org/simulator/embed/nor-latch-0869192c-7d7b-4161-b13f-3f72c1bce8e9" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="400" width="600" allowFullScreen></iframe>

  <br />
  Let's talk through the logic at each state change:

  - When _S_ is set to 1, output _Q_ becomes 1 also
    - N1 is receiving 1 from S and 0 from R by way of N2. This is the inversion of
      OR so TF equals F. Thus N1 is outputting 0.
    - Thus N2 is receiving 0 from N1 as its top input and 0 from R as its bottom
      input. It is therefore outputting 1 because with NOR, FF equals T. For this
      reason, _Q_ is 1 because is directly connected to _R_.
  - When _S_ goes to 0, _Q_ remains 1
    - N2 is receiving 0 from N1 as the top input and 0 from R as the bottom input
      hence the overall input is FF which means N2 is outputting T and Q remains 1
    - N1 is outputting 0 because it is receiving 0 as its top input and 1 from its
      bottom input
  - When _R_ is set to 1, the memory bit is cleared and _Q_ becomes 0.
    - N2 is receiving 1 from R as its bottom input and 1 from the output of N1 as
      its top input. Therefore it is outputting TT which in NOR evaluates to F
      hence Q is 0
    - N1 is outputting 1 because it is receiving 0 from S as its top input and 0
      from its bottom input coming from N2. FF equals T in NOR therefore 1 is
      outputting 1
  - _Q_ remains at 0 even if _R_ goes back to 0
    - N2 is receiving 0 as its bottom input from R and 1 as its top input from N1.
      TF equals F in NOR hence the output of N2 is 0 and Q remains 0
    - N1 is outputting 1 because it is receiving 0 as its top input from S and 0
      as its bottom input from N2. FF equals T in NOR hence N1 is 1


path: Law_of_Non_Contradiction.md
sortable_path: Law_of_Non_Contradiction.md
title: Law of Non-Contradiction
lead: > A proposition cannot be true and false at the same time.
body:
  > A proposition cannot be true and false at the same time.

  $$
  \lnot (P \land \lnot P)
  $$

checksum: ddf0f94542234a0692e65d40c5bc7349d5e4783a9033ede76e874c531e7e2c0a
created: 2024-10-22 16:52:57.355047484+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
raw_content:
  ---
  tags:
    - propositional-logic
    - logic
  ---

  # Law of Non-Contradiction

  > A proposition cannot be true and false at the same time.

  $$
  \lnot (P \land  \lnot P)
  $$


path: Law_of_the_Excluded_Middle.md
sortable_path: Law_of_the_Excluded_Middle.md
title: Law of the Excluded Middle
lead:
  > Every proposition has to be either true or false. There can be no middle
  > ground.

checksum: a53ec515846745f28a9a89390a50710c2ca90f7f1a1180acfa550231761cc6b5
created: 2024-10-22 16:52:57.357920491+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  > Every proposition has to be either true or false. There can be no middle
  > ground.

  $$
  P \lor \sim P
  $$

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

  # Law of the Excluded Middle

  > Every proposition has to be either true or false. There can be no middle
  > ground.

  $$
  P \lor \sim P
  $$


path: Lifecycle_methods.md
sortable_path: Lifecycle_methods.md
title: Lifecycle methods
checksum: f28d1ec8c52599af3685974a5c17a22f2d0042b296ee9ba49ed379592fa024b7
created: 2024-10-22 16:52:57.361535062+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["javascript","react"]}
lead:
  Lifecycles are familiar from nature: something is born, it grows and reaches
  maturity and then dies and the process continues as what dies is broken down
  into constituent parts which form the basis of new life. And so on and so on.

body:
  Lifecycles are familiar from nature: something is born, it grows and reaches
  maturity and then dies and the process continues as what dies is broken down
  into constituent parts which form the basis of new life. And so on and so on.

  The same metaphor can be applied to React components relative to the document
  object model:

  - Components are 'born' (created and mounted on the DOM)
  - They 'grow' by changing and updating their state
  - They 'die' by being unmounted from the DOM

  ## Specific lifecycle phases

  The outline above corresponds to specific methods that execute when React code
  runs:

  1. `componentWillMount()` : this method is called just before the `render()`
     method is called
  2. `componentDidMount()` : this method is called after the component is rendered
     in the DOM

  The two methods above only occur once in a lifecycle.

  The 'updating' phase is achieved with the following methods. This is when the
  data of the component, in the form of state and props updates in response to
  user interaction. We call this process 're-rendering':

  3. `shouldComponentUpdate()` : determines whether component should update

  4. `componentWillUpdate()` : called before re-rerendering occurs

  5. `componentDidUpdate()` : called after re-rendering occurs

  The final phase is unmounting: when the component is removed from the DOM:

  6. `componentWillUnmount()`

  ![React component lifecycle](react-lifecycle.png)

  ## Side-effects: why lifecycle phases matter

  > React is built based on the principles of functional programming. In this
  > paradigm, a side-effect (something that is generally discouraged) is any
  > process that is not the direct result of a function executing or anything that
  > is not covered by the `return` keyword.

  Many of the methods detailed in the previous section will rarely be used in
  React development since they pertain to the inner engineering of the library,
  rather than its application in custom contexts. However sometimes you do need
  access to these methods, depending on the background context of what you are
  trying to achieving. This context could include external API and database
  requests or **anything that does not directly pertain to the rendering of the
  component on the screen**.

  React groups these processes under the heading of **side-effects.**

  ### Examples of side-effects and where in the lifecycle they should run

  - `componentWillMount` : It's a good idea to run set-up scripts at this point,
    such as connecting to and authenticating access to a database. This process
    runs before the `render` method is called so it is an optimal point to
    establish these connections
    - You should not however make data requests at this point since data is
      typically returned asynchronously. You cannot therefore be sure that the
      data will be returned before mounting occurs.
  - `componentDidMount` : This is the time to do things that can't occur without
    the DOM:
    - retrieve data
    - add event listeners
    - set timers using `setTimeout` or `setInterval`
  - Updating methods (`shouldComponentUpdate`, `componentWillUpdate` etc.)
    - As these precede or take place concurrently with the rerendering process,
      this phase is a good point to retrieve new data or run necessary external
      processes that are required by the re-rendering.

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

  # Lifecycle methods

  Lifecycles are familiar from nature: something is born, it grows and reaches
  maturity and then dies and the process continues as what dies is broken down
  into constituent parts which form the basis of new life. And so on and so on.

  The same metaphor can be applied to React components relative to the document
  object model:

  - Components are 'born' (created and mounted on the DOM)
  - They 'grow' by changing and updating their state
  - They 'die' by being unmounted from the DOM

  ## Specific lifecycle phases

  The outline above corresponds to specific methods that execute when React code
  runs:

  1. `componentWillMount()` : this method is called just before the `render()`
     method is called
  2. `componentDidMount()` : this method is called after the component is rendered
     in the DOM

  The two methods above only occur once in a lifecycle.

  The 'updating' phase is achieved with the following methods. This is when the
  data of the component, in the form of state and props updates in response to
  user interaction. We call this process 're-rendering':

  3. `shouldComponentUpdate()` : determines whether component should update

  4. `componentWillUpdate()` : called before re-rerendering occurs

  5. `componentDidUpdate()` : called after re-rendering occurs

  The final phase is unmounting: when the component is removed from the DOM:

  6. `componentWillUnmount()`

  ![React component lifecycle](react-lifecycle.png)

  ## Side-effects: why lifecycle phases matter

  > React is built based on the principles of functional programming. In this
  > paradigm, a side-effect (something that is generally discouraged) is any
  > process that is not the direct result of a function executing or anything that
  > is not covered by the `return` keyword.

  Many of the methods detailed in the previous section will rarely be used in
  React development since they pertain to the inner engineering of the library,
  rather than its application in custom contexts. However sometimes you do need
  access to these methods, depending on the background context of what you are
  trying to achieving. This context could include external API and database
  requests or **anything that does not directly pertain to the rendering of the
  component on the screen**.

  React groups these processes under the heading of **side-effects.**

  ### Examples of side-effects and where in the lifecycle they should run

  - `componentWillMount` : It's a good idea to run set-up scripts at this point,
    such as connecting to and authenticating access to a database. This process
    runs before the `render` method is called so it is an optimal point to
    establish these connections
    - You should not however make data requests at this point since data is
      typically returned asynchronously. You cannot therefore be sure that the
      data will be returned before mounting occurs.
  - `componentDidMount` : This is the time to do things that can't occur without
    the DOM:
    - retrieve data
    - add event listeners
    - set timers using `setTimeout` or `setInterval`
  - Updating methods (`shouldComponentUpdate`, `componentWillUpdate` etc.)
    - As these precede or take place concurrently with the rerendering process,
      this phase is a good point to retrieve new data or run necessary external
      processes that are required by the re-rendering.


path: Link_Layer_of_Internet_Protocol.md
sortable_path: Link_Layer_of_Internet_Protocol.md
title: Link Layer of the Internet Protocol Suite
checksum: aa296b50c848330270028a57ee3f3d5ebe1b7a401db10a81319b40e651035091
created: 2024-10-22 16:52:57.3655865+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"created":"Tuesday, August 06, 2024","tags":["networks","internet"]}
lead:
  > The Link Layer is the lowest level of the network stack that makes up the
  > Internet Protocol Suite. It is concerned with the physical and logical
  > connections between [hosts](./Network_hosts.md) on the same local network.

body:
  > The Link Layer is the lowest level of the network stack that makes up the
  > Internet Protocol Suite. It is concerned with the physical and logical
  > connections between [hosts](./Network_hosts.md) on the same local network.

  The physical and logical connections are known as **Links**.

  Link Layer protocols goven communication between devices. Examples include:

  - **WiFi**
    - For wireless communication between devices using radio waves
  - **Ethernet**
    - Wired communication

  All devices connected to the internet participate in the Link Layer since it is
  the base level and the gateway out of the local network to other networks. The
  same is not true for other layers. For example a device like a laptop
  participates in all four layers up to the Application Layer but a network router
  does not:

  ![Link Layer diagram](../img/link-layer-frame-revised.png)

  ## MAC addresses

  Each device on a link has a network address that uniquely identifies it. For
  most Link Layer Protocols this is a
  [Media Access Control (MAC) address](MAC_addresses.md).

  A device receives data addressed to its MAC address.

  MAC addresses are limited to local networks. Remote networks cannot send data to
  a specific MAC address. This is off-limits from the outside.

  ## Frames

  Link Layer data is divided into small units called "frames". The anatomy of a
  frame is as follows:

  ![Link Layer frame](../img/link-layer-frame-revised.png)

  The header contains the source and destination MAC address plus a descriptor of
  the type of data it contains. The data is the payload. The footer is used to
  detect errors.

raw_content:
  ---
  tags: [networks, internet]
  created: Tuesday, August 06, 2024
  ---

  # Link Layer of the Internet Protocol Suite

  > The Link Layer is the lowest level of the network stack that makes up the
  > Internet Protocol Suite. It is concerned with the physical and logical
  > connections between [hosts](./Network_hosts.md) on the same local network.

  The physical and logical connections are known as **Links**.

  Link Layer protocols goven communication between devices. Examples include:

  - **WiFi**
    - For wireless communication between devices using radio waves
  - **Ethernet**
    - Wired communication

  All devices connected to the internet participate in the Link Layer since it is
  the base level and the gateway out of the local network to other networks. The
  same is not true for other layers. For example a device like a laptop
  participates in all four layers up to the Application Layer but a network router
  does not:

  ![Link Layer diagram](../img/link-layer-frame-revised.png)

  ## MAC addresses

  Each device on a link has a network address that uniquely identifies it. For
  most Link Layer Protocols this is a
  [Media Access Control (MAC) address](MAC_addresses.md).

  A device receives data addressed to its MAC address.

  MAC addresses are limited to local networks. Remote networks cannot send data to
  a specific MAC address. This is off-limits from the outside.

  ## Frames

  Link Layer data is divided into small units called "frames". The anatomy of a
  frame is as follows:

  ![Link Layer frame](../img/link-layer-frame-revised.png)

  The header contains the source and destination MAC address plus a descriptor of
  the type of data it contains. The data is the payload. The footer is used to
  detect errors.


path: Linux_disk_partitions.md
sortable_path: Linux_disk_partitions.md
title: Linux disk partitions
checksum: 73864c1fb6e564152cb3aaf302a8247e12a520c1a1e1d755db9d25d5611d14fa
created: 2024-10-22 16:52:57.377410215+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["disks","Linux","procedural"]}
lead:
  A disk is divided up into partitions which are subsections of the overall disk.
  The kernel presents each partition as a [block device](Devices.md#Devices) as it
  would with an entire disk.

body:
  A disk is divided up into partitions which are subsections of the overall disk.
  The kernel presents each partition as a [block device](Devices.md#Devices) as it
  would with an entire disk.

  The disk dedicates a small part of its contents to a **partition table**: this
  defines the different partitions that comprise the total disk space.

  ## Viewing current partitions

  Whenever you install a Linux distribution on a real or virtual machine, you must
  partition the drive. There are three main tools to choose from: `parted`,
  `g(raphical)parted`, `fdisk`.

  For a top-level overview of your disks and their main partitions you can run
  `lsblk` (_list block devices_):

  ```bash
  $ lsblk

  NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
  sda           8:0    0 465.7G  0 disk
  ├─sda1        8:1    0   200M  0 part
  └─sda2        8:2    0 465.4G  0 part /run/media/thomas/ganesh
  nvme0n1     259:0    0 476.9G  0 disk
  ├─nvme0n1p1 259:1    0   488M  0 part
  ├─nvme0n1p2 259:2    0  27.9G  0 part /
  └─nvme0n1p3 259:3    0 448.5G  0 part /home
  ```

  We can use `parted -l` to view the partition table for the current machine:

  ```bash
  Model: SKHynix_HFS512GDE9X081N (nvme)
  Disk /dev/nvme0n1: 512GB
  Sector size (logical/physical): 512B/512B
  Partition Table: gpt
  Disk Flags:

  Number  Start   End     Size    File system  Name  Flags
   1      1049kB  513MB   512MB   fat32              boot, esp
   2      513MB   30.5GB  30.0GB  ext4
   3      30.5GB  512GB   482GB   ext4
  ```

  We can use `fdisk -l` to get slightly more info:

  ```bash
  disk /dev/nvme0n1: 476.94 GiB, 512110190592 bytes, 1000215216 sectors
  Disk model: SKHynix_HFS512GDE9X081N
  Units: sectors of 1 * 512 = 512 bytes
  Sector size (logical/physical): 512 bytes / 512 bytes
  I/O size (minimum/optimal): 512 bytes / 512 bytes
  Disklabel type: gpt
  Disk identifier: 08175E77-CB9F-C34A-9032-DF29A3F8F0FE

  Device            Start        End   Sectors   Size Type
  /dev/nvme0n1p1     2048    1001471    999424   488M EFI System
  /dev/nvme0n1p2  1001472   59594751  58593280  27.9G Linux filesystem
  /dev/nvme0n1p3 59594752 1000214527 940619776 448.5G Linux filesystem
  ```

  ## Standard Linux partitions

  The two tools disclose that the main harddrive is `/dev/nvme0n1` (equivalent to
  `sda` on older machines running Linux) and it has the standard three partitions:

  - Boot partition (`/dev/nvme0n1p1`)
  - Root dir (`/dev/nvme0n1p2`)
  - Home dir (`/dev/nvme0n1p3`)

  ### Boot partition

  - Takes up the smallest amount of space
  - Contains the Linux kernel and the bootloader (GRUB, usually) and any other
    files required for booting.
  - Once the BIOS has initialized the hardware components it hands control to the
    bootloader stored in this partition.
  - The bootloader loads the kernel contained in its partition, into memory

  ### Root partition (`/`)

  - This houses everything to do with the operation of the operating system apart
    from the initial booting process. Once the kernel is in memory it accesses the
    resources in the root partition to get the OS up and running. These resources
    include system libraries, utilities, system-wide configs and application
    software.

  - The root partition is vital for the system's functionality. Without it, the
    system cannot boot or operate. It needs to be  
     large enough to accommodate the operating system, installed programs, and system
    libraries, but it doesn't need to store user data or  
     additional applications, which can be located on other partitions.

  - This is the domain of the [superuser](User_Space.md#root-user-superuser). The
    part of the filesystem that you need sudo priveleges to access and where you
    manage users

  ### Home partition

  - This is presented as `/home/`, i.e. that it is a descendent of `/`. But this
    is because the Linux file system is hierarchical. Typically `/home/` is
    usually its own partition.
  - Separating user data and system data in this way protects user data during
    upgrades or reinstallation. Reinstalling or upgrading the OS can be done
    without affecting these files.
  - Each user has their own directory off of `/home`, e.g `/home/thomas`.
  - While the root partition (`/`) is essential for the operation of the OS,
    placing `/home` on a separate partition allows for better management of user
    data, enhancing the system's flexibility and the user's control over their own
    files, without compromising the integrity or performance of the OS. This
    approach exemplifies the Linux philosophy of giving the user control while
    maintaining a clear distinction between system  
    resources and user data.

  ## Types of partition table

  In the Linux world there are two main types: MBR and GPT. The type of table used
  determines how the OS boots. So although partition tables are also responsible
  for the partitioning of non-bootable sectors of a disk, **they are distinguished
  by the boot system they implement**. If we look at the output from `parted` and
  `fdisk` above we see that the harddrive uses the GPT partition type.

  #### Primary, extended and logical partitions

  Most standard partition tables allow for primary, extended and logical
  partitions. The primary partition is the part of the harddisk that contains the
  operating system and is thus described as 'bootable' and may be called the 'boot
  partition'. During the bootstrapping process this is injected into memory as the
  [kernel](The_kernel.md).

  The extended partition is basically everything other than the primary partition.
  This is typically subdivided into other partitions that are called _logical_
  partitions. This is because they physically reside in the same sector of the
  disk (the extended partition) but are treated as virtual and independent disks.

  In our example above:

  - `/dev/nvme0n1p1` is the primary/boot partition
  - `/dev/nvme0n1p2` and `/dev/nvme0n1p3` comprise the extended partition and by
    themselves are each logical partitions.

  <dl>
    <dt>MBR</dt>
    <dd> 
      <ul>
        <li>Stands for Master Boot Record</li> 
        <li>Uses BIOS in the boot process</li> 
        <li>Can only works with disks up to 2TB in size</li> 
        <li>Only supports 4 primary partitions. This means the number of operating systems you install is limitied to this number.</li> 
        <li>This is the first 512 bytes of a storage device, preceding the first partition.</li>
      </ul> 
    </dd>
    <dt>GPT</dt>
    <dd>
    <ul>
        <li>Stands for GUID Partition Table </li>
        <li>Gradually replacing MBR</li>
        <li>Uses UEFI instead of BIOS</li>
        <li>As name indicates, every partition on disk has its own globally-unique identifier</li>
        <li>Vastly more partitions available than with MBR (dependent on operating system)</li>
        <li>Offers greater recovery options and anti-corruption safeguards</li> 
      </ul>
  </dd>
  </dl>

  ## Related notes

  ![Creating a Linux partition table](./Creating_a_Linux_partition_table.md)

raw_content:
  ---
  tags:
    - disks
    - Linux
    - procedural
  ---

  # Linux disk partitions

  A disk is divided up into partitions which are subsections of the overall disk.
  The kernel presents each partition as a [block device](Devices.md#Devices) as it
  would with an entire disk.

  The disk dedicates a small part of its contents to a **partition table**: this
  defines the different partitions that comprise the total disk space.

  ## Viewing current partitions

  Whenever you install a Linux distribution on a real or virtual machine, you must
  partition the drive. There are three main tools to choose from: `parted`,
  `g(raphical)parted`, `fdisk`.

  For a top-level overview of your disks and their main partitions you can run
  `lsblk` (_list block devices_):

  ```bash
  $ lsblk

  NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
  sda           8:0    0 465.7G  0 disk
  ├─sda1        8:1    0   200M  0 part
  └─sda2        8:2    0 465.4G  0 part /run/media/thomas/ganesh
  nvme0n1     259:0    0 476.9G  0 disk
  ├─nvme0n1p1 259:1    0   488M  0 part
  ├─nvme0n1p2 259:2    0  27.9G  0 part /
  └─nvme0n1p3 259:3    0 448.5G  0 part /home
  ```

  We can use `parted -l` to view the partition table for the current machine:

  ```bash
  Model: SKHynix_HFS512GDE9X081N (nvme)
  Disk /dev/nvme0n1: 512GB
  Sector size (logical/physical): 512B/512B
  Partition Table: gpt
  Disk Flags:

  Number  Start   End     Size    File system  Name  Flags
   1      1049kB  513MB   512MB   fat32              boot, esp
   2      513MB   30.5GB  30.0GB  ext4
   3      30.5GB  512GB   482GB   ext4
  ```

  We can use `fdisk -l` to get slightly more info:

  ```bash
  disk /dev/nvme0n1: 476.94 GiB, 512110190592 bytes, 1000215216 sectors
  Disk model: SKHynix_HFS512GDE9X081N
  Units: sectors of 1 * 512 = 512 bytes
  Sector size (logical/physical): 512 bytes / 512 bytes
  I/O size (minimum/optimal): 512 bytes / 512 bytes
  Disklabel type: gpt
  Disk identifier: 08175E77-CB9F-C34A-9032-DF29A3F8F0FE

  Device            Start        End   Sectors   Size Type
  /dev/nvme0n1p1     2048    1001471    999424   488M EFI System
  /dev/nvme0n1p2  1001472   59594751  58593280  27.9G Linux filesystem
  /dev/nvme0n1p3 59594752 1000214527 940619776 448.5G Linux filesystem
  ```

  ## Standard Linux partitions

  The two tools disclose that the main harddrive is `/dev/nvme0n1` (equivalent to
  `sda` on older machines running Linux) and it has the standard three partitions:

  - Boot partition (`/dev/nvme0n1p1`)
  - Root dir (`/dev/nvme0n1p2`)
  - Home dir (`/dev/nvme0n1p3`)

  ### Boot partition

  - Takes up the smallest amount of space
  - Contains the Linux kernel and the bootloader (GRUB, usually) and any other
    files required for booting.
  - Once the BIOS has initialized the hardware components it hands control to the
    bootloader stored in this partition.
  - The bootloader loads the kernel contained in its partition, into memory

  ### Root partition (`/`)

  - This houses everything to do with the operation of the operating system apart
    from the initial booting process. Once the kernel is in memory it accesses the
    resources in the root partition to get the OS up and running. These resources
    include system libraries, utilities, system-wide configs and application
    software.

  - The root partition is vital for the system's functionality. Without it, the
    system cannot boot or operate. It needs to be  
     large enough to accommodate the operating system, installed programs, and system
    libraries, but it doesn't need to store user data or  
     additional applications, which can be located on other partitions.

  - This is the domain of the [superuser](User_Space.md#root-user-superuser). The
    part of the filesystem that you need sudo priveleges to access and where you
    manage users

  ### Home partition

  - This is presented as `/home/`, i.e. that it is a descendent of `/`. But this
    is because the Linux file system is hierarchical. Typically `/home/` is
    usually its own partition.
  - Separating user data and system data in this way protects user data during
    upgrades or reinstallation. Reinstalling or upgrading the OS can be done
    without affecting these files.
  - Each user has their own directory off of `/home`, e.g `/home/thomas`.
  - While the root partition (`/`) is essential for the operation of the OS,
    placing `/home` on a separate partition allows for better management of user
    data, enhancing the system's flexibility and the user's control over their own
    files, without compromising the integrity or performance of the OS. This
    approach exemplifies the Linux philosophy of giving the user control while
    maintaining a clear distinction between system  
    resources and user data.

  ## Types of partition table

  In the Linux world there are two main types: MBR and GPT. The type of table used
  determines how the OS boots. So although partition tables are also responsible
  for the partitioning of non-bootable sectors of a disk, **they are distinguished
  by the boot system they implement**. If we look at the output from `parted` and
  `fdisk` above we see that the harddrive uses the GPT partition type.

  #### Primary, extended and logical partitions

  Most standard partition tables allow for primary, extended and logical
  partitions. The primary partition is the part of the harddisk that contains the
  operating system and is thus described as 'bootable' and may be called the 'boot
  partition'. During the bootstrapping process this is injected into memory as the
  [kernel](The_kernel.md).

  The extended partition is basically everything other than the primary partition.
  This is typically subdivided into other partitions that are called _logical_
  partitions. This is because they physically reside in the same sector of the
  disk (the extended partition) but are treated as virtual and independent disks.

  In our example above:

  - `/dev/nvme0n1p1` is the primary/boot partition
  - `/dev/nvme0n1p2` and `/dev/nvme0n1p3` comprise the extended partition and by
    themselves are each logical partitions.

  <dl>
    <dt>MBR</dt>
    <dd> 
      <ul>
        <li>Stands for Master Boot Record</li> 
        <li>Uses BIOS in the boot process</li> 
        <li>Can only works with disks up to 2TB in size</li> 
        <li>Only supports 4 primary partitions. This means the number of operating systems you install is limitied to this number.</li> 
        <li>This is the first 512 bytes of a storage device, preceding the first partition.</li>
      </ul> 
    </dd>
    <dt>GPT</dt>
    <dd>
    <ul>
        <li>Stands for GUID Partition Table </li>
        <li>Gradually replacing MBR</li>
        <li>Uses UEFI instead of BIOS</li>
        <li>As name indicates, every partition on disk has its own globally-unique identifier</li>
        <li>Vastly more partitions available than with MBR (dependent on operating system)</li>
        <li>Offers greater recovery options and anti-corruption safeguards</li> 
      </ul>
  </dd>
  </dl>

  ## Related notes

  ![Creating a Linux partition table](./Creating_a_Linux_partition_table.md)


path: List_comprehension_in_Python.md
sortable_path: List_comprehension_in_Python.md
title: List comprehension
checksum: 47f1ec37b472ca033ebbdfae22ba2e8c7ac0b0943d70dcb158fa782b62e3f28f
created: 2024-10-22 16:52:57.391463162+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["python"]}
lead:
  List comprehension is a syntactic shorthand for applying a function to each
  element in a list without explicitly using loop syntax.

body:
  List comprehension is a syntactic shorthand for applying a function to each
  element in a list without explicitly using loop syntax.

  Since its introduction to the language, the same functionality has become
  achievable by using functional methods like
  [`map` and `filter`](Map_and_filter_in_Python.md),
  utilising [lambdas](Lambdas_in_Python.md)
  however list comprehension is often more straightforward and easier to read.

  ## Syntax

  Here is a basic example which applies `+ 1` to each integer in a list:

  ```python
  values = [1, 2, 4, 6, 8, 9]

  new_values = [i + 1 for i in values]
  print('new_values', new_values)
  # new_values [2, 3, 5, 7, 9, 10]
  ```

  The basic syntax is as follows:

  ```
  new_list  = [expression for each member in an iterable]
  ```

  - The _expression_ can be the member itself, a call to a method, or any other
    valid expression that returns a value. In the example above, the expression
    `i + i` adds one to each member value.

  - The _member_ is the object or value in the list or iterable. In the example
    above, the member value is _i_.

  - The _iterable_ is a list, set, dictionary or any other object that can return
    its elements one at a time. In the example above, the iterable is each value
    in `values`.

  This is a much more condensed way of achieving the same outcome with a
  traditional loop:

  ```py
  values = [1, 2, 4, 6, 8, 9]
  new_list = []
  for i in values:
    values.append(i+1)
  ```

  ### Another example

  In the following example, we apply list comprehension with a `in range` loop
  structure:

  ```py
  new_list = [i * i for i in range(10) ]
  print(new_list)
  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

  ```

  ### Adding a condition

  We can apply a conditional to a comprehension:

  ```py
  new_list = [item + 1 for item in values if item % 2 == 0]

  print('new_list:', new_list)
  # new_list: [3, 5, 7, 9]
  ```

  ### Filters

  By applying a condition (and no execution to each element) we effectively create
  a filter:

  ```py
  numbers = [1, 2, 3, 4, 4, 4]
  numbers_filtered = [i for i in numbers if i > 2]
  print(numbers_filtered)
  # [3, 4, 5]
  ```

  For comparison, the same outcome could be achieved with a filter and lambda:

  ```py
  with_filter = list(filter(lambda x: x > 2, numbers))
  print(with_filter)
  # [3, 4, 5]
  ```

  ## Set comprehension

  We can also apply comprehension to sets. The syntax is practically identical but
  the difference is the resultant data structure will not contain duplicates.

  ```py
  numbers = [1, 2, 3, 4, 4, 4]
  unique =  {i for i in numbers}
  print(unique)
  # {1,2,3,4}
  ```

  ## Dictionary comprehension

  ```py
  squares = {i: i * i for i in range(5)}
  print(squares)
  {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
  ```

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

  # List comprehension

  List comprehension is a syntactic shorthand for applying a function to each
  element in a list without explicitly using loop syntax.

  Since its introduction to the language, the same functionality has become
  achievable by using functional methods like
  [`map` and `filter`](Map_and_filter_in_Python.md),
  utilising [lambdas](Lambdas_in_Python.md)
  however list comprehension is often more straightforward and easier to read.

  ## Syntax

  Here is a basic example which applies `+ 1` to each integer in a list:

  ```python
  values = [1, 2, 4, 6, 8, 9]

  new_values = [i + 1 for i in values]
  print('new_values', new_values)
  # new_values [2, 3, 5, 7, 9, 10]
  ```

  The basic syntax is as follows:

  ```
  new_list  = [expression for each member in an iterable]
  ```

  - The _expression_ can be the member itself, a call to a method, or any other
    valid expression that returns a value. In the example above, the expression
    `i + i` adds one to each member value.

  - The _member_ is the object or value in the list or iterable. In the example
    above, the member value is _i_.

  - The _iterable_ is a list, set, dictionary or any other object that can return
    its elements one at a time. In the example above, the iterable is each value
    in `values`.

  This is a much more condensed way of achieving the same outcome with a
  traditional loop:

  ```py
  values = [1, 2, 4, 6, 8, 9]
  new_list = []
  for i in values:
    values.append(i+1)
  ```

  ### Another example

  In the following example, we apply list comprehension with a `in range` loop
  structure:

  ```py
  new_list = [i * i for i in range(10) ]
  print(new_list)
  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

  ```

  ### Adding a condition

  We can apply a conditional to a comprehension:

  ```py
  new_list = [item + 1 for item in values if item % 2 == 0]

  print('new_list:', new_list)
  # new_list: [3, 5, 7, 9]
  ```

  ### Filters

  By applying a condition (and no execution to each element) we effectively create
  a filter:

  ```py
  numbers = [1, 2, 3, 4, 4, 4]
  numbers_filtered = [i for i in numbers if i > 2]
  print(numbers_filtered)
  # [3, 4, 5]
  ```

  For comparison, the same outcome could be achieved with a filter and lambda:

  ```py
  with_filter = list(filter(lambda x: x > 2, numbers))
  print(with_filter)
  # [3, 4, 5]
  ```

  ## Set comprehension

  We can also apply comprehension to sets. The syntax is practically identical but
  the difference is the resultant data structure will not contain duplicates.

  ```py
  numbers = [1, 2, 3, 4, 4, 4]
  unique =  {i for i in numbers}
  print(unique)
  # {1,2,3,4}
  ```

  ## Dictionary comprehension

  ```py
  squares = {i: i * i for i in range(5)}
  print(squares)
  {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
  ```


path: Listing_and_navigating_directories_in_the_Shell.md
sortable_path: Listing_and_navigating_directories_in_the_Shell.md
title: Listing and navigating directories in the shell
lead: ## Listing options
checksum: 66c71341ea13a02e6448279e89744a8559b1000947bbab0dc5bf8de95dcd77e4
created: 2024-10-22 16:52:57.403083899+00:00
modified: 2024-10-18 18:16:18.745720434+00:00
metadata: {"tags":["shell"]}
body:
  ## Listing options

  Obviously we know that in order to list the files and sub-directories in our
  current directory we use `ls` but here are some of the most useful of the
  different modifiers:

  - `**ls -a**`
    - list and include hidden dot files
  - `**ls -l**`
    - list with user permissions, file-size and date-modified (most detailed)
  - `**ls ***`
    - list organised by folders one level deep

  ## Navigation shorthands

  - `cd -`
    - Return to the directory you were last in
  - `!!`
    - Repeat the last command

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

  # Listing and navigating directories in the shell

  ## Listing options

  Obviously we know that in order to list the files and sub-directories in our
  current directory we use `ls` but here are some of the most useful of the
  different modifiers:

  - `**ls -a**`
    - list and include hidden dot files
  - `**ls -l**`
    - list with user permissions, file-size and date-modified (most detailed)
  - `**ls ***`
    - list organised by folders one level deep

  ## Navigation shorthands

  - `cd -`
    - Return to the directory you were last in
  - `!!`
    - Repeat the last command


path: Lists_and_arrays_in_Bash.md
sortable_path: Lists_and_arrays_in_Bash.md
title: Arrays in Bash
lead: We define an array as follows:
checksum: 69b5c6bbf1a810f3021b6854ef962a87e0212935962e8063311cdb7e11703947
created: 2024-10-22 16:52:57.406475562+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["shell","data-structures"]}
body:
  We define an array as follows:

  ```bash
  words=(here are some words)
  ```

  We can also explicitly define an array using `declare` :

  ```bash
  declare -a words=("element1" "element2" "element3")
  ```

  ## Index notation

  We access specific array elements by their index using the same braces style we
  use with variables:

  ```bash
  echo "${words[2]}"

  # element3
  ```

  ## Iterating through arrays

  ```bash
  for i in "${words[@]}"
  do
     echo "$i"
     # or do whatever with individual element of the array
  done
  # element1 element2 element3
  ```

  Note that `@` here is a special symbol standing for all the members of the
  `words` array.

  ## Sorting arrays

  ### Sorting an integer array highests to lowest

  ```bash
  sorted_array=($(echo "${array[@]}" | tr " " "\n" | sort -nr))
  ```

  Where `array` is the name of the original array. The sorted array will be stored
  in the `sorted_array` array.

  The `sort` command sorts the input in reverse numerical order (`-n` for
  numerical sort and `-r` for reverse sort). The `tr` command is used to convert
  the spaces in the array to newline characters so that each element is sorted on
  a separate line.

  ## Pushing, appending to an array

  We use the `+=` shorthand to add elements to a preexisting array:

  ```bash
  preExistingArray=(1 2 3)
  preExistingArray+=(4)
  echo preExistingArray
  # 1 2 3 4

  ```

  > Note that we have to put the item we want to addend within array brackets

  ## Check if array empty

  ```sh
  an_array=()

  if [ -z "${another_array[@]}" ]; then
      echo "Array is empty"
  else
      echo "Array is not empty"
  fi
  ```

  Here we pass all the elements of the array to a
  [test](Test_values_in_Bash.md) condition which
  tests for an empty string.

  > NB: This will not immediately work in the context of a function. See below.

  ## Weirdness with functions

  When you pass an array as an argument to a
  [function](Functions_in_Bash.md) it will not
  immediately be understood to be an array.

  When we use `$1` to individuate the first function argument this is read as
  string. So if you parsed an array argument as `$1`, any logic you have in the
  function will work on the assumption that the argument is a string, not an
  array.

  To get round this we have to effectively _redeclare_ the argument as an array
  before running any array logic. We do this through a **nameref** (a reference to
  a variable). The nameref resolves to the value of the variable it references.
  This allows you to indirectly manipulate the value of the original variable
  through the nameref.

  A nameref is created with the `-n` flag. The following function uses this method
  to check if an array is empty:

  ```sh
  function array_empty() {
      declare -n arr=$1
      if [ ${#arr[@]} -gt 0 ]; then
          echo "array is not empty"
      else
          echo "array is empty"
      fi
  }

  my_array=()
  array_empty "my_array"
  ```

  You'll notice when we invoke the function we pass the array as a string, this
  facilitates the nameref operation in the function.

  ## Associational arrays / maps

  With Bash 4 we gained an additional array-like data structure that is key-value
  based and similar to maps in other languages.

  ```bash
  declare -A rock=(["win"]="scissors" ["lose"]="paper")
  ```

  We would then individuate a value with `"${rock[win]}"`

  ## Lists as implicit string arrays

  When we use the term **list** in bash, we are not actually referring to a
  specific type of data structure. Instead a **list variable** is really just a
  normal variable wrapped in quote marks that has strings separated by spaces.
  Despite the fact that this is not an actual iterative data structure, we are
  still able to loop through variables of this type.

  ```bash
  A_STR_LIST="cat dog hamster"
  AN_INT_LIST="1 2 3"
  ```

  To iterate through a list variable, we can use a for loop:

  ```bash
  for ele in $A_STR_LIST; do
      echo $ele
  done
  ```

  We are leveraging this aspect of Bash when we
  [loop through each character in a string](Strings_in_Bash.md#loop-through-each-character-in-a-string).

raw_content:
  ---
  tags:
    - shell
    - data-structures
  ---

  # Arrays in Bash

  We define an array as follows:

  ```bash
  words=(here are some words)
  ```

  We can also explicitly define an array using `declare` :

  ```bash
  declare -a words=("element1" "element2" "element3")
  ```

  ## Index notation

  We access specific array elements by their index using the same braces style we
  use with variables:

  ```bash
  echo "${words[2]}"

  # element3
  ```

  ## Iterating through arrays

  ```bash
  for i in "${words[@]}"
  do
     echo "$i"
     # or do whatever with individual element of the array
  done
  # element1 element2 element3
  ```

  Note that `@` here is a special symbol standing for all the members of the
  `words` array.

  ## Sorting arrays

  ### Sorting an integer array highests to lowest

  ```bash
  sorted_array=($(echo "${array[@]}" | tr " " "\n" | sort -nr))
  ```

  Where `array` is the name of the original array. The sorted array will be stored
  in the `sorted_array` array.

  The `sort` command sorts the input in reverse numerical order (`-n` for
  numerical sort and `-r` for reverse sort). The `tr` command is used to convert
  the spaces in the array to newline characters so that each element is sorted on
  a separate line.

  ## Pushing, appending to an array

  We use the `+=` shorthand to add elements to a preexisting array:

  ```bash
  preExistingArray=(1 2 3)
  preExistingArray+=(4)
  echo preExistingArray
  # 1 2 3 4

  ```

  > Note that we have to put the item we want to addend within array brackets

  ## Check if array empty

  ```sh
  an_array=()

  if [ -z "${another_array[@]}" ]; then
      echo "Array is empty"
  else
      echo "Array is not empty"
  fi
  ```

  Here we pass all the elements of the array to a
  [test](Test_values_in_Bash.md) condition which
  tests for an empty string.

  > NB: This will not immediately work in the context of a function. See below.

  ## Weirdness with functions

  When you pass an array as an argument to a
  [function](Functions_in_Bash.md) it will not
  immediately be understood to be an array.

  When we use `$1` to individuate the first function argument this is read as
  string. So if you parsed an array argument as `$1`, any logic you have in the
  function will work on the assumption that the argument is a string, not an
  array.

  To get round this we have to effectively _redeclare_ the argument as an array
  before running any array logic. We do this through a **nameref** (a reference to
  a variable). The nameref resolves to the value of the variable it references.
  This allows you to indirectly manipulate the value of the original variable
  through the nameref.

  A nameref is created with the `-n` flag. The following function uses this method
  to check if an array is empty:

  ```sh
  function array_empty() {
      declare -n arr=$1
      if [ ${#arr[@]} -gt 0 ]; then
          echo "array is not empty"
      else
          echo "array is empty"
      fi
  }

  my_array=()
  array_empty "my_array"
  ```

  You'll notice when we invoke the function we pass the array as a string, this
  facilitates the nameref operation in the function.

  ## Associational arrays / maps

  With Bash 4 we gained an additional array-like data structure that is key-value
  based and similar to maps in other languages.

  ```bash
  declare -A rock=(["win"]="scissors" ["lose"]="paper")
  ```

  We would then individuate a value with `"${rock[win]}"`

  ## Lists as implicit string arrays

  When we use the term **list** in bash, we are not actually referring to a
  specific type of data structure. Instead a **list variable** is really just a
  normal variable wrapped in quote marks that has strings separated by spaces.
  Despite the fact that this is not an actual iterative data structure, we are
  still able to loop through variables of this type.

  ```bash
  A_STR_LIST="cat dog hamster"
  AN_INT_LIST="1 2 3"
  ```

  To iterate through a list variable, we can use a for loop:

  ```bash
  for ele in $A_STR_LIST; do
      echo $ele
  done
  ```

  We are leveraging this aspect of Bash when we
  [loop through each character in a string](Strings_in_Bash.md#loop-through-each-character-in-a-string).


path: Lists_in_Python.md
sortable_path: Lists_in_Python.md
title: Lists in Python
lead: Lists are the equivalent of a simple array in JavaScript.
checksum: d7a681d3fad55e070d3a77f946837900fdbc71c3f7e07af28fe7d4415f0075a9
created: 2024-10-22 16:52:57.421977293+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["python","data-structures"]}
body:
  Lists are the equivalent of a simple array in JavaScript.

  Lists have the following properties:

  - They are **ordered**
  - They are **mutable** and can be modified
  - They **allow duplicate** members
  - They are **indexed**
  - You can increase/decrease their length by adding/removing new members

  > Lists are denoted with `[...]`

  ## Duplicating lists

  When we want to duplicate a list, we can't just reassign the list to a new
  variable and expect this to be a copy.

  If we edit this "copy" it will update the original list since it copies the
  pointer and will therefore point to the same address in memory. Instead we have
  to use the List `copy()` function which returns a new list and doesn't modify
  the original list.

  Relatedly, we should distinguish those List methods and functions which create a
  new list (non-destructive) and those that mutate the existing list.

  ## Basic usage

  ```python
  # Defining a list
  list1 = ['John', 'Paul', 'George', 'Ringo']
  list2 = [4]

  # Empty list
  list3 = []  # empty list
  list3 = list()  # Also empty list

  # Nested list
  list5 = [[2, 3], [6, 8]]
  ```

  ## Slicing

  ```python
  list1 = ['John', 'Paul', 'George', 'Ringo']

  print(list1[1])
  print(list1[-1])
  print(list1[1:3])
  print(list1[:3])
  print(list1[1:])

  """
  'John'
  'Ringo'
  ['Paul', 'George']
  ['John', 'Paul', 'George']
  ['Paul', 'George', 'Ringo']
  """
  ```

  ## Adding additional values to existing list

  ### Single value

  ```py
  list1 = ['John', 'Paul', 'George', 'Ringo']

  # Add single element to the end of a list
  list1.append('Pete')
  # ['John', 'Paul', 'George', 'Ringo', 'Pete']
  ```

  ### Multiple values

  ```py
  list1.extend(['Albert', 'Bob'])
  list1 += ['Ginger', 'Sporty']
  # ['John', 'Paul', 'George', 'Ringo', 'Pete', 'Albert', 'Bob', 'Ginger', 'Sporty']
  ```

  ### Specific index

  ```python

  ## Insert at specific index

  a_list = ['Adele', 'Madonna', 'Cher']
  a_list.insert(1, 'Paloma')
  print(a_list)

  # ['Adele', 'Paloma', 'Madonna', 'Cher']
  ```

  ## Removing elements

  We distinguish `del` from `remove` when removing elements from lists:

  - `del` requires an index value
  - `remove` requires a value reference (i.e. the name of the element rather than

  `del` is simple deletion whereas `remove` searches the list. Therefore `del` is
  more efficient.

  ### `del`

  ```py
  my_list = [10, 20, 30, 40, 50]
  del my_list[1]
  print(my_list)  # Output will be [10, 30, 40, 50]
  ```

  We can remove multiple items at once via a slice:

  ```py
  my_list = [10, 20, 30, 40, 50]

  # Delete the elements from index 1 to 3 (inclusive of start index and exclusive of end index)
  del my_list[1:4]

  # Print the updated list
  print(my_list)  # Output will be [10, 50]
  ```

  ### `remove()`

  ```py
  my_list = [10, 20, 30, 40, 50]

  # Remove the element with value 30
  my_list.remove(30)

  # Print the updated list
  print(my_list)  # Output will be [10, 20, 40, 50]
  ```

  > If the value is not found in the list,
  > ` remove()`` will raise a ValueError. To avoid this, you can check whether the value exists in the list before calling  `remove()`

  ### pop()

  Remove and return the element removed

  ```python
  list6 = ['Once', 'Upon', 'a', 'Time']
  print(list6.pop(2))
  # a

  ```

  ## Return index of a list element

  ```python
  list7 = [2, 3, 6, 8]
  print(list7.index(8))
  # 3

  list6 = ['Once', 'Upon', 'a', 'Time']
  print(list6.index('a'))
  # 2
  ```

  ## Nesting lists

  ```python
  l1 = [1, 43.5, 'Phoebe', True]
  l2 = ['apple', 'orange', 31]
  root_list = ['John', l1, l2, 'Denise']
  print(root_list)
  # ['John', [1, 43.5, 'Phoebe', True], ['apple', 'orange', 31], 'Denise']
  ```

  ## Merging (flattening) lists

  ```py
  list1 = [1, 2, 3]
  list2 = [4, 5, 6]

  merged_list = list1 + list2
  print(merged_list)  # Output: [1, 2, 3, 4, 5, 6]
  ```

  ## See also

  [Sorting lists in Python](Sorting_lists_in_Python.md)

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

  # Lists in Python

  Lists are the equivalent of a simple array in JavaScript.

  Lists have the following properties:

  - They are **ordered**
  - They are **mutable** and can be modified
  - They **allow duplicate** members
  - They are **indexed**
  - You can increase/decrease their length by adding/removing new members

  > Lists are denoted with `[...]`

  ## Duplicating lists

  When we want to duplicate a list, we can't just reassign the list to a new
  variable and expect this to be a copy.

  If we edit this "copy" it will update the original list since it copies the
  pointer and will therefore point to the same address in memory. Instead we have
  to use the List `copy()` function which returns a new list and doesn't modify
  the original list.

  Relatedly, we should distinguish those List methods and functions which create a
  new list (non-destructive) and those that mutate the existing list.

  ## Basic usage

  ```python
  # Defining a list
  list1 = ['John', 'Paul', 'George', 'Ringo']
  list2 = [4]

  # Empty list
  list3 = []  # empty list
  list3 = list()  # Also empty list

  # Nested list
  list5 = [[2, 3], [6, 8]]
  ```

  ## Slicing

  ```python
  list1 = ['John', 'Paul', 'George', 'Ringo']

  print(list1[1])
  print(list1[-1])
  print(list1[1:3])
  print(list1[:3])
  print(list1[1:])

  """
  'John'
  'Ringo'
  ['Paul', 'George']
  ['John', 'Paul', 'George']
  ['Paul', 'George', 'Ringo']
  """
  ```

  ## Adding additional values to existing list

  ### Single value

  ```py
  list1 = ['John', 'Paul', 'George', 'Ringo']

  # Add single element to the end of a list
  list1.append('Pete')
  # ['John', 'Paul', 'George', 'Ringo', 'Pete']
  ```

  ### Multiple values

  ```py
  list1.extend(['Albert', 'Bob'])
  list1 += ['Ginger', 'Sporty']
  # ['John', 'Paul', 'George', 'Ringo', 'Pete', 'Albert', 'Bob', 'Ginger', 'Sporty']
  ```

  ### Specific index

  ```python

  ## Insert at specific index

  a_list = ['Adele', 'Madonna', 'Cher']
  a_list.insert(1, 'Paloma')
  print(a_list)

  # ['Adele', 'Paloma', 'Madonna', 'Cher']
  ```

  ## Removing elements

  We distinguish `del` from `remove` when removing elements from lists:

  - `del` requires an index value
  - `remove` requires a value reference (i.e. the name of the element rather than

  `del` is simple deletion whereas `remove` searches the list. Therefore `del` is
  more efficient.

  ### `del`

  ```py
  my_list = [10, 20, 30, 40, 50]
  del my_list[1]
  print(my_list)  # Output will be [10, 30, 40, 50]
  ```

  We can remove multiple items at once via a slice:

  ```py
  my_list = [10, 20, 30, 40, 50]

  # Delete the elements from index 1 to 3 (inclusive of start index and exclusive of end index)
  del my_list[1:4]

  # Print the updated list
  print(my_list)  # Output will be [10, 50]
  ```

  ### `remove()`

  ```py
  my_list = [10, 20, 30, 40, 50]

  # Remove the element with value 30
  my_list.remove(30)

  # Print the updated list
  print(my_list)  # Output will be [10, 20, 40, 50]
  ```

  > If the value is not found in the list,
  > ` remove()`` will raise a ValueError. To avoid this, you can check whether the value exists in the list before calling  `remove()`

  ### pop()

  Remove and return the element removed

  ```python
  list6 = ['Once', 'Upon', 'a', 'Time']
  print(list6.pop(2))
  # a

  ```

  ## Return index of a list element

  ```python
  list7 = [2, 3, 6, 8]
  print(list7.index(8))
  # 3

  list6 = ['Once', 'Upon', 'a', 'Time']
  print(list6.index('a'))
  # 2
  ```

  ## Nesting lists

  ```python
  l1 = [1, 43.5, 'Phoebe', True]
  l2 = ['apple', 'orange', 31]
  root_list = ['John', l1, l2, 'Denise']
  print(root_list)
  # ['John', [1, 43.5, 'Phoebe', True], ['apple', 'orange', 31], 'Denise']
  ```

  ## Merging (flattening) lists

  ```py
  list1 = [1, 2, 3]
  list2 = [4, 5, 6]

  merged_list = list1 + list2
  print(merged_list)  # Output: [1, 2, 3, 4, 5, 6]
  ```

  ## See also

  [Sorting lists in Python](Sorting_lists_in_Python.md)


path: Local_AWS_development_with_SAM.md
sortable_path: Local_AWS_development_with_SAM.md
title: Local AWS development with SAM
checksum: 125550b5c17fa1ce41fcfd41f42c41f618882dfec80122c235a838aadee59d73
created: 2024-10-22 16:52:57.431737938+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["AWS","docker"]}
lead:
  We can run a local instance of our SAM stack for a given application without
  sending requests to the cloud. This is implemented through Docker.

body:
  We can run a local instance of our SAM stack for a given application without
  sending requests to the cloud. This is implemented through Docker.

  The SAM CLI handles all the Docker-related tasks, such as pulling the required
  Lambda runtime images, creating containers, mounting your code and dependencies,
  and running your Lambda functions inside those containers.

  ## Basic set up

  Enter your project directory.

  First build your SAM application:

  ```sh
  sam build
  ```

  We then run:

  ```sh
  sam local start-api
  ```

  If you have an API Gateway endpoint that you want to call over the local server.
  You will be able to call it after executing the above.

  This will be indicated by:

  ![](/img/local-sam-docker.png)

  If we want to invoke the function directly we use:

  ```sh
   sam local invoke [FunctionName]
  ```

  ## Using environment variables

  If you have an API key or database credentials, you are going to typically want
  to use different values dependent on environment.

  Even if the values are the same accross environments, it's a good idea to not
  call a secret when working locally since this request is billable.

  In the example below I show how to set up environment variables for an API key
  locally and in production.

  ## Create secret

  Go to AWS SecretsManager and add the API key as a secret. This will be sourced
  in production.

  ## Create local ENV file

  > These must be in JSON to work with SAM:

  ### Local env

  ```json
  // local-env.json
  {
    "FunctionName": {
      "API_KEY": "xxx-yyy-xxx",
      "NODE_ENV": "development"
    }
  }
  ```

  We save these to the root of the given function's directory not at the global
  repo level.

  > Be sure to add this to `.gitignore` so that it does not become public

  ### Update `template.yaml`

  Every environment variable you intend to use, must exist in the `template.yaml`,
  otherwise it will not be sourced at runtime:

  ```yaml
  ...
  Resources:
    Properties:
      Environment:
        Variables:
          SECRET_ARN: "arn:aws:secretsmanager:eu-west-2:885135949562:secret:wakatime-api-key-X9oF3v",
          NODE_ENV: production
          API_KEY:
  ...
  ```

  > We go ahead and populate the values for production. But we leave the variable
  > we use in development blank, since we don't want it committed and we will
  > source it at the SAM invocation. It still needs to exist though.

  ### Pass in the environment variable at invocation:

  ```sh
  sam local start-api --env-vars /home/thomas/repos/lambdas/wakatime-api/get-coding-stats/local-env.json
  ```

  In production, the variables required will be automatically sourced from the
  `template.yaml`

  ### Create handler within the Lambda itself

  You are obviously going to need to distinguish between the different deployments
  when the Lambda executes. Here is an example helper function:

  ```ts
  import * as AWS from "aws-sdk";

  const secretsManager = new AWS.SecretsManager();

  async function getApiKey(): Promise<string> {
    if (process.env.NODE_ENV === "production") {
      const response = await secretsManager
        .getSecretValue({ SecretId: process.env.SECRET_ARN as string })
        .promise();
      const secretValues = JSON.parse(response.SecretString as string);
      return secretValues.API_KEY;
    } else {
      return process.env.API_KEY as string;
    }
  }
  ```

raw_content:
  ---
  tags: [AWS, docker]
  ---

  # Local AWS development with SAM

  We can run a local instance of our SAM stack for a given application without
  sending requests to the cloud. This is implemented through Docker.

  The SAM CLI handles all the Docker-related tasks, such as pulling the required
  Lambda runtime images, creating containers, mounting your code and dependencies,
  and running your Lambda functions inside those containers.

  ## Basic set up

  Enter your project directory.

  First build your SAM application:

  ```sh
  sam build
  ```

  We then run:

  ```sh
  sam local start-api
  ```

  If you have an API Gateway endpoint that you want to call over the local server.
  You will be able to call it after executing the above.

  This will be indicated by:

  ![](/img/local-sam-docker.png)

  If we want to invoke the function directly we use:

  ```sh
   sam local invoke [FunctionName]
  ```

  ## Using environment variables

  If you have an API key or database credentials, you are going to typically want
  to use different values dependent on environment.

  Even if the values are the same accross environments, it's a good idea to not
  call a secret when working locally since this request is billable.

  In the example below I show how to set up environment variables for an API key
  locally and in production.

  ## Create secret

  Go to AWS SecretsManager and add the API key as a secret. This will be sourced
  in production.

  ## Create local ENV file

  > These must be in JSON to work with SAM:

  ### Local env

  ```json
  // local-env.json
  {
    "FunctionName": {
      "API_KEY": "xxx-yyy-xxx",
      "NODE_ENV": "development"
    }
  }
  ```

  We save these to the root of the given function's directory not at the global
  repo level.

  > Be sure to add this to `.gitignore` so that it does not become public

  ### Update `template.yaml`

  Every environment variable you intend to use, must exist in the `template.yaml`,
  otherwise it will not be sourced at runtime:

  ```yaml
  ...
  Resources:
    Properties:
      Environment:
        Variables:
          SECRET_ARN: "arn:aws:secretsmanager:eu-west-2:885135949562:secret:wakatime-api-key-X9oF3v",
          NODE_ENV: production
          API_KEY:
  ...
  ```

  > We go ahead and populate the values for production. But we leave the variable
  > we use in development blank, since we don't want it committed and we will
  > source it at the SAM invocation. It still needs to exist though.

  ### Pass in the environment variable at invocation:

  ```sh
  sam local start-api --env-vars /home/thomas/repos/lambdas/wakatime-api/get-coding-stats/local-env.json
  ```

  In production, the variables required will be automatically sourced from the
  `template.yaml`

  ### Create handler within the Lambda itself

  You are obviously going to need to distinguish between the different deployments
  when the Lambda executes. Here is an example helper function:

  ```ts
  import * as AWS from "aws-sdk";

  const secretsManager = new AWS.SecretsManager();

  async function getApiKey(): Promise<string> {
    if (process.env.NODE_ENV === "production") {
      const response = await secretsManager
        .getSecretValue({ SecretId: process.env.SECRET_ARN as string })
        .promise();
      const secretValues = JSON.parse(response.SecretString as string);
      return secretValues.API_KEY;
    } else {
      return process.env.API_KEY as string;
    }
  }
  ```


path: Logarithms.md
sortable_path: Logarithms.md
title: Using exponents to calculate logarithms
checksum: 741db4c876f0fe9332002a963e75702af62c7cd91a1d7e03790b8b372d0b720c
created: 2024-10-22 16:52:57.441879427+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["algebra"]}
lead:
  This approach becomes rapidly difficult when working with larger numbers. It's
  not as obvious what $\log \_{5} 625$ would be using this method. For this
  reason, we use exponents which are intimately related to logarithms.

body:
  This approach becomes rapidly difficult when working with larger numbers. It's
  not as obvious what $\log \_{5} 625$ would be using this method. For this
  reason, we use exponents which are intimately related to logarithms.

  A logarithm can be expressed identically using exponents for example:

  $$
  \log \_{3} 9 = 2 \leftrightarrow 3^2 = 9


  $$

  By carrying out the conversion in stages, we can work out the answer to the
  question a logarithm poses.

  Let's work out $\log \_{2} 8$ using this method.

  1. First we add a variable (x) to the expression on the right hand:

     $$
     \log \_{2} 8 \leftrightarrow x


     $$

  1. Next we take the base of the logarithm and combine it with x as an exponent.
     Now our formula looks like this:

     $$
     \log \_{2} 8 \leftrightarrow 2^x


     $$

  1. Next we add an equals and the number that is left from the logarithm (8):

  $$
  \log \_{2} 8 \leftrightarrow 2^x = 8


  $$

  Then the problem is reduced to: how many times do you need to multiply two by
  itself to get 8? The answer is 3 : 2 x 2 x 2 or 2 p3. Hence we have the balanced
  equation:

  $$
  \log \_{2} 8 \leftrightarrow 2^3 = 8


  $$

  ## Common base values

  Often times a base won't be specified in a log expression. For example:

  $$
  \log1000


  $$

  This is just a shorthand and it means that the base value is ten, i.e that the
  logarithm is written in denary (base 10). So the above actually means:

  $$
  \log \_{10} 1000 = 3


  $$

  This is referred to as the **common logarithm**

  Another frequent base is Euler's number (approx. 2.71828) known as the **natural
  logarithm**

  An example

  $$
  \log \_{e} 7.389 = 2


  $$

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

  Most simply a logarithm is a way of answering the question:

  > How many of one number do we need to get another number. How many of x do we
  > need to get y

  More formally:

  > x raised to what power gives me y

  Below is an example of a logarithm:

  $$
  \log \_{3} 9


  $$

  We read it:

  > log base 3 of 9

  And it means:

  > 3 raised to what power gives me 9?

  In this case the answer is easy: $3^2$ gives me nine, which is to say: three
  multiplied by itself.

  ## Using exponents to calculate logarithms

  This approach becomes rapidly difficult when working with larger numbers. It's
  not as obvious what $\log \_{5} 625$ would be using this method. For this
  reason, we use exponents which are intimately related to logarithms.

  A logarithm can be expressed identically using exponents for example:

  $$
  \log \_{3} 9 = 2 \leftrightarrow 3^2 = 9


  $$

  By carrying out the conversion in stages, we can work out the answer to the
  question a logarithm poses.

  Let's work out $\log \_{2} 8$ using this method.

  1. First we add a variable (x) to the expression on the right hand:

     $$
     \log \_{2} 8 \leftrightarrow x


     $$

  1. Next we take the base of the logarithm and combine it with x as an exponent.
     Now our formula looks like this:

     $$
     \log \_{2} 8 \leftrightarrow 2^x


     $$

  1. Next we add an equals and the number that is left from the logarithm (8):

  $$
  \log \_{2} 8 \leftrightarrow 2^x = 8


  $$

  Then the problem is reduced to: how many times do you need to multiply two by
  itself to get 8? The answer is 3 : 2 x 2 x 2 or 2 p3. Hence we have the balanced
  equation:

  $$
  \log \_{2} 8 \leftrightarrow 2^3 = 8


  $$

  ## Common base values

  Often times a base won't be specified in a log expression. For example:

  $$
  \log1000


  $$

  This is just a shorthand and it means that the base value is ten, i.e that the
  logarithm is written in denary (base 10). So the above actually means:

  $$
  \log \_{10} 1000 = 3


  $$

  This is referred to as the **common logarithm**

  Another frequent base is Euler's number (approx. 2.71828) known as the **natural
  logarithm**

  An example

  $$
  \log \_{e} 7.389 = 2


  $$


path: Logic_gates.md
sortable_path: Logic_gates.md
title: Logic gates
checksum: 2690421e13abf67dfa546b560e0b6867deaed7409216467fccebb8a9e82ff62e
created: 2024-10-22 16:52:57.446688413+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["logic-gates","binary"]}
lead:
  > [A logic gate consists in] three connections where there may or may not be
  > some electricity. Two of those connections are places where electricity may be
  > put into the device, and the third connection is a place where electricity may
  > come out of the device.

body:
  > [A logic gate consists in] three connections where there may or may not be
  > some electricity. Two of those connections are places where electricity may be
  > put into the device, and the third connection is a place where electricity may
  > come out of the device.

  [J.C. Scott. 2009. **But How Do It Know? The Basics of Computers for Everyone**,
  21]

  Logic gates are the basic building blocks of digital computing. **A logic gate
  is an electrical circuit that has one or more than one input and only one
  output.** The input and output points of the gate are
  [pins](Integrated_circuits.md) The
  input controls the output and the logic determining which types of input
  (on/off) lead to specific outputs (on/off) is isomorphic with the
  truth-conditions of the
  [Boolean connectives](Truth-functional_connectives.md) specifiable in
  terms of [truth tables](Truth-tables.md).

  Physically, what 'travels through' the gates is electrical current and what
  constitutes the 'gate' is a
  [transistor](Transistors.md)
  responding to the current. Going up a level of abstraction, the current/ charge
  is identified with a
  [bit](Binary_units_of_measurement.md#binary-units-of-measurement).
  It is bits that go into the gate and bits which come out: binary information
  that may be either 1 or 0.

  ## Elementary and composite gates

  We distinguish elementary from composite logic gates. An elementary gate is a
  single gate embodying a single logical connective. It cannot be reduced any
  lower as a logical abstraction. A composite gate is a gate made up of more than
  one elemen>tary gate and/or other composite gates.

  An example of a composite gate would be a three-way AND. An AND with three
  inputs rather than the standard two that furnish the elementary AND gate. This
  gate would output 1 when all three gates have the value 1 and 0 otherwise.
  [Adders](Half_adder_and_full_adder.md)
  and [latche>s](Latches.md) whilst
  being
  [integrated circuits](Integrated_circuits.md)
  are also, technically speaking, composite gates.

  ## Gate interface / gate implementation

  The gate _interface_ is an abstraction that the enables the user to think of the
  gate simply in terms of inputs and outputs, without being conc>erned with the
  technical details of how this is achieved. How it is achieved is the gate
  _implementation_.

  We can demonstrate this with the earlier example of a three-way AND. The diagram
  below represents the gate as an interface:

  // Add: Interface diagram

  Whereas this diagram presents the implementation of the gate: it shows the
  specific combination of gates which creates the enables the behaviour
  represented in the interface diagram.

  // Add: Implementation diagram

  > Importantly, a single interface may be implemented in a variety of ways. There
  > is a one-to-many relationship at work here. From the point of view of the user
  > interface these differences should not be detectable. This is another example
  > of
  > [hardware abstraction](Hardware_abstraction_and_modularity.md)

  ## NOT gate

  > The NOT gate inverts the value of whatever input it receives

  ### Truth conditions

  | $P$ | $\lnot P$ |
  | --- | --------- |
  | 1   | 0         |
  | 0   | 1         |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/not-gate-aeb5f9e5-9f58-4883-b8e5-d70f6d023185?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginhe

  ### Truth conditions

  tion) truth functional connective

  ### Symbol

  ![](/img/and-gate-new-2.png)

  ### Truth conditions

  ### Truth conditions

  | 1 | 0 | 0 | | 0 | 0 | 0 | | 0 | 0 | 0 |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/and-gate-b6937338-e83f-474b-af79-854b39c151a3?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## NAND gate

  > The NAND gate inverts the truth conditions of AND.

  ### Symbol

  ![](/img/nand-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $\lnot(P \land Q)$ |
  | --- | --- | ------------------ |
  | 1   | 1   | 0                  |
  | 1   | 0   | 0                  |
  | 0   | 1   | 0                  |
  | 0   | 0   | 1                  |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/nand-gate-60613ab9-9562-445e-9883-c4ea1920e206?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  NAND is a **universal logic gate**: equipped with just a NAND we can represent
  every other possible logical condition. In practice with circuits, it is more
  efficient to use specific dedicated gates (i.e OR, AND, NOT etc) for the other
  Boolean connectives but in principle the same output can be achieved through
  NANDs alone.

  ## OR gate

  > The OR gate represents the truth conditions of the
  > [disjunction](Truth-functional_connectives### Truth
  > conditions.md#disjunction) truth functional connective

  ### Symbol

  ![](/img/or-gate-new.png)

  ### Truth condition

  ### Truth conditions

  s

  | $P$ | $Q$ | $P \lor Q$ |
  | --- | --- | ---------- |
  | 1   | 1   | 1          |
  | 1   | 0   | 1          |
  | 0   | 1   | 1          |
  | 0   | 0   | 0          |

  ### Interactive circui

  ### Truth conditions

  t

  <iframe src="https://circuitverse.org/simulator/embed/or-gate-087e4933-7963-482d-b4bf-9e130ef05706?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## XOR gate

  > The OR gate represents the truth conditions of the exclusive OR

  ### Symbol

  ![](/img/xor-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $\lnot(P \Leftrightarrow Q)$ |
  | --- | --- | ---------------------------- |
  | 1   | 1   | 0                            |
  | 1   | 0   | 1                            |
  | 0   | 1   | 1                            |
  | 0   | 0   | 0                            |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/xor-gate-a240131e-a9c3-4240-a52b-e14412fdd654?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## NOR gate

  > The NOR gate inverts the function of an OR gate

  ### Symbol

  ![](/img/nor-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $P \lor Q$ |
  | --- | --- | ---------- |
  | 1   | 1   | 0          |
  | 1   | 0   | 0          |
  | 0   | 1   | 0          |
  | 0   | 0   | 1          |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/nor-gate-ac7946b4-f5d0-4c87-afd1-a2e92326d006?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

raw_content:
  ---
  tags: [logic-gates, binary]
  ---

  # Logic gates

  > [A logic gate consists in] three connections where there may or may not be
  > some electricity. Two of those connections are places where electricity may be
  > put into the device, and the third connection is a place where electricity may
  > come out of the device.

  [J.C. Scott. 2009. **But How Do It Know? The Basics of Computers for Everyone**,
  21]

  Logic gates are the basic building blocks of digital computing. **A logic gate
  is an electrical circuit that has one or more than one input and only one
  output.** The input and output points of the gate are
  [pins](Integrated_circuits.md) The
  input controls the output and the logic determining which types of input
  (on/off) lead to specific outputs (on/off) is isomorphic with the
  truth-conditions of the
  [Boolean connectives](Truth-functional_connectives.md) specifiable in
  terms of [truth tables](Truth-tables.md).

  Physically, what 'travels through' the gates is electrical current and what
  constitutes the 'gate' is a
  [transistor](Transistors.md)
  responding to the current. Going up a level of abstraction, the current/ charge
  is identified with a
  [bit](Binary_units_of_measurement.md#binary-units-of-measurement).
  It is bits that go into the gate and bits which come out: binary information
  that may be either 1 or 0.

  ## Elementary and composite gates

  We distinguish elementary from composite logic gates. An elementary gate is a
  single gate embodying a single logical connective. It cannot be reduced any
  lower as a logical abstraction. A composite gate is a gate made up of more than
  one elemen>tary gate and/or other composite gates.

  An example of a composite gate would be a three-way AND. An AND with three
  inputs rather than the standard two that furnish the elementary AND gate. This
  gate would output 1 when all three gates have the value 1 and 0 otherwise.
  [Adders](Half_adder_and_full_adder.md)
  and [latche>s](Latches.md) whilst
  being
  [integrated circuits](Integrated_circuits.md)
  are also, technically speaking, composite gates.

  ## Gate interface / gate implementation

  The gate _interface_ is an abstraction that the enables the user to think of the
  gate simply in terms of inputs and outputs, without being conc>erned with the
  technical details of how this is achieved. How it is achieved is the gate
  _implementation_.

  We can demonstrate this with the earlier example of a three-way AND. The diagram
  below represents the gate as an interface:

  // Add: Interface diagram

  Whereas this diagram presents the implementation of the gate: it shows the
  specific combination of gates which creates the enables the behaviour
  represented in the interface diagram.

  // Add: Implementation diagram

  > Importantly, a single interface may be implemented in a variety of ways. There
  > is a one-to-many relationship at work here. From the point of view of the user
  > interface these differences should not be detectable. This is another example
  > of
  > [hardware abstraction](Hardware_abstraction_and_modularity.md)

  ## NOT gate

  > The NOT gate inverts the value of whatever input it receives

  ### Truth conditions

  | $P$ | $\lnot P$ |
  | --- | --------- |
  | 1   | 0         |
  | 0   | 1         |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/not-gate-aeb5f9e5-9f58-4883-b8e5-d70f6d023185?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginhe

  ### Truth conditions

  tion) truth functional connective

  ### Symbol

  ![](/img/and-gate-new-2.png)

  ### Truth conditions

  ### Truth conditions

  | 1 | 0 | 0 | | 0 | 0 | 0 | | 0 | 0 | 0 |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/and-gate-b6937338-e83f-474b-af79-854b39c151a3?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## NAND gate

  > The NAND gate inverts the truth conditions of AND.

  ### Symbol

  ![](/img/nand-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $\lnot(P \land Q)$ |
  | --- | --- | ------------------ |
  | 1   | 1   | 0                  |
  | 1   | 0   | 0                  |
  | 0   | 1   | 0                  |
  | 0   | 0   | 1                  |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/nand-gate-60613ab9-9562-445e-9883-c4ea1920e206?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  NAND is a **universal logic gate**: equipped with just a NAND we can represent
  every other possible logical condition. In practice with circuits, it is more
  efficient to use specific dedicated gates (i.e OR, AND, NOT etc) for the other
  Boolean connectives but in principle the same output can be achieved through
  NANDs alone.

  ## OR gate

  > The OR gate represents the truth conditions of the
  > [disjunction](Truth-functional_connectives### Truth
  > conditions.md#disjunction) truth functional connective

  ### Symbol

  ![](/img/or-gate-new.png)

  ### Truth condition

  ### Truth conditions

  s

  | $P$ | $Q$ | $P \lor Q$ |
  | --- | --- | ---------- |
  | 1   | 1   | 1          |
  | 1   | 0   | 1          |
  | 0   | 1   | 1          |
  | 0   | 0   | 0          |

  ### Interactive circui

  ### Truth conditions

  t

  <iframe src="https://circuitverse.org/simulator/embed/or-gate-087e4933-7963-482d-b4bf-9e130ef05706?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## XOR gate

  > The OR gate represents the truth conditions of the exclusive OR

  ### Symbol

  ![](/img/xor-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $\lnot(P \Leftrightarrow Q)$ |
  | --- | --- | ---------------------------- |
  | 1   | 1   | 0                            |
  | 1   | 0   | 1                            |
  | 0   | 1   | 1                            |
  | 0   | 0   | 0                            |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/xor-gate-a240131e-a9c3-4240-a52b-e14412fdd654?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## NOR gate

  > The NOR gate inverts the function of an OR gate

  ### Symbol

  ![](/img/nor-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $P \lor Q$ |
  | --- | --- | ---------- |
  | 1   | 1   | 0          |
  | 1   | 0   | 0          |
  | 0   | 1   | 0          |
  | 0   | 0   | 1          |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/nor-gate-ac7946b4-f5d0-4c87-afd1-a2e92326d006?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>


path: Logical_consistency.md
sortable_path: Logical_consistency.md
title: Logical consistency
lead: ## Informal definition
checksum: 25b31fea3e617c20721052b3fb39a4b363a687e9183bbcf5cc6abab51e4219b6
created: 2024-10-22 16:52:57.482552077+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  ## Informal definition

  A set of propositions is consistent if and only if **it is possible for all the
  members of the set to be true at the same time**. A set of propositions is
  inconsistent if and only if it is not consistent.

  ### Demonstration

  The following set of propositions form an inconsistent set:

  1. Anyone who takes astrology seriously is a lunatic.
  2. Alice is my sister and no sister of mine has a lunatic for a husband.
  3. David is Alice's husband and he read's the horoscope column every morning.
  4. Anyone who reads the horoscope column every morning takes astrology
     seriously.

  The set is inconsistent because not all of them can be true. If (1), (3), (4)
  are true, (2) cannot be. If (2), (3),(4) are true, (1) cannot be.

  ## Formal definition

  > A finite set of propositions $\Gamma$ is truth-functionally consistent if and
  > only if there is at least one truth-assignment in which all propositions of
  > $\Gamma$ are true.

  ### Informal expression

  ```
  The book is blue or the book is brown
  The book is brown
  ```

  ### Formal expression

  $$
  \{P \lor Q, Q\}
  $$

  ### Truth table

  $ \{P, Q\} $ form a consistent set because there is at least one assignment when
  both propositions are true. In fact there are two (corresponding to each
  disjunct) but one is sufficient.

  | $P$ | $Q$ | $ P \lor Q $ | $Q$ |
  | --- | --- | ------------ | --- |
  | T   | T   | T            | T   |
  | T   | F   | T            | F   |
  | F   | T   | T            | T   |
  | F   | F   | F            | F   |

  ## Derivation

  > In terms of logical derivation, a finite $\Gamma$ of propositions is
  > **inconsistent** in a system of derivation for propositional logic if and only
  > if a proposition of the form $P \& \lnot P$ is derivable from $\Gamma$. It is
  > **consistent** just if this is not the case.

  In other terms, if you can derive a contradiction from the set, the set is
  logically inconsistent.

  A
  [contradiction](Logical_truth_and_falsity.md#logical-falsity)
  has very important consequences for reasoning because if a set of propositions
  is inconsistent, any other proposition is derivable from it.

  ![](/img/derivation_from_contradiction.png)

  _A demonstration of the the consequences of deriving a contradiction in a
  sequence of reasoning._

  Here we want to derive some proposition $Q$. If we can derive a contradiction
  from its negation as an assumption then, by the
  [negation elimination](Negation_Elimination.md)) rule, we can
  assert $Q$. This is why contradictions should be avoided in arguments, they
  'prove' everything which, by association, undermines any particular premise you
  are trying to assert.

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

  # Logical consistency

  ## Informal definition

  A set of propositions is consistent if and only if **it is possible for all the
  members of the set to be true at the same time**. A set of propositions is
  inconsistent if and only if it is not consistent.

  ### Demonstration

  The following set of propositions form an inconsistent set:

  1. Anyone who takes astrology seriously is a lunatic.
  2. Alice is my sister and no sister of mine has a lunatic for a husband.
  3. David is Alice's husband and he read's the horoscope column every morning.
  4. Anyone who reads the horoscope column every morning takes astrology
     seriously.

  The set is inconsistent because not all of them can be true. If (1), (3), (4)
  are true, (2) cannot be. If (2), (3),(4) are true, (1) cannot be.

  ## Formal definition

  > A finite set of propositions $\Gamma$ is truth-functionally consistent if and
  > only if there is at least one truth-assignment in which all propositions of
  > $\Gamma$ are true.

  ### Informal expression

  ```
  The book is blue or the book is brown
  The book is brown
  ```

  ### Formal expression

  $$
  \{P \lor Q, Q\}
  $$

  ### Truth table

  $ \{P, Q\} $ form a consistent set because there is at least one assignment when
  both propositions are true. In fact there are two (corresponding to each
  disjunct) but one is sufficient.

  | $P$ | $Q$ | $ P \lor Q $ | $Q$ |
  | --- | --- | ------------ | --- |
  | T   | T   | T            | T   |
  | T   | F   | T            | F   |
  | F   | T   | T            | T   |
  | F   | F   | F            | F   |

  ## Derivation

  > In terms of logical derivation, a finite $\Gamma$ of propositions is
  > **inconsistent** in a system of derivation for propositional logic if and only
  > if a proposition of the form $P \& \lnot P$ is derivable from $\Gamma$. It is
  > **consistent** just if this is not the case.

  In other terms, if you can derive a contradiction from the set, the set is
  logically inconsistent.

  A
  [contradiction](Logical_truth_and_falsity.md#logical-falsity)
  has very important consequences for reasoning because if a set of propositions
  is inconsistent, any other proposition is derivable from it.

  ![](/img/derivation_from_contradiction.png)

  _A demonstration of the the consequences of deriving a contradiction in a
  sequence of reasoning._

  Here we want to derive some proposition $Q$. If we can derive a contradiction
  from its negation as an assumption then, by the
  [negation elimination](Negation_Elimination.md)) rule, we can
  assert $Q$. This is why contradictions should be avoided in arguments, they
  'prove' everything which, by association, undermines any particular premise you
  are trying to assert.


path: Logical_equivalence.md
sortable_path: Logical_equivalence.md
title: Logical equivalence
checksum: b08ec98b4bd43e93cacd533e9b9d0847dc5ae2454cd8d160eed3a4b7a635fb90
created: 2024-10-22 16:52:57.498611424+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  > Two sentences, P and Q, are truth-functionally equivalent if and only if there
  > is no truth assignment in which P is true and Q is false

body:
  > Two sentences, P and Q, are truth-functionally equivalent if and only if there
  > is no truth assignment in which P is true and Q is false

  ### Informal expression

  P: If it is raining then the pavement will be wet.

  Q: The pavement is not wet unless it is raining.

  ### Formal expression

  $$
  (P \rightarrow Q) \longleftrightarrow (\lnot P \lor Q)
  $$

  ### Truth-tables

  | $P$ | $Q$ | $ P \rightarrow Q $ | $ \lnot P \lor Q$ |
  | --- | --- | ------------------- | ----------------- |
  | T   | T   | T                   | T                 |
  | T   | F   | T                   | F                 |
  | F   | T   | T                   | T                 |
  | F   | F   | F                   | T                 |

  ### Derivation

  > Propositions $P$ and $Q$ are equivalent in a system of
  > [derivation](Formal%20proofs%20in%20propositional%20logic.md) for
  > propositional logic if $Q$ is derivable from $P$ and $P$ is derivable from
  > $Q$.

  Note that the property of equivalence stated in terms of derivablity above is
  identical to the derivation rule for the
  [material biconditional](Biconditional_Introduction.md):

  ![bi-intro.png](bi-intro.png)

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

  # Logical equivalence

  > Two sentences, P and Q, are truth-functionally equivalent if and only if there
  > is no truth assignment in which P is true and Q is false

  ### Informal expression

  P: If it is raining then the pavement will be wet.

  Q: The pavement is not wet unless it is raining.

  ### Formal expression

  $$
  (P \rightarrow Q) \longleftrightarrow (\lnot P \lor Q)
  $$

  ### Truth-tables

  | $P$ | $Q$ | $ P \rightarrow Q $ | $ \lnot P \lor Q$ |
  | --- | --- | ------------------- | ----------------- |
  | T   | T   | T                   | T                 |
  | T   | F   | T                   | F                 |
  | F   | T   | T                   | T                 |
  | F   | F   | F                   | T                 |

  ### Derivation

  > Propositions $P$ and $Q$ are equivalent in a system of
  > [derivation](Formal%20proofs%20in%20propositional%20logic.md) for
  > propositional logic if $Q$ is derivable from $P$ and $P$ is derivable from
  > $Q$.

  Note that the property of equivalence stated in terms of derivablity above is
  identical to the derivation rule for the
  [material biconditional](Biconditional_Introduction.md):

  ![bi-intro.png](bi-intro.png)


path: Logical_indeterminacy.md
sortable_path: Logical_indeterminacy.md
title: Logical indeterminacy
checksum: 32e3fa137e65eff2c6f39ea7a5fca9d67f8a160dfbe8ad1551a0c8995a001af3
created: 2024-10-22 16:52:57.512602452+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  The vast majority of propositions in natural and formal logical languages are
  **neither
  [logically true](Logical_truth_and_falsity.md#logical-truth)
  or
  [logically false](Logical_truth_and_falsity.md#logical-falsity)**.
  This makes sense because propositions of this form are all either tautologies or
  contradictions and as such do not express information about the state of events
  in the world. We call propositions that are neither logically true or logically
  false, **logically indeterminate** propositions.

body:
  The vast majority of propositions in natural and formal logical languages are
  **neither
  [logically true](Logical_truth_and_falsity.md#logical-truth)
  or
  [logically false](Logical_truth_and_falsity.md#logical-falsity)**.
  This makes sense because propositions of this form are all either tautologies or
  contradictions and as such do not express information about the state of events
  in the world. We call propositions that are neither logically true or logically
  false, **logically indeterminate** propositions.

  ## Informal definition

  A proposition is logically indeterminate if it is neither logically true or
  logically false. This is to say: it can be both [consistently](Consistency.md)
  asserted and consistently denied.

  For example the proposition:

  ```
  It is raining.
  ```

  May be true or false thus it can it both be asserted and denied quite
  consistently. It is true if it actually is raining and false if it actually is
  not raining. There is no logical contradiction implied by saying it is raining
  when it isn't raining, this assertion is simply false. There is a contradiction
  in saying that both states obtain. Thus the proposition:

  ```
  It is raining and it is not raining.
  ```

  Cannot be consistently asserted as there is no possibility of the proposition
  being true. It is either raining or it isn't raining. Given the law for
  conjunction, both conjuncts must be true for the proposition as a whole to be
  true. But in the case of this proposition if one conjunct is true, the other
  must be false and vice versa, hence it is not possible for the proposition to be
  true at all. It can _only_ be false.

  Contrariwise the proposition:

  ```
  It is raining or it is not raining.
  ```

  Cannot be consistently denied as there is no possibility of it being false. It
  is either raining or not raining. Given the law for disjunction, either disjunct
  can be true to make the proposition as a whole true. Given that it is either
  raining or not raining in either scenario, the proposition as a whole will be
  true. Therefore there is no possibility of it being false, it can _only_ be
  true.

  ## Formal definition

  > A proposition P is truth-functionally indeterminate if and only if it is
  > neither truth-functionally true or truth-functionally false. should be avoided
  > in arguments, they 'prove' everything whi

  ```
  P
  ```

  ### Truth-table

  | $P$ | $P$ |
  | --- | --- |
  | T   | T   |
  | F   | F   |

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

  # Logical indeterminacy

  The vast majority of propositions in natural and formal logical languages are
  **neither
  [logically true](Logical_truth_and_falsity.md#logical-truth)
  or
  [logically false](Logical_truth_and_falsity.md#logical-falsity)**.
  This makes sense because propositions of this form are all either tautologies or
  contradictions and as such do not express information about the state of events
  in the world. We call propositions that are neither logically true or logically
  false, **logically indeterminate** propositions.

  ## Informal definition

  A proposition is logically indeterminate if it is neither logically true or
  logically false. This is to say: it can be both [consistently](Consistency.md)
  asserted and consistently denied.

  For example the proposition:

  ```
  It is raining.
  ```

  May be true or false thus it can it both be asserted and denied quite
  consistently. It is true if it actually is raining and false if it actually is
  not raining. There is no logical contradiction implied by saying it is raining
  when it isn't raining, this assertion is simply false. There is a contradiction
  in saying that both states obtain. Thus the proposition:

  ```
  It is raining and it is not raining.
  ```

  Cannot be consistently asserted as there is no possibility of the proposition
  being true. It is either raining or it isn't raining. Given the law for
  conjunction, both conjuncts must be true for the proposition as a whole to be
  true. But in the case of this proposition if one conjunct is true, the other
  must be false and vice versa, hence it is not possible for the proposition to be
  true at all. It can _only_ be false.

  Contrariwise the proposition:

  ```
  It is raining or it is not raining.
  ```

  Cannot be consistently denied as there is no possibility of it being false. It
  is either raining or not raining. Given the law for disjunction, either disjunct
  can be true to make the proposition as a whole true. Given that it is either
  raining or not raining in either scenario, the proposition as a whole will be
  true. Therefore there is no possibility of it being false, it can _only_ be
  true.

  ## Formal definition

  > A proposition P is truth-functionally indeterminate if and only if it is
  > neither truth-functionally true or truth-functionally false. should be avoided
  > in arguments, they 'prove' everything whi

  ```
  P
  ```

  ### Truth-table

  | $P$ | $P$ |
  | --- | --- |
  | T   | T   |
  | F   | F   |


path: Logical_possibility_and_necessity.md
sortable_path: Logical_possibility_and_necessity.md
title: Logical possibility and necessity
lead: ## Logical possibility
checksum: f2b8d371f555e5b90f5e19beba80d8f0b6a488c50cc44d3ee4aca2bab05158c9
created: 2024-10-22 16:52:57.533755454+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  ## Logical possibility

  In distinguishing the properties of
  [logical consistency](Logical_consistency.md) and
  [validity](Validity_and_entailment.md) we make tacit use
  of the notion of **possibility**. This is because when we consider the validity
  of an argument we are assessing truth-conditions and this consists in asking
  ourselves what could or could not be the case: were it such that _P_, then it
  would be the case that _Q_. It is important to understand what possibility means
  in the context of logic and how it differs from what we might mean ordinarily
  when we use the term.

  It is evident from the case of arguments that are valid but not sound that logic
  operates with a specialised notion of possibility. For example it has to be the
  case that the proposition _Every woman can levitate_ is logically possible since
  the following argument is valid:

  ```
  1.  P: Janice is a woman.
  2.  P: Every woman can levitate.
  3.  C: Janice can levitate.
  ```

  But we know of course that women cannot levitate. When we assert that this is
  impossible we are relying on a stronger notion of possibility than logical
  possibility. It follows that the concept of possibility can have different
  degrees. The scope of the concept of possibility has been the concern of
  logicians and philosophers since at least the time of Plato and numerous
  different formulations exist. The notion that we mostly work with unreflectively
  in everyday life is nomological possibility. This means ‘governed by the
  application of laws’ where these laws pertain to our current understanding of
  the natural world as determined by physics. Levitation is therefore
  nomologically impossible but logically possible.

  If logical possibility is not constrained by the laws of physics does it place
  any restrictions on what is possible? Logic applies a single restriction, the
  law of non-contradiction: a proposition cannot both be true and false at once.
  The following propositions are examples of a contradictory propositions.

  Some examples of contradictions:

  - There is a dog that is not a dog
  - Today is Tuesday and today is not Tuesday
  - The cat that is dead is alive

  From this we can derive the following property of logical possibility:

  > A proposition is logically possible just if it does not imply a contradiction.

  ## Logical necessity

  A proposition is _logically necessary_ if it is true in every logically possible
  circumstance which is to say: true on every possible truth functional
  assignment. Necessity and
  [logical truth](Logical_truth_and_falsity.md#logical-truth)
  are therefore synonyms: anything that is logically true (a tautology) is true by
  necessity (could not be otherwise.)

  Further, every logical truth is logically possible but not everything that is
  logically possible is logically true. It is possible that it is raining but this
  is not logically necessary - it could be otherwise, i.e not raining. However it
  is not possible that it could be both raining and not raining.

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

  # Logical possibility and necessity

  ## Logical possibility

  In distinguishing the properties of
  [logical consistency](Logical_consistency.md) and
  [validity](Validity_and_entailment.md) we make tacit use
  of the notion of **possibility**. This is because when we consider the validity
  of an argument we are assessing truth-conditions and this consists in asking
  ourselves what could or could not be the case: were it such that _P_, then it
  would be the case that _Q_. It is important to understand what possibility means
  in the context of logic and how it differs from what we might mean ordinarily
  when we use the term.

  It is evident from the case of arguments that are valid but not sound that logic
  operates with a specialised notion of possibility. For example it has to be the
  case that the proposition _Every woman can levitate_ is logically possible since
  the following argument is valid:

  ```
  1.  P: Janice is a woman.
  2.  P: Every woman can levitate.
  3.  C: Janice can levitate.
  ```

  But we know of course that women cannot levitate. When we assert that this is
  impossible we are relying on a stronger notion of possibility than logical
  possibility. It follows that the concept of possibility can have different
  degrees. The scope of the concept of possibility has been the concern of
  logicians and philosophers since at least the time of Plato and numerous
  different formulations exist. The notion that we mostly work with unreflectively
  in everyday life is nomological possibility. This means ‘governed by the
  application of laws’ where these laws pertain to our current understanding of
  the natural world as determined by physics. Levitation is therefore
  nomologically impossible but logically possible.

  If logical possibility is not constrained by the laws of physics does it place
  any restrictions on what is possible? Logic applies a single restriction, the
  law of non-contradiction: a proposition cannot both be true and false at once.
  The following propositions are examples of a contradictory propositions.

  Some examples of contradictions:

  - There is a dog that is not a dog
  - Today is Tuesday and today is not Tuesday
  - The cat that is dead is alive

  From this we can derive the following property of logical possibility:

  > A proposition is logically possible just if it does not imply a contradiction.

  ## Logical necessity

  A proposition is _logically necessary_ if it is true in every logically possible
  circumstance which is to say: true on every possible truth functional
  assignment. Necessity and
  [logical truth](Logical_truth_and_falsity.md#logical-truth)
  are therefore synonyms: anything that is logically true (a tautology) is true by
  necessity (could not be otherwise.)

  Further, every logical truth is logically possible but not everything that is
  logically possible is logically true. It is possible that it is raining but this
  is not logically necessary - it could be otherwise, i.e not raining. However it
  is not possible that it could be both raining and not raining.


