# notes

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

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

checksum: 13c4e831cb314f4643cba5f5b4c5916209be8b3e3ba9aad98fae1ca25f315bc4
created: 2024-03-30 09:03:21.038991087+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["Linux","procedural"]}
raw_content:
  ---
  tags:
    - Linux
    - procedural
  ---

  # Save read only Vim file

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


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

checksum: 594b1e9ce9125afba02b6d41efe4af9a53aff06bff3ab7739bc2e89e65f6d664
created: 2024-03-30 09:03:21.052315622+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":[]}
body:
  ```
  3.4.1 === major.minor.patch
  ```

  - Major

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

  - Minor

    - New features that do not break the existing API

  - Patch

    - Bug fixes for the current minor version

  ## Glob patterns for versioning

  ### Caret

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

  This is equivalent to `4.x`

  ### Tilde

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

  ### No tilde or caret

  Use the _exact_ version specified

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

  # Semantic versioning

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

  - Major

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

  - Minor

    - New features that do not break the existing API

  - Patch

    - Bug fixes for the current minor version

  ## Glob patterns for versioning

  ### Caret

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

  This is equivalent to `4.x`

  ### Tilde

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

  ### No tilde or caret

  Use the _exact_ version specified


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

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

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

  ## Basic usage

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

  ## Looping through sets

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

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

  ## Check for membership

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

  ## Remove items from set

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

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

  basket.clear()
  print(basket)
  #set

  ```

  ## Add items to a set

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

  ## Start with empty set

  To declare an empty set you cannot just do:

  ```py
  my_set = {}
  ```

  You have to use a constructor:

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

  ## Apply unions and intersections

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

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


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

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

  ```

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

  # Sets in Python

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

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

  ## Basic usage

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

  ## Looping through sets

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

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

  ## Check for membership

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

  ## Remove items from set

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

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

  basket.clear()
  print(basket)
  #set

  ```

  ## Add items to a set

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

  ## Start with empty set

  To declare an empty set you cannot just do:

  ```py
  my_set = {}
  ```

  You have to use a constructor:

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

  ## Apply unions and intersections

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

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


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

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

  ```


path: zk/Shell_sessions.md
sortable_path: zkShell_sessions.md
title: Shell sessions
lead: ## **Types of shell session**
checksum: 26f3c11dcd8dbb538a894ab5e4ce2d6cb0c116f02b0ae59b2745a90995db3502
created: 2024-03-30 09:03:21.071997795+00:00
modified: 2024-10-19 08:57:41.05474052+00:00
metadata: {"tags":["shell"]}
body:
  ## **Types of shell session**

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

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

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

  ## Shell sessions and access

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

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

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

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

  # Shell sessions

  ## **Types of shell session**

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

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

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

  ## Shell sessions and access

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

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

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


path: zk/Signed_and_unsigned_numbers.md
sortable_path: zkSigned_and_unsigned_numbers.md
title: Signed and unsigned numbers
lead: ## Summary
checksum: 0dd060a4662e8b9daf6a58073529520afc171a28313f6d60e2b57179598a782c
created: 2024-03-30 09:03:21.09131187+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["binary"]}
body:
  ## Summary

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

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

  ## Related notes

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

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

  # Signed and unsigned numbers

  ## Summary

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

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

  ## Related notes

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


path: zk/Signed_magnitude_representation.md
sortable_path: zkSigned_magnitude_representation.md
title: Signed magnitude representation
lead: ## Summary
checksum: e67bf5f2f83b3ec515f89bcc2427c89b3922143c936bd0c8130a383a4c654f62
created: 2024-03-30 09:03:21.124300039+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"created":"Wednesday, March 20, 2024","id":"3wsh","tags":["binary"]}
body:
  ## Summary

  ## Detail

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

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

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

  ### Advantages

  ### Limitations

  ## Applications

  ## Related notes

  [[Signed_and_unsigned_numbers|signed_and_unsigned_numbers]]

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

  # Signed magnitude representation

  ## Summary

  ## Detail

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

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

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

  ### Advantages

  ### Limitations

  ## Applications

  ## Related notes

  [[Signed_and_unsigned_numbers|signed_and_unsigned_numbers]]


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

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

  ## Use-cases

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

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

  ## Implementation

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

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

  ### Using an object

  We ensure singularity by:

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

  ```js
  const _data = [];

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

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

  ### Using a class

  The same process can be created using classes:

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

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

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

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

  export default instance;
  ```

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

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

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

      return UserStore.instance;
    }

    //rest is the same code as preceding example
  }

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

  export default instance;
  ```

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

  # Singleton pattern

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

  ## Use-cases

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

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

  ## Implementation

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

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

  ### Using an object

  We ensure singularity by:

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

  ```js
  const _data = [];

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

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

  ### Using a class

  The same process can be created using classes:

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

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

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

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

  export default instance;
  ```

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

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

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

      return UserStore.instance;
    }

    //rest is the same code as preceding example
  }

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

  export default instance;
  ```


path: zk/Sliding_window.md
sortable_path: zkSliding_window.md
title: Sliding window
checksum: 0875187e1b5247dc2b2f87bb9c15e41780ada662f3b8e6efbbcd79a6d0a7b251
created: 2024-03-30 09:03:21.140319034+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["design-patterns"]}
lead:
  Whereas the [multiple pointer](Multiple_pointers.md) pattern works by keeping
  two array indices running at once and comparing their values on each iteration,
  the sliding window has a running value (the 'window') that is updated at each
  iteration and which compares itself against its most recent previous value.

body:
  Whereas the [multiple pointer](Multiple_pointers.md) pattern works by keeping
  two array indices running at once and comparing their values on each iteration,
  the sliding window has a running value (the 'window') that is updated at each
  iteration and which compares itself against its most recent previous value.

  This is what makes it 'sliding': the value isn't constant, it changes (or
  doesn't) based on what it was previously.

  ## Example: maximum sum of sub-array

  We create a function that takes an array and a sub-array length _n_. The
  objective is to find the maximum value that can be created by summing _n_
  elements of the array. It moves through the array, summing by _n_ keeping track
  of the highest sum value so far and the current sum value. At the end it should
  return the highest possible sum value.

  Here we do this for a sub-array of length 2:

  ```ts
  function maxSubarraySum(arr, subArrLength) {
    let maxSum = 0; // Largest sum value so far
    let tempSum = 0; // Current sum value

    //  Establish the first 'maxSum'
    // At the beginning this will just be the sum of the first two array elements
    for (let i = 0; i < subArrLength; i++) {
      maxSum += arr[i];
    }

    // Map temporary sum to maxSum
    // Accordingly, as we have only mapped the first sub-array, the max sum will be the same
    // as the current sum
    tempSum = maxSum;

    // Move through the array one element at a time (`i++`) via a window starting from the element that is equal to `subArrLength`
    // The first sum calculation is already taken care of in the earlier loop and stored in `maxSum`, so we don't have to worry about missing the elements in indices less than subArrLength
    for (let i = subArrLength; i < arr.length; i++) {
      // Temp sum becomes a moveable window value equal to the subtraction of the previous element and the addition of next element in line
      tempSum = tempSum - arr[i - subArrLength] + arr[i];
      // Max sum is redefined as the largest subArrLengthber between the previous highest and the current value of tempSum
      maxSum = Math.max(maxSum, tempSum);
    }
    return maxSum;
  }

  console.log(maxSubarraySum([100, 200, 300, 400], 2)); // 700
  ```

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

  # Sliding window

  Whereas the [multiple pointer](Multiple_pointers.md) pattern works by keeping
  two array indices running at once and comparing their values on each iteration,
  the sliding window has a running value (the 'window') that is updated at each
  iteration and which compares itself against its most recent previous value.

  This is what makes it 'sliding': the value isn't constant, it changes (or
  doesn't) based on what it was previously.

  ## Example: maximum sum of sub-array

  We create a function that takes an array and a sub-array length _n_. The
  objective is to find the maximum value that can be created by summing _n_
  elements of the array. It moves through the array, summing by _n_ keeping track
  of the highest sum value so far and the current sum value. At the end it should
  return the highest possible sum value.

  Here we do this for a sub-array of length 2:

  ```ts
  function maxSubarraySum(arr, subArrLength) {
    let maxSum = 0; // Largest sum value so far
    let tempSum = 0; // Current sum value

    //  Establish the first 'maxSum'
    // At the beginning this will just be the sum of the first two array elements
    for (let i = 0; i < subArrLength; i++) {
      maxSum += arr[i];
    }

    // Map temporary sum to maxSum
    // Accordingly, as we have only mapped the first sub-array, the max sum will be the same
    // as the current sum
    tempSum = maxSum;

    // Move through the array one element at a time (`i++`) via a window starting from the element that is equal to `subArrLength`
    // The first sum calculation is already taken care of in the earlier loop and stored in `maxSum`, so we don't have to worry about missing the elements in indices less than subArrLength
    for (let i = subArrLength; i < arr.length; i++) {
      // Temp sum becomes a moveable window value equal to the subtraction of the previous element and the addition of next element in line
      tempSum = tempSum - arr[i - subArrLength] + arr[i];
      // Max sum is redefined as the largest subArrLengthber between the previous highest and the current value of tempSum
      maxSum = Math.max(maxSum, tempSum);
    }
    return maxSum;
  }

  console.log(maxSubarraySum([100, 200, 300, 400], 2)); // 700
  ```


path: zk/Solving_equations.md
sortable_path: zkSolving_equations.md
title: Solving equations
lead: ## Use inversion of operators
checksum: 9e92801bf8f641a6153875adfecdb611e634a9cc7d61d3dc7f72993961c41550
created: 2024-03-30 09:03:21.150362187+00:00
modified: 2024-10-19 08:58:04.678474029+00:00
metadata: {"tags":["algebra"]}
body:
  ## Use inversion of operators

  When solving equations we frequently make use of the
  [ operator inversion rules](Inversion%20of%20operators.md) to find the
  solutions.

  ### Example: inversion of addition

  For example, the equation $9 = 3 + x$ has the solution $6$ ($x$ is equal to
  $6$). To arrive at this, we can use the inverse of the main operator in the
  equation (addition): $9-3 = 6$.

  ### Example: inversion of subtraction

  Now consider $19 = x - 3$. The solution to this equation is $22$ ($x$ is equal
  to $22$). To arrive at this, we can use the inverse of the main operator in the
  equation (subtraction): $19 + 3 = 22$.

  ### Example: inversion of division

  The equation we want to solve: $$\frac{x}{6} = 4$$

  Now we invert it by multiplying the denominator by the quotient:
  $6\cdot 4 = 24$. Therefore: $$ \frac{24}{6} = 4$$
  The solution is $24$

  ### Example: inversion of multiplication

  The equation we want to solve: $$4x = 36$$ Now we invert it by dividing the
  product by the coefficient: !Add link to 'coefficient'

  $$\frac{36}{4} = 9$$

  Therefore the solution is $9$: $$ 4(9) = 36$$

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

  # Solving equations

  ## Use inversion of operators

  When solving equations we frequently make use of the
  [ operator inversion rules](Inversion%20of%20operators.md) to find the
  solutions.

  ### Example: inversion of addition

  For example, the equation $9 = 3 + x$ has the solution $6$ ($x$ is equal to
  $6$). To arrive at this, we can use the inverse of the main operator in the
  equation (addition): $9-3 = 6$.

  ### Example: inversion of subtraction

  Now consider $19 = x - 3$. The solution to this equation is $22$ ($x$ is equal
  to $22$). To arrive at this, we can use the inverse of the main operator in the
  equation (subtraction): $19 + 3 = 22$.

  ### Example: inversion of division

  The equation we want to solve: $$\frac{x}{6} = 4$$

  Now we invert it by multiplying the denominator by the quotient:
  $6\cdot 4 = 24$. Therefore: $$ \frac{24}{6} = 4$$
  The solution is $24$

  ### Example: inversion of multiplication

  The equation we want to solve: $$4x = 36$$ Now we invert it by dividing the
  product by the coefficient: !Add link to 'coefficient'

  $$\frac{36}{4} = 9$$

  Therefore the solution is $9$: $$ 4(9) = 36$$


path: zk/Sorting_lists_in_Python.md
sortable_path: zkSorting_lists_in_Python.md
title: Sorting lists in Python
lead: ### Using `sorted()` Function:
checksum: ac48196d81b67d9d944918b8fa4b520193798f40404205ad6e31b34dec410986
created: 2024-03-30 09:03:21.164687713+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["python"]}
body:
  ### Using `sorted()` Function:

  The built-in `sorted()` function returns a new list containing all the elements
  of the original list sorted in the specified order (either ascending, which is
  default, or descending).

  ```python
  my_list = [3, 1, 4, 1, 5, 9, 2, 6, 5]
  sorted_list = sorted(my_list)
  print(sorted_list)  # Output: [1, 1, 2, 3, 4, 5, 5, 6, 9]
  ```

  For sorting in descending order, you can use the `reverse=True` argument:

  ```python
  sorted_list_desc = sorted(my_list, reverse=True)
  print(sorted_list_desc)  # Output: [9, 6, 5, 5, 4, 3, 2, 1, 1]
  ```

  ### Using `.sort()` Method:

  The `.sort()` method sorts the elements of a list in-place and returns `None`.
  This modifies the original list.

  ```python
  my_list = [3, 1, 4, 1, 5, 9, 2, 6, 5]
  my_list.sort()
  print(my_list)  # Output: [1, 1, 2, 3, 4, 5, 5, 6, 9]
  ```

  For sorting in descending order:

  ```python
  my_list.sort(reverse=True)
  print(my_list)  # Output: [9, 6, 5, 5, 4, 3, 2, 1, 1]
  ```

  ### Using a Custom Key Function:

  Both `sorted()` and `.sort()` accept a `key` parameter that allows you to
  specify a function to determine the sorting order. The function should take a
  single argument and return a value that will be used for sorting purposes.

  For example, if you have a list of strings and you want to sort them by their
  length:

  ```python
  my_str_list = ["apple", "banana", "cherry", "date"]
  sorted_by_length = sorted(my_str_list, key=len)
  print(sorted_by_length)  # Output: ['date', 'apple', 'cherry', 'banana']
  ```

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

  # Sorting lists in Python

  ### Using `sorted()` Function:

  The built-in `sorted()` function returns a new list containing all the elements
  of the original list sorted in the specified order (either ascending, which is
  default, or descending).

  ```python
  my_list = [3, 1, 4, 1, 5, 9, 2, 6, 5]
  sorted_list = sorted(my_list)
  print(sorted_list)  # Output: [1, 1, 2, 3, 4, 5, 5, 6, 9]
  ```

  For sorting in descending order, you can use the `reverse=True` argument:

  ```python
  sorted_list_desc = sorted(my_list, reverse=True)
  print(sorted_list_desc)  # Output: [9, 6, 5, 5, 4, 3, 2, 1, 1]
  ```

  ### Using `.sort()` Method:

  The `.sort()` method sorts the elements of a list in-place and returns `None`.
  This modifies the original list.

  ```python
  my_list = [3, 1, 4, 1, 5, 9, 2, 6, 5]
  my_list.sort()
  print(my_list)  # Output: [1, 1, 2, 3, 4, 5, 5, 6, 9]
  ```

  For sorting in descending order:

  ```python
  my_list.sort(reverse=True)
  print(my_list)  # Output: [9, 6, 5, 5, 4, 3, 2, 1, 1]
  ```

  ### Using a Custom Key Function:

  Both `sorted()` and `.sort()` accept a `key` parameter that allows you to
  specify a function to determine the sorting order. The function should take a
  single argument and return a value that will be used for sorting purposes.

  For example, if you have a list of strings and you want to sort them by their
  length:

  ```python
  my_str_list = ["apple", "banana", "cherry", "date"]
  sorted_by_length = sorted(my_str_list, key=len)
  print(sorted_by_length)  # Output: ['date', 'apple', 'cherry', 'banana']
  ```


path: zk/Soundness.md
sortable_path: zkSoundness.md
title: Soundness
checksum: d7502906245e61759a05792bb1ac1529a125a95843565c46f58eb1f289042564
created: 2024-03-30 09:03:21.16931637+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  Recall that in the definition of
  [deductive validity](Validity_and_entailment.md#validity)
  we do not say: an argument is valid iff if the premises _are true_ and the
  conclusion _is true_. We say _if it is possible for the premises to be true_.
  This is important: we are not interested in the actual truth of the premises or
  the conclusion.

body:
  Recall that in the definition of
  [deductive validity](Validity_and_entailment.md#validity)
  we do not say: an argument is valid iff if the premises _are true_ and the
  conclusion _is true_. We say _if it is possible for the premises to be true_.
  This is important: we are not interested in the actual truth of the premises or
  the conclusion.

  #### Demonstration

  Therefore this argument is valid:

  ```
  (P1) Oranges are the same colour as bananas.
  (P2) Bananas are yellow.
  ____________________________________________
  (C) Oranges are yellow.
  ```

  Of course oranges are not yellow but _were_ (P1) true, then given (P2), the
  conclusion must also be true.

  This argument is also valid:

  ```
  (P1) Oranges are the same colour as carrots.
  (P2) Carrots are orange.
  ____________________________________________
  (C) Oranges are orange in colour.
  ```

  The difference here is that the premises happen to be true and, given that the
  argument is valid, the conclusion must also be true. What we have defined here
  is **soundness**: the argument is said to be sound as well as valid. This is an
  additional and stronger criterion of evaluation.

  > An argument is sound if and only if it is deductively valid and all its
  > premises are true.

  We must not forget that truth alone is not the sole condition for soundness. We
  can have arguments whose conclusion and premises are all true without the
  argument being sound:

  ```
  (P1) London is the capital of the United Kingdom
  (P2) The capital of the United Kingdom is in the southern part of the United Kingdom.
  (P3) Cambridge is not the capital of the United Kingdom
  ____________________________________________
  (C) London is south of Cambridge
  ```

  All propositions are true here but the argument is not deductively valid: the
  premises are all true but the conclusion is false.

  We can also have arguments which are valid but which are not sound:

  ```
  (P1) Vitamin C prevents colds.
  (P2) Vitamin C does not prevent colds.
  ____________________________________________
  (C) Vitamin C is harmless
  ```

  This argument is valid because we cannot consistently assert the premises and
  deny the conclusion. In either case, the conclusion can be said to follow from
  the premises. The problem is that we cannot consistently assert both premises:
  it is not possible for both propositions to be true at the same time.

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

  # Soundness

  Recall that in the definition of
  [deductive validity](Validity_and_entailment.md#validity)
  we do not say: an argument is valid iff if the premises _are true_ and the
  conclusion _is true_. We say _if it is possible for the premises to be true_.
  This is important: we are not interested in the actual truth of the premises or
  the conclusion.

  #### Demonstration

  Therefore this argument is valid:

  ```
  (P1) Oranges are the same colour as bananas.
  (P2) Bananas are yellow.
  ____________________________________________
  (C) Oranges are yellow.
  ```

  Of course oranges are not yellow but _were_ (P1) true, then given (P2), the
  conclusion must also be true.

  This argument is also valid:

  ```
  (P1) Oranges are the same colour as carrots.
  (P2) Carrots are orange.
  ____________________________________________
  (C) Oranges are orange in colour.
  ```

  The difference here is that the premises happen to be true and, given that the
  argument is valid, the conclusion must also be true. What we have defined here
  is **soundness**: the argument is said to be sound as well as valid. This is an
  additional and stronger criterion of evaluation.

  > An argument is sound if and only if it is deductively valid and all its
  > premises are true.

  We must not forget that truth alone is not the sole condition for soundness. We
  can have arguments whose conclusion and premises are all true without the
  argument being sound:

  ```
  (P1) London is the capital of the United Kingdom
  (P2) The capital of the United Kingdom is in the southern part of the United Kingdom.
  (P3) Cambridge is not the capital of the United Kingdom
  ____________________________________________
  (C) London is south of Cambridge
  ```

  All propositions are true here but the argument is not deductively valid: the
  premises are all true but the conclusion is false.

  We can also have arguments which are valid but which are not sound:

  ```
  (P1) Vitamin C prevents colds.
  (P2) Vitamin C does not prevent colds.
  ____________________________________________
  (C) Vitamin C is harmless
  ```

  This argument is valid because we cannot consistently assert the premises and
  deny the conclusion. In either case, the conclusion can be said to follow from
  the premises. The problem is that we cannot consistently assert both premises:
  it is not possible for both propositions to be true at the same time.


path: zk/Stacks.md
sortable_path: zkStacks.md
title: A stack is a linear data structure that observes LIFO
checksum: a141ae765f7298ff9bd03a8d68bdae6ac6ac56e84d1369198460bc254462f9a8
created: 2024-03-30 09:03:21.188736548+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["data-structures"]}
lead:
  Think of a stack like a pile of books: the last book that you add is the nearest
  to the top and therefore the first one that you can remove.

body:
  Think of a stack like a pile of books: the last book that you add is the nearest
  to the top and therefore the first one that you can remove.

  If you want to get a book that is not at the top, you have to first remove the
  books that are above it to get to it.

  This type of data structure is linear and only allows sequential, not random,
  access. It is an example of 'last one in first one out'.

  ## We can build a stack from an array

  A stack is an example of a data structure that can be built by adapting an
  array. If you think about it, all that is needed is an array to store the data,
  an `array push` method to add elements to the 'end' or the 'bottom' of the stack
  and an `array pop` method to remove the element at the top.

  ## Demonstration

  Below we create a stack constructor, using a class. An object created from this
  template will have the following properties and methods:

  - `items[]` → an array to store the data
  - `push()` → a method to add an element to the end of the stack
  - `pop()` → a method to remove an element from the front

  In addition we have the following helpers, which allow us to check the status of
  the stack and retrieve information about it:

  - `isEmpty()` → check if the stack is populated or not
  - `clear()` → empty the stack of its content (therefore making `isEmpty()`
    return `true`)
  - `size` → a property corresponding to the stack's length

  ```js
  class Stack {
    items = []; // the array that will store the elements that comprise the stack
    push = (element) => this.items.push(element); // add an element to the end of the stack
    pop = () => this.items.pop(); // remove and return the last element from the stack

    // We can add some useful helper methods, that return info about the state of the stack:
    isEmpty = () => this.items.length === 0; // return true if the stack is empty
    clear = () => (this.items.length = 0); // empty the stack
    size = () => this.items.length; // count elements in stack
  }
  ```

  ## Run through

  ```js
  let stack = new Stack();
  test.push(1); // Add some elements to the stack
  test.push(2);
  test.push(3);

  // Stack now looks like:
  console.log(stack.items); // [1, 2, 3]

  // Let's try removing the last element
  stack.pop(); // 3 -> this was the last element we added, so it's the first one that comes out

  // Now the stack looks like this:
  // [1,2]

  // Let's add a new element
  test.push(true);

  // Now the stack looks like:
  // [1,2, true]
  ```

  ## Practical applications

  - Any application that wants to got 'back in time' must utilise a stack. For
    example, the 'undo' function in most software is a function of a stack. The
    most recent action is at the top, and under that is the second most recent and
    so on all the way back to the first action.
  - Recursive functions: a function that calls itself repeatedly until a boundary
    condition is met is using a stack structure. As you drill down through the
    function calls you start from the most recent down to the last.
  - Balancing parentheses. Say you want to check if the following string is
    balanced `[()]` . Every time you find an opening parentheses. You push that to
    the front of a stack. You then compare the closing parentheses with the order
    of the stack. The same could be done when seeking to find palindromes. This
    sort of thing could be a code challenge so build an example.

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

  _A stack visualised vertically_ ![stack2.svg](stack2.svg)

  _A stack visualised horizontally_ ![stack1.svg](stack1.svg)

  ## A stack is a linear data structure that observes LIFO

  Think of a stack like a pile of books: the last book that you add is the nearest
  to the top and therefore the first one that you can remove.

  If you want to get a book that is not at the top, you have to first remove the
  books that are above it to get to it.

  This type of data structure is linear and only allows sequential, not random,
  access. It is an example of 'last one in first one out'.

  ## We can build a stack from an array

  A stack is an example of a data structure that can be built by adapting an
  array. If you think about it, all that is needed is an array to store the data,
  an `array push` method to add elements to the 'end' or the 'bottom' of the stack
  and an `array pop` method to remove the element at the top.

  ## Demonstration

  Below we create a stack constructor, using a class. An object created from this
  template will have the following properties and methods:

  - `items[]` → an array to store the data
  - `push()` → a method to add an element to the end of the stack
  - `pop()` → a method to remove an element from the front

  In addition we have the following helpers, which allow us to check the status of
  the stack and retrieve information about it:

  - `isEmpty()` → check if the stack is populated or not
  - `clear()` → empty the stack of its content (therefore making `isEmpty()`
    return `true`)
  - `size` → a property corresponding to the stack's length

  ```js
  class Stack {
    items = []; // the array that will store the elements that comprise the stack
    push = (element) => this.items.push(element); // add an element to the end of the stack
    pop = () => this.items.pop(); // remove and return the last element from the stack

    // We can add some useful helper methods, that return info about the state of the stack:
    isEmpty = () => this.items.length === 0; // return true if the stack is empty
    clear = () => (this.items.length = 0); // empty the stack
    size = () => this.items.length; // count elements in stack
  }
  ```

  ## Run through

  ```js
  let stack = new Stack();
  test.push(1); // Add some elements to the stack
  test.push(2);
  test.push(3);

  // Stack now looks like:
  console.log(stack.items); // [1, 2, 3]

  // Let's try removing the last element
  stack.pop(); // 3 -> this was the last element we added, so it's the first one that comes out

  // Now the stack looks like this:
  // [1,2]

  // Let's add a new element
  test.push(true);

  // Now the stack looks like:
  // [1,2, true]
  ```

  ## Practical applications

  - Any application that wants to got 'back in time' must utilise a stack. For
    example, the 'undo' function in most software is a function of a stack. The
    most recent action is at the top, and under that is the second most recent and
    so on all the way back to the first action.
  - Recursive functions: a function that calls itself repeatedly until a boundary
    condition is met is using a stack structure. As you drill down through the
    function calls you start from the most recent down to the last.
  - Balancing parentheses. Say you want to check if the following string is
    balanced `[()]` . Every time you find an opening parentheses. You push that to
    the front of a stack. You then compare the closing parentheses with the order
    of the stack. The same could be done when seeking to find palindromes. This
    sort of thing could be a code challenge so build an example.


path: zk/Stale_branches_and_pruning.md
sortable_path: zkStale_branches_and_pruning.md
title: Stale branches and pruning
checksum: d34c069d9cd0bcb5b5771381334d486c897fc97126ae662b213ca3fd8c8dfd30
created: 2024-03-30 09:03:21.194912257+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["git"]}
lead:
  A **stale branch** is a
  [remote tracking branch](Remote_tracking_branches.md) that **no
  longer tracks anything** because the actual branch on the remote has been
  deleted.

body:
  A **stale branch** is a
  [remote tracking branch](Remote_tracking_branches.md) that **no
  longer tracks anything** because the actual branch on the remote has been
  deleted.

  If _you_ delete the remote branch, the remote tracking branch will also be
  deleted. However if a _collaborator_ deletes the remote branch, your remote
  tracking branch will remain, becoming a stale branch.

  In order to get rid of it, we need to manually remove it. We can do this with
  the general `prune` command:

  ```sh
  git remote prune origin

  # or run a preview of the action

  git remote prune origin --dry-run
  ```

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

  # Stale branches and pruning

  A **stale branch** is a
  [remote tracking branch](Remote_tracking_branches.md) that **no
  longer tracks anything** because the actual branch on the remote has been
  deleted.

  If _you_ delete the remote branch, the remote tracking branch will also be
  deleted. However if a _collaborator_ deletes the remote branch, your remote
  tracking branch will remain, becoming a stale branch.

  In order to get rid of it, we need to manually remove it. We can do this with
  the general `prune` command:

  ```sh
  git remote prune origin

  # or run a preview of the action

  git remote prune origin --dry-run
  ```


path: zk/Step_functions_and_state_machines.md
sortable_path: zkStep_functions_and_state_machines.md
title: AWS Step functions and state machines
checksum: a290427d05b45787caf9f55ac711de8b9361af425c2875ebe09a2fa9ffe38ba2
created: 2024-03-30 09:03:21.203563401+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["AWS"]}
lead:
  _AWS Step Functions_ is an AWS service that allows you to coordinate distributed
  applications and microservices by creating workflows.

body:
  _AWS Step Functions_ is an AWS service that allows you to coordinate distributed
  applications and microservices by creating workflows.

  Workflows are made up of a series of steps and are known as **state machines**.
  A state machine defines:

  - the states of the workflow
  - the transitions between states
  - the inputs and outputs of each state

  State machines are defined via a JSON object which specifies the states and the
  transitions between them. It also includes error catchers and retry logic.

  At the beginning you define a `StartAt` state which is the entrypoint of the
  state machine. This can be manually triggered, or more likely, triggered by
  another AWS service such as a
  [Lambda](Lambda_programming_model.md), an
  [API Gateway](AWS_API_Gateway.md) request or a messaging/queue
  event.

  The state machine ultimately ends at an end state. In between are various
  intermediary states which can include:

  - Task states (do something)
  - Choice states (do something based on conditions)
  - Wait states
  - Success states (if preceding state succeeds do X)
  - Fail states (if preceding state fails do Y)
  - Parallel states

  The diagram below shows the definition of a given state machine. On the left is
  the JSON specification. On the right is a diagramatic representation that shows
  the control flow and all possible steps that comprise a given pathway / state:

  ![](/img/state-machine-definition.png)

  For each execution of the state machine (each time it is triggered) you can
  review the runtime. The flow diagram will highlight green to show the given
  pathway, and you can also see the inputs and outputs for each step and any
  errors:

  ![](/img/state-machine-execution.png)

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

  # AWS Step functions and state machines

  _AWS Step Functions_ is an AWS service that allows you to coordinate distributed
  applications and microservices by creating workflows.

  Workflows are made up of a series of steps and are known as **state machines**.
  A state machine defines:

  - the states of the workflow
  - the transitions between states
  - the inputs and outputs of each state

  State machines are defined via a JSON object which specifies the states and the
  transitions between them. It also includes error catchers and retry logic.

  At the beginning you define a `StartAt` state which is the entrypoint of the
  state machine. This can be manually triggered, or more likely, triggered by
  another AWS service such as a
  [Lambda](Lambda_programming_model.md), an
  [API Gateway](AWS_API_Gateway.md) request or a messaging/queue
  event.

  The state machine ultimately ends at an end state. In between are various
  intermediary states which can include:

  - Task states (do something)
  - Choice states (do something based on conditions)
  - Wait states
  - Success states (if preceding state succeeds do X)
  - Fail states (if preceding state fails do Y)
  - Parallel states

  The diagram below shows the definition of a given state machine. On the left is
  the JSON specification. On the right is a diagramatic representation that shows
  the control flow and all possible steps that comprise a given pathway / state:

  ![](/img/state-machine-definition.png)

  For each execution of the state machine (each time it is triggered) you can
  review the runtime. The flow diagram will highlight green to show the given
  pathway, and you can also see the inputs and outputs for each step and any
  errors:

  ![](/img/state-machine-execution.png)


path: zk/Strategies_for_constructing_proofs.md
sortable_path: zkStrategies_for_constructing_proofs.md
title: Strategies for constructing proofs in propositional logic
lead: ## General strategy
checksum: d836e2a38a2df56b856a2642dd68e59a49bec4680cf6d9dae8c194f2536358c0
created: 2024-03-30 09:03:21.227368981+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  ## General strategy

  - Break complex propositions into simpler sentences by using the elimination
    rules
  - Recombine simple propositions into complex propositions using the introduction
    rules.

  ## Goal analysis

  The approach above describes the general form of a proof but of course it will
  not always work and there will be cases where the route to the desired
  derivation is more circuitous. In these instances it is to best to combine this
  general top level strategy with goal analysis.

  Goal analysis is a [recursive](Recursion.md) strategy which
  proceeds by using a 'goal' proposition to guide the construction of intermediary
  derivations.

  Assume that we want to show that an argument is
  [valid](Validity_and_entailment.md#validity). Then our
  ultimate goal is to derive the conclusion from the premises we are given. We
  first ask ourselves: _which propositions if we could derive them, would allow us
  to easily derive the conclusion_? (For example, these propositions might be two
  simple propositions that when combined with
  [Conjunction Introduction](Conjunction_Introduction.md) give us
  the conclusion.) Deriving these propositions then becomes the new intermediate
  goal.

  If arriving at these propositions is not trivial, we then ask ourselves the
  question again: _which propositions would permit us to derive the intermediary
  propositions we need_? You keep working back in this manner until you reach a
  base level. Then it is just a matter or working upwards from each set of derived
  intermediary propositions until you reach the ultimate goal.

  ### Demonstration

  Let's say we want to prove $(L \lor A) \land D$ from the propositions $\lnot N$
  and $((\lnot N \rightarrow L) \land (D \leftrightarrow \lnot N))$.

  First, we consider what is the easiest possible way of achieving the proposition
  $(L \lor A) \land D$. Clearly it is to separately derive each disjunct
  ($L \lor A$ and $D$) and then combine them with
  [Conjunction Introduction](Conditional_Introduction.md). This
  provides us with our first goal: to derive each of the separate conjuncts.

  Let's start with $D$: where does it occur in the assumptions? It occurs in the
  compound $(\lnot N \rightarrow L) \land (D \leftrightarrow \lnot N)$, but only
  in the first conjunct. We can get this simply but applying
  [Conjunction Elimination](Conjunction%20Elimination.md).

  So far we have:

  ![](/img/step1.png)

  Now we just need to get $D$ from the proposition at line 3. This is easy since
  we already have access to the consequent of the biconditional at line 1.
  Therefore we can apply
  [Biconditional Elimination](Biconditional_Elimination.md)) at line
  3 to get $D$. We are now halfway there:

  ![](/img/step2.png)

  Next we need to turn our attention to deriving $L \lor A$. How can we obtain $L$
  ? Well it is contained within the first conjunct of the assumption on line 2.
  Again, we can get this through the application of
  [Conjunction Elimination](Conjunction_Elimination.md). Now, how do
  we get $L$ from $(\lnot N \rightarrow L)$? Well, we already have the antecedent
  $\lnot N$ as an assumption on the first line, so we can use
  [Conditional Elimination](Conditional_Elimination.md) to derive
  $L$. These two steps give us:

  ![](/img/step3.png)

  Now we need to get from $L$ to $L \lor A$. This is really straightforward
  because by using
  [Disjunction Introduction](Disjunction_Introduction.md)) we can
  get from any sentence to a disjunction. Finally, having assembled all the
  constituent parts of the conjunction that is the conclusion, we can combine them
  with [Conjunction Introduction](Conjunction_Introduction.md) as we
  had planned at the outset.

  ![](/img/step4.png)

  ### A further example

  We will seek to prove the following:

  $$
  \{ \lnot L \leftrightarrow [X \land (\lnot S \lor B) ], (E \land C) \rightarrow \lnot L, (E \land R) \land C \} \vdash X \land (\lnot S \lor B)
  $$

  The requirements here could easily mislead us. We see that the target
  proposition is a conjunction so we might think that the best strategy is to seek
  to derive each conjunct and then combine them via
  [Conjunction Introduction](Conjunction_Introduction.md)).

  Actually, if we look more closely, there is a better approach. The target
  proposition is contained in the first premise as the consequent to the
  biconditional ($\lnot L \leftrightarrow [X \land (\lnot S \lor B)]$). A better
  approach is therefore to seek to derive the antecedent ($\lnot L$) and then use
  [Biconditional Elimination](Biconditional_Elimination.md) to
  extract the target sentence which is the consequent.

  ![proof.png](proof.png)

  ## Proving theorems

  When we are proving
  [theorems](Theorems_and_empty_sets.md#theorems-and-empty-sets)
  we do not have a set of assumptions to work from when constructing the proof. We
  must derive the target sentence from the 'empty set' which is the target
  sentence itself. It is therefore like a process of reverse engineering.

  ### Demonstration

  _Prove:_ $\vdash (U \land Y) \rightarrow [L \rightarrow (U \land L)]$

  Our strategy here is to identify the main connective in the proposition we want
  to derive (the material conditional). We then assume the antecedent and attempt
  to derive the consequent from it.

  ![](/img/theoremproof.png)

  ## A complex theorem proof

  _Prove_ $\vdash (\lnot A \lor \lnot B) \leftrightarrow \lnot(A \land B)$

  ![](/img/dsfdsfsdfwe.png)

  ### Walkthrough

  **Lines 1-12**

  - Our auxiliary goal is to prove
    $\lnot (A \lor B) \rightarrow \lnot (A \land B)$.

  - Our starting assumption is to a disjunction. Thus we can apply
    [Disjunction Elimination](Disjunction_Elimination.md) to show
    that our goal sentence $\lnot(A \land B)$ follows from each of the disjuncts
    ($\lnot A$ and $\lnot B$) in dedicated sub-proofs. If we can do this, we have
    the right to derive $\lnot (A \land B)$.

  - In both cases($\lnot A \vdash \lnot (A \land B)$) and
    ($\lnot B \vdash \lnot (A \land B)$ we require another sub-proof to reach the
    target as there is no easy path available. So we derive a negation from
    $A \land  B$ so that we can negate it as $\lnot (A \land B)$.

  - Having done this, we can discharge the
    [Disjunction Elimination](Disjunction_Elimination.md) sub-proofs
    and derive $\lnot (A \land B)$ from $\lnot A \lor \lnot B$

  **Lines 13-26**

  - Our auxiliary goal is to prove
    $\lnot (A \land B) \rightarrow \lnot A \lor  \lnot B$. This will require a
    different approach to the above because we are not working from a disjunction
    anymore, we have a negated conjunction.
  - We will do this by assuming the negation of what we want to prove
    ($\lnot (\lnot A \lor \lnot B)$) and then apply
    [Negation Elimination](Negation_Elimination.md) to get
    $\lnot A \lor \lnot B$.
  - This requires us to derive a contradiction. We get this on lines 23 and 24.
    This requires as previous steps that we have two sub-proofs that use
    [Negation Elimination](Negation_Elimination.md) to release $A$
    and $B$

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

  # Strategies for constructing proofs in propositional logic

  ## General strategy

  - Break complex propositions into simpler sentences by using the elimination
    rules
  - Recombine simple propositions into complex propositions using the introduction
    rules.

  ## Goal analysis

  The approach above describes the general form of a proof but of course it will
  not always work and there will be cases where the route to the desired
  derivation is more circuitous. In these instances it is to best to combine this
  general top level strategy with goal analysis.

  Goal analysis is a [recursive](Recursion.md) strategy which
  proceeds by using a 'goal' proposition to guide the construction of intermediary
  derivations.

  Assume that we want to show that an argument is
  [valid](Validity_and_entailment.md#validity). Then our
  ultimate goal is to derive the conclusion from the premises we are given. We
  first ask ourselves: _which propositions if we could derive them, would allow us
  to easily derive the conclusion_? (For example, these propositions might be two
  simple propositions that when combined with
  [Conjunction Introduction](Conjunction_Introduction.md) give us
  the conclusion.) Deriving these propositions then becomes the new intermediate
  goal.

  If arriving at these propositions is not trivial, we then ask ourselves the
  question again: _which propositions would permit us to derive the intermediary
  propositions we need_? You keep working back in this manner until you reach a
  base level. Then it is just a matter or working upwards from each set of derived
  intermediary propositions until you reach the ultimate goal.

  ### Demonstration

  Let's say we want to prove $(L \lor A) \land D$ from the propositions $\lnot N$
  and $((\lnot N \rightarrow L) \land (D \leftrightarrow \lnot N))$.

  First, we consider what is the easiest possible way of achieving the proposition
  $(L \lor A) \land D$. Clearly it is to separately derive each disjunct
  ($L \lor A$ and $D$) and then combine them with
  [Conjunction Introduction](Conditional_Introduction.md). This
  provides us with our first goal: to derive each of the separate conjuncts.

  Let's start with $D$: where does it occur in the assumptions? It occurs in the
  compound $(\lnot N \rightarrow L) \land (D \leftrightarrow \lnot N)$, but only
  in the first conjunct. We can get this simply but applying
  [Conjunction Elimination](Conjunction%20Elimination.md).

  So far we have:

  ![](/img/step1.png)

  Now we just need to get $D$ from the proposition at line 3. This is easy since
  we already have access to the consequent of the biconditional at line 1.
  Therefore we can apply
  [Biconditional Elimination](Biconditional_Elimination.md)) at line
  3 to get $D$. We are now halfway there:

  ![](/img/step2.png)

  Next we need to turn our attention to deriving $L \lor A$. How can we obtain $L$
  ? Well it is contained within the first conjunct of the assumption on line 2.
  Again, we can get this through the application of
  [Conjunction Elimination](Conjunction_Elimination.md). Now, how do
  we get $L$ from $(\lnot N \rightarrow L)$? Well, we already have the antecedent
  $\lnot N$ as an assumption on the first line, so we can use
  [Conditional Elimination](Conditional_Elimination.md) to derive
  $L$. These two steps give us:

  ![](/img/step3.png)

  Now we need to get from $L$ to $L \lor A$. This is really straightforward
  because by using
  [Disjunction Introduction](Disjunction_Introduction.md)) we can
  get from any sentence to a disjunction. Finally, having assembled all the
  constituent parts of the conjunction that is the conclusion, we can combine them
  with [Conjunction Introduction](Conjunction_Introduction.md) as we
  had planned at the outset.

  ![](/img/step4.png)

  ### A further example

  We will seek to prove the following:

  $$
  \{ \lnot L \leftrightarrow [X \land (\lnot S \lor B) ], (E \land C) \rightarrow \lnot L, (E \land R) \land C \} \vdash X \land (\lnot S \lor B)
  $$

  The requirements here could easily mislead us. We see that the target
  proposition is a conjunction so we might think that the best strategy is to seek
  to derive each conjunct and then combine them via
  [Conjunction Introduction](Conjunction_Introduction.md)).

  Actually, if we look more closely, there is a better approach. The target
  proposition is contained in the first premise as the consequent to the
  biconditional ($\lnot L \leftrightarrow [X \land (\lnot S \lor B)]$). A better
  approach is therefore to seek to derive the antecedent ($\lnot L$) and then use
  [Biconditional Elimination](Biconditional_Elimination.md) to
  extract the target sentence which is the consequent.

  ![proof.png](proof.png)

  ## Proving theorems

  When we are proving
  [theorems](Theorems_and_empty_sets.md#theorems-and-empty-sets)
  we do not have a set of assumptions to work from when constructing the proof. We
  must derive the target sentence from the 'empty set' which is the target
  sentence itself. It is therefore like a process of reverse engineering.

  ### Demonstration

  _Prove:_ $\vdash (U \land Y) \rightarrow [L \rightarrow (U \land L)]$

  Our strategy here is to identify the main connective in the proposition we want
  to derive (the material conditional). We then assume the antecedent and attempt
  to derive the consequent from it.

  ![](/img/theoremproof.png)

  ## A complex theorem proof

  _Prove_ $\vdash (\lnot A \lor \lnot B) \leftrightarrow \lnot(A \land B)$

  ![](/img/dsfdsfsdfwe.png)

  ### Walkthrough

  **Lines 1-12**

  - Our auxiliary goal is to prove
    $\lnot (A \lor B) \rightarrow \lnot (A \land B)$.

  - Our starting assumption is to a disjunction. Thus we can apply
    [Disjunction Elimination](Disjunction_Elimination.md) to show
    that our goal sentence $\lnot(A \land B)$ follows from each of the disjuncts
    ($\lnot A$ and $\lnot B$) in dedicated sub-proofs. If we can do this, we have
    the right to derive $\lnot (A \land B)$.

  - In both cases($\lnot A \vdash \lnot (A \land B)$) and
    ($\lnot B \vdash \lnot (A \land B)$ we require another sub-proof to reach the
    target as there is no easy path available. So we derive a negation from
    $A \land  B$ so that we can negate it as $\lnot (A \land B)$.

  - Having done this, we can discharge the
    [Disjunction Elimination](Disjunction_Elimination.md) sub-proofs
    and derive $\lnot (A \land B)$ from $\lnot A \lor \lnot B$

  **Lines 13-26**

  - Our auxiliary goal is to prove
    $\lnot (A \land B) \rightarrow \lnot A \lor  \lnot B$. This will require a
    different approach to the above because we are not working from a disjunction
    anymore, we have a negated conjunction.
  - We will do this by assuming the negation of what we want to prove
    ($\lnot (\lnot A \lor \lnot B)$) and then apply
    [Negation Elimination](Negation_Elimination.md) to get
    $\lnot A \lor \lnot B$.
  - This requires us to derive a contradiction. We get this on lines 23 and 24.
    This requires as previous steps that we have two sub-proofs that use
    [Negation Elimination](Negation_Elimination.md) to release $A$
    and $B$


path: zk/Strings_in_Python.md
sortable_path: zkStrings_in_Python.md
title: Strings in Python
checksum: 3281a0dcc11d012d63621b70be662326f2a19865f9e34b84f32e7679bff270a7
created: 2024-03-30 09:03:21.329698316+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["python","data-types"]}
lead:
  > Generally, anything that changes a string will be a method on the `str` class,
  > rather than a built-in function like `len()`, as such it will use dot notation

body:
  > Generally, anything that changes a string will be a method on the `str` class,
  > rather than a built-in function like `len()`, as such it will use dot notation

  - Strings are **immutable**: string operations produce a new string.

  ```python
  # Working with Strings

  my_variable = 'Bob'
  print(my_variable)
  # Bob

  my_variable = "Eloise"
  print(my_variable)
  # Eloise

  # A multi line string
  my_variable = """
  Hello
    World
  """
  print(my_variable)
  """
  Hello
    World
  """

  my_string = 'Hello World'
  print(len(my_string))
  # 11

  string_1 = 'Good'
  string_2 = " day"
  string_3 = string_1 + string_2
  print(string_3)
  # Good day

  msg = 'Hello Lloyd you are ' + str(21)
  print(msg)
  # Hello Lloyd you are 21

  # Range of String operations
  msg = 'Hello World'
  print(msg.replace("Hello", "Goodbye"))
  # Goodbye World

  print('Edward Alan Rawlings'.find('Alan'))
  # 7

  print('Edward John Rawlings'.find('Alan'))
  # -1

  print('James' == 'James') # prints True
  print('James' != 'John') # prints True

  print("msg.startswith('H')", msg.startswith('H'))
  # msg.startswith('H') True

  print("msg.endswith('d')", msg.endswith('d'))
  # msg.endswith('d') TRUE

  print('some_string.upper()', msg.upper())
  # some_string.upper() HELLO WORLD

  print('sub string: ', 'Hello-World'[1:5])
  # sub string: ello

  # String interpolation
  user_age = input("Please enter your age: ")
  print(f'You are {user_age}')
  ```

  ## `str.split()`

  The `split()` function in Python is used to divide a string into multiple parts
  at the occurrence of a given separator. This function returns a
  [list](Lists_in_Python.md) of substrings.

  ### General syntax

  ```py
  str.split(separator, maxsplit)
  ```

  - The `separator` is optional. It specifies the separator to use when splitting
    the string. If no separator is provided, it will default to using whitespace.

  - `maxsplit` is also optional and specifies how many splits to do. Default value
    is -1, which is "all occurrences".

  ### Examples

  ```py

  text = "Hello world, how are you?"

  # Default usage:
  x = text.split()
  print(x)
  # ['Hello', 'world', 'how', 'are', 'you?']

  # Using a specific separator
  x = text.split(",")
  print(x)
  # ['Hello', 'world how are you?']

  # Specifiying a maxplit value
  x = text.split(" ", 1)
  print(x)
  # ['Hello']
  ```

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

  # Strings in Python

  > Generally, anything that changes a string will be a method on the `str` class,
  > rather than a built-in function like `len()`, as such it will use dot notation

  - Strings are **immutable**: string operations produce a new string.

  ```python
  # Working with Strings

  my_variable = 'Bob'
  print(my_variable)
  # Bob

  my_variable = "Eloise"
  print(my_variable)
  # Eloise

  # A multi line string
  my_variable = """
  Hello
    World
  """
  print(my_variable)
  """
  Hello
    World
  """

  my_string = 'Hello World'
  print(len(my_string))
  # 11

  string_1 = 'Good'
  string_2 = " day"
  string_3 = string_1 + string_2
  print(string_3)
  # Good day

  msg = 'Hello Lloyd you are ' + str(21)
  print(msg)
  # Hello Lloyd you are 21

  # Range of String operations
  msg = 'Hello World'
  print(msg.replace("Hello", "Goodbye"))
  # Goodbye World

  print('Edward Alan Rawlings'.find('Alan'))
  # 7

  print('Edward John Rawlings'.find('Alan'))
  # -1

  print('James' == 'James') # prints True
  print('James' != 'John') # prints True

  print("msg.startswith('H')", msg.startswith('H'))
  # msg.startswith('H') True

  print("msg.endswith('d')", msg.endswith('d'))
  # msg.endswith('d') TRUE

  print('some_string.upper()', msg.upper())
  # some_string.upper() HELLO WORLD

  print('sub string: ', 'Hello-World'[1:5])
  # sub string: ello

  # String interpolation
  user_age = input("Please enter your age: ")
  print(f'You are {user_age}')
  ```

  ## `str.split()`

  The `split()` function in Python is used to divide a string into multiple parts
  at the occurrence of a given separator. This function returns a
  [list](Lists_in_Python.md) of substrings.

  ### General syntax

  ```py
  str.split(separator, maxsplit)
  ```

  - The `separator` is optional. It specifies the separator to use when splitting
    the string. If no separator is provided, it will default to using whitespace.

  - `maxsplit` is also optional and specifies how many splits to do. Default value
    is -1, which is "all occurrences".

  ### Examples

  ```py

  text = "Hello world, how are you?"

  # Default usage:
  x = text.split()
  print(x)
  # ['Hello', 'world', 'how', 'are', 'you?']

  # Using a specific separator
  x = text.split(",")
  print(x)
  # ['Hello', 'world how are you?']

  # Specifiying a maxplit value
  x = text.split(" ", 1)
  print(x)
  # ['Hello']
  ```


path: zk/Structuring_Express_applications.md
sortable_path: zkStructuring_Express_applications.md
title: Structuring Express applications
lead: In general, the following application structure works well:
checksum: 7a70ba15689a6e7e95f95149fa25048890b62d3003d249060f54bf4a76b99b6d
created: 2024-03-30 09:03:21.345088468+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["node-js","REST","APIs"]}
body:
  In general, the following application structure works well:

  ```
  \
      routes\
      models\
      middlewear\
      config\
      public\
    index.js

  ```

  - **`routes/`**

    - This should house all our RESTful API endpoints for a given resource which
      we define as a module. For instance if we are handling API requests to
      `/api/courses/` we would have a module at `/routes/courses.js`

  - **`models/`**
    - Here we define our database schemas: definitions for the structure of the
      data objects we want to return from the database.
    - In the case of Mongo we would have a file for each of our Mongoose models
      which will belong to an overall collection of which there are document
      instances.
    - Our model files should map onto our route modules; there should generally be
      at least one model file for each module. The model stands as an abstract
      representation of the data that we will be handling with our request paths
      in the routes file.

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

  # Structuring Express applications

  In general, the following application structure works well:

  ```
  \
      routes\
      models\
      middlewear\
      config\
      public\
    index.js

  ```

  - **`routes/`**

    - This should house all our RESTful API endpoints for a given resource which
      we define as a module. For instance if we are handling API requests to
      `/api/courses/` we would have a module at `/routes/courses.js`

  - **`models/`**
    - Here we define our database schemas: definitions for the structure of the
      data objects we want to return from the database.
    - In the case of Mongo we would have a file for each of our Mongoose models
      which will belong to an overall collection of which there are document
      instances.
    - Our model files should map onto our route modules; there should generally be
      at least one model file for each module. The model stands as an abstract
      representation of the data that we will be handling with our request paths
      in the routes file.


path: zk/Swap_space.md
sortable_path: zkSwap_space.md
title: Swap space
checksum: 97e8698380c0d8084842f783b7b5347d4c22bff686d8e8150f37e223ec3d98d8
created: 2024-03-30 09:03:21.353806715+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["memory","disks","Linux","operating-systems"]}
lead:
  A swap partition is a partition on a disk that is not intended to be used as a
  filesystem. Instead, it is a part of the disk that is used to augment the main
  memory.

body:
  A swap partition is a partition on a disk that is not intended to be used as a
  filesystem. Instead, it is a part of the disk that is used to augment the main
  memory.

  If you run out of memory and have set up a swap partition, the OS will be able
  to move pieces of memory to and from disk storage. This is called _swapping_
  because pieces of idle programs are swapped to the disk in exchange for active
  pieces residing on the disk.

  ## View current swap usage

  If you have a swap space established, the command `free` will show current
  usage:

  ```bash
  free
                 total        used        free      shared  buff/cache   available
  Mem:        16099420     3031572    10157652     1153144     2910196    11605820
  Swap:        3145724           0     3145724
  ```

  ## Create a swap partition

  To use an existing disk partition as a swap you can run the command
  `mkswap [device]` and then `swapon [device]` to register the space with the
  [kernel](The_kernel.md).

  ### Add to `fstab`

  You will want the swap to be activated every time the OS boots so add the
  following line to the [fstab](Filesystems.md#fstab),
  where `/sda3` is used as the example partition:

  ```bash
  /dev/sda3e none swap sw 0 0
  ```

  ## Create a swap file

  // Add info here

raw_content:
  ---
  tags:
    - memory
    - disks
    - Linux
    - operating-systems
  ---

  # Swap space

  A swap partition is a partition on a disk that is not intended to be used as a
  filesystem. Instead, it is a part of the disk that is used to augment the main
  memory.

  If you run out of memory and have set up a swap partition, the OS will be able
  to move pieces of memory to and from disk storage. This is called _swapping_
  because pieces of idle programs are swapped to the disk in exchange for active
  pieces residing on the disk.

  ## View current swap usage

  If you have a swap space established, the command `free` will show current
  usage:

  ```bash
  free
                 total        used        free      shared  buff/cache   available
  Mem:        16099420     3031572    10157652     1153144     2910196    11605820
  Swap:        3145724           0     3145724
  ```

  ## Create a swap partition

  To use an existing disk partition as a swap you can run the command
  `mkswap [device]` and then `swapon [device]` to register the space with the
  [kernel](The_kernel.md).

  ### Add to `fstab`

  You will want the swap to be activated every time the OS boots so add the
  following line to the [fstab](Filesystems.md#fstab),
  where `/sda3` is used as the example partition:

  ```bash
  /dev/sda3e none swap sw 0 0
  ```

  ## Create a swap file

  // Add info here


path: zk/Syllogism.md
sortable_path: zkSyllogism.md
title: Syllogism
checksum: efa3d071823652b3ce7959d58ea9a4b400250d7f5cb9fe963479e825295704cd
created: 2024-03-30 09:03:21.374253335+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  In order to make assertions about the relative
  [consistency](Logical_consistency.md) or inconsistency
  of a set of propositions we advance arguments. Consider everyday life: if we are
  having an argument with someone, we believe that they are wrong. A more logical
  way to say this is that we believe that their beliefs are inconsistent. In order
  to change their viewpoint or point out why they are wrong we advance an argument
  intended to show that belief A conflicts with belief B. Or if C is true, then
  you cannot believe that D.

body:
  In order to make assertions about the relative
  [consistency](Logical_consistency.md) or inconsistency
  of a set of propositions we advance arguments. Consider everyday life: if we are
  having an argument with someone, we believe that they are wrong. A more logical
  way to say this is that we believe that their beliefs are inconsistent. In order
  to change their viewpoint or point out why they are wrong we advance an argument
  intended to show that belief A conflicts with belief B. Or if C is true, then
  you cannot believe that D.

  In formal terms **an argument is a set of propositions comprising one or more
  premises and a conclusion. The conclusion is taken to be supported by the
  premises.**

  > The terms **argument** and **syllogism** are used interchangeably in logic to
  > describe the above feature of a set of propositions.

  ### Demonstration

  ```
  (P1) All men are mortal.
  (P2) Socrates is a man.
  _____________________
  (C) Socrates is mortal
  ```

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

  # Syllogism

  In order to make assertions about the relative
  [consistency](Logical_consistency.md) or inconsistency
  of a set of propositions we advance arguments. Consider everyday life: if we are
  having an argument with someone, we believe that they are wrong. A more logical
  way to say this is that we believe that their beliefs are inconsistent. In order
  to change their viewpoint or point out why they are wrong we advance an argument
  intended to show that belief A conflicts with belief B. Or if C is true, then
  you cannot believe that D.

  In formal terms **an argument is a set of propositions comprising one or more
  premises and a conclusion. The conclusion is taken to be supported by the
  premises.**

  > The terms **argument** and **syllogism** are used interchangeably in logic to
  > describe the above feature of a set of propositions.

  ### Demonstration

  ```
  (P1) All men are mortal.
  (P2) Socrates is a man.
  _____________________
  (C) Socrates is mortal
  ```


path: zk/Symlinks.md
sortable_path: zkSymlinks.md
title: Syntax
lead:
  ```
  ln -s -f ~/[existing_file] ~/.[file_you_want_to_symbolise]
  ```

checksum: 4b58c5eb0129339e5fe3e417845dbf94a8b384f545a3b991c3e52bc6659425ca
created: 2024-03-30 09:03:21.384507293+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["shell","Linux","procedural"]}
body:
  ```
  ln -s -f ~/[existing_file] ~/.[file_you_want_to_symbolise]
  ```

  Real example:

  ```
  ln -s -f ~/dotfiles/.vimrc ~/.vimrc
  ```

raw_content:
  ---
  tags:
    - shell
    - Linux
    - procedural
  ---

  > A symbolic link, also termed a soft link, is a special kind of file that
  > points to another file. Unlike a hard link, a symbolic link does not contain
  > the data in the target file. It simply points to another entry somewhere in
  > the file system.

  # Syntax

  ```
  ln -s -f ~/[existing_file] ~/.[file_you_want_to_symbolise]
  ```

  Real example:

  ```
  ln -s -f ~/dotfiles/.vimrc ~/.vimrc
  ```


path: zk/Syntax_of_propositional_logic.md
sortable_path: zkSyntax_of_propositional_logic.md
title: Syntax of propositional logic
lead: ## Syntax of formal languages versus semantics
checksum: 62edccda4593be282121ee3b522e1dc1bd9a989fa7475c32470c54ea34730039
created: 2024-03-30 09:03:21.390639601+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  ## Syntax of formal languages versus semantics

  > The syntactical study of a language is the study of the expressions of the
  > language and the relations among them _without regard_ to the possible
  > interpretations or 'meaning' of these expressions.

  Syntax is talking about the order and placement of propositions relative to
  connectives and what constitutes a well-formed expression in these terms.
  Semantics is about what the connectives mean, in other words: truth-functions
  and truth-values and not just placement and order.

  ## Formal specification of the syntax of the language of Sentential Logic

  ### Vocabulary

  Propositions in SL are capitalised Roman letters (non-bold) with or without
  natural number subscripts. We may call these proposition letters. For example:

  $$
    P, Q, R,... P_{1}, Q_{1}, R_{1}, ...
  $$

  The connectives of SL are the five truth-functional connectives:

  $$
    \lnot, \land, \lor, \rightarrow, \leftrightarrow
  $$

  The punctuation marks of SL consist in the left and right parentheses:

  $$
    ( )
  $$

  ### Grammar

  1. Every letter in a statement is a proposition.
  2. If $P$ is a proposition then $\lnot P$ is a proposition.
  3. If $P$ and $Q$ are propositions, then $P \land Q$ is a proposition
  4. If $P$ and $Q$ are propositions, then $P \lor Q$ is a proposition
  5. If $P$ and $Q$ are propositions, then $P \rightarrow Q$ is a proposition
  6. If $P$ and $Q$ are propositions, then $P \leftrightarrow Q$ is a proposition
  7. Nothing is a proposition unless it can be formed by repeated application of
     rules 1-6

  ### Additional syntactic concepts

  We also distinguish:

  - the **main connective**
  - **immediate sentential components**
  - **sentential components**
  - **atomic components**

  These definitions provide a formal specification of the concepts of
  [atomic and molecular propositions](Atomic_and_molecular_propositions.md)
  introduced previously.

  1. If $P$ is an atomic proposition, $P$ contains no connectives and hence does
     not have a main connective. $P$ has no immediate propositional components.
  1. If $P$ is of the form $\lnot Q$ where $Q$ is a proposition, then the main
     connective of $P$ is the negation symbol that occurs before $Q$ and $Q$ is
     the immediate propositional component of $P$
  1. If P is of the form:
     1. $Q \land R$
     1. $Q \lor R$
     1. $Q \rightarrow R$
     1. $Q \leftrightarrow R$

  where $Q$ and $R$ are propositions, then the main connective of $P$ is the
  connective that occurs between $Q$ and $R$ and $Q$ and $R$ are the immediate
  propositional components of $P$.

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

  # Syntax of propositional logic

  ## Syntax of formal languages versus semantics

  > The syntactical study of a language is the study of the expressions of the
  > language and the relations among them _without regard_ to the possible
  > interpretations or 'meaning' of these expressions.

  Syntax is talking about the order and placement of propositions relative to
  connectives and what constitutes a well-formed expression in these terms.
  Semantics is about what the connectives mean, in other words: truth-functions
  and truth-values and not just placement and order.

  ## Formal specification of the syntax of the language of Sentential Logic

  ### Vocabulary

  Propositions in SL are capitalised Roman letters (non-bold) with or without
  natural number subscripts. We may call these proposition letters. For example:

  $$
    P, Q, R,... P_{1}, Q_{1}, R_{1}, ...
  $$

  The connectives of SL are the five truth-functional connectives:

  $$
    \lnot, \land, \lor, \rightarrow, \leftrightarrow
  $$

  The punctuation marks of SL consist in the left and right parentheses:

  $$
    ( )
  $$

  ### Grammar

  1. Every letter in a statement is a proposition.
  2. If $P$ is a proposition then $\lnot P$ is a proposition.
  3. If $P$ and $Q$ are propositions, then $P \land Q$ is a proposition
  4. If $P$ and $Q$ are propositions, then $P \lor Q$ is a proposition
  5. If $P$ and $Q$ are propositions, then $P \rightarrow Q$ is a proposition
  6. If $P$ and $Q$ are propositions, then $P \leftrightarrow Q$ is a proposition
  7. Nothing is a proposition unless it can be formed by repeated application of
     rules 1-6

  ### Additional syntactic concepts

  We also distinguish:

  - the **main connective**
  - **immediate sentential components**
  - **sentential components**
  - **atomic components**

  These definitions provide a formal specification of the concepts of
  [atomic and molecular propositions](Atomic_and_molecular_propositions.md)
  introduced previously.

  1. If $P$ is an atomic proposition, $P$ contains no connectives and hence does
     not have a main connective. $P$ has no immediate propositional components.
  1. If $P$ is of the form $\lnot Q$ where $Q$ is a proposition, then the main
     connective of $P$ is the negation symbol that occurs before $Q$ and $Q$ is
     the immediate propositional component of $P$
  1. If P is of the form:
     1. $Q \land R$
     1. $Q \lor R$
     1. $Q \rightarrow R$
     1. $Q \leftrightarrow R$

  where $Q$ and $R$ are propositions, then the main connective of $P$ is the
  connective that occurs between $Q$ and $R$ and $Q$ and $R$ are the immediate
  propositional components of $P$.


path: zk/Tags_in_Git.md
sortable_path: zkTags_in_Git.md
title: Tags in Git
checksum: b684f8ef44eb962d1c0262ba8934999fc798aa1d14d52b9dacc2cb0b680da031
created: 2024-03-30 09:03:21.399470534+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["git"]}
lead:
  Tags exist to reference specific commits in a more meaningful way than using a
  SHA reference. They are a **named reference to a commit**. As they refer to a
  singular commit they do not update but are a fixed point in the Git history.

body:
  Tags exist to reference specific commits in a more meaningful way than using a
  SHA reference. They are a **named reference to a commit**. As they refer to a
  singular commit they do not update but are a fixed point in the Git history.

  > Tags do not care about branches. They are an objective reference to a commit
  > regardless of which branch it occured on. You can reference a tag without
  > needing to be in the branch where the tagging originally took place.

  Tags are frequently used to mark software release versions. But they can also be
  used for other designations such as the addition of a key feature or bug.

  ## Creating a tag

  ```
  git tag -am "Version 1.0" v1.0 dd5c4539a0
  ```

  `am` is for "annotated + message". The actual tag name is `v1.0`

  Now you can refer to that commit with `v1.0` rather than use the SHA

  ## Deleting a tag

  ```
  git tag -d v1.0
  ```

  > When you delete the tag you are only deleting the tag reference not the commit
  > that it refers to.

  ## Listing tags

  ```
  git tag --list
  ```

  ## Examples of use

  ```
  git show v1.1

  git diff v1.0..v1.1
  ```

  ## Pushing tags to a remote

  In the examples so far, all the tags have been **local tags**. They only exist
  in our local workflow and are not accessible to our collaborators. If we do a
  `git push`, this doesn't transfer our local tags. You have to do:

  ```
  git push origin [tag-name]
  ```

  Push all tags:

  ```
  git push origin --tags

  # Delete remote tag

  git push -d origin [tag-name]
  ```

  ## Getting tags from the remote

  ```
  git fetch
  ```

  This automatically fetches tags too.

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

  # Tags in Git

  Tags exist to reference specific commits in a more meaningful way than using a
  SHA reference. They are a **named reference to a commit**. As they refer to a
  singular commit they do not update but are a fixed point in the Git history.

  > Tags do not care about branches. They are an objective reference to a commit
  > regardless of which branch it occured on. You can reference a tag without
  > needing to be in the branch where the tagging originally took place.

  Tags are frequently used to mark software release versions. But they can also be
  used for other designations such as the addition of a key feature or bug.

  ## Creating a tag

  ```
  git tag -am "Version 1.0" v1.0 dd5c4539a0
  ```

  `am` is for "annotated + message". The actual tag name is `v1.0`

  Now you can refer to that commit with `v1.0` rather than use the SHA

  ## Deleting a tag

  ```
  git tag -d v1.0
  ```

  > When you delete the tag you are only deleting the tag reference not the commit
  > that it refers to.

  ## Listing tags

  ```
  git tag --list
  ```

  ## Examples of use

  ```
  git show v1.1

  git diff v1.0..v1.1
  ```

  ## Pushing tags to a remote

  In the examples so far, all the tags have been **local tags**. They only exist
  in our local workflow and are not accessible to our collaborators. If we do a
  `git push`, this doesn't transfer our local tags. You have to do:

  ```
  git push origin [tag-name]
  ```

  Push all tags:

  ```
  git push origin --tags

  # Delete remote tag

  git push -d origin [tag-name]
  ```

  ## Getting tags from the remote

  ```
  git fetch
  ```

  This automatically fetches tags too.


path: zk/Test_values_in_Bash.md
sortable_path: zkTest_values_in_Bash.md
title: Test values in Bash
checksum: d30c66e930746ebe15a95780057d6e630825c2911fddc143375e875c159e3141
created: 2024-03-30 09:03:21.406143623+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["shell","regex"]}
lead:
  `test` is a built-in command that is used to compare values or determine whether
  something is the case.

body:
  `test` is a built-in command that is used to compare values or determine whether
  something is the case.

  We can use the command `test` but it is more common to test a condition
  implicity by using square brackets. The square brackets are an alias for `test`.
  We use this alias when we use `IF` logic.

  When we run a test the result we get back is a return status of a `0` or a `1`.
  `0` indicates that the test was a success and `1` means failure. (Bear in mind
  this is in contrast to most all other programming languages.)

  If we run a test in the command line we won't get a `0` or a `1` or back, there
  will just be silence from the shell. We can explicitly invoke the return value
  with variable `$?`, e.g:

  ```bash
  [ -d ~ ] # is the home directory a directory?
  echo $?
  0   # yes

  [ -d /bin/zsh ] # is this binary a directory?
  echo $
  1  # no
  ```

  ## Test structures

  Many tests can be run with flags as a shorthand like we saw above:

  ### File operators

  ```
  -a FILE        True if file exists.
  -d FILE        True if file is a directory.
  -e FILE        True if file exists.
  -h FILE        True if file is a symbolic link
  -s FILE        True if file exists and is not empty.
  -N FILE        True if the file has been modified since it was last read.
  ```

  ### String operators

  ```
  -z STRING      True if string is empty.
  -n STRING      True if string is not empty.
  ```

  ## Differences between comparing numbers and strings

  - `=` is reserved for comparing strings
  - For numbers we use, e.g, `[ 4 -lt 5 ]`

  ## Negation

  We can negate a test condition with `!`:

  ```bash
  [ ! 4 -lt 3 ]; echo $?
  0
  ```

  ## Running process if test succeeds or fails

  We can use the following structure to run a process if a test condition obtains:

  ```bash
  [[ -d ~ ]] && echo "/home is a directory"
  ```

  ## Extended test: `[[...]]`

  When we use **double brackets** we are using _extended_ `test`.

  The extended test supports the standard `test` comparisons and adds other
  features:

  - The use of Boolean operators:
    ```
    [[ -d ~ || -a /bin/mash ]]; echo $?
    ```

  ### Using regular expressions

  Extended test also allows us to use regular expressions as part of our test
  conditions. In order to test against a regular expression we use `=~` as the
  comparison operator.

  ```bash
  [[ "thomas" =~ t.* ]]; echo $?
  ```

  Here the test succeeds because "thomas" begins with "t" followed by any other
  character.

  ## Further examples

  **Test if a character exists in two strings**

  ```bash
  string1="hello"
  string2="world"
  char="l"

  if [[ "$string1" == *"$char"* ]] && [[ "$string2" == *"$char"* ]]; then
      echo "Character '$char' exists in both strings."
  else
      echo "Character '$char' does not exist in both strings."
  fi
  ```

  > Note: this syntax can also be used to test if a given element exists in an
  > [array](Lists_and_arrays_in_Bash.md).

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

  # Test values in Bash

  `test` is a built-in command that is used to compare values or determine whether
  something is the case.

  We can use the command `test` but it is more common to test a condition
  implicity by using square brackets. The square brackets are an alias for `test`.
  We use this alias when we use `IF` logic.

  When we run a test the result we get back is a return status of a `0` or a `1`.
  `0` indicates that the test was a success and `1` means failure. (Bear in mind
  this is in contrast to most all other programming languages.)

  If we run a test in the command line we won't get a `0` or a `1` or back, there
  will just be silence from the shell. We can explicitly invoke the return value
  with variable `$?`, e.g:

  ```bash
  [ -d ~ ] # is the home directory a directory?
  echo $?
  0   # yes

  [ -d /bin/zsh ] # is this binary a directory?
  echo $
  1  # no
  ```

  ## Test structures

  Many tests can be run with flags as a shorthand like we saw above:

  ### File operators

  ```
  -a FILE        True if file exists.
  -d FILE        True if file is a directory.
  -e FILE        True if file exists.
  -h FILE        True if file is a symbolic link
  -s FILE        True if file exists and is not empty.
  -N FILE        True if the file has been modified since it was last read.
  ```

  ### String operators

  ```
  -z STRING      True if string is empty.
  -n STRING      True if string is not empty.
  ```

  ## Differences between comparing numbers and strings

  - `=` is reserved for comparing strings
  - For numbers we use, e.g, `[ 4 -lt 5 ]`

  ## Negation

  We can negate a test condition with `!`:

  ```bash
  [ ! 4 -lt 3 ]; echo $?
  0
  ```

  ## Running process if test succeeds or fails

  We can use the following structure to run a process if a test condition obtains:

  ```bash
  [[ -d ~ ]] && echo "/home is a directory"
  ```

  ## Extended test: `[[...]]`

  When we use **double brackets** we are using _extended_ `test`.

  The extended test supports the standard `test` comparisons and adds other
  features:

  - The use of Boolean operators:
    ```
    [[ -d ~ || -a /bin/mash ]]; echo $?
    ```

  ### Using regular expressions

  Extended test also allows us to use regular expressions as part of our test
  conditions. In order to test against a regular expression we use `=~` as the
  comparison operator.

  ```bash
  [[ "thomas" =~ t.* ]]; echo $?
  ```

  Here the test succeeds because "thomas" begins with "t" followed by any other
  character.

  ## Further examples

  **Test if a character exists in two strings**

  ```bash
  string1="hello"
  string2="world"
  char="l"

  if [[ "$string1" == *"$char"* ]] && [[ "$string2" == *"$char"* ]]; then
      echo "Character '$char' exists in both strings."
  else
      echo "Character '$char' does not exist in both strings."
  fi
  ```

  > Note: this syntax can also be used to test if a given element exists in an
  > [array](Lists_and_arrays_in_Bash.md).


path: zk/Testing_Python_code.md
sortable_path: zkTesting_Python_code.md
title: Testing Python code
lead: ## `pytest`
checksum: f21ee764b3d4c67afa18fdc25fbee482906dd1434c85f34660de4f8726cbaeab
created: 2024-03-30 09:03:21.415795229+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["python","testing"]}
body:
  ## `pytest`

  Pytest is the most popular testing library for Python. It is not included with
  the Python standard library so it must be installed with
  [pip](Python_package_management.md).
  While it does not include a declaration library, it is robust enough to handle
  most scenarios having a rich and expressive set of constructs and decorators
  that let you declare what your tests should do, under what conditions they
  should run, and how they should interact with the rest of your system.

  ### Using `pytest`

  - Pytest will automatically detect test files so long as they are named
    appropriately. E.g. for a module called `lorem`, it will detzect the unit test
    files `lorem_test.py` and `test_lorem.py`.
  - In order to detect tests it should be run from a directory level above them

  ### Examples

  Here is a basic example of using pytest for a local module callled `palindrome`:

  ```py
  import palindrome

  def test_is_palindrome():
      assert palindrome.is_palindrome('soros')
      assert palindrome.is_palindrome('torot')
      assert not palindrome.is_palindrome('chair')
  ```

  ## Mocking

  `patch()` and `Mock` enable us to mock objects whilst testing (classes,
  functions, methods and properties belonging). They are used in combination.

  The `@patch` decorator temporarily replaces a specified object in your code with
  a mock object and restores the original object after the test is complete

  A Mock object simulates the object it replaces so that the object behaves as
  expected during testing. For example, if your code calls
  `some_function.some_method()`, and `some_method` is mocked, calling
  `some_method` will not execute real logic but will interact with the Mock object
  instead. Mock objects record details about how they have been used, like what
  methods have been called, with what arguments, etc., allowing you to make
  assertions about how they have been used.

  > `@patch` and `Mock` work together because a patch is used to replace an object
  > or attribute with a Mock object. `Mock` handles the simulated functionality,
  > and `@patch` designates the real value we are replacing with the mock.

  ### Example case

  I will use the following example from one my projects:

  ```py
  # get_articles.py
  def get_articles(article_type: str) -> Optional[Dict[str, Any]]:
      """Retrieve articles from Pocket API"""

      if POCKET_LAMBDA_ENDPOINT is None:
          logging.error("Error: POCKET_LAMBDA_ENDPOINT envinronment variable is not set")
          return None
      else:
          # Interpolate the article_type into the Pocket request URL
          endpoint = POCKET_LAMBDA_ENDPOINT.format(article_type=article_type)

      try:
          response = requests.get(endpoint)
          response.raise_for_status()
          return response.json()

      except RequestException as e:
          print(f"An error occurred: {e}")
          return None
  ```

  This function: sources a URL from an environment variable, interpolates a query
  string into the URL (which comes in as a parameter), makes a request to the URL,
  and returns the response as JSON.

  It has some safeguards in place:

  - It checks that the environment variable is set
  - It checks that the request was successful

  In the example we could use a Mock object to simulate the response from the
  Pocket API. This would allow us to test the function without having to make a
  real request to the API:

  ```py
  def test_successful_request():
      # Replace the requests.get function with a Mock object (mock_get)
      with patch("requests.get") as mock_get:
          # Specify the return value of the mock_get object)
          mock_get.return_value = Mock(ok=true)
          mock_get.return_value.json.return_value = {"value": "test"}

          # Call the function under test
          result = get_articles("gaby")

      # Assert expected outcomes
      mock_get.assert_called_once_with(endpoint)
      assert result == mock_json_response
  ```

  The example above follows the **Arrange, Act, Assert** pattern:

  | Stage   | Action                                                                          |
  | ------- | ------------------------------------------------------------------------------- |
  | Arrange | Replace the `requests.get` function with `patch` and set properties with `Mock` |
  | Act     | Call the function under test                                                    |
  | Assert  | Assert that the function under test behaved as expected                         |

  ### Alternative mock syntax

  The `with patch(...) as mock_name` syntax is fine for small-scale mocking but
  can become cumbersome when you are mocking several dependencies.

  There is another syntax (which I actually find clearer). Say we have a function
  with three dependencies: `update_worksheet`, `process_articles`, `get_articles`.
  We could mock like so:

  ```py
  @patch("app.update_worksheet")
  @patch("app.process_articles")
  @patch("app.get_articles")
  def test_success(
      mock_get_articles, mock_process_articles, mock_update_worksheet
  ):
      mock_get_articles.return_value = [1, 2, 3]
  ```

  Here the patching is done by the decorator and the mocks are defined as
  parameters to the test function (always in reverse order)

  ### Mock assertion lexicon

  #### `return_value`

  State what the mock should return

  ```py
  my_mocked_function.return_value = ['one', 'two', 'three']
  ```

  #### `call_count`

  Test how many times a dependent function is called

  ```py
  assert my_mocked_function.call_count = 3
  ```

  #### `assert_any_call()`

  Test that a given mock is called at least once during the execution of the
  function under test

  ```py
  my_mocked_function.assert_any_call(some_mocked_return_value)
  ```

  When the output of one function is used as a parameter to another, and we don't
  particularly care about the details of what is concerned we can just pass the
  executed function, e.g:

  ```py
  my_mocked_function.assert_any_call(preceding_function())
  ```

  #### `call_args_list`

  Get a list of all the arguments that a mock object was called with during the
  test.

  `call_args_list` is useful when you want to check the arguments that a mock
  object was called with during the test, especially if the mock object was called
  multiple times with different arguments. You can use it to inspect the arguments
  of each call and make assertions based on them.

  ```py
  second_my_mocked_function_call = my_mocked_function.call_args_list[1]

  # check the first argument of the second call:

  assert second_my_mocked_function_call[0][0] == "expected arg"
  ```

  #### `side_effect`

  Use to trigger a side effect when returning a value from a mock. Most useful for
  mocking exceptions.

  ```py
  my_mocked_function.side_effect = Exception("Some exception raised")
  ```

  ## Testing exceptions with `raises`

  Testing exceptions is quite straightforward. You can use the `raises` helper
  provided by pytest, and combine this with `excinfo` ("exception info") to
  inspect the exception message.

  ```py
  if POCKET_LAMBDA_ENDPOINT is None:
      raise ValueError(
          "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set"
      )
  ```

  Then to test this, we would use pytest's `excinfo` fixture along with `raises`:

  ```py
   with pytest.raises(ValueError) as excinfo:  # Watch for the ValueError
      get_articles("some_type")

      assert "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set" in str(
          excinfo.value
      )
  ```

  We could actually simplify the above test by using the `match` parameter with
  `raise`. This way we do not need the separate assertion:

  ```py
  with pytest.raises(ValueError, match="Error: POCKET_LAMBDA_ENDPOINT environment variable is not set"):
      get_articles("some_type")
  ```

  Note that `excinfo` is best used for testing the exception text that you the
  developer explicitly `raise`. For exceptions tha may occur naturaly in the code
  you are testing, you should use `caplog` or `capsys` (see below).

  ## Before-each and after-each

  When testing functions, we achieve this in Python using `setup_function` and
  `teardown_function` methods. These methods are called before and after each test
  method respectively.

  To apply a "before each" to _every test_ just put your setup function and/or
  teardown function at the top level of your test module.

  For example, below we set and remove an env var before and after each test:

  ```py
  @pytest.fixture(scope="function") # specify that this fixture should be run before each function test
  def setup_function():
      print("Setting up test environment...")
      os.environ["POCKET_LAMBDA_ENDPOINT"] = "https://some_endpoint.com/{article_type}"


  def teardown_function():
      print("Tearing down test environment...")
      del os.environ["POCKET_LAMBDA_ENDPOINT"]
  ```

  If the setup/teardown should only be applied to a subset of tests, just pass the
  name of the fixture as a parameter to the test function:

  ```py
  def some_function(setup_function):
      # setup_function will be run before this test
  ```

  You don't need to use the names `setup_function` and `teardown_function` so long
  as you are passing the fixture as a parameter.

  You can also use `yield` to combine the setup and teardown into a single
  function:

  ```py
  @pytest.fixture(scope="function")
  def setup_function():
      os.environ["POCKET_LAMBDA_ENDPOINT"] = "https://some_endpoint.com/{article_type}"
      yield
      del os.environ["POCKET_LAMBDA_ENDPOINT"]
  ```

  ### Another example:

  The following test suite uses the same three mocked functions in every test. The
  following set-up assigns the mocks before each test and resets after each
  individual test has run:

  ```py
  @pytest.fixture(scope="function")
  def setup_function():
      with patch("app.get_articles") as mock_get_articles, patch(
          "app.process_articles"
      ) as mock_process_articles, patch("app.update_worksheet") as mock_update_worksheet:
          yield mock_get_articles, mock_process_articles, mock_update_worksheet
  ```

  Then to use:

  ```py
  def individual_test(setup_function):
      mock_get_articles, mock_process_articles, mock_update_worksheet = setup_function
      # Now each mock can be referenced using the vars above

  ```

  ## Parameterized tests

  For a sequence of tests that are repetitive, to avoid repeating the same code
  over and over again, we can use parameterized tests. This is where we pass in a
  list of parameters to the test function and the test function is run once for
  each parameter.

  For example, in the function below I am handling numerous possible Exceptions
  that could be raised by the `requests.get` method:

  ```py
      try:
          response = requests.get(endpoint)
          response.raise_for_status()
          return response.json()

      except HTTPError as http_err:
          logging.error(f"HTTP Error occurred: {http_err}")

      except ConnectionError as conn_err:
          logging.error(f"Connection Error occurred: {conn_err}")

      except Timeout as timeout_err:
          logging.error(f"Timeout Error occurred: {timeout_err}")

      except RequestException as e:
          logging.error(f"Request Exception occurred: {e}")

      return None
  ```

  Instead of writing something like the following for each of the four exceptions:

  ```py

   def test_exception_generic(caplog):
      with patch("requests.get", side_effect=RequestException("Some error")):
          result = get_articles("some_type")

      assert "Request Exception occurred" in caplog.text
      assert result is None
  ```

  I could parameterize like so:

  ```py
  @pytest.mark.parametrize(
      "exception_type, log_message",
      [
          (RequestException, "Request Exception occurred: "),
          (HTTPError, "HTTP Error occurred: "),
          (Timeout, "Timeout Error occurred: "),
          (ConnectionError, "Connection Error occurred: "),
      ],
  )
  def test_exceptions(caplog, exception_type, log_message):
      with patch("requests.get", side_effect=exception_type("Some error")):
          result = get_articles("some_type")

      assert log_message in caplog.text
      assert result is None
  ```

  ## Caplog, syslog, excinfo

  `caplog` and `capsys` are built-in pytest fixtures. `caplog` lets you test log
  messages. `capsys` lets you test stdout and stderr. As such they are very useful
  when testing that error messages are logged correctly.

  ### `caplog`

  In our example, if the endpoing environment is not set, we log an error message.
  We can test that this message is logged correctly using `caplog`:

  ```py
  def test_no_endpoint_env_var(caplog):
      os.environ.pop("POCKET_LAMBDA_ENDPOINT", None)  # Remove env variable if it exists

      with caplog.at_level(logging.ERROR):
          result = get_articles("some_type")

      assert (
          "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set" in caplog.text
      )
      assert result is None
  ```

  > Note tha we pass in `caplog` as a parameter to the test function. This is how
  > pytest knows to use it as a fixture.

  ### `capsys`

  In our example, if the request is unsuccessful, we log an error message with
  `print` rather than `logging`. We can test that this message is printed
  correctly using `capsys` to check the stdout:

  ```py
  def test_http_error(capsys):
      with patch("requests.get") as mock_get:
          mock_get.return_value = Mock(ok=False, status_code=404)

          # Raise an HTTP error when raise_for_status is called
          mock_get.return_value.raise_for_status.side_effect = RequestException()

          result = get_articles("some_type")

      captured = capsys.readouterr()
      assert "An error occurred" in captured.out
  ```

  ### `excinfo`

raw_content:
  ---
  tags: [python, testing]
  ---

  # Testing Python code

  ## `pytest`

  Pytest is the most popular testing library for Python. It is not included with
  the Python standard library so it must be installed with
  [pip](Python_package_management.md).
  While it does not include a declaration library, it is robust enough to handle
  most scenarios having a rich and expressive set of constructs and decorators
  that let you declare what your tests should do, under what conditions they
  should run, and how they should interact with the rest of your system.

  ### Using `pytest`

  - Pytest will automatically detect test files so long as they are named
    appropriately. E.g. for a module called `lorem`, it will detzect the unit test
    files `lorem_test.py` and `test_lorem.py`.
  - In order to detect tests it should be run from a directory level above them

  ### Examples

  Here is a basic example of using pytest for a local module callled `palindrome`:

  ```py
  import palindrome

  def test_is_palindrome():
      assert palindrome.is_palindrome('soros')
      assert palindrome.is_palindrome('torot')
      assert not palindrome.is_palindrome('chair')
  ```

  ## Mocking

  `patch()` and `Mock` enable us to mock objects whilst testing (classes,
  functions, methods and properties belonging). They are used in combination.

  The `@patch` decorator temporarily replaces a specified object in your code with
  a mock object and restores the original object after the test is complete

  A Mock object simulates the object it replaces so that the object behaves as
  expected during testing. For example, if your code calls
  `some_function.some_method()`, and `some_method` is mocked, calling
  `some_method` will not execute real logic but will interact with the Mock object
  instead. Mock objects record details about how they have been used, like what
  methods have been called, with what arguments, etc., allowing you to make
  assertions about how they have been used.

  > `@patch` and `Mock` work together because a patch is used to replace an object
  > or attribute with a Mock object. `Mock` handles the simulated functionality,
  > and `@patch` designates the real value we are replacing with the mock.

  ### Example case

  I will use the following example from one my projects:

  ```py
  # get_articles.py
  def get_articles(article_type: str) -> Optional[Dict[str, Any]]:
      """Retrieve articles from Pocket API"""

      if POCKET_LAMBDA_ENDPOINT is None:
          logging.error("Error: POCKET_LAMBDA_ENDPOINT envinronment variable is not set")
          return None
      else:
          # Interpolate the article_type into the Pocket request URL
          endpoint = POCKET_LAMBDA_ENDPOINT.format(article_type=article_type)

      try:
          response = requests.get(endpoint)
          response.raise_for_status()
          return response.json()

      except RequestException as e:
          print(f"An error occurred: {e}")
          return None
  ```

  This function: sources a URL from an environment variable, interpolates a query
  string into the URL (which comes in as a parameter), makes a request to the URL,
  and returns the response as JSON.

  It has some safeguards in place:

  - It checks that the environment variable is set
  - It checks that the request was successful

  In the example we could use a Mock object to simulate the response from the
  Pocket API. This would allow us to test the function without having to make a
  real request to the API:

  ```py
  def test_successful_request():
      # Replace the requests.get function with a Mock object (mock_get)
      with patch("requests.get") as mock_get:
          # Specify the return value of the mock_get object)
          mock_get.return_value = Mock(ok=true)
          mock_get.return_value.json.return_value = {"value": "test"}

          # Call the function under test
          result = get_articles("gaby")

      # Assert expected outcomes
      mock_get.assert_called_once_with(endpoint)
      assert result == mock_json_response
  ```

  The example above follows the **Arrange, Act, Assert** pattern:

  | Stage   | Action                                                                          |
  | ------- | ------------------------------------------------------------------------------- |
  | Arrange | Replace the `requests.get` function with `patch` and set properties with `Mock` |
  | Act     | Call the function under test                                                    |
  | Assert  | Assert that the function under test behaved as expected                         |

  ### Alternative mock syntax

  The `with patch(...) as mock_name` syntax is fine for small-scale mocking but
  can become cumbersome when you are mocking several dependencies.

  There is another syntax (which I actually find clearer). Say we have a function
  with three dependencies: `update_worksheet`, `process_articles`, `get_articles`.
  We could mock like so:

  ```py
  @patch("app.update_worksheet")
  @patch("app.process_articles")
  @patch("app.get_articles")
  def test_success(
      mock_get_articles, mock_process_articles, mock_update_worksheet
  ):
      mock_get_articles.return_value = [1, 2, 3]
  ```

  Here the patching is done by the decorator and the mocks are defined as
  parameters to the test function (always in reverse order)

  ### Mock assertion lexicon

  #### `return_value`

  State what the mock should return

  ```py
  my_mocked_function.return_value = ['one', 'two', 'three']
  ```

  #### `call_count`

  Test how many times a dependent function is called

  ```py
  assert my_mocked_function.call_count = 3
  ```

  #### `assert_any_call()`

  Test that a given mock is called at least once during the execution of the
  function under test

  ```py
  my_mocked_function.assert_any_call(some_mocked_return_value)
  ```

  When the output of one function is used as a parameter to another, and we don't
  particularly care about the details of what is concerned we can just pass the
  executed function, e.g:

  ```py
  my_mocked_function.assert_any_call(preceding_function())
  ```

  #### `call_args_list`

  Get a list of all the arguments that a mock object was called with during the
  test.

  `call_args_list` is useful when you want to check the arguments that a mock
  object was called with during the test, especially if the mock object was called
  multiple times with different arguments. You can use it to inspect the arguments
  of each call and make assertions based on them.

  ```py
  second_my_mocked_function_call = my_mocked_function.call_args_list[1]

  # check the first argument of the second call:

  assert second_my_mocked_function_call[0][0] == "expected arg"
  ```

  #### `side_effect`

  Use to trigger a side effect when returning a value from a mock. Most useful for
  mocking exceptions.

  ```py
  my_mocked_function.side_effect = Exception("Some exception raised")
  ```

  ## Testing exceptions with `raises`

  Testing exceptions is quite straightforward. You can use the `raises` helper
  provided by pytest, and combine this with `excinfo` ("exception info") to
  inspect the exception message.

  ```py
  if POCKET_LAMBDA_ENDPOINT is None:
      raise ValueError(
          "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set"
      )
  ```

  Then to test this, we would use pytest's `excinfo` fixture along with `raises`:

  ```py
   with pytest.raises(ValueError) as excinfo:  # Watch for the ValueError
      get_articles("some_type")

      assert "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set" in str(
          excinfo.value
      )
  ```

  We could actually simplify the above test by using the `match` parameter with
  `raise`. This way we do not need the separate assertion:

  ```py
  with pytest.raises(ValueError, match="Error: POCKET_LAMBDA_ENDPOINT environment variable is not set"):
      get_articles("some_type")
  ```

  Note that `excinfo` is best used for testing the exception text that you the
  developer explicitly `raise`. For exceptions tha may occur naturaly in the code
  you are testing, you should use `caplog` or `capsys` (see below).

  ## Before-each and after-each

  When testing functions, we achieve this in Python using `setup_function` and
  `teardown_function` methods. These methods are called before and after each test
  method respectively.

  To apply a "before each" to _every test_ just put your setup function and/or
  teardown function at the top level of your test module.

  For example, below we set and remove an env var before and after each test:

  ```py
  @pytest.fixture(scope="function") # specify that this fixture should be run before each function test
  def setup_function():
      print("Setting up test environment...")
      os.environ["POCKET_LAMBDA_ENDPOINT"] = "https://some_endpoint.com/{article_type}"


  def teardown_function():
      print("Tearing down test environment...")
      del os.environ["POCKET_LAMBDA_ENDPOINT"]
  ```

  If the setup/teardown should only be applied to a subset of tests, just pass the
  name of the fixture as a parameter to the test function:

  ```py
  def some_function(setup_function):
      # setup_function will be run before this test
  ```

  You don't need to use the names `setup_function` and `teardown_function` so long
  as you are passing the fixture as a parameter.

  You can also use `yield` to combine the setup and teardown into a single
  function:

  ```py
  @pytest.fixture(scope="function")
  def setup_function():
      os.environ["POCKET_LAMBDA_ENDPOINT"] = "https://some_endpoint.com/{article_type}"
      yield
      del os.environ["POCKET_LAMBDA_ENDPOINT"]
  ```

  ### Another example:

  The following test suite uses the same three mocked functions in every test. The
  following set-up assigns the mocks before each test and resets after each
  individual test has run:

  ```py
  @pytest.fixture(scope="function")
  def setup_function():
      with patch("app.get_articles") as mock_get_articles, patch(
          "app.process_articles"
      ) as mock_process_articles, patch("app.update_worksheet") as mock_update_worksheet:
          yield mock_get_articles, mock_process_articles, mock_update_worksheet
  ```

  Then to use:

  ```py
  def individual_test(setup_function):
      mock_get_articles, mock_process_articles, mock_update_worksheet = setup_function
      # Now each mock can be referenced using the vars above

  ```

  ## Parameterized tests

  For a sequence of tests that are repetitive, to avoid repeating the same code
  over and over again, we can use parameterized tests. This is where we pass in a
  list of parameters to the test function and the test function is run once for
  each parameter.

  For example, in the function below I am handling numerous possible Exceptions
  that could be raised by the `requests.get` method:

  ```py
      try:
          response = requests.get(endpoint)
          response.raise_for_status()
          return response.json()

      except HTTPError as http_err:
          logging.error(f"HTTP Error occurred: {http_err}")

      except ConnectionError as conn_err:
          logging.error(f"Connection Error occurred: {conn_err}")

      except Timeout as timeout_err:
          logging.error(f"Timeout Error occurred: {timeout_err}")

      except RequestException as e:
          logging.error(f"Request Exception occurred: {e}")

      return None
  ```

  Instead of writing something like the following for each of the four exceptions:

  ```py

   def test_exception_generic(caplog):
      with patch("requests.get", side_effect=RequestException("Some error")):
          result = get_articles("some_type")

      assert "Request Exception occurred" in caplog.text
      assert result is None
  ```

  I could parameterize like so:

  ```py
  @pytest.mark.parametrize(
      "exception_type, log_message",
      [
          (RequestException, "Request Exception occurred: "),
          (HTTPError, "HTTP Error occurred: "),
          (Timeout, "Timeout Error occurred: "),
          (ConnectionError, "Connection Error occurred: "),
      ],
  )
  def test_exceptions(caplog, exception_type, log_message):
      with patch("requests.get", side_effect=exception_type("Some error")):
          result = get_articles("some_type")

      assert log_message in caplog.text
      assert result is None
  ```

  ## Caplog, syslog, excinfo

  `caplog` and `capsys` are built-in pytest fixtures. `caplog` lets you test log
  messages. `capsys` lets you test stdout and stderr. As such they are very useful
  when testing that error messages are logged correctly.

  ### `caplog`

  In our example, if the endpoing environment is not set, we log an error message.
  We can test that this message is logged correctly using `caplog`:

  ```py
  def test_no_endpoint_env_var(caplog):
      os.environ.pop("POCKET_LAMBDA_ENDPOINT", None)  # Remove env variable if it exists

      with caplog.at_level(logging.ERROR):
          result = get_articles("some_type")

      assert (
          "Error: POCKET_LAMBDA_ENDPOINT environment variable is not set" in caplog.text
      )
      assert result is None
  ```

  > Note tha we pass in `caplog` as a parameter to the test function. This is how
  > pytest knows to use it as a fixture.

  ### `capsys`

  In our example, if the request is unsuccessful, we log an error message with
  `print` rather than `logging`. We can test that this message is printed
  correctly using `capsys` to check the stdout:

  ```py
  def test_http_error(capsys):
      with patch("requests.get") as mock_get:
          mock_get.return_value = Mock(ok=False, status_code=404)

          # Raise an HTTP error when raise_for_status is called
          mock_get.return_value.raise_for_status.side_effect = RequestException()

          result = get_articles("some_type")

      captured = capsys.readouterr()
      assert "An error occurred" in captured.out
  ```

  ### `excinfo`


path: zk/Testing_with_Jest.md
sortable_path: zkTesting_with_Jest.md
title: Testing with Jest
lead: ## Mocking classes/modules
checksum: 073f7ee1dd7b138b9e24d7765cf0ae3b60643eb0ef5c9f391187fd72c1e98968
created: 2024-03-30 09:03:21.426349635+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["javascript","testing","jest"]}
body:
  ## Mocking classes/modules

  ### Classes

  Let' say we have this class:

  ```js
  // database.js
  class Database {
    connect() {}
    save(data) {}
  }
  ```

  Then to mock:

  ```js
  import Database from "./database";

  // This will mock the whole Database class, replacing all methods with jest mock functions.
  jest.mock("./database");

  test("should use mocked save method", () => {
    const dbInstance = new Database();

    // Mocking the save method with a specific return value
    dbInstance.save.mockReturnValue(true);

    const result = dbInstance.save({ key: "value" });

    expect(result).toBe(true);
    expect(dbInstance.save).toHaveBeenCalledWith({ key: "value" });

    // The connect method is still a mock function (but without a specific behavior).
    dbInstance.connect();
    expect(dbInstance.connect).toHaveBeenCalled();
  });
  ```

  ### Modules

  Say we have the following module file:

  ```js
  // utils.js
  export const doSomething = () => {
    // ...
  };

  export const fetchUserData = async (userId) => {
    const response = await axios.get(`/api/users/${userId}`);
    return response.data;
  };
  ```

  Mocked:

  ```js
  jest.mock("./utils", () => {
    return {
      doSomething: jest.fn(() => "mocked doSomething"),
      fetchUserData: jest.fn((userId) =>
        Promise.resolve({ id: userId, name: "Mock User" })
      ),
    };
  });

  test("should use mocked module functions", () => {
    expect(utils.doSomething()).toBe("mocked doSomething");
    expect(utils.doSomething).toHaveBeenCalled();

    const result = await utils.fetchUserData(123);

    expect(result).toEqual({ id: 123, name: "Mock User" });
    expect(utils.fetchUserData).toHaveBeenCalledWith(123);

  });
  ```

  ### Inline mocking versus "per test" mocking

  There are two different architectures that we can use when mocking modules and
  classes: **inline** and **per test** mocking.

  Here is the inline case:

  ```js
  jest.mock("./some_module.js", () => {
    return {
      someFunction: jest.fn(() => "value"),
      someFunctionWithParam: jest.fn((param) => ({
        property: param,
      })),
      someAsyncFunction: jest.fn(() => Promise.resolve("value")),
    };
  });
  ```

  Here is the per test case:

  ```js
  import { someModule } from "./some_module.js";

  let someModuleMock;

  someModuleMock = {
    someFunction: jest.fn(() => "value"),
    someFunctionWithParam: jest.fn((param) => ({
      property: param,
    })),
    someAsyncFunction: jest.fn(() => Promise.resolve("value")),
  };

  someModule.mockImplementation(() => someModuleMock);

  it("should do something", () => {
    const newValue = "new value";
    someModule.someFunction.mockReturnValue(newValue);
  });
  ```

  The benefits of inline:

  - Inline is good because everything is set up in one place
  - Inline keeps consistency accross tests: every test case in the file will use
    the same mocked function unless overwritten within a test
  - It lends itself to being a _global_ mock that can be used accross test files
    in a `__mocks__/` directory

  The benefits of per-test:

  - You can very mock implementations within the file, providing more granular
    control. You can redefine `someModuleMock` or parts of it
    (`someModule.someFunction`) throughout your test file to accomodate varied
    requirements between tests
  - It’s beneficial when your tests have divergent requirements, as you can
    perform more detailed setups and overrides for each individual test case or
    suite, ensuring mocks are configured exactly as required.

  #### Overriding inline mocks

  Per test mocking makes it straightforward to change the test parameters of the
  mocked module or class but you can also override inline mocks.

  If we were using the `someModule` inline mock and we wanted to override the
  `someFunction` function that we have defined inline, we would first import the
  `someFunction` function and then use `mockImplementation` against it:

  ```js
  import { someFunction } from "./some_module.js";
  someFunction.mockImplementation(() => "custom value");
  expect(someFunction()).toBe("custom value");

  // Optional: Restore the original mock implementation after the test
  someFunction.mockRestore();
  ```

  Note: although we are importing `someFunction` we are not actually importing the
  real function tha belongs to the module. Because Jest mocks all of its
  properties and methods with the inline syntax, we are actually just importing
  that which Jest has aready mocked, but the syntax is a bit misleading.

  #### Applied to classes

  The same approaches (with minor differences) can be used with classes:

  Using inline (where the class is not the default export):

  ```js
  jest.mock("./SomeClass", () => {
    return {
      SomeClass: jest.fn().mockImplementation(() => {
        return {
          someFunction: jest.fn(() => "value"),
          someFunctionWithParam: jest.fn((param) => ({ property: param })),
          someAsyncFunction: jest.fn(() => Promise.resolve("value")),
          someOtherFunctionThatResolves: jest.fn().mockResolvedValue("some data"),
        };
      }),
    };
  });
  ```

  Using per test:

  ```js
  import SomeClass from "./someClass";

  jest.mock("./someClass");

  let someClassMock = {
    someFunction: jest.fn(() => "value"),
    someFunctionWithParam: jest.fn((param) => ({ property: param })),
    someAsyncFunction: jest.fn(() => Promise.resolve("value")),
  };

  // Mock class implementation
  SomeClass.mockImplementation(() => someClassMock);

  it("should do something", () => {
    const newValue = "new value";
    someClassMock.someFunction.mockReturnValue(newValue);
  });
  ```

  ## Check that a function has been called within another function

  ```js
  function toBeCalledFunction() {
    console.log("Original function called");
  }

  function callerFunction() {
    toBeCalledFunction();
  }
  ```

  ```js
  test("spy on toBeCalledFunction", () => {
    const spy = jest.spyOn(global, "toBeCalledFunction"); // Replace `global` with the appropriate object/context if the function is not global
    callerFunction();
    expect(spy).toHaveBeenCalled();
    spy.mockRestore(); // Restore the original function after spying
  });
  ```

  ## Mock a function that needs to resolve to something within another function

  We have two functions, one that gets data and another that processes it. We want
  to mock the function that gets data and return a value that the processing
  function can use.

  ```js
  async function getData() {
    // ... Fetch some data from an API or database
    return fetchedData;
  }

  async function processData() {
    const data = await getData();
    // ... Process the data
    return processedData;
  }
  ```

  The mocking part:

  ```js
  const mockData = { key: "value" }; // Mocked data

  jest.mock("./path-to-file-where-getData-is", () => ({
    getData: jest.fn().mockResolvedValue(mockData),
  }));

  test("test processData function", async () => {
    const result = await processData();
    // Now, result contains the processed version of mockData
    expect(result).toEqual(/* expected processed data based on mockData */);
  });
  ```

  We could also combine the above with a spy to check that the `getData` function
  was called:

  ```js
  const getDataSpy = jest
    .spyOn(moduleContainingGetData, "getData")
    .mockResolvedValue(mockData);

  const result = await processData();
  expect(getDataSpy).toHaveBeenCalled();
  expect(result).toEqual(/* expected processed data based on mockData */);
  getDataSpy.mockRestore();
  ```

  ## Mock a function that takes arguments

  ```js
  function addPrefix(str) {
    return `prefix-${str}`;
  }
  ```

  ```js
  test("dynamic mock for addPrefix function", () => {
    const mockFunction = jest.fn((str) => `mock-${str}`);

    // Example usage of mockFunction
    const result1 = mockFunction("test");
    const result2 = mockFunction("example");

    expect(result1).toBe("mock-test");
    expect(result2).toBe("mock-example");
  });
  ```

  ## Mocking network requests

  ### Mocking Axios

  ```js
  jest.mock("axios", () => ({
    get: jest.fn().mockResolvedValue(mockData),
    post: jest.fn().mockResolvedValue(mockData),
  }));
  ```

  Or we could implement this way:

  ```js
  jest.mock("axios");
  axios.get.mockResolvedValue({ data: "mockedData" });
  axios.post.mockResolvedValue({ data: "mockedData" });
  ```

  Then we can use the mocked axios functions in our tests:

  ```js
  const result = await fetchData(); // the function that uses Axios `get``
  expect(result).toBe("mockedGetData");

  const result = await sendData({ key: "value" }); // the function tha uses Axios `post`
  expect(result).toBe("mockedPostData");
  ```

  ### `mockImplementation`

  For more configurable cases we can use `mockImplementation`:

  ```js
  it("sends data", async () => {
    // Mock axios.post using mockImplementation
    axios.post.mockImplementation((url, data) => {
      if (data.key === "value") {
        return Promise.resolve({ data: "mockedPostData" });
      } else {
        return Promise.reject({ error: "An error occurred" });
      }
    });

    const result = await sendData({ key: "value" });

    expect(result).toBe("mockedPostData");
  });
  ```

  If we want to change the `get` and `post` values in different tests, we can do
  so by using `mockImplementation`:

  ## Mocking exceptions

  Again we use `mockImplementation`:

  Say we have the following function:

  ```js
  // fetchData.js
  import axios from "axios";

  const fetchData = async (url) => {
    try {
      const response = await axios.get(url);
      return response.data;
    } catch (error) {
      throw new Error("Error fetching data");
    }
  };

  export default fetchData;
  ```

  We would mock the success and the error as follows:

  ```js
  import axios from "axios";
  import fetchData from "./fetchData";

  jest.mock("axios");

  describe("fetchData", () => {
    it("fetches data successfully", async () => {
      axios.get.mockResolvedValue({ data: "mockedData" });

      const result = await fetchData("https://api.example.com/data");

      expect(result).toBe("mockedData");
    });

    it("throws an error when fetching fails", async () => {
      axios.get.mockImplementation(() => {
        throw new Error("API error");
      });

      // We use an asynchronous assertion here because we're expecting a promise to reject
      await expect(fetchData("https://api.example.com/data")).rejects.toThrow(
        "Error fetching data"
      );
    });
  });
  ```

  ## Parameterization

  The following offers a good opportunity for parameterisation:

  ```js
  it("should return page for deletion from `ipages-live`", async () => {
    // preview = false, isInternal = false
    await deletePageFromS3("url", false, false);
    const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
    expect(deleteObjectCommand.input).toEqual({
      Bucket: "bbc-ise-ipages-live",
      Key: "url/index.html",
    });
  });

  it("should return page for deletion from `preview`", async () => {
    // preview = true, isInternal = false
    await deletePageFromS3("url", true, false);
    const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
    expect(deleteObjectCommand.input).toEqual({
      Bucket: "staff.bbc.com-preview",
      Key: "preview/url/index.html",
    });
  });

  ...
  ```

  Each time we are passing in three parameters to the `deletePageFromS3` function
  which is the object under test. Each time there are different variations in the
  object that is output.

  To parameterize the process rather than use repeated `it` blocks we can combine
  the input paramters and outputs into an array:

  ```js
  const testParams = [
    {
      preview: false,
      isInternal: false,
      bucket: "ipages-live",
      key: "url/index.html",
    },
    {
      preview: true,
      isInternal: false,
      bucket: "staff.com-preview",
      key: "preview/url/index.html",
    },
  ];
  ```

  Then use `it.each` to loop through all possible parameter combinations:

  ```js
  it.each(testParams)(
    "should return page for deletion from %s",
    async ({ preview, isInternal, bucket, key }) => {
      await deletePageFromS3("url", preview, isInternal);
      const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
      expect(deleteObjectCommand.input).toEqual({
        Bucket: bucket,
        Key: key,
      });
    }
  );
  ```

  This uses the `%s` variable to print the parameters from each test, which
  outputs:

  ```
    ✓ should return page for deletion from {
    preview: false,
    isInternal: false,
    bucket: 'ipages-live',
    key: 'url/index.html'
  } (1 ms)
      ✓ should return page for deletion from {
    preview: true,
    isInternal: false,
    bucket: 'staff.com-preview',
    key: 'preview/url/index.html'
  }
  ```

raw_content:
  ---
  tags: [javascript, testing, jest]
  ---

  # Testing with Jest

  ## Mocking classes/modules

  ### Classes

  Let' say we have this class:

  ```js
  // database.js
  class Database {
    connect() {}
    save(data) {}
  }
  ```

  Then to mock:

  ```js
  import Database from "./database";

  // This will mock the whole Database class, replacing all methods with jest mock functions.
  jest.mock("./database");

  test("should use mocked save method", () => {
    const dbInstance = new Database();

    // Mocking the save method with a specific return value
    dbInstance.save.mockReturnValue(true);

    const result = dbInstance.save({ key: "value" });

    expect(result).toBe(true);
    expect(dbInstance.save).toHaveBeenCalledWith({ key: "value" });

    // The connect method is still a mock function (but without a specific behavior).
    dbInstance.connect();
    expect(dbInstance.connect).toHaveBeenCalled();
  });
  ```

  ### Modules

  Say we have the following module file:

  ```js
  // utils.js
  export const doSomething = () => {
    // ...
  };

  export const fetchUserData = async (userId) => {
    const response = await axios.get(`/api/users/${userId}`);
    return response.data;
  };
  ```

  Mocked:

  ```js
  jest.mock("./utils", () => {
    return {
      doSomething: jest.fn(() => "mocked doSomething"),
      fetchUserData: jest.fn((userId) =>
        Promise.resolve({ id: userId, name: "Mock User" })
      ),
    };
  });

  test("should use mocked module functions", () => {
    expect(utils.doSomething()).toBe("mocked doSomething");
    expect(utils.doSomething).toHaveBeenCalled();

    const result = await utils.fetchUserData(123);

    expect(result).toEqual({ id: 123, name: "Mock User" });
    expect(utils.fetchUserData).toHaveBeenCalledWith(123);

  });
  ```

  ### Inline mocking versus "per test" mocking

  There are two different architectures that we can use when mocking modules and
  classes: **inline** and **per test** mocking.

  Here is the inline case:

  ```js
  jest.mock("./some_module.js", () => {
    return {
      someFunction: jest.fn(() => "value"),
      someFunctionWithParam: jest.fn((param) => ({
        property: param,
      })),
      someAsyncFunction: jest.fn(() => Promise.resolve("value")),
    };
  });
  ```

  Here is the per test case:

  ```js
  import { someModule } from "./some_module.js";

  let someModuleMock;

  someModuleMock = {
    someFunction: jest.fn(() => "value"),
    someFunctionWithParam: jest.fn((param) => ({
      property: param,
    })),
    someAsyncFunction: jest.fn(() => Promise.resolve("value")),
  };

  someModule.mockImplementation(() => someModuleMock);

  it("should do something", () => {
    const newValue = "new value";
    someModule.someFunction.mockReturnValue(newValue);
  });
  ```

  The benefits of inline:

  - Inline is good because everything is set up in one place
  - Inline keeps consistency accross tests: every test case in the file will use
    the same mocked function unless overwritten within a test
  - It lends itself to being a _global_ mock that can be used accross test files
    in a `__mocks__/` directory

  The benefits of per-test:

  - You can very mock implementations within the file, providing more granular
    control. You can redefine `someModuleMock` or parts of it
    (`someModule.someFunction`) throughout your test file to accomodate varied
    requirements between tests
  - It’s beneficial when your tests have divergent requirements, as you can
    perform more detailed setups and overrides for each individual test case or
    suite, ensuring mocks are configured exactly as required.

  #### Overriding inline mocks

  Per test mocking makes it straightforward to change the test parameters of the
  mocked module or class but you can also override inline mocks.

  If we were using the `someModule` inline mock and we wanted to override the
  `someFunction` function that we have defined inline, we would first import the
  `someFunction` function and then use `mockImplementation` against it:

  ```js
  import { someFunction } from "./some_module.js";
  someFunction.mockImplementation(() => "custom value");
  expect(someFunction()).toBe("custom value");

  // Optional: Restore the original mock implementation after the test
  someFunction.mockRestore();
  ```

  Note: although we are importing `someFunction` we are not actually importing the
  real function tha belongs to the module. Because Jest mocks all of its
  properties and methods with the inline syntax, we are actually just importing
  that which Jest has aready mocked, but the syntax is a bit misleading.

  #### Applied to classes

  The same approaches (with minor differences) can be used with classes:

  Using inline (where the class is not the default export):

  ```js
  jest.mock("./SomeClass", () => {
    return {
      SomeClass: jest.fn().mockImplementation(() => {
        return {
          someFunction: jest.fn(() => "value"),
          someFunctionWithParam: jest.fn((param) => ({ property: param })),
          someAsyncFunction: jest.fn(() => Promise.resolve("value")),
          someOtherFunctionThatResolves: jest.fn().mockResolvedValue("some data"),
        };
      }),
    };
  });
  ```

  Using per test:

  ```js
  import SomeClass from "./someClass";

  jest.mock("./someClass");

  let someClassMock = {
    someFunction: jest.fn(() => "value"),
    someFunctionWithParam: jest.fn((param) => ({ property: param })),
    someAsyncFunction: jest.fn(() => Promise.resolve("value")),
  };

  // Mock class implementation
  SomeClass.mockImplementation(() => someClassMock);

  it("should do something", () => {
    const newValue = "new value";
    someClassMock.someFunction.mockReturnValue(newValue);
  });
  ```

  ## Check that a function has been called within another function

  ```js
  function toBeCalledFunction() {
    console.log("Original function called");
  }

  function callerFunction() {
    toBeCalledFunction();
  }
  ```

  ```js
  test("spy on toBeCalledFunction", () => {
    const spy = jest.spyOn(global, "toBeCalledFunction"); // Replace `global` with the appropriate object/context if the function is not global
    callerFunction();
    expect(spy).toHaveBeenCalled();
    spy.mockRestore(); // Restore the original function after spying
  });
  ```

  ## Mock a function that needs to resolve to something within another function

  We have two functions, one that gets data and another that processes it. We want
  to mock the function that gets data and return a value that the processing
  function can use.

  ```js
  async function getData() {
    // ... Fetch some data from an API or database
    return fetchedData;
  }

  async function processData() {
    const data = await getData();
    // ... Process the data
    return processedData;
  }
  ```

  The mocking part:

  ```js
  const mockData = { key: "value" }; // Mocked data

  jest.mock("./path-to-file-where-getData-is", () => ({
    getData: jest.fn().mockResolvedValue(mockData),
  }));

  test("test processData function", async () => {
    const result = await processData();
    // Now, result contains the processed version of mockData
    expect(result).toEqual(/* expected processed data based on mockData */);
  });
  ```

  We could also combine the above with a spy to check that the `getData` function
  was called:

  ```js
  const getDataSpy = jest
    .spyOn(moduleContainingGetData, "getData")
    .mockResolvedValue(mockData);

  const result = await processData();
  expect(getDataSpy).toHaveBeenCalled();
  expect(result).toEqual(/* expected processed data based on mockData */);
  getDataSpy.mockRestore();
  ```

  ## Mock a function that takes arguments

  ```js
  function addPrefix(str) {
    return `prefix-${str}`;
  }
  ```

  ```js
  test("dynamic mock for addPrefix function", () => {
    const mockFunction = jest.fn((str) => `mock-${str}`);

    // Example usage of mockFunction
    const result1 = mockFunction("test");
    const result2 = mockFunction("example");

    expect(result1).toBe("mock-test");
    expect(result2).toBe("mock-example");
  });
  ```

  ## Mocking network requests

  ### Mocking Axios

  ```js
  jest.mock("axios", () => ({
    get: jest.fn().mockResolvedValue(mockData),
    post: jest.fn().mockResolvedValue(mockData),
  }));
  ```

  Or we could implement this way:

  ```js
  jest.mock("axios");
  axios.get.mockResolvedValue({ data: "mockedData" });
  axios.post.mockResolvedValue({ data: "mockedData" });
  ```

  Then we can use the mocked axios functions in our tests:

  ```js
  const result = await fetchData(); // the function that uses Axios `get``
  expect(result).toBe("mockedGetData");

  const result = await sendData({ key: "value" }); // the function tha uses Axios `post`
  expect(result).toBe("mockedPostData");
  ```

  ### `mockImplementation`

  For more configurable cases we can use `mockImplementation`:

  ```js
  it("sends data", async () => {
    // Mock axios.post using mockImplementation
    axios.post.mockImplementation((url, data) => {
      if (data.key === "value") {
        return Promise.resolve({ data: "mockedPostData" });
      } else {
        return Promise.reject({ error: "An error occurred" });
      }
    });

    const result = await sendData({ key: "value" });

    expect(result).toBe("mockedPostData");
  });
  ```

  If we want to change the `get` and `post` values in different tests, we can do
  so by using `mockImplementation`:

  ## Mocking exceptions

  Again we use `mockImplementation`:

  Say we have the following function:

  ```js
  // fetchData.js
  import axios from "axios";

  const fetchData = async (url) => {
    try {
      const response = await axios.get(url);
      return response.data;
    } catch (error) {
      throw new Error("Error fetching data");
    }
  };

  export default fetchData;
  ```

  We would mock the success and the error as follows:

  ```js
  import axios from "axios";
  import fetchData from "./fetchData";

  jest.mock("axios");

  describe("fetchData", () => {
    it("fetches data successfully", async () => {
      axios.get.mockResolvedValue({ data: "mockedData" });

      const result = await fetchData("https://api.example.com/data");

      expect(result).toBe("mockedData");
    });

    it("throws an error when fetching fails", async () => {
      axios.get.mockImplementation(() => {
        throw new Error("API error");
      });

      // We use an asynchronous assertion here because we're expecting a promise to reject
      await expect(fetchData("https://api.example.com/data")).rejects.toThrow(
        "Error fetching data"
      );
    });
  });
  ```

  ## Parameterization

  The following offers a good opportunity for parameterisation:

  ```js
  it("should return page for deletion from `ipages-live`", async () => {
    // preview = false, isInternal = false
    await deletePageFromS3("url", false, false);
    const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
    expect(deleteObjectCommand.input).toEqual({
      Bucket: "bbc-ise-ipages-live",
      Key: "url/index.html",
    });
  });

  it("should return page for deletion from `preview`", async () => {
    // preview = true, isInternal = false
    await deletePageFromS3("url", true, false);
    const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
    expect(deleteObjectCommand.input).toEqual({
      Bucket: "staff.bbc.com-preview",
      Key: "preview/url/index.html",
    });
  });

  ...
  ```

  Each time we are passing in three parameters to the `deletePageFromS3` function
  which is the object under test. Each time there are different variations in the
  object that is output.

  To parameterize the process rather than use repeated `it` blocks we can combine
  the input paramters and outputs into an array:

  ```js
  const testParams = [
    {
      preview: false,
      isInternal: false,
      bucket: "ipages-live",
      key: "url/index.html",
    },
    {
      preview: true,
      isInternal: false,
      bucket: "staff.com-preview",
      key: "preview/url/index.html",
    },
  ];
  ```

  Then use `it.each` to loop through all possible parameter combinations:

  ```js
  it.each(testParams)(
    "should return page for deletion from %s",
    async ({ preview, isInternal, bucket, key }) => {
      await deletePageFromS3("url", preview, isInternal);
      const deleteObjectCommand = s3ClientMock.calls()[0].args[0];
      expect(deleteObjectCommand.input).toEqual({
        Bucket: bucket,
        Key: key,
      });
    }
  );
  ```

  This uses the `%s` variable to print the parameters from each test, which
  outputs:

  ```
    ✓ should return page for deletion from {
    preview: false,
    isInternal: false,
    bucket: 'ipages-live',
    key: 'url/index.html'
  } (1 ms)
      ✓ should return page for deletion from {
    preview: true,
    isInternal: false,
    bucket: 'staff.com-preview',
    key: 'preview/url/index.html'
  }
  ```


path: zk/The_Little_Man_computer.md
sortable_path: zkThe_Little_Man_computer.md
title: The Little Man Computer
checksum: 7ade3a76827f3f0535ca49bfb1e97fd63aba137e7816612848aa576e8cf04234
created: 2024-03-30 09:03:21.460679122+00:00
modified: 2024-10-19 08:59:33.256642074+00:00
metadata: {"tags":["CPU"]}
lead:
  The [Little Man Computer]() is a simplified computer that works on Von Neuman
  principles. It has all the CPU components we have detailed above. It is
  programmed in machine code but for simplicity it uses the denary rather than the
  binary number system.

body:
  The [Little Man Computer]() is a simplified computer that works on Von Neuman
  principles. It has all the CPU components we have detailed above. It is
  programmed in machine code but for simplicity it uses the denary rather than the
  binary number system.

  ![LMC_5.gif](LMC_5.gif)

  On the left is the instruction set. Each number constitutes and execution
  routine and the `xx` stand for the address in RAM that the execution will work
  on.

  Each row of the RAM has a denary address, 1 through to 99. Each address can hold
  three digits.

  - So the instruction `560` would mean _load the number at address 60._
  - The instruction `340` would mean _store a datum at address 40_

  ### Working through a basic computation

  We are going to add two numbers together as a basic example.

  1. First we need to place the two numbers in RAM we are going to use `5` and `3`
     - At address `60` we will put the number `5` and at address `61` we will put
       the number `3`
     - We are going to start at address `0` in the top left of the RAM grid
  1. The first instruction will be _load address 60_ which in the assembly will be
     `560` . We put this in address `0`, our starting point.
  1. This first instruction is now stored in the accumulator.
  1. Now we want to _add this number (in the accumulator) to the number in address
     61_
  1. This second instruction is `161` . We write this in address `1`
  1. Finally we want to store the output of the calculation in the RAM, let's say
     at address `62`
  1. So we store the command `362` at address `2`

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

  # The Little Man Computer

  The [Little Man Computer]() is a simplified computer that works on Von Neuman
  principles. It has all the CPU components we have detailed above. It is
  programmed in machine code but for simplicity it uses the denary rather than the
  binary number system.

  ![LMC_5.gif](LMC_5.gif)

  On the left is the instruction set. Each number constitutes and execution
  routine and the `xx` stand for the address in RAM that the execution will work
  on.

  Each row of the RAM has a denary address, 1 through to 99. Each address can hold
  three digits.

  - So the instruction `560` would mean _load the number at address 60._
  - The instruction `340` would mean _store a datum at address 40_

  ### Working through a basic computation

  We are going to add two numbers together as a basic example.

  1. First we need to place the two numbers in RAM we are going to use `5` and `3`
     - At address `60` we will put the number `5` and at address `61` we will put
       the number `3`
     - We are going to start at address `0` in the top left of the RAM grid
  1. The first instruction will be _load address 60_ which in the assembly will be
     `560` . We put this in address `0`, our starting point.
  1. This first instruction is now stored in the accumulator.
  1. Now we want to _add this number (in the accumulator) to the number in address
     61_
  1. This second instruction is `161` . We write this in address `1`
  1. Finally we want to store the output of the calculation in the RAM, let's say
     at address `62`
  1. So we store the command `362` at address `2`


path: zk/The_PATH.md
sortable_path: zkThe_PATH.md
title: The $PATH
checksum: 4f73dff47c7baf9d241cb89b6685f5c22038ece2bf4e6585bfc08810220df656
created: 2024-03-30 09:03:21.465683781+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["shell"]}
lead:
  We know that `$PATH` is an
  [environment variable](Environmental_and_shell_variables.md).
  This variable keeps track of directories **where executables are found**.

body:
  We know that `$PATH` is an
  [environment variable](Environmental_and_shell_variables.md).
  This variable keeps track of directories **where executables are found**.

  Whenever any command is run, the shell looks up the directories contained in the
  `PATH` for the target executable file and runs it. We can see this is the case
  by using the `which` command which traces the executable of bash commands. Take
  the `echo` program:

  ```bash
  which echo
  # echo: shell built-in command
  ```

  Or `npm` :

  ```bash
  which npm
  /home/thomas/.nvm/versions/node/v19.4.0/bin/npm
  ```

  By default the path will always contain the following locations:

  - `/usr/bin`
  - `/usr/sbin`
  - `/usr/local/bin`
  - `/usr/local/sbin`
  - `/bin`
  - `/sbin`

  All the inbuilt terminal programs reside at these locations and most of them are
  at `/usr/bin`. This is why they run automatically without error. If you attempt
  to run a program that doesn’t reside at these locations then you will get an
  error along the lines of `program x is not found in PATH`.

  ## Structure of the PATH

  ```bash
  /home/thomas/.nvm/versions/node/v19.4.0/bin:
  /usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:
  /usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
  ```

  ## Adding to the PATH

  Only the default directories load to the PATH on every session. How then can we
  add custom directories to the path without them being lost every time we start a
  new session? Remember that the user config `.bashrc` loads on init for every
  bash session. Therefore, if we set the custom path in this file, it will be
  created every time we start a session. This is why when you add a new program it
  often ask you to append a script to the bottom of your `.bashrc` .

  For example, at the bottom of my `.zshrc` on my work computer I have:

  ```bash
  export CHROME_BIN=/mnt/c/Program\\ Files\\ \\(x86\\)/Google/Chrome/Application/chrome.exe
  ```

  This enables me to access the Chromium binaries from my terminal session (needed
  for running Angular tests) but it doesn’t add it to the path, it creates an
  environment variable on every session.

  For demonstration, let’s add a user’s desktop directory to the PATH.

  First we go to the `.bashrc` and add the `export` command. Remember that this is
  the command for creating a new environment variable:

  ```bash
  export PATH="$PATH=:~/Desktop"
  ```

  We force a reload of the `.bashrc` with the command:

  ```bash
  source ~/.bashrc
  ```

  Then we can check this directory has been added to the path with an echo

  ```bash
  echo $PATH
  ...:~/Desktop
  ```

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

  # The `$PATH`

  We know that `$PATH` is an
  [environment variable](Environmental_and_shell_variables.md).
  This variable keeps track of directories **where executables are found**.

  Whenever any command is run, the shell looks up the directories contained in the
  `PATH` for the target executable file and runs it. We can see this is the case
  by using the `which` command which traces the executable of bash commands. Take
  the `echo` program:

  ```bash
  which echo
  # echo: shell built-in command
  ```

  Or `npm` :

  ```bash
  which npm
  /home/thomas/.nvm/versions/node/v19.4.0/bin/npm
  ```

  By default the path will always contain the following locations:

  - `/usr/bin`
  - `/usr/sbin`
  - `/usr/local/bin`
  - `/usr/local/sbin`
  - `/bin`
  - `/sbin`

  All the inbuilt terminal programs reside at these locations and most of them are
  at `/usr/bin`. This is why they run automatically without error. If you attempt
  to run a program that doesn’t reside at these locations then you will get an
  error along the lines of `program x is not found in PATH`.

  ## Structure of the PATH

  ```bash
  /home/thomas/.nvm/versions/node/v19.4.0/bin:
  /usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:
  /usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
  ```

  ## Adding to the PATH

  Only the default directories load to the PATH on every session. How then can we
  add custom directories to the path without them being lost every time we start a
  new session? Remember that the user config `.bashrc` loads on init for every
  bash session. Therefore, if we set the custom path in this file, it will be
  created every time we start a session. This is why when you add a new program it
  often ask you to append a script to the bottom of your `.bashrc` .

  For example, at the bottom of my `.zshrc` on my work computer I have:

  ```bash
  export CHROME_BIN=/mnt/c/Program\\ Files\\ \\(x86\\)/Google/Chrome/Application/chrome.exe
  ```

  This enables me to access the Chromium binaries from my terminal session (needed
  for running Angular tests) but it doesn’t add it to the path, it creates an
  environment variable on every session.

  For demonstration, let’s add a user’s desktop directory to the PATH.

  First we go to the `.bashrc` and add the `export` command. Remember that this is
  the command for creating a new environment variable:

  ```bash
  export PATH="$PATH=:~/Desktop"
  ```

  We force a reload of the `.bashrc` with the command:

  ```bash
  source ~/.bashrc
  ```

  Then we can check this directory has been added to the path with an echo

  ```bash
  echo $PATH
  ...:~/Desktop
  ```


path: zk/The_Pragmatic_Programmer_1999.md
sortable_path: zkThe_Pragmatic_Programmer_1999.md
title: The Pragmatic Programmer (Hunt/Thomas, 1999)
lead: ## General
checksum: b889e9eee019a923f22718f398b0395d5f234d3045f4a6c3fcc063893630b0b4
created: 2024-03-30 09:03:21.477651731+00:00
modified: 2024-10-19 09:00:04.743850954+00:00
metadata: {"tags":[]}
body:
  ## General

  ### Meyer's Uniform Access Principle

  > All services offered by a module should be available through a uniform
  > notation, which does not betray whether they are implemented through storage
  > or through computation

  This is a clear recommendation for using getters and setters with classes. You
  should not see method calls outside of the class, they should appear as
  properties of the object.

  ## Don't Repeat Yourself

  > Every piece of knowledge must have a single, unambiguous, authoritative
  > representation within a system

  ## The Principle of Orthogonality

  This notion comes from geometry. Two lines are orthogonal to each other if they
  form a right-angle when they meet.

  Their meeting isn't the important part. Think of a simple x, y graph:

  > If you move along one of the lines, **your position projected onto the other
  > doesn't change**

  In computing this is expressed in terms of **decoupling** and is implemented
  through modular, component-based architectures. As much as possible code should
  be scoped narrowly so that a change in one area does not cause changes in
  others. By keeping components discrete it is easier to make changes, refactor,
  improve and extend the codebase.

  > We want to design components that are self-contained: independent and with a
  > single, well-defined purpose. When components are isolated from one another,
  > you know that you can change one without having to worry about the rest. As
  > long as you don't change that component's external interfaces, you can be
  > comfortable that you won't cause problems that ripple through the entire
  > system.

  ### Benefits of orthogonality: productivity

  - Changes are localised so development time and testing time are reduced
  - Orthogonality promotes reuse: if components have specific, well-defined
    responsibilities, they can be combined with new components in ways that were
    not envisioned by their original implementors. The more loosely coupled your
    systems, the easier they are to reconfigure and reengineer.
  - Assume that one component does _M_ distinct things and another does _N_
    things. If they are orthogonal and you combine them, the result does _M x N_
    things. However if the two components are not orthogonal, there will be
    overlap, and the result will do less. You get more functionality per unit
    effort by combining orthogonal components.

  ### Benefits of orthogonality: reduced risk

  - Diseased sections of code are isolated. If a module is sick, it is less likely
    to spread the symptoms around the rest of the system.
  - Overall the system is less fragile: make small changes to a particular area
    and any problems you generate will be restricted to that area.
  - Orthogonal systems are better tested because it is easier to run and design
    discrete tests on modularised components.

  > Building a unit test us itself a an interesting test of orthogonality: what
  > does it take to build and link a unit test? Do you have to drag in a large
  > percentage of the rest of the system just to get a test to compile or link?
  > **If so, you've found a module that is not well decoupled from the rest of the
  > system**

  ### Relationship between DRY and orthogonality

  With DRY you're looking to minimize duplication within a system, whereas with
  orthogonality, you reduce the interdependency among the system's components. If
  you use the principle of orthogonality combined closely with the DRY principle,
  you'll find that the systems you develop are more flexible, more understandable
  and easier to debug, test, and maintain.

  ### Reversibility

  The principles of orthogonality and DRY result in code that is reversible. This
  means it is able to change in an agile way when the circumstances of its use and
  deployment change. This is important because when developing software in a
  business setting, the best decisions are not always made the first time around.
  By following the principles it should be relatively easy to change your
  program's interfaces, platform and scale. In other words, with the principle of
  orthogonality and DRY, refactoring becomes less of a chore.

  ## Prototyping and Tracer Bullets

  'Tracer bullets' are used by the military for night warfare. They are
  phosphorous bullets that are included in the magazines of guns alongside normal
  bullets. They are not intended to kill but instead light-up the surrounding area
  making it easier to see the terrain and target more efficiently.

  The authors use the notion of tracer bullets as a metaphor for developing
  software at the early stages of a project. This is **not** the same thing as
  prototyping. A tracer bullet model is useful for building things that haven't
  been built before. They exist to 'shed light' on the project's needs and to help
  the client understand what they want.

  They differ from prototypes in that they include integrated overall
  functionality but in a rough state. Whereas prototypes are more for singular,
  specific subcomponents of the project. Because tracer bullet models are
  joined-up in this way, even if they turn out to be inappropriate in some regard,
  they can be adapted and developed into a better form, without losing the core
  functionality.

  > Tracer bullets work because they operate in the same environment and under the
  > same constraints as the real bullets. They get to the target fast, so the
  > gunner gets immediate feedback. And from a practical standpoint they are a
  > relatively cheap solution. To get the same effect in code, we're looking for
  > something that gets us from a requirement to some aspect of the final system
  > quickly, visibly and repeatably.

  > Tracer code is not disposable: you write it for keeps. It contains all the
  > error-checking, structuring, documentation and self-checking that a piece of
  > production code has. It simply is not fully functional. However, once you have
  > made an end-to-end connection among the components of your system, you can
  > check how close to the target you are, adjusting as necessary.

  ### Distinguishing from prototyping

  > Prototyping generates disposable code. Tracer code is lean but complete, and
  > forms part of the skeleton of the final system. Think of prototyping as the
  > reconnaissance and intelligence gathering that takes place before a single
  > tracer bullet is fired.

  ## Design by contract

  To understand DBC we have to think of a computational process as involving two
  stages: the call and the execution of the routine that happens in response to
  the call (henceforth **caller** and **routine**).

  - the caller could be a function expression that invokes a function and passes
    arguments to it expecting a given output. The function that executes is the
    routine
  - the caller could be an object instantiation that calls a method belonging to
    its parent class
  - the caller could be a parent React component that passes props to a child
    component

  Design by contract means specifying clear and inviolable rules detailing what
  must obtain at both the call stage and the routine stage if the process is to
  execute.

  Every function and method in a software system does something. Before it starts
  that something, the routine may have some expectation of the state of the world
  and it may be able to make a statement about the state of the world when it
  concludes. These expectations are defined in terms of preconditions,
  postconditions, and invariants. They form that basis of a **contract** between
  the caller and the routine. Hence _design by contract\*\*._\*\*

  ### Preconditions

  Preconditions specify what must be true in order for the routine to be called.
  In other words, the requirements of the routine. What it needs and what should
  be the case before it even considers executing the task. A **routine should
  never get called when its preconditions would be violated**.

  ### Postconditions

  Providing the preconditions are met, this is what the routine is guaranteed to
  do. In other words: the state of affairs that must obtain after the routine has
  ran.

  ### Invariants

  Once established, the preconditions and postconditions should not change. If
  they need to change, that is a separate process and contract. In the processing
  of a routine, the data may be variant relative to the contract, but by the end
  the overall conditions establish the equilibrium of the contract.

  There is an analogue here with functional programming philosophy: the function
  should always return the same sort of output, without ancillary processes
  happening, i.e side-effects.

  One way to achieve this is to be miserly when setting up the contract, which
  overlaps with orthogonality. Only specify the minimum return on a contract
  rather than multiple postconditions. This only increases the likelihood that the
  contract will be breached at some point. If you need multiple postconditions,
  spread them out an achieve them in a compositional way, with multiple separate
  and modular processes.

  > Be strict in what you will accept before you begin, and promise as little as
  > possible in return. If your contract indicates that you'll accept anything and
  > promise the world in return, then you've got a lot of code to write!

  ### Division of responsibilities

  > If all the routine's preconditions are met by the caller, the routine shall
  > guarantee that all postconditions and invariants will be true when it
  > completes.

  Note that the emphasis of responsibilities is on the caller.

  Imagine that we have a function that returns the count of an array of integers.
  It is not the job of the count routine to verify that it has been passed
  integers and then to execute the count. Or, in the event that it is not passed
  integers, to mutate the data to integers and then execute.

  This should be resolved by the caller: it is the responsibility of the caller to
  pass integers. If it doesn't, the routine simply crashes or raises an exception.
  It doesn't try to accommodate the input because that does not come down on its
  side of the contract. The caller has failed to meet the preconditions. If, due
  to some bug, the routine receives integers and fails to output the sum, then it
  has failed on its side

  ### Example: type checking

  An obvious example of this philosophy is when you perform checks or validation
  within your code (although validation is more of an issue when you are dealing
  with user data, not your own internal code). For instance using type checking
  with dynamically-typed languages.

  When we use the `prop-types` library with React we are specifying preconditions:
  so long at the prop (effectively the caller) passed to the component
  (effectively the routine) is of type X, the component will render invariantly as
  R. If the prop is of type Y, an exception will be raised highlighting a breach
  in the contract.

  Another example would be more advanced type checking with Javascript written
  using Typescript.

  ## The Law of Demeter

  Demeter's Law has applicability chiefly when programming with classes.

  It's a fancy name for a simple principle summarised by 'don't talk to
  strangers'. Demeter's law is violated when code has more than one step between
  classes. You should avoid invoking methods of an object returned by another
  method. You should only use your own methods when dealing with it.

  ### Formal

  A method _m_ of object _O_ may only invoke the methods of the following kinds of
  objects:

  - _O_ itself
  - _m_'s parameters
  - any objects created or instantiated within _m_
  - _O_'s direct component objects (in other words nested objects)
  - a global variable (over and above _O_) accessible by _O_, within the scope of
    _m_

  ## Model, View, Controller design pattern

  The key concept behind the MVC idiom is separating the model from both the GUI
  that represents it and the controls that manage the view.

  - **Model**
    - The abstract data model representing the target object
    - The model has no direct knowledge of any views or controllers
  - **View**
    - A way to interpret the model. It subscribes to changes in the model and
      logical events from the controller
  - **Controller**
    - A way to control the view and provide the model with new data. It publishes
      events to both the model and the view

  For comparison, distinguish React from MVC. In React data is unidirectional: the
  JSX component as controller cannot change the state. The state is passed down to
  the controller. Also MVC lends itself to separation of technologies: code used
  to create the View is different from Code that manages Controller and data
  Model. In React it's all one integrated system.

  ## Refactoring

  > Rewriting, reworking, and re-architecting code is collectively known as
  > refactoring

  ### When to refactor

  - **Duplication**: you've discovered a violation of the DRY principle
  - **Non-orthogonal design**: you've discovered some code or design that could be
    made more orthogonal
  - **Outdated knowledge**: your knowledge about the problem and you skills at
    implementing a solution have changed since the code was first written. Update
    and improve the code to reflect these changes
  - **Performance: y**ou need to move functionality from one area of the system to
    another to improve performance

  ### Tips when refactoring

  - Don't try to refactor and add new functionality at the same time!
  - Make sure you have good tests before you begin refactoring. Run the tests as
    you refactor. That way you will know quickly if your changes have broken
    anything
  - Take short, deliberative steps. Refactoring often involves making many
    localised changes that result in a larger-scale change.

  ## Testing

  > Most developers hate testing. They tend to test-gently, subconsciously knowing
  > where the code will break and avoiding the weak spots. Pragmatic Programmers
  > are different. We are _driven_ to find our bugs _now_, so we don't have to
  > endure the shame of others finding our bugs later.

  ### Unit testing

  A unit test is code that exercises a module. It consists in testing each module
  in isolation to verify its behaviour. Unit testing is the foundation of all
  other forms of testing. If the parts don't work by themselves, they probably
  won't work well together. All the modules you are using must pass their own unit
  tests before you can proceed.

  We can think of unit testing as **testing against contract** (detailed above).
  We want to test that the module delivers the functionality it promises over a
  wide range of test cases and boundary conditions.

  Scope for unit testing should cover:

  - Obviously, returning the expected value/outcome
  - Ensuring that faulty arguments/ types are rejected and initiate error handling
    (deliberately breaking your code to ensure it is handled appropriately)
  - Pass in the boundary and maximum value
  - Pass in values between the zero and the maximum expressible argument to cover
    a range of cases

  Benefits of unit testing include:

  - It creates an example to other developers how to use all of the functionality
    of a given module
  - It is a means to build **regression tests** which can be used to validate any
    future changes to the code. In other words, the future changes should pass the
    older tests to prove they are consistent with the code base

  ### Integration testing

  Integration testing shows that the major subsystems that make up the project
  work and play well with each other.

  Integration testing is really just an extension of the unit testing described,
  only know you're testing how entire subsystems honour their contracts.

  ## Commenting your code

  In general, comments should detail **why** something is done, its purpose and
  its goal. The code already shows _how_ it's done, so commenting on this is
  redundant, and violates the DRY principle.

  > We like to see a simple module-level comment, comments for significant data
  > and type declarations, and a brief class and per-method header describing how
  > the function is used and anything it does that is not obvious

  ```js
  /*
  Find the highest value within a specified data range of samples

  Parameter: aRange = range of dates to search for data
  Parameter: aThreshold = minimum value to consider

  Return: the value, or null if no value found that is greater than or equal to the threshold
  */
  ```

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

  # The Pragmatic Programmer (Hunt/Thomas, 1999)

  ## General

  ### Meyer's Uniform Access Principle

  > All services offered by a module should be available through a uniform
  > notation, which does not betray whether they are implemented through storage
  > or through computation

  This is a clear recommendation for using getters and setters with classes. You
  should not see method calls outside of the class, they should appear as
  properties of the object.

  ## Don't Repeat Yourself

  > Every piece of knowledge must have a single, unambiguous, authoritative
  > representation within a system

  ## The Principle of Orthogonality

  This notion comes from geometry. Two lines are orthogonal to each other if they
  form a right-angle when they meet.

  Their meeting isn't the important part. Think of a simple x, y graph:

  > If you move along one of the lines, **your position projected onto the other
  > doesn't change**

  In computing this is expressed in terms of **decoupling** and is implemented
  through modular, component-based architectures. As much as possible code should
  be scoped narrowly so that a change in one area does not cause changes in
  others. By keeping components discrete it is easier to make changes, refactor,
  improve and extend the codebase.

  > We want to design components that are self-contained: independent and with a
  > single, well-defined purpose. When components are isolated from one another,
  > you know that you can change one without having to worry about the rest. As
  > long as you don't change that component's external interfaces, you can be
  > comfortable that you won't cause problems that ripple through the entire
  > system.

  ### Benefits of orthogonality: productivity

  - Changes are localised so development time and testing time are reduced
  - Orthogonality promotes reuse: if components have specific, well-defined
    responsibilities, they can be combined with new components in ways that were
    not envisioned by their original implementors. The more loosely coupled your
    systems, the easier they are to reconfigure and reengineer.
  - Assume that one component does _M_ distinct things and another does _N_
    things. If they are orthogonal and you combine them, the result does _M x N_
    things. However if the two components are not orthogonal, there will be
    overlap, and the result will do less. You get more functionality per unit
    effort by combining orthogonal components.

  ### Benefits of orthogonality: reduced risk

  - Diseased sections of code are isolated. If a module is sick, it is less likely
    to spread the symptoms around the rest of the system.
  - Overall the system is less fragile: make small changes to a particular area
    and any problems you generate will be restricted to that area.
  - Orthogonal systems are better tested because it is easier to run and design
    discrete tests on modularised components.

  > Building a unit test us itself a an interesting test of orthogonality: what
  > does it take to build and link a unit test? Do you have to drag in a large
  > percentage of the rest of the system just to get a test to compile or link?
  > **If so, you've found a module that is not well decoupled from the rest of the
  > system**

  ### Relationship between DRY and orthogonality

  With DRY you're looking to minimize duplication within a system, whereas with
  orthogonality, you reduce the interdependency among the system's components. If
  you use the principle of orthogonality combined closely with the DRY principle,
  you'll find that the systems you develop are more flexible, more understandable
  and easier to debug, test, and maintain.

  ### Reversibility

  The principles of orthogonality and DRY result in code that is reversible. This
  means it is able to change in an agile way when the circumstances of its use and
  deployment change. This is important because when developing software in a
  business setting, the best decisions are not always made the first time around.
  By following the principles it should be relatively easy to change your
  program's interfaces, platform and scale. In other words, with the principle of
  orthogonality and DRY, refactoring becomes less of a chore.

  ## Prototyping and Tracer Bullets

  'Tracer bullets' are used by the military for night warfare. They are
  phosphorous bullets that are included in the magazines of guns alongside normal
  bullets. They are not intended to kill but instead light-up the surrounding area
  making it easier to see the terrain and target more efficiently.

  The authors use the notion of tracer bullets as a metaphor for developing
  software at the early stages of a project. This is **not** the same thing as
  prototyping. A tracer bullet model is useful for building things that haven't
  been built before. They exist to 'shed light' on the project's needs and to help
  the client understand what they want.

  They differ from prototypes in that they include integrated overall
  functionality but in a rough state. Whereas prototypes are more for singular,
  specific subcomponents of the project. Because tracer bullet models are
  joined-up in this way, even if they turn out to be inappropriate in some regard,
  they can be adapted and developed into a better form, without losing the core
  functionality.

  > Tracer bullets work because they operate in the same environment and under the
  > same constraints as the real bullets. They get to the target fast, so the
  > gunner gets immediate feedback. And from a practical standpoint they are a
  > relatively cheap solution. To get the same effect in code, we're looking for
  > something that gets us from a requirement to some aspect of the final system
  > quickly, visibly and repeatably.

  > Tracer code is not disposable: you write it for keeps. It contains all the
  > error-checking, structuring, documentation and self-checking that a piece of
  > production code has. It simply is not fully functional. However, once you have
  > made an end-to-end connection among the components of your system, you can
  > check how close to the target you are, adjusting as necessary.

  ### Distinguishing from prototyping

  > Prototyping generates disposable code. Tracer code is lean but complete, and
  > forms part of the skeleton of the final system. Think of prototyping as the
  > reconnaissance and intelligence gathering that takes place before a single
  > tracer bullet is fired.

  ## Design by contract

  To understand DBC we have to think of a computational process as involving two
  stages: the call and the execution of the routine that happens in response to
  the call (henceforth **caller** and **routine**).

  - the caller could be a function expression that invokes a function and passes
    arguments to it expecting a given output. The function that executes is the
    routine
  - the caller could be an object instantiation that calls a method belonging to
    its parent class
  - the caller could be a parent React component that passes props to a child
    component

  Design by contract means specifying clear and inviolable rules detailing what
  must obtain at both the call stage and the routine stage if the process is to
  execute.

  Every function and method in a software system does something. Before it starts
  that something, the routine may have some expectation of the state of the world
  and it may be able to make a statement about the state of the world when it
  concludes. These expectations are defined in terms of preconditions,
  postconditions, and invariants. They form that basis of a **contract** between
  the caller and the routine. Hence _design by contract\*\*._\*\*

  ### Preconditions

  Preconditions specify what must be true in order for the routine to be called.
  In other words, the requirements of the routine. What it needs and what should
  be the case before it even considers executing the task. A **routine should
  never get called when its preconditions would be violated**.

  ### Postconditions

  Providing the preconditions are met, this is what the routine is guaranteed to
  do. In other words: the state of affairs that must obtain after the routine has
  ran.

  ### Invariants

  Once established, the preconditions and postconditions should not change. If
  they need to change, that is a separate process and contract. In the processing
  of a routine, the data may be variant relative to the contract, but by the end
  the overall conditions establish the equilibrium of the contract.

  There is an analogue here with functional programming philosophy: the function
  should always return the same sort of output, without ancillary processes
  happening, i.e side-effects.

  One way to achieve this is to be miserly when setting up the contract, which
  overlaps with orthogonality. Only specify the minimum return on a contract
  rather than multiple postconditions. This only increases the likelihood that the
  contract will be breached at some point. If you need multiple postconditions,
  spread them out an achieve them in a compositional way, with multiple separate
  and modular processes.

  > Be strict in what you will accept before you begin, and promise as little as
  > possible in return. If your contract indicates that you'll accept anything and
  > promise the world in return, then you've got a lot of code to write!

  ### Division of responsibilities

  > If all the routine's preconditions are met by the caller, the routine shall
  > guarantee that all postconditions and invariants will be true when it
  > completes.

  Note that the emphasis of responsibilities is on the caller.

  Imagine that we have a function that returns the count of an array of integers.
  It is not the job of the count routine to verify that it has been passed
  integers and then to execute the count. Or, in the event that it is not passed
  integers, to mutate the data to integers and then execute.

  This should be resolved by the caller: it is the responsibility of the caller to
  pass integers. If it doesn't, the routine simply crashes or raises an exception.
  It doesn't try to accommodate the input because that does not come down on its
  side of the contract. The caller has failed to meet the preconditions. If, due
  to some bug, the routine receives integers and fails to output the sum, then it
  has failed on its side

  ### Example: type checking

  An obvious example of this philosophy is when you perform checks or validation
  within your code (although validation is more of an issue when you are dealing
  with user data, not your own internal code). For instance using type checking
  with dynamically-typed languages.

  When we use the `prop-types` library with React we are specifying preconditions:
  so long at the prop (effectively the caller) passed to the component
  (effectively the routine) is of type X, the component will render invariantly as
  R. If the prop is of type Y, an exception will be raised highlighting a breach
  in the contract.

  Another example would be more advanced type checking with Javascript written
  using Typescript.

  ## The Law of Demeter

  Demeter's Law has applicability chiefly when programming with classes.

  It's a fancy name for a simple principle summarised by 'don't talk to
  strangers'. Demeter's law is violated when code has more than one step between
  classes. You should avoid invoking methods of an object returned by another
  method. You should only use your own methods when dealing with it.

  ### Formal

  A method _m_ of object _O_ may only invoke the methods of the following kinds of
  objects:

  - _O_ itself
  - _m_'s parameters
  - any objects created or instantiated within _m_
  - _O_'s direct component objects (in other words nested objects)
  - a global variable (over and above _O_) accessible by _O_, within the scope of
    _m_

  ## Model, View, Controller design pattern

  The key concept behind the MVC idiom is separating the model from both the GUI
  that represents it and the controls that manage the view.

  - **Model**
    - The abstract data model representing the target object
    - The model has no direct knowledge of any views or controllers
  - **View**
    - A way to interpret the model. It subscribes to changes in the model and
      logical events from the controller
  - **Controller**
    - A way to control the view and provide the model with new data. It publishes
      events to both the model and the view

  For comparison, distinguish React from MVC. In React data is unidirectional: the
  JSX component as controller cannot change the state. The state is passed down to
  the controller. Also MVC lends itself to separation of technologies: code used
  to create the View is different from Code that manages Controller and data
  Model. In React it's all one integrated system.

  ## Refactoring

  > Rewriting, reworking, and re-architecting code is collectively known as
  > refactoring

  ### When to refactor

  - **Duplication**: you've discovered a violation of the DRY principle
  - **Non-orthogonal design**: you've discovered some code or design that could be
    made more orthogonal
  - **Outdated knowledge**: your knowledge about the problem and you skills at
    implementing a solution have changed since the code was first written. Update
    and improve the code to reflect these changes
  - **Performance: y**ou need to move functionality from one area of the system to
    another to improve performance

  ### Tips when refactoring

  - Don't try to refactor and add new functionality at the same time!
  - Make sure you have good tests before you begin refactoring. Run the tests as
    you refactor. That way you will know quickly if your changes have broken
    anything
  - Take short, deliberative steps. Refactoring often involves making many
    localised changes that result in a larger-scale change.

  ## Testing

  > Most developers hate testing. They tend to test-gently, subconsciously knowing
  > where the code will break and avoiding the weak spots. Pragmatic Programmers
  > are different. We are _driven_ to find our bugs _now_, so we don't have to
  > endure the shame of others finding our bugs later.

  ### Unit testing

  A unit test is code that exercises a module. It consists in testing each module
  in isolation to verify its behaviour. Unit testing is the foundation of all
  other forms of testing. If the parts don't work by themselves, they probably
  won't work well together. All the modules you are using must pass their own unit
  tests before you can proceed.

  We can think of unit testing as **testing against contract** (detailed above).
  We want to test that the module delivers the functionality it promises over a
  wide range of test cases and boundary conditions.

  Scope for unit testing should cover:

  - Obviously, returning the expected value/outcome
  - Ensuring that faulty arguments/ types are rejected and initiate error handling
    (deliberately breaking your code to ensure it is handled appropriately)
  - Pass in the boundary and maximum value
  - Pass in values between the zero and the maximum expressible argument to cover
    a range of cases

  Benefits of unit testing include:

  - It creates an example to other developers how to use all of the functionality
    of a given module
  - It is a means to build **regression tests** which can be used to validate any
    future changes to the code. In other words, the future changes should pass the
    older tests to prove they are consistent with the code base

  ### Integration testing

  Integration testing shows that the major subsystems that make up the project
  work and play well with each other.

  Integration testing is really just an extension of the unit testing described,
  only know you're testing how entire subsystems honour their contracts.

  ## Commenting your code

  In general, comments should detail **why** something is done, its purpose and
  its goal. The code already shows _how_ it's done, so commenting on this is
  redundant, and violates the DRY principle.

  > We like to see a simple module-level comment, comments for significant data
  > and type declarations, and a brief class and per-method header describing how
  > the function is used and anything it does that is not obvious

  ```js
  /*
  Find the highest value within a specified data range of samples

  Parameter: aRange = range of dates to search for data
  Parameter: aThreshold = minimum value to consider

  Return: the value, or null if no value found that is greater than or equal to the threshold
  */
  ```


path: zk/Theorems_and_empty_sets.md
sortable_path: zkTheorems_and_empty_sets.md
title: Theorems and empty sets
checksum: 52cac37f8c279c1d947878b3bd503eac5dc8f7c50468f4bdaec0cf7e7e2f04fe
created: 2024-03-30 09:03:21.481695218+00:00
modified: 2024-10-19 09:00:27.867625356+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  We know that when we construct a
  [derivation](Formal_proofs_in_propositional_logic.md#derivation-rules) we start
  from a set of assumptions and then attempt to reach a proposition that is a
  consequence of the starting assumptions. However it does not always have to be
  the case that the starting set contains members. The set can in fact be empty.

body:
  We know that when we construct a
  [derivation](Formal_proofs_in_propositional_logic.md#derivation-rules) we start
  from a set of assumptions and then attempt to reach a proposition that is a
  consequence of the starting assumptions. However it does not always have to be
  the case that the starting set contains members. The set can in fact be empty.

  _Demonstration_

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

  We see in this example that there is no starting set and thus no primary
  assumptions. Instead we start with nothing other than the proposition we wish to
  derive. The proposition is effectively derived from itself. In these scenarios
  we say that we are constructing a derivation from an **empty set**.

  Propositions which possess this property are called theorems:

  > A proposition $P$ or a system of propositions in propositional logic is a
  > theorem in a system of derivation for that logic if $P$ is derivable from the
  > empty set.

  We represent a theorem as:

  $$
  \vdash P
  $$

  (There is no preceding $\Gamma$ as the set is empty. )

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

  # Theorems and empty sets

  We know that when we construct a
  [derivation](Formal_proofs_in_propositional_logic.md#derivation-rules) we start
  from a set of assumptions and then attempt to reach a proposition that is a
  consequence of the starting assumptions. However it does not always have to be
  the case that the starting set contains members. The set can in fact be empty.

  _Demonstration_

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

  We see in this example that there is no starting set and thus no primary
  assumptions. Instead we start with nothing other than the proposition we wish to
  derive. The proposition is effectively derived from itself. In these scenarios
  we say that we are constructing a derivation from an **empty set**.

  Propositions which possess this property are called theorems:

  > A proposition $P$ or a system of propositions in propositional logic is a
  > theorem in a system of derivation for that logic if $P$ is derivable from the
  > empty set.

  We represent a theorem as:

  $$
  \vdash P
  $$

  (There is no preceding $\Gamma$ as the set is empty. )


path: zk/Three_bit_counter.md
sortable_path: zkThree_bit_counter.md
title: 3-bit Counter
checksum: 3ce02ec5bc1d8a10a459d711a57cd60465a70e680b6b4f7b2b66a443232fa403
created: 2024-03-30 09:03:21.492163634+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["logic-gates","binary","memory"]}
lead:
  To demonstrate the use of
  [Flip-Flops](Flip_flops.md) we will
  create the circuit for a 3-bit counter. This simply counts up from 0 to 7
  because 7 is the maximum decimal number we can create with three bits ($2^3$):

body:
  To demonstrate the use of
  [Flip-Flops](Flip_flops.md) we will
  create the circuit for a 3-bit counter. This simply counts up from 0 to 7
  because 7 is the maximum decimal number we can create with three bits ($2^3$):

  | Binary | Decimal |
  | ------ | ------- |
  | 000    | 0       |
  | 001    | 1       |
  | 010    | 2       |
  | 011    | 3       |
  | 100    | 4       |
  | 101    | 5       |
  | 110    | 6       |
  | 111    | 7       |

  The circuit will have three memory components, each representing one bit of the
  3-bit number. When the
  [clock pulses](Clock_signals.md), the
  3-bit number increments by one. We need to synchronise the operation with a
  clock because each bit by itself is meaningless, it only gains meaning by the
  relation it sustains to the other two bits hence it must be kept in sync with
  them.

  We can derive the circuit arrangement by analysing the outputs of a flipflop at
  each pulse:

  | Combined bits | Q2  | Q1  | Q0  | Decimal |
  | ------------- | --- | --- | --- | ------- |
  | 000           | 0   | 0   | 0   | 0       |
  | 001           | 0   | 0   | 1   | 1       |
  | 010           | 0   | 1   | 0   | 2       |
  | 011           | 0   | 1   | 1   | 3       |
  | 100           | 1   | 0   | 0   | 4       |
  | 101           | 1   | 0   | 1   | 5       |
  | 110           | 1   | 1   | 0   | 6       |
  | 111           | 1   | 1   | 1   | 7       |

  If we look at the pattern of each flip-flops' output we notice the following:

  - _Q0_ toggles on every pulse
  - _Q1_ toggles every time _Q0_ was previously 1
  - _Q2_ toggles when both _Q1_ and _Q0_ were previously 1

  This means that to construct a circuit that displays this behaviour we just have
  to use a
  [T flip-flop](Flip_flops.md#t-flip-flops)
  since the only state change we need is a single bit toggle three times that
  retains its value.

  Using these pulse patterns we can construct a circuit as follows:
  ![](/img/3-bit-adder-diagram.png)

  <iframe src="https://circuitverse.org/simulator/embed/3-bit-counter-d33846e3-7538-427d-b4cc-dc64fdaf0af3?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="500" width="600" allowFullScreen></iframe>

  <br />

  > Remember from the table: Q2 is the most significant bit and Q0 is the least
  > significant bit so we should read the output in the interactive circuit
  > backwards, reversing the order. Then we will see that the cicuit is cyling
  > through 0-7

  This is how the circuit works:

  - All three flip-flops are connected to the same clock signal so that they are
    synchnonised
  - Because _T0_ is connected to the voltage source _Q0_ toggles every time the
    clock pulses
  - _T1_ is connected to _Q0_ so a clock pulse causes _Q1_ to toggle only when
    _Q0_ is high in line with the table
  - _T2_ is connected to _Q0 AND Q1_ so _Q2_ only toggles on a clock pulse when
    _Q0_ and _Q1_ are both high

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

  # 3-bit Counter

  To demonstrate the use of
  [Flip-Flops](Flip_flops.md) we will
  create the circuit for a 3-bit counter. This simply counts up from 0 to 7
  because 7 is the maximum decimal number we can create with three bits ($2^3$):

  | Binary | Decimal |
  | ------ | ------- |
  | 000    | 0       |
  | 001    | 1       |
  | 010    | 2       |
  | 011    | 3       |
  | 100    | 4       |
  | 101    | 5       |
  | 110    | 6       |
  | 111    | 7       |

  The circuit will have three memory components, each representing one bit of the
  3-bit number. When the
  [clock pulses](Clock_signals.md), the
  3-bit number increments by one. We need to synchronise the operation with a
  clock because each bit by itself is meaningless, it only gains meaning by the
  relation it sustains to the other two bits hence it must be kept in sync with
  them.

  We can derive the circuit arrangement by analysing the outputs of a flipflop at
  each pulse:

  | Combined bits | Q2  | Q1  | Q0  | Decimal |
  | ------------- | --- | --- | --- | ------- |
  | 000           | 0   | 0   | 0   | 0       |
  | 001           | 0   | 0   | 1   | 1       |
  | 010           | 0   | 1   | 0   | 2       |
  | 011           | 0   | 1   | 1   | 3       |
  | 100           | 1   | 0   | 0   | 4       |
  | 101           | 1   | 0   | 1   | 5       |
  | 110           | 1   | 1   | 0   | 6       |
  | 111           | 1   | 1   | 1   | 7       |

  If we look at the pattern of each flip-flops' output we notice the following:

  - _Q0_ toggles on every pulse
  - _Q1_ toggles every time _Q0_ was previously 1
  - _Q2_ toggles when both _Q1_ and _Q0_ were previously 1

  This means that to construct a circuit that displays this behaviour we just have
  to use a
  [T flip-flop](Flip_flops.md#t-flip-flops)
  since the only state change we need is a single bit toggle three times that
  retains its value.

  Using these pulse patterns we can construct a circuit as follows:
  ![](/img/3-bit-adder-diagram.png)

  <iframe src="https://circuitverse.org/simulator/embed/3-bit-counter-d33846e3-7538-427d-b4cc-dc64fdaf0af3?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="500" width="600" allowFullScreen></iframe>

  <br />

  > Remember from the table: Q2 is the most significant bit and Q0 is the least
  > significant bit so we should read the output in the interactive circuit
  > backwards, reversing the order. Then we will see that the cicuit is cyling
  > through 0-7

  This is how the circuit works:

  - All three flip-flops are connected to the same clock signal so that they are
    synchnonised
  - Because _T0_ is connected to the voltage source _Q0_ toggles every time the
    clock pulses
  - _T1_ is connected to _Q0_ so a clock pulse causes _Q1_ to toggle only when
    _Q0_ is high in line with the table
  - _T2_ is connected to _Q0 AND Q1_ so _Q2_ only toggles on a clock pulse when
    _Q0_ and _Q1_ are both high


path: zk/Transistors.md
sortable_path: zkTransistors.md
title: Transistors
checksum: 0614b5cebf4f7eb2dc413e31ea7e64a83a356d6362d4970ffc14917642857bb7
created: 2024-03-30 09:03:21.508773552+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["logic-gates","binary","electronics"]}
lead:
  A digital circuit requires that electrical phenomena be treated as discrete
  rather than continuous values.

body:
  A digital circuit requires that electrical phenomena be treated as discrete
  rather than continuous values.

  Although a given voltage at a point in the circuit can vary widely, in order to
  represent the binary states of 'on' and 'off' we need it to remain fixed within
  certain narrow parameters. This is achieved with transistors.

  Transistors are an electrical component that is capable of controlling the flow
  of current in the manner of a switch where the 'off' and 'on' states are
  represented by [voltage](Voltage.md) values within set parameters.

  There are different types of transistors but the simplest for the purposes of
  explanation are **bipolar junction transistors**.

  ![BJT transistor terminals](../img/bjt-terminals.jpg)

  The pins:

  - C: collector
  - B: base
  - E: emitter

  Applying a small amount of current at the base allows a larger current to flow
  from the collector to the emitter. Applying current to the base is like turning
  the switch on. Removing this current is like turning the switch off.

  This happens because of how current and voltage interact in a transistor. The
  small base current controls the larger collector-emitter current through a
  process called current amplification.

  When a small voltage is applied to the base, it allows a small current to flow
  from the base to the emitter. This base current triggers a much larger current
  to flow from the collector to the emitter. Think of it like a water valve: a
  small turn of the handle (base current) can control a large flow of water
  (collector-emitter current). The transistor amplifies this effect, allowing a
  tiny input signal to control a much larger output.

  ## Transistors and logic gates

  We can combine transistors to create logic gates. A logic gate is a combination
  of transistors arranged such that the logical function is embodied by the
  characteristic input and output voltages to the transistor.

  For example to create an [AND](Logic_gates.md#and-gate) gate we would have two
  voltage inputs going into two transistors that are connected in sequence. The
  two transistors create a continuous line going from the collector of one to the
  emitter of the other. If either voltage input is low then the voltage of the
  combined line is low (equivalent to the circuit being broken) and there is no
  current flowing.

  ![](/img/and-transistor.png)

  // Add example of OR gate created with transistors

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

  # Transistors

  A digital circuit requires that electrical phenomena be treated as discrete
  rather than continuous values.

  Although a given voltage at a point in the circuit can vary widely, in order to
  represent the binary states of 'on' and 'off' we need it to remain fixed within
  certain narrow parameters. This is achieved with transistors.

  Transistors are an electrical component that is capable of controlling the flow
  of current in the manner of a switch where the 'off' and 'on' states are
  represented by [voltage](Voltage.md) values within set parameters.

  There are different types of transistors but the simplest for the purposes of
  explanation are **bipolar junction transistors**.

  ![BJT transistor terminals](../img/bjt-terminals.jpg)

  The pins:

  - C: collector
  - B: base
  - E: emitter

  Applying a small amount of current at the base allows a larger current to flow
  from the collector to the emitter. Applying current to the base is like turning
  the switch on. Removing this current is like turning the switch off.

  This happens because of how current and voltage interact in a transistor. The
  small base current controls the larger collector-emitter current through a
  process called current amplification.

  When a small voltage is applied to the base, it allows a small current to flow
  from the base to the emitter. This base current triggers a much larger current
  to flow from the collector to the emitter. Think of it like a water valve: a
  small turn of the handle (base current) can control a large flow of water
  (collector-emitter current). The transistor amplifies this effect, allowing a
  tiny input signal to control a much larger output.

  ## Transistors and logic gates

  We can combine transistors to create logic gates. A logic gate is a combination
  of transistors arranged such that the logical function is embodied by the
  characteristic input and output voltages to the transistor.

  For example to create an [AND](Logic_gates.md#and-gate) gate we would have two
  voltage inputs going into two transistors that are connected in sequence. The
  two transistors create a continuous line going from the collector of one to the
  emitter of the other. If either voltage input is low then the voltage of the
  combined line is low (equivalent to the circuit being broken) and there is no
  current flowing.

  ![](/img/and-transistor.png)

  // Add example of OR gate created with transistors


path: zk/Truth_functional_connectives.md
sortable_path: zkTruth_functional_connectives.md
title: Truth-functional connectives
checksum: 3c886282a2241af43af36a2ea37b6afc0518d29737ccc87107855f072226efeb
created: 2024-03-30 09:03:21.539863826+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  Propositions generated from other (simple) propositions by means of
  propositional connectives are
  [compound propositions](Atomic_and_molecular_propositions.md).

body:
  Propositions generated from other (simple) propositions by means of
  propositional connectives are
  [compound propositions](Atomic_and_molecular_propositions.md).

  We know that
  [logically determinant](Logical_indeterminacy.md)
  propositions express a truth value. When simple propositions are joined with a
  connective to make a compound proposition they also have a truth value. This is
  determined by the nature of the connective and the truth value of the
  constituent propositions. We therefore call connectives of this nature truth
  _functional_ connectives since the **truth value of the compound is a function
  of the truth values of its components**.

  > A propositional connective is used truth-functionally if and only if it is
  > used to generate a compound proposition from one or more propositions in such
  > a way that the truth value of the generated compound is wholly determined by
  > the truth-values of those one or more propositions from which the compound is
  > generated, no matter what the truth values may be.

  Each truth-functional connective has a characteristic **truth table**. This
  discloses the conditions under which the constituent propositions have a given
  truth value when combined with one or more connectives.

  We shall now review each of the truth-functional connectives in detail.

  ### Conjunction

  Conjunction is equivalent to the word AND in natural language. We use $\land$ as
  the symbol for this connective.

  A molecular proposition joining two conjuncts P and Q is true iff both conjuncts
  are true and false otherwise:

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  ### Disjunction

  Conjunction is equivalent to the word OR in natural language. We use `v` as the
  symbol of this connective.

  A molecular proposition joining two disjuncts P and Q is true if either disjunct
  is true or if both disjuncts are true and false otherwise. This corresponds to
  the inclusive sense of OR in natural language.

  ```
  P	Q				P	∨	Q
  T	T					T
  T	F					T
  F	T					T
  F	F					F
  ```

  ### Negation

  In contrast to the two previous connectives, negation is a unary connective not
  a binary connective. We use `~` to symbolise negation. It does not join two or
  more propositions, it applies to one proposition as a whole. This can be a
  simple proposition or a complex proposition. It simply negates the truth-value
  of whichever proposition it is applied to. Hence applied to P, it is true if P
  is false. And if P is false, it is true when P is true. !

  ```
  P			~	P
  T			F
  F			T
  ```

  ### Material conditional (a.k.a implication)

  The material conditional approximates the meaning expressed in natural language
  when we say _if_ such-and-such is the case _then_ such-and-such will the case.
  Another way of expressing the sense of the material conditional is to say that
  **P** implies **Q.**

  ```
  If it rains today the pavement will be wet.
  ```

  We call the proposition that expresses the 'if' proposition the **antecedent**
  and the proposition that expresses the 'then' statement the **consequent**. The
  symbol we use to represent the material conditional is `⊃` although you may see
  `→` used as well.

  The truth table is as follows:

  ```
  P	Q				P	⊃	Q
  T	T					T
  T	F					F
  F	T					T
  F	F					T
  ```

  The material conditional is perhaps the least intuitive of the logical
  connectives. The first case (TT) closely matches what we expect the connective
  to mean: it has rained so the pavement is wet. The antecedent is true and
  therefore the consequent is true. This chimes with what we tend to mean by 'if'
  in natural language. In the second case (TF) it also makes sense: the complex
  proposition is false because it rained and the pavement wasn't wet: this negates
  the truth of the expression. The final case (FF) is also straight forward. It
  didn't rain therefore the pavement wasn't wet, thus the overall assertion that
  rain implies wet pavements is retained.

  FT is less intuitive:

  ```
  It did not rain today. The pavement was wet.
  ```

  To some degree one just has to take these statements as axioms, whether or not
  they have intuitive sense is a secondary, more philosophical question. The
  semantic issues arise because we tacitly assume the material conditional to be a
  causal connective: there is something about the nature of **P** that _engenders_
  or _brings about_ **Q** but causality is not a logical concern.

  If we instead just focus on the simple propositions that comprise the truth
  value it is more plausible. In the case of FT we can say it didn't rain yet the
  pavement was wet does not stop the pavement being wet when it rains. The fact
  that I can pour a beer on the pavement thereby making it wet doesn't stop or
  render false the idea that the rain can also make the pavement wet. The same
  explanation covers the FF case: it hasn't rained and so the pavement is not wet
  does not contradict the assertion that when it rains the pavement will be wet.

  Things are elucidated when we look at an equivalent expression of P ⊃ Q, ~P v Q:

  ```
  P	Q				~	P	∨	Q
  T	T						T
  T	F						F
  F	T						T
  F	F						T
  ```

  A disjunction is true whenever either disjunct is true so when both are false
  the overall expression is false, the same as with FT and FF with the material
  conditional.

  ### Material biconditional (a.k.a equivalence)

  The material biconditional equates to the English expression 'if and only if',
  as a conditional connective it therefore avoids some of the perplexity aroused
  by its material cousin. In this scenario both antecedent and consequent have to
  be true for the overall expression to be true. If either is false the complex
  proposition is false. Other ways of expressing the semantics of this connective
  is to say that one proposition implies the other or that **P** and **Q** are
  equivalent.

  ```
  If and only if James studies every day he will pass the exam.
  ```

  There is no possibility in which James passes the exam and has not studied every
  day. If he studies for three out of the seven days leading up to the exam he
  will not pass. Alternatively, there is no possibility that James studied every
  day yet failed the exam. The antecedent and consequent are locked, as indicated
  by the truth-table:

  ```
  P	Q				P	≡	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					T
  ```

  The last condition (FF) maybe requires some explanation: if he has not studied
  every day then he cannot have passed the exam. Therefore, to say that he will
  pass iff he studies every day is rendered true.

  ## Combinations of truth-functional connectives

  ---

  So far we have applied connectives to simple propositions. In so doing we
  generate complex propositions. However propositions and connectives are
  inherently generative: we can build more complex expressions from less complex
  parts, using more than one type of connective or several different connectives
  to make larger complex propositions and express more detailed logical conditions
  ans statements about the world.

  For example the proposition:

  ```
  Socrates was either a philosopher or a drinker but he wasn't a politician.
  ```

  Can be expressed with greater logical clarity as:

  ```
  Socrates was a philosopher or Socrates was a drinker and Socrates was not a politician.
  ```

  Using P for 'Socrates was a philosopher', Q for 'Socrates was a drinker' and R
  for 'Socrates was a politician' we can express this symbolically as:

  ```
  (P v Q) & ~R
  ```

  Which has the truth table:

  ```
  P	Q	R				(	P	∨	Q	)	&	~	R
  T	T	T									F
  T	T	F									T
  T	F	T									F
  T	F	F									T
  F	T	T									F
  F	T	F									T
  F	F	T									F
  F	F	F									F
  ```

  Let's walk through each case where S stands for the overall proposition.

  1. S is false if Socrates was a philosopher, a drinker and a politician.
  1. **S is true if Socrates was a philosopher, a drinker but not a politician.**
  1. S is false if Socrates was a philosopher, a politician but not a drinker.
  1. **S is true if Socrates was a philosopher but not a drinker or politician.**
  1. S is false if Socrates was not a philosopher but was a drinker and politician
  1. **S is true if Socrates was not a philosopher or politician but was a
     drinker.**
  1. S is false if Socrates was neither a philosopher or drinker but was a
     politician.
  1. S is false if Socrates was neither a philosopher, drinker, or politician.

  If we look just at the true cases for simplicity, it becomes obvious that the
  truth value of the whole is a function of the truth-values of the parts.

  At the highest level of generality the proposition is a conjunction with two
  disjuncts: `P v Q` and `~R` . Therefore, for the proposition to be true both
  conjuncts must be true. The first conjunct is true just if one of the
  subordinate disjuncts is true (Socrates is either a philosopher, a drinker, or
  both). The second conjunct is true just if Socrates is not a politician. Thus
  there is only one variation for the second conjunct (not being a politician) and
  two variations for the first conjunct (being a drinker/being a philosopher)
  hence there are three cases where the overall proposition is true.

  ### Logical equivalence

  Once we start working with complex propositions with more than one
  truth-functional connective it becomes clear that the same proposition expressed
  in natural language can be expressed formally more than one way and thus that in
  logical terms, both formal expressions are equivalent. We can prove this
  equivalence by comparing truth tables.

  For example the proposition:

  ```
  I am going to the shops and the gym.
  ```

  Can obviously be expressed formally as:

  ```
  P & Q
  ```

  But also as:

  ```
  ~ (~P v ~Q)
  ```

  And we know this because the truth-tables are identical:consistency

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  ```
  P	Q			~	(	~	P	∨	~	Q	)
  T	T			T
  T	F			F
  F	T			F
  F	F			F
  ```

  Another example of equivalent expressions:

  ```
  Neither Watson or Sherlock Holmes is fond of criminals
  ```

  The first formalisation:

  ```
  ~P & ~Q
  ```

  Equivalent to:

  ```
  ~(P v Q)
  ```

  Again the truth-tables for verification:

  ```
  P	Q				~	P	&	~	Q
  T	T						F
  T	F						F
  F	T						F
  F	F						T
  ```

  `~P & ~Q`

  ```
  P	Q			~	(	P	∨	Q	)
  T	T			F
  T	F			F
  F	T			F
  F	F			T
  ```

  ### Important equivalences

  The example above is a key equivalence that you will encounter a lot especially
  when deriving formal proofs. It goes together with another one. We have noted
  them both below for future reference:

  ```
  ~P & ~Q = ~P v ~Q
  ```

  ```
  ~P v ~Q = ~(P & Q)
  ```

  ## Enforcing binary connectives through bracketing

  If we had a proposition of the form

  ```
  Socrates is man, is mortal and a philosopher.
  ```

  We could not write this as:

  ```
  P & Q & R
  ```

  This would not be a well-formed proposition because at most truth functional
  connectives can only connect two simple propositions. It would not be possible
  to generate truth conditions for this proposition in its current form. Instead
  we introduce brackets to enforce a binary grouping of simple propositions. In
  this instance, the placement of the brackets does not affect the accurate
  interpretation of the truth conditions of the compound, so the following two
  formalisations are equivalent:

  ```
  (P & Q) & R

  P & (Q & R)
  ```

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

  # Truth-functional connectives

  Propositions generated from other (simple) propositions by means of
  propositional connectives are
  [compound propositions](Atomic_and_molecular_propositions.md).

  We know that
  [logically determinant](Logical_indeterminacy.md)
  propositions express a truth value. When simple propositions are joined with a
  connective to make a compound proposition they also have a truth value. This is
  determined by the nature of the connective and the truth value of the
  constituent propositions. We therefore call connectives of this nature truth
  _functional_ connectives since the **truth value of the compound is a function
  of the truth values of its components**.

  > A propositional connective is used truth-functionally if and only if it is
  > used to generate a compound proposition from one or more propositions in such
  > a way that the truth value of the generated compound is wholly determined by
  > the truth-values of those one or more propositions from which the compound is
  > generated, no matter what the truth values may be.

  Each truth-functional connective has a characteristic **truth table**. This
  discloses the conditions under which the constituent propositions have a given
  truth value when combined with one or more connectives.

  We shall now review each of the truth-functional connectives in detail.

  ### Conjunction

  Conjunction is equivalent to the word AND in natural language. We use $\land$ as
  the symbol for this connective.

  A molecular proposition joining two conjuncts P and Q is true iff both conjuncts
  are true and false otherwise:

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  ### Disjunction

  Conjunction is equivalent to the word OR in natural language. We use `v` as the
  symbol of this connective.

  A molecular proposition joining two disjuncts P and Q is true if either disjunct
  is true or if both disjuncts are true and false otherwise. This corresponds to
  the inclusive sense of OR in natural language.

  ```
  P	Q				P	∨	Q
  T	T					T
  T	F					T
  F	T					T
  F	F					F
  ```

  ### Negation

  In contrast to the two previous connectives, negation is a unary connective not
  a binary connective. We use `~` to symbolise negation. It does not join two or
  more propositions, it applies to one proposition as a whole. This can be a
  simple proposition or a complex proposition. It simply negates the truth-value
  of whichever proposition it is applied to. Hence applied to P, it is true if P
  is false. And if P is false, it is true when P is true. !

  ```
  P			~	P
  T			F
  F			T
  ```

  ### Material conditional (a.k.a implication)

  The material conditional approximates the meaning expressed in natural language
  when we say _if_ such-and-such is the case _then_ such-and-such will the case.
  Another way of expressing the sense of the material conditional is to say that
  **P** implies **Q.**

  ```
  If it rains today the pavement will be wet.
  ```

  We call the proposition that expresses the 'if' proposition the **antecedent**
  and the proposition that expresses the 'then' statement the **consequent**. The
  symbol we use to represent the material conditional is `⊃` although you may see
  `→` used as well.

  The truth table is as follows:

  ```
  P	Q				P	⊃	Q
  T	T					T
  T	F					F
  F	T					T
  F	F					T
  ```

  The material conditional is perhaps the least intuitive of the logical
  connectives. The first case (TT) closely matches what we expect the connective
  to mean: it has rained so the pavement is wet. The antecedent is true and
  therefore the consequent is true. This chimes with what we tend to mean by 'if'
  in natural language. In the second case (TF) it also makes sense: the complex
  proposition is false because it rained and the pavement wasn't wet: this negates
  the truth of the expression. The final case (FF) is also straight forward. It
  didn't rain therefore the pavement wasn't wet, thus the overall assertion that
  rain implies wet pavements is retained.

  FT is less intuitive:

  ```
  It did not rain today. The pavement was wet.
  ```

  To some degree one just has to take these statements as axioms, whether or not
  they have intuitive sense is a secondary, more philosophical question. The
  semantic issues arise because we tacitly assume the material conditional to be a
  causal connective: there is something about the nature of **P** that _engenders_
  or _brings about_ **Q** but causality is not a logical concern.

  If we instead just focus on the simple propositions that comprise the truth
  value it is more plausible. In the case of FT we can say it didn't rain yet the
  pavement was wet does not stop the pavement being wet when it rains. The fact
  that I can pour a beer on the pavement thereby making it wet doesn't stop or
  render false the idea that the rain can also make the pavement wet. The same
  explanation covers the FF case: it hasn't rained and so the pavement is not wet
  does not contradict the assertion that when it rains the pavement will be wet.

  Things are elucidated when we look at an equivalent expression of P ⊃ Q, ~P v Q:

  ```
  P	Q				~	P	∨	Q
  T	T						T
  T	F						F
  F	T						T
  F	F						T
  ```

  A disjunction is true whenever either disjunct is true so when both are false
  the overall expression is false, the same as with FT and FF with the material
  conditional.

  ### Material biconditional (a.k.a equivalence)

  The material biconditional equates to the English expression 'if and only if',
  as a conditional connective it therefore avoids some of the perplexity aroused
  by its material cousin. In this scenario both antecedent and consequent have to
  be true for the overall expression to be true. If either is false the complex
  proposition is false. Other ways of expressing the semantics of this connective
  is to say that one proposition implies the other or that **P** and **Q** are
  equivalent.

  ```
  If and only if James studies every day he will pass the exam.
  ```

  There is no possibility in which James passes the exam and has not studied every
  day. If he studies for three out of the seven days leading up to the exam he
  will not pass. Alternatively, there is no possibility that James studied every
  day yet failed the exam. The antecedent and consequent are locked, as indicated
  by the truth-table:

  ```
  P	Q				P	≡	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					T
  ```

  The last condition (FF) maybe requires some explanation: if he has not studied
  every day then he cannot have passed the exam. Therefore, to say that he will
  pass iff he studies every day is rendered true.

  ## Combinations of truth-functional connectives

  ---

  So far we have applied connectives to simple propositions. In so doing we
  generate complex propositions. However propositions and connectives are
  inherently generative: we can build more complex expressions from less complex
  parts, using more than one type of connective or several different connectives
  to make larger complex propositions and express more detailed logical conditions
  ans statements about the world.

  For example the proposition:

  ```
  Socrates was either a philosopher or a drinker but he wasn't a politician.
  ```

  Can be expressed with greater logical clarity as:

  ```
  Socrates was a philosopher or Socrates was a drinker and Socrates was not a politician.
  ```

  Using P for 'Socrates was a philosopher', Q for 'Socrates was a drinker' and R
  for 'Socrates was a politician' we can express this symbolically as:

  ```
  (P v Q) & ~R
  ```

  Which has the truth table:

  ```
  P	Q	R				(	P	∨	Q	)	&	~	R
  T	T	T									F
  T	T	F									T
  T	F	T									F
  T	F	F									T
  F	T	T									F
  F	T	F									T
  F	F	T									F
  F	F	F									F
  ```

  Let's walk through each case where S stands for the overall proposition.

  1. S is false if Socrates was a philosopher, a drinker and a politician.
  1. **S is true if Socrates was a philosopher, a drinker but not a politician.**
  1. S is false if Socrates was a philosopher, a politician but not a drinker.
  1. **S is true if Socrates was a philosopher but not a drinker or politician.**
  1. S is false if Socrates was not a philosopher but was a drinker and politician
  1. **S is true if Socrates was not a philosopher or politician but was a
     drinker.**
  1. S is false if Socrates was neither a philosopher or drinker but was a
     politician.
  1. S is false if Socrates was neither a philosopher, drinker, or politician.

  If we look just at the true cases for simplicity, it becomes obvious that the
  truth value of the whole is a function of the truth-values of the parts.

  At the highest level of generality the proposition is a conjunction with two
  disjuncts: `P v Q` and `~R` . Therefore, for the proposition to be true both
  conjuncts must be true. The first conjunct is true just if one of the
  subordinate disjuncts is true (Socrates is either a philosopher, a drinker, or
  both). The second conjunct is true just if Socrates is not a politician. Thus
  there is only one variation for the second conjunct (not being a politician) and
  two variations for the first conjunct (being a drinker/being a philosopher)
  hence there are three cases where the overall proposition is true.

  ### Logical equivalence

  Once we start working with complex propositions with more than one
  truth-functional connective it becomes clear that the same proposition expressed
  in natural language can be expressed formally more than one way and thus that in
  logical terms, both formal expressions are equivalent. We can prove this
  equivalence by comparing truth tables.

  For example the proposition:

  ```
  I am going to the shops and the gym.
  ```

  Can obviously be expressed formally as:

  ```
  P & Q
  ```

  But also as:

  ```
  ~ (~P v ~Q)
  ```

  And we know this because the truth-tables are identical:consistency

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  ```
  P	Q			~	(	~	P	∨	~	Q	)
  T	T			T
  T	F			F
  F	T			F
  F	F			F
  ```

  Another example of equivalent expressions:

  ```
  Neither Watson or Sherlock Holmes is fond of criminals
  ```

  The first formalisation:

  ```
  ~P & ~Q
  ```

  Equivalent to:

  ```
  ~(P v Q)
  ```

  Again the truth-tables for verification:

  ```
  P	Q				~	P	&	~	Q
  T	T						F
  T	F						F
  F	T						F
  F	F						T
  ```

  `~P & ~Q`

  ```
  P	Q			~	(	P	∨	Q	)
  T	T			F
  T	F			F
  F	T			F
  F	F			T
  ```

  ### Important equivalences

  The example above is a key equivalence that you will encounter a lot especially
  when deriving formal proofs. It goes together with another one. We have noted
  them both below for future reference:

  ```
  ~P & ~Q = ~P v ~Q
  ```

  ```
  ~P v ~Q = ~(P & Q)
  ```

  ## Enforcing binary connectives through bracketing

  If we had a proposition of the form

  ```
  Socrates is man, is mortal and a philosopher.
  ```

  We could not write this as:

  ```
  P & Q & R
  ```

  This would not be a well-formed proposition because at most truth functional
  connectives can only connect two simple propositions. It would not be possible
  to generate truth conditions for this proposition in its current form. Instead
  we introduce brackets to enforce a binary grouping of simple propositions. In
  this instance, the placement of the brackets does not affect the accurate
  interpretation of the truth conditions of the compound, so the following two
  formalisations are equivalent:

  ```
  (P & Q) & R

  P & (Q & R)
  ```


path: zk/Truth_tables.md
sortable_path: zkTruth_tables.md
title: Truth-tables
checksum: b6e6d1948359ad35aa044e4981d8103136f4294c4447567608a2b2a15e38db4d
created: 2024-03-30 09:03:21.555210921+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["logic","propositional-logic"]}
lead:
  We are already familiar with truth-tables from the previous entry on the
  _truth-functional connectives_ and the relationship between sentences,
  connectives and the overall truth-value of a sentence. Here we will look in
  further depth at how to build truth-tables and on their mathematical relation to
  binary truth-values. We will also look at examples of complex truth-tables for
  large compound expressions and the systematic steps we follow to derive the
  truth conditions of compound sentences from their simple constituents.

body:
  We are already familiar with truth-tables from the previous entry on the
  _truth-functional connectives_ and the relationship between sentences,
  connectives and the overall truth-value of a sentence. Here we will look in
  further depth at how to build truth-tables and on their mathematical relation to
  binary truth-values. We will also look at examples of complex truth-tables for
  large compound expressions and the systematic steps we follow to derive the
  truth conditions of compound sentences from their simple constituents.

  ## Formulae for constructing truth-tables

  For any truth-table, the number of rows it will contain is equal to $2n$ where:

  - $n$ stands for the number of sentences
  - $2$ is the total number of possible truth values that the sentence may have:
    true or false.

  When we count the number of sentences, we mean atomic sentences. And we only
  count each sentence once. Hence for a compound sentence of the form
  $(\sim B \supset C) & (A \equiv B)$, $B$ occurs twice but there are only three
  sentences: $A$, $B$, and $C$.

  Thus for the sentence $P & Q$ ,we have two sentences so $n$ is 2 which equals 4
  rows (2 x 2):

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  For the sentence $(P \lor Q) & R$ we have three sentences so $n$ is 3 which
  equals 8 rows (2 x 2 x 2):

  ```
  P	Q	R				(	P	∨	Q	)	&	R
  T	T	T									T
  T	T	F									F
  T	F	T									T
  T	F	F									F
  F	T	T									T
  F	T	F									F
  F	F	T									F
  F	F	F									F
  ```

  For the single sentence $P$ we have one sentence so $n$ is 1 which equals 2 rows
  (2 x 1):

  ```
  P			P
  T			T
  F			F
  ```

  This tells us how many rows the truth-table should have but it doesn't tell us
  what each row should consist in. In other words: how many Ts and Fs it should
  contain. This is fine with simple truth-tables since we can just alternate each
  value but for truth-tables with three sentences and more it is easy to make
  mistakes.

  To simplify this and ensure that we are including the right number of possible
  truth-values we can extend the formula to $2n^-i$. This formula tells us how
  many groups of T and F we should have in each column.

  We can already see that there is a pattern at work by looking at the columns of
  the truth tables above. If we take the sentence $(P \lor Q) & R$ we can see that
  for each sentence:

  - $P$ consists in two sets of ${\textsf{T,T,T,T}}$ and ${\textsf{F,F,F,F}}$ with
    **four** elements per set
  - $Q$ consists in four sets of ${\textsf{T,T}}$ , ${\textsf{F,F}}$,
    ${\textsf{T,T}}$ , ${\textsf{F,F}}$ with **two** elements per set
  - $R$ consists in eight sets of ${\textsf{T}}$, ${\textsf{F}}$, ${\textsf{T}}$,
    ${\textsf{F}}$, ${\textsf{T}}$, ${\textsf{F}}$, ${\textsf{T}}$, ${\textsf{F}}$
    with **one** element per set.

  If we work through the formula we see that it returns 4, 2, 1:

  $$\begin{equation} \begin{split} 2n^-1 = 3 -1 \\ = 2 \\ = 2 \cdot 2 \\ = 4 \end{split} \end{equation}$$

  $$
  \\begin{equation} \begin{split} 2n^-2 = 3 - 2 \\ = 1 \\ = 2 \cdot 1 \\ = 2 \end{split} \end{equation}
  $$

  $$
  \\begin{equation} \begin{split} 2n^-3 = 3 - 3 \\ = 0 \\ = 2 \cdot 0 \\ = 1 \end{split} \end{equation}
  $$

  ## Truth-table concepts

  ### Recursion

  When we move to complex truth-tables with more than one connective we realise
  that truth-tables are recursive. The truth-tables for the truth-functional
  connectives provide all that we need to determine the truth-values of complex
  sentences:

  > The core truth-tables tell us how to determine the truth-value of a molecular
  > sentence given the truth-values of its
  > [immediate sentential components](Syntax%20of%20sentential%20logic.md). And if
  > the immediate sentential components of a molecular sentence are also
  > molecular, we can use the information in the characteristic truth-tables to
  > determine how the truth-value of each immediate component depends n the
  > truth-values of _its_ components and so on.

  ### Truth-value assignment

  > A truth-value assignment is an assignment of truth-values (either T or F) to
  > the atomic sentences of SL.

  When working on complex truth tables, we use the truth-assignment of atomic
  sentences to count as the values that we feed into the larger expressions at a
  higher level of the sentential abstraction.

  ### Partial assignment

  We talk about partial assignments of truth-values when we look at one specific
  row of the truth-table, independently of the others. The total set of partial
  assignments comprise all possible truth assignments for the given sentence.

  ## Working through complex truth-tables

  The truth-table below shows all truth-value assignments for the sentence
  $(\sim B \supset C) & (A \equiv B)$ :

  ```
  A	B	C				(	~	B	⊃	C	)	&	(	A	≡	B	)
  T	T	T					F	T	T	T		T		T	T	T
  T	T	F					F	T	T	F		T		T	T	T
  T	F	T					T	F	T	T		F		T	F	F
  T	F	F					T	F	F	F		F		T	F	F
  F	T	T					F	T	T	T		F		F	F	T
  F	T	F					F	T	T	F		F		F	F	T
  F	F	T					T	F	T	T		T		F	T	F
  F	F	F					T	F	F	F		F		F	T	F
  ```

  As with algebra we work outwards from each set of brackets. The sequence for
  manually arriving at the above table would be roughly as follows:

  1. For each sentence letter, copy the truth value for it in each row.
  1. Identify the connectives in the atomic sentences and the main overall
     sentence.
  1. Work out the truth-values for the smallest connectives and sub-compound
     sentences. The first should always be negation and then the other atomic
     connectives.
  1. Feed-in the truth-values of the atomic sentences as values into the main
     connective, through a process of elimination you then reach the core
     truth-assignments:

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

  # Truth-tables

  We are already familiar with truth-tables from the previous entry on the
  _truth-functional connectives_ and the relationship between sentences,
  connectives and the overall truth-value of a sentence. Here we will look in
  further depth at how to build truth-tables and on their mathematical relation to
  binary truth-values. We will also look at examples of complex truth-tables for
  large compound expressions and the systematic steps we follow to derive the
  truth conditions of compound sentences from their simple constituents.

  ## Formulae for constructing truth-tables

  For any truth-table, the number of rows it will contain is equal to $2n$ where:

  - $n$ stands for the number of sentences
  - $2$ is the total number of possible truth values that the sentence may have:
    true or false.

  When we count the number of sentences, we mean atomic sentences. And we only
  count each sentence once. Hence for a compound sentence of the form
  $(\sim B \supset C) & (A \equiv B)$, $B$ occurs twice but there are only three
  sentences: $A$, $B$, and $C$.

  Thus for the sentence $P & Q$ ,we have two sentences so $n$ is 2 which equals 4
  rows (2 x 2):

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  For the sentence $(P \lor Q) & R$ we have three sentences so $n$ is 3 which
  equals 8 rows (2 x 2 x 2):

  ```
  P	Q	R				(	P	∨	Q	)	&	R
  T	T	T									T
  T	T	F									F
  T	F	T									T
  T	F	F									F
  F	T	T									T
  F	T	F									F
  F	F	T									F
  F	F	F									F
  ```

  For the single sentence $P$ we have one sentence so $n$ is 1 which equals 2 rows
  (2 x 1):

  ```
  P			P
  T			T
  F			F
  ```

  This tells us how many rows the truth-table should have but it doesn't tell us
  what each row should consist in. In other words: how many Ts and Fs it should
  contain. This is fine with simple truth-tables since we can just alternate each
  value but for truth-tables with three sentences and more it is easy to make
  mistakes.

  To simplify this and ensure that we are including the right number of possible
  truth-values we can extend the formula to $2n^-i$. This formula tells us how
  many groups of T and F we should have in each column.

  We can already see that there is a pattern at work by looking at the columns of
  the truth tables above. If we take the sentence $(P \lor Q) & R$ we can see that
  for each sentence:

  - $P$ consists in two sets of ${\textsf{T,T,T,T}}$ and ${\textsf{F,F,F,F}}$ with
    **four** elements per set
  - $Q$ consists in four sets of ${\textsf{T,T}}$ , ${\textsf{F,F}}$,
    ${\textsf{T,T}}$ , ${\textsf{F,F}}$ with **two** elements per set
  - $R$ consists in eight sets of ${\textsf{T}}$, ${\textsf{F}}$, ${\textsf{T}}$,
    ${\textsf{F}}$, ${\textsf{T}}$, ${\textsf{F}}$, ${\textsf{T}}$, ${\textsf{F}}$
    with **one** element per set.

  If we work through the formula we see that it returns 4, 2, 1:

  $$\begin{equation} \begin{split} 2n^-1 = 3 -1 \\ = 2 \\ = 2 \cdot 2 \\ = 4 \end{split} \end{equation}$$

  $$
  \\begin{equation} \begin{split} 2n^-2 = 3 - 2 \\ = 1 \\ = 2 \cdot 1 \\ = 2 \end{split} \end{equation}
  $$

  $$
  \\begin{equation} \begin{split} 2n^-3 = 3 - 3 \\ = 0 \\ = 2 \cdot 0 \\ = 1 \end{split} \end{equation}
  $$

  ## Truth-table concepts

  ### Recursion

  When we move to complex truth-tables with more than one connective we realise
  that truth-tables are recursive. The truth-tables for the truth-functional
  connectives provide all that we need to determine the truth-values of complex
  sentences:

  > The core truth-tables tell us how to determine the truth-value of a molecular
  > sentence given the truth-values of its
  > [immediate sentential components](Syntax%20of%20sentential%20logic.md). And if
  > the immediate sentential components of a molecular sentence are also
  > molecular, we can use the information in the characteristic truth-tables to
  > determine how the truth-value of each immediate component depends n the
  > truth-values of _its_ components and so on.

  ### Truth-value assignment

  > A truth-value assignment is an assignment of truth-values (either T or F) to
  > the atomic sentences of SL.

  When working on complex truth tables, we use the truth-assignment of atomic
  sentences to count as the values that we feed into the larger expressions at a
  higher level of the sentential abstraction.

  ### Partial assignment

  We talk about partial assignments of truth-values when we look at one specific
  row of the truth-table, independently of the others. The total set of partial
  assignments comprise all possible truth assignments for the given sentence.

  ## Working through complex truth-tables

  The truth-table below shows all truth-value assignments for the sentence
  $(\sim B \supset C) & (A \equiv B)$ :

  ```
  A	B	C				(	~	B	⊃	C	)	&	(	A	≡	B	)
  T	T	T					F	T	T	T		T		T	T	T
  T	T	F					F	T	T	F		T		T	T	T
  T	F	T					T	F	T	T		F		T	F	F
  T	F	F					T	F	F	F		F		T	F	F
  F	T	T					F	T	T	T		F		F	F	T
  F	T	F					F	T	T	F		F		F	F	T
  F	F	T					T	F	T	T		T		F	T	F
  F	F	F					T	F	F	F		F		F	T	F
  ```

  As with algebra we work outwards from each set of brackets. The sequence for
  manually arriving at the above table would be roughly as follows:

  1. For each sentence letter, copy the truth value for it in each row.
  1. Identify the connectives in the atomic sentences and the main overall
     sentence.
  1. Work out the truth-values for the smallest connectives and sub-compound
     sentences. The first should always be negation and then the other atomic
     connectives.
  1. Feed-in the truth-values of the atomic sentences as values into the main
     connective, through a process of elimination you then reach the core
     truth-assignments:


path: zk/Truth_trees.md
sortable_path: zkTruth_trees.md
title: Truth trees
lead: ## Rationale
checksum: 345d5759680369e156c87dc4229f912c1cd9ae3319d8d3e509a792ac372c1366
created: 2024-03-30 09:03:21.573138301+00:00
modified: 2024-10-19 09:00:39.867853525+00:00
metadata: {"tags":["logic","propositional-logic"]}
body:
  ## Rationale

  Like [truth-tables](Truth-tables.md), truth-trees are a means of graphically
  representing the logical relationships that may obtain between propositions.
  Truth-trees and truth-tables complement each other and which method you choose
  depends on which logical property you are seeking to derive.

  Whilst truth-tables have the benefit of being exhaustive - every possible truth
  assignment is factored into the representation - their complexity grows
  exponentially with each additional proposition they contain. This can make
  manually constructing truth tables long-winded and prone to mistakes.

  Truth-trees are less onerous but they lack the exhaustive scope of a
  truth-table. They are more targeted and are best used for demonstrating _that
  something is the case_ rather than _all the possible states that could be the
  case_. For example, a truth tree will tell us that a set _S is logically
  consistent_ whereas a truth-table will tell us that _S is consistent on the
  following three assignments._

  ## Logical consistency

  Recall that a set of propositions is logically or truth-functionally
  [consistent](Consistency.md) just if there is at least one assignment of truth
  conditions which results in all members of the set being true. To identify
  consistency for a set of three propositions via the truth table approach we
  would need to construct a truth table with $2^3$ (8) rows. Assume that this set
  is consistent on one partial assignment only. This means that 87.5% of our rows
  are redundant, they are not required to prove the consistency of the set.
  However we can only know this and we can only be sure of consistency once we
  have gone through the process of generating an assignment for each row.

  Truth trees allow us to reduce the amount of work required and go straight to
  the assignment that proves consistency, disregarding the rest which are
  irrelevant.

  ## Truth tree structure and key terms

  **When using a truth tree to derive logical consistency, the goal is to
  determine whether there is a truth-value assignment on which all of the
  sentences of a set are true. If the set is consistent we should be able to
  derive a partial assignment from the tree that demonstrates consistency.**

  Each truth tree begins with a series of sentences one on top of the other in a
  column. We call the sentences that comprise the initial column **set members**.
  In constructing the tree, we work downwards from the initial column decomposing
  set members into their atomic constituents. We a call an atomic sentence that
  has been decomposed a **literal.** A literal will either be an atomic sentence
  or the negation of an atomic sentence. If one of the set members is already a
  literal, there is no need to decompose it; it can remain as it is.

  Once every set member has been decomposed the truth tree is complete. It can
  then be interpreted in order to derive logical consistency or inconsistency. If
  the set is consistent, we are able to derive the partial assignment(s) that
  demonstrate consistency.

  The rules for decomposing compound sentences match the truth conditions of the
  logical connectives. There are rules for every possible connective and the
  negation of every possible connective however in terms of their tree shape they
  all correspond to either a conjunction or a disjunction. Disjunctive
  decomposition results in new branches being formed off the main column (or
  trunk). Conjunctive decomposition is non-branching which means the decomposed
  constituents are placed within the trunk of whichever tree or branch they are
  decomposed within.

  As we construct the tree we list each line in the left-hand margin and the
  decomposition rule in the right-hand margin. When we apply a decomposition rule
  we must cite the lines to which it applies.

  ### Closed and open branches

  Any branch on which an atomic sentence ($P$) and the negation of that sentence
  ($\sim P$) both occur is a **closed branch**. A branch that is not closed is an
  **open branch**. No partial assignment is recoverable from a closed branch. An
  open branch allows truth to ‘flow up’ to the original set members whereas a
  closed branch blocks this passage.

  ### Completed open branch

  A completed open branch occurs when we have an open branch that has been fully
  decomposed: the branch is open and all molecular sentences have been ticked off
  such that it contains only literals.

  ### Completed tree

  A tree where all its branches are either completed open branches or closed
  branches.

  ### Closed tree

  A tree where all the branches are closed

  ### Open tree

  A tree with at least one completed open branch

  ## Deriving consistency

  Using the definitions above, we can now define truth-functional consistency and
  inconsistency in terms of truth trees:

  > A finite set ($\Gamma$ ) of sentences is truth-functionally inconsistent if
  > $\Gamma$ is a closed tree

  > A finite set ($\Gamma$ ) of sentences is truth-functionally consistent if
  > $\Gamma$ is an open tree

  ## Examples

  ### First example

  The following is a truth tree for the set ${P \lor Q, \sim P }$:

  ![basic-open-tree 1.svg](basic-open-tree%201.svg)

  ### Interpretation

  - We decompose the disjunction at line 1 on line 3. We tick off the compound
    sentence to indicate that it is now decomposed and no longer under
    consideration.
  - Both P and its negation exist on a single branch (at line 2 and line 3). This
    makes it a closed branch. We indicate this by the X beneath the branch that is
    closed, citing the source of the closure by line number.
  - The rightward branch is a completed open branch given the decomposition at 3
    and the lack of negation of Q. Overall this makes the tree an open tree.

  As the set gives us an open tree, it must be truth-functionally consistent. If
  this is the case we should be able to determine the partial assignment in which
  each set member is true. Given that Q is not negated the assignment of
  consistency will contain Q but we have both P and ~P. This means there are two
  possible assignments where the set is consistent: $P, Q$ and $\sim P, Q$. This
  is confirmed by the truth-table:

  ```
  P	Q				P	∨	~	P				Q
  T	T					T						T     *
  T	F					T						F
  F	T					T						T     *
  F	F					T						F
  ```

  **Any time there is an open tree with a closed branch it will be the case that
  the negated sentences of the closed branch will appear both as** $S$ and
  $\sim S$ i**n the resultant assignment.**

  Invoking the truth-table highlights the differences between the two techniques.
  The values that are derived when we interpret a truth tree are not the
  truth-functions of the set members but the truth-values for when they are
  simultaneously true. With truth-tables in contrast, we are deriving the truth
  functions for every possible truth-value assignment. In other words the values
  derived from a truth tree correspond to the left hand side of the truth table
  not the right hand side.

  ### Second example

  The following is a truth tree for the set
  ${A & \sim B, C, \sim A \lor \sim B }$.

  ![basic-closed-tree 1.svg](basic-closed-tree%201.svg)

  ### Interpretation

  - The two molecular set members are decomposed. The disjunction (line 3) results
    in a branching tree. The conjunction (line 1) results in the continuation of
    the trunk.
  - Both branches are completed making it a completed tree. As each branch is
    closed this is a closed tree.

  As this is a closed tree, the set is not truth-functionally consistent. This is
  confirmed by the truth table where there is no partial assignment where all set
  members are true.

  ```
  A	B	C				A	&	~	B				C				~	A	∨	~	C
  T	T	T					F						T						F
  T	T	F					F						F						T
  T	F	T					T						T						F
  T	F	F					T						F						T
  F	T	T					F						T						T
  F	T	F					F						F						T
  F	F	T					F						T						T
  F	F	F					F						F						T
  ```

  ## Truth tree decomposition rules

  ---

  So far we have encountered the decomposition rules for conjunction (`&D`) and
  disjunction (`vD`). We will now list all the rules. We will see that for each
  rule, the decomposition either branches or does not branch which is to say that
  each rule either has the shape of a conjunction or a disjunction (however the
  permitted values of the specific disjuncts/conjuncts obviously differ in each
  case). Moreover there is a parallel rule for the decomposition of the negation
  of each of the main connectives and these rules rely on logical equivalences

  ### Negated negation decomposition: `~~D`

  ![negated-negation-decomposition-rule 2.svg](negated-negation-decomposition-rule%202.svg)

  Truth passes only if $P$ is true

  ### Conjunction decomposition: `&D`

  ![conjunction-decomposition-rule.svg](conjunction-decomposition-rule.svg)

  Truth passes only $P$ and $Q$ are both true.

  ### Negated Conjunction decomposition: `~&D`

  ![negated-conjunction-decomposition-rule.svg](negated-conjunction-decomposition-rule.svg)

  Truth passes if either $\sim P$ or $\sim Q$ is true. This rule is a consequence
  of the equivalence between $\sim (P & Q)$ and $\sim P \lor \sim Q$ , the first
  of DeMorgan’s Laws.

  ### Disjunction decomposition: `vD`

  ![disjunction-decomposition-rule.svg](disjunction-decomposition-rule.svg)

  Truth passes if either $P$or $Q$ are true.

  ### Negated Disjunction decomposition: `~vD`

  ![negated-disjunction-decomposition-rule.svg](negated-disjunction-decomposition-rule.svg)

  Truth passes if both $P$ and $Q$ are false. This rule is a consequence of the
  equivalence between $\sim (P \lor Q)$ and $\sim P & \sim Q$, the second of
  DeMorgan’s Laws.

  ### Conditional decomposition: `⊃D`

  ![conditional-decomposition-rule.svg](conditional-decomposition-rule.svg)

  Truth passes if either $\sim P$ or $Q$ are true. This rule is a consequence of
  the equivalence between $P \supset Q$ and $\sim P \lor Q$ therefore this branch
  has the shape of a disjunction with $\sim P$ , $Q$ as its disjuncts.

  ### Negated Conditional decomposition: `~⊃D`

  Truth passes if both $P$ and $\sim Q$ are true. This is a consequence of the
  equivalence between $\sim (P \supset Q)$ and $P & \sim Q$.

  ![negated-conditional-decomposition-rule.svg](negated-conditional-decomposition-rule.svg)

  ### Biconditional decomposition: `≡D`

  ![biconditional-decomposition-rule.drawio(1).svg](biconditional-decomposition-rule.drawio%281%29.svg)

  Truth passes if either $P$ and $Q$ are true or $\sim P & \sim Q$ are true. This
  is an interesting rule because it combines the disjunction and conjunction tree
  shapes.

  ### Negated biconditional decomposition: `~≡D`

  ![negated-biconditional-decomposition-rule.drawio.svg](negated-biconditional-decomposition-rule.drawio.svg)

  Truth passes if either $P$ and $\sim Q$ is true or if $\sim P$ and $Q$ is true.

  ## Further examples and heuristics for complex truth trees

  With truth-trees regardless of which order you decompose the set members, the
  conclusion should always be the same. This said, there more are more efficient
  ways than others to construct the trees. You want to find the route that will
  demonstrate consistency or non-consistency with the shortest amount of work. The
  following heuristic techniques followed in order, facilitate this:

  1. Decompose those molecular sentences the decomposition of which does not
     produce new branches. In other words that are decompositions of double
     negations or pure conjunctions.
  1. Perform those decompositions that will rapidly generate closed branches.
  1. If neither (1) or (2) is applicable, decompose **the most complex** sentence
     first.

  Here are some examples of these rules applied:

  ![complex-tree.svg](complex-tree.svg)

  Observe that here we don’t bother to decompose the sentence on line 1. This is
  because, having decomposed the sentences on lines 2 and 3 we have arrived at a
  closed tree. It is therefore unnecessary to go any further for if two sentences
  in the set are inconsistent with each other, adding another sentence is not
  going to change the overall assignment of inconsistency.

  ## Deriving properties other than logical consistency from truth trees

  So far truth trees have been discussed purely in terms of logical consistency
  however they can be used to derive all the other key truth-functional properties
  of propositional logic. Given the foundational role of consistency to logic,
  these properties are expressible in terms of consistency which is what makes
  them amenable to formulation in terms of truth trees.

  ### Logical falsity

  For a given finite set $\Gamma$, $\Gamma$ is logically consistent just if all of
  its members can be true at once. Expressed in terms of truth trees, this is
  equivalent to an open tree. Contrariwise, $\Gamma$ is inconsistent if it is not
  possible for every member of the set to be true at once. This is the same as a
  tree where all of the branches are closed (i.e. a closed tree).

  When we wish to assess
  [logical falsity](Logical%20truth%20and%20falsity.md#logical-falsity) we are not
  focused on sets however, we are interested in a property of a sentence. However
  we can easily construe single sentences as unit sets: sets with a single member.
  With this in mind and the above accounts of consistency and logical falsity we
  are equipped to express logical falsity in terms of truth-trees with the
  following rule:

  > A sentence $P$ is logically false if and only if the unit set ${ P }$ has a
  > closed tree

  A logically false sentence cannot be true on any assignment. This is the same
  thing as an inconsistent set. Thus it will be represented in a truth tree as
  inconsistency which is disclosed via a closed tree.

  ![logical-falsity-tree.svg](logical-falsity-tree.svg)

  ### Logical truth

  For a sentence $P$ to be
  [logically true](Logical%20truth%20and%20falsity.md#logical-truth), there must
  be no possible assignment in which $P$ is false. We express this informally by
  saying _it is not possible to consistently deny $P$._ We know that in terms of
  truth trees an inconsistent set is a closed tree therefore a unit set of ${ P }$
  is logically true if ${ \sim P }$ is a closed tree. This is to say: if the
  negation of $P$ is inconsistent.

  > A sentence $P$ is logically true if and only if the set ${ \sim P }$ has a
  > closed tree

  ### Logical indeterminacy

  [Indeterminacy](Indeterminacy.md) follows from the two definitions above; we do
  not require any additional apparatus. We recall that a sentence $P$ is logically
  indeterminate just if it is neither logically true or logically false. Thus the
  truth tree for an indeterminate sentence is straightforward:

  > A sentence $P$ is logically indeterminate if and only if neither the set
  >  ${ P }$ nor the set ${ \sim P }$ has a closed tree

  This follows because a closed tree for  ${ P }$ means it is not logically false
  and an open tree for ${ \sim P }$ means it is not logically true. So if it is
  neither of these things, $P$ must be indeterminate.

  ### Logical equivalence

  Recall that $P$ and $Q$ are [logically equivalent](Logical%20equivalence.md)
  just if there is no truth assignment on which one is true and the other is
  false. We know from the
  [material biconditional shorthand](Corresponding%20material%20and%20biconditional.md#corresponding-material-biconditional)
  that this state of affairs can be expressed as $P \equiv Q$ and that if this
  compound sentence is true on every assignment then both simple sentences are
  equivalent. But ‘true on every assignment’ is another way of saying _logically
  true_ since there is no possibility of a false assignment. We already know what
  logical truth looks like as a truth tree: it is a closed tree for the negation
  of the sentence being tested. Therefore, to test the logical equivalence of two
  sentences it is necessary to construct a truth tree for the negation of the
  sentences conjoined by the biconditional (i.e. $\sim (P \equiv Q)$ )and see if
  this results in a closed tree. If it does, the two sentences are logically
  equivalent.

  > Sentences $P$ and $Q$ are truth-functionally equivalent if and only if the set
  > $\sim (P \equiv Q)$ has a closed tree

  ![logical-equivalence-tree.svg](logical-equivalence-tree.svg)

  ### Logical entailment and validity

  Let’s remind ourselves of the meaning of truth-functional
  [entailment](Validity%20and%20entailment.md#entailment) and
  [validity](Validity%20and%20entailment.md#validity) and the relation between the
  two. $\Gamma$ $\vdash$ $P$ is true if and only if there is no truth-assignment
  in which every member of $\Gamma$ is true and $P$ is false. Entailment is
  closely related to validity; it is really just a matter of emphasis: we say that
  $\Gamma$ are the premises and $P$ is the conclusion and that this is a valid
  argument if there is no assignment in which every member of $\Gamma$ is true and
  $P$ is false.

  As with the previous properties, to express validity and entailment in terms of
  truth trees we need to express these concepts in the language of logical
  consistency. $\Gamma$ entails $P$ just if one cannot consistently assert
  $\Gamma$ whilst denying $P$. This is to say that the set $\Gamma \cup {\sim P}$
  is inconsistent. So we just need a closed truth tree for $\Gamma \cup {\sim P}$
  to demonstrate the validity of this set.

  > A finite set of sentences $\Gamma$ truth-functionally entails a sentence $P$
  > if and only if the set $\Gamma \cup {\sim P}$ has a closed truth tree.

  > An argument is truth functionally valid if and only if the set consisting of
  > the premises and the negation of the conclusion has a closed truth tree.

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

  # Truth trees

  ## Rationale

  Like [truth-tables](Truth-tables.md), truth-trees are a means of graphically
  representing the logical relationships that may obtain between propositions.
  Truth-trees and truth-tables complement each other and which method you choose
  depends on which logical property you are seeking to derive.

  Whilst truth-tables have the benefit of being exhaustive - every possible truth
  assignment is factored into the representation - their complexity grows
  exponentially with each additional proposition they contain. This can make
  manually constructing truth tables long-winded and prone to mistakes.

  Truth-trees are less onerous but they lack the exhaustive scope of a
  truth-table. They are more targeted and are best used for demonstrating _that
  something is the case_ rather than _all the possible states that could be the
  case_. For example, a truth tree will tell us that a set _S is logically
  consistent_ whereas a truth-table will tell us that _S is consistent on the
  following three assignments._

  ## Logical consistency

  Recall that a set of propositions is logically or truth-functionally
  [consistent](Consistency.md) just if there is at least one assignment of truth
  conditions which results in all members of the set being true. To identify
  consistency for a set of three propositions via the truth table approach we
  would need to construct a truth table with $2^3$ (8) rows. Assume that this set
  is consistent on one partial assignment only. This means that 87.5% of our rows
  are redundant, they are not required to prove the consistency of the set.
  However we can only know this and we can only be sure of consistency once we
  have gone through the process of generating an assignment for each row.

  Truth trees allow us to reduce the amount of work required and go straight to
  the assignment that proves consistency, disregarding the rest which are
  irrelevant.

  ## Truth tree structure and key terms

  **When using a truth tree to derive logical consistency, the goal is to
  determine whether there is a truth-value assignment on which all of the
  sentences of a set are true. If the set is consistent we should be able to
  derive a partial assignment from the tree that demonstrates consistency.**

  Each truth tree begins with a series of sentences one on top of the other in a
  column. We call the sentences that comprise the initial column **set members**.
  In constructing the tree, we work downwards from the initial column decomposing
  set members into their atomic constituents. We a call an atomic sentence that
  has been decomposed a **literal.** A literal will either be an atomic sentence
  or the negation of an atomic sentence. If one of the set members is already a
  literal, there is no need to decompose it; it can remain as it is.

  Once every set member has been decomposed the truth tree is complete. It can
  then be interpreted in order to derive logical consistency or inconsistency. If
  the set is consistent, we are able to derive the partial assignment(s) that
  demonstrate consistency.

  The rules for decomposing compound sentences match the truth conditions of the
  logical connectives. There are rules for every possible connective and the
  negation of every possible connective however in terms of their tree shape they
  all correspond to either a conjunction or a disjunction. Disjunctive
  decomposition results in new branches being formed off the main column (or
  trunk). Conjunctive decomposition is non-branching which means the decomposed
  constituents are placed within the trunk of whichever tree or branch they are
  decomposed within.

  As we construct the tree we list each line in the left-hand margin and the
  decomposition rule in the right-hand margin. When we apply a decomposition rule
  we must cite the lines to which it applies.

  ### Closed and open branches

  Any branch on which an atomic sentence ($P$) and the negation of that sentence
  ($\sim P$) both occur is a **closed branch**. A branch that is not closed is an
  **open branch**. No partial assignment is recoverable from a closed branch. An
  open branch allows truth to ‘flow up’ to the original set members whereas a
  closed branch blocks this passage.

  ### Completed open branch

  A completed open branch occurs when we have an open branch that has been fully
  decomposed: the branch is open and all molecular sentences have been ticked off
  such that it contains only literals.

  ### Completed tree

  A tree where all its branches are either completed open branches or closed
  branches.

  ### Closed tree

  A tree where all the branches are closed

  ### Open tree

  A tree with at least one completed open branch

  ## Deriving consistency

  Using the definitions above, we can now define truth-functional consistency and
  inconsistency in terms of truth trees:

  > A finite set ($\Gamma$ ) of sentences is truth-functionally inconsistent if
  > $\Gamma$ is a closed tree

  > A finite set ($\Gamma$ ) of sentences is truth-functionally consistent if
  > $\Gamma$ is an open tree

  ## Examples

  ### First example

  The following is a truth tree for the set ${P \lor Q, \sim P }$:

  ![basic-open-tree 1.svg](basic-open-tree%201.svg)

  ### Interpretation

  - We decompose the disjunction at line 1 on line 3. We tick off the compound
    sentence to indicate that it is now decomposed and no longer under
    consideration.
  - Both P and its negation exist on a single branch (at line 2 and line 3). This
    makes it a closed branch. We indicate this by the X beneath the branch that is
    closed, citing the source of the closure by line number.
  - The rightward branch is a completed open branch given the decomposition at 3
    and the lack of negation of Q. Overall this makes the tree an open tree.

  As the set gives us an open tree, it must be truth-functionally consistent. If
  this is the case we should be able to determine the partial assignment in which
  each set member is true. Given that Q is not negated the assignment of
  consistency will contain Q but we have both P and ~P. This means there are two
  possible assignments where the set is consistent: $P, Q$ and $\sim P, Q$. This
  is confirmed by the truth-table:

  ```
  P	Q				P	∨	~	P				Q
  T	T					T						T     *
  T	F					T						F
  F	T					T						T     *
  F	F					T						F
  ```

  **Any time there is an open tree with a closed branch it will be the case that
  the negated sentences of the closed branch will appear both as** $S$ and
  $\sim S$ i**n the resultant assignment.**

  Invoking the truth-table highlights the differences between the two techniques.
  The values that are derived when we interpret a truth tree are not the
  truth-functions of the set members but the truth-values for when they are
  simultaneously true. With truth-tables in contrast, we are deriving the truth
  functions for every possible truth-value assignment. In other words the values
  derived from a truth tree correspond to the left hand side of the truth table
  not the right hand side.

  ### Second example

  The following is a truth tree for the set
  ${A & \sim B, C, \sim A \lor \sim B }$.

  ![basic-closed-tree 1.svg](basic-closed-tree%201.svg)

  ### Interpretation

  - The two molecular set members are decomposed. The disjunction (line 3) results
    in a branching tree. The conjunction (line 1) results in the continuation of
    the trunk.
  - Both branches are completed making it a completed tree. As each branch is
    closed this is a closed tree.

  As this is a closed tree, the set is not truth-functionally consistent. This is
  confirmed by the truth table where there is no partial assignment where all set
  members are true.

  ```
  A	B	C				A	&	~	B				C				~	A	∨	~	C
  T	T	T					F						T						F
  T	T	F					F						F						T
  T	F	T					T						T						F
  T	F	F					T						F						T
  F	T	T					F						T						T
  F	T	F					F						F						T
  F	F	T					F						T						T
  F	F	F					F						F						T
  ```

  ## Truth tree decomposition rules

  ---

  So far we have encountered the decomposition rules for conjunction (`&D`) and
  disjunction (`vD`). We will now list all the rules. We will see that for each
  rule, the decomposition either branches or does not branch which is to say that
  each rule either has the shape of a conjunction or a disjunction (however the
  permitted values of the specific disjuncts/conjuncts obviously differ in each
  case). Moreover there is a parallel rule for the decomposition of the negation
  of each of the main connectives and these rules rely on logical equivalences

  ### Negated negation decomposition: `~~D`

  ![negated-negation-decomposition-rule 2.svg](negated-negation-decomposition-rule%202.svg)

  Truth passes only if $P$ is true

  ### Conjunction decomposition: `&D`

  ![conjunction-decomposition-rule.svg](conjunction-decomposition-rule.svg)

  Truth passes only $P$ and $Q$ are both true.

  ### Negated Conjunction decomposition: `~&D`

  ![negated-conjunction-decomposition-rule.svg](negated-conjunction-decomposition-rule.svg)

  Truth passes if either $\sim P$ or $\sim Q$ is true. This rule is a consequence
  of the equivalence between $\sim (P & Q)$ and $\sim P \lor \sim Q$ , the first
  of DeMorgan’s Laws.

  ### Disjunction decomposition: `vD`

  ![disjunction-decomposition-rule.svg](disjunction-decomposition-rule.svg)

  Truth passes if either $P$or $Q$ are true.

  ### Negated Disjunction decomposition: `~vD`

  ![negated-disjunction-decomposition-rule.svg](negated-disjunction-decomposition-rule.svg)

  Truth passes if both $P$ and $Q$ are false. This rule is a consequence of the
  equivalence between $\sim (P \lor Q)$ and $\sim P & \sim Q$, the second of
  DeMorgan’s Laws.

  ### Conditional decomposition: `⊃D`

  ![conditional-decomposition-rule.svg](conditional-decomposition-rule.svg)

  Truth passes if either $\sim P$ or $Q$ are true. This rule is a consequence of
  the equivalence between $P \supset Q$ and $\sim P \lor Q$ therefore this branch
  has the shape of a disjunction with $\sim P$ , $Q$ as its disjuncts.

  ### Negated Conditional decomposition: `~⊃D`

  Truth passes if both $P$ and $\sim Q$ are true. This is a consequence of the
  equivalence between $\sim (P \supset Q)$ and $P & \sim Q$.

  ![negated-conditional-decomposition-rule.svg](negated-conditional-decomposition-rule.svg)

  ### Biconditional decomposition: `≡D`

  ![biconditional-decomposition-rule.drawio(1).svg](biconditional-decomposition-rule.drawio%281%29.svg)

  Truth passes if either $P$ and $Q$ are true or $\sim P & \sim Q$ are true. This
  is an interesting rule because it combines the disjunction and conjunction tree
  shapes.

  ### Negated biconditional decomposition: `~≡D`

  ![negated-biconditional-decomposition-rule.drawio.svg](negated-biconditional-decomposition-rule.drawio.svg)

  Truth passes if either $P$ and $\sim Q$ is true or if $\sim P$ and $Q$ is true.

  ## Further examples and heuristics for complex truth trees

  With truth-trees regardless of which order you decompose the set members, the
  conclusion should always be the same. This said, there more are more efficient
  ways than others to construct the trees. You want to find the route that will
  demonstrate consistency or non-consistency with the shortest amount of work. The
  following heuristic techniques followed in order, facilitate this:

  1. Decompose those molecular sentences the decomposition of which does not
     produce new branches. In other words that are decompositions of double
     negations or pure conjunctions.
  1. Perform those decompositions that will rapidly generate closed branches.
  1. If neither (1) or (2) is applicable, decompose **the most complex** sentence
     first.

  Here are some examples of these rules applied:

  ![complex-tree.svg](complex-tree.svg)

  Observe that here we don’t bother to decompose the sentence on line 1. This is
  because, having decomposed the sentences on lines 2 and 3 we have arrived at a
  closed tree. It is therefore unnecessary to go any further for if two sentences
  in the set are inconsistent with each other, adding another sentence is not
  going to change the overall assignment of inconsistency.

  ## Deriving properties other than logical consistency from truth trees

  So far truth trees have been discussed purely in terms of logical consistency
  however they can be used to derive all the other key truth-functional properties
  of propositional logic. Given the foundational role of consistency to logic,
  these properties are expressible in terms of consistency which is what makes
  them amenable to formulation in terms of truth trees.

  ### Logical falsity

  For a given finite set $\Gamma$, $\Gamma$ is logically consistent just if all of
  its members can be true at once. Expressed in terms of truth trees, this is
  equivalent to an open tree. Contrariwise, $\Gamma$ is inconsistent if it is not
  possible for every member of the set to be true at once. This is the same as a
  tree where all of the branches are closed (i.e. a closed tree).

  When we wish to assess
  [logical falsity](Logical%20truth%20and%20falsity.md#logical-falsity) we are not
  focused on sets however, we are interested in a property of a sentence. However
  we can easily construe single sentences as unit sets: sets with a single member.
  With this in mind and the above accounts of consistency and logical falsity we
  are equipped to express logical falsity in terms of truth-trees with the
  following rule:

  > A sentence $P$ is logically false if and only if the unit set ${ P }$ has a
  > closed tree

  A logically false sentence cannot be true on any assignment. This is the same
  thing as an inconsistent set. Thus it will be represented in a truth tree as
  inconsistency which is disclosed via a closed tree.

  ![logical-falsity-tree.svg](logical-falsity-tree.svg)

  ### Logical truth

  For a sentence $P$ to be
  [logically true](Logical%20truth%20and%20falsity.md#logical-truth), there must
  be no possible assignment in which $P$ is false. We express this informally by
  saying _it is not possible to consistently deny $P$._ We know that in terms of
  truth trees an inconsistent set is a closed tree therefore a unit set of ${ P }$
  is logically true if ${ \sim P }$ is a closed tree. This is to say: if the
  negation of $P$ is inconsistent.

  > A sentence $P$ is logically true if and only if the set ${ \sim P }$ has a
  > closed tree

  ### Logical indeterminacy

  [Indeterminacy](Indeterminacy.md) follows from the two definitions above; we do
  not require any additional apparatus. We recall that a sentence $P$ is logically
  indeterminate just if it is neither logically true or logically false. Thus the
  truth tree for an indeterminate sentence is straightforward:

  > A sentence $P$ is logically indeterminate if and only if neither the set
  >  ${ P }$ nor the set ${ \sim P }$ has a closed tree

  This follows because a closed tree for  ${ P }$ means it is not logically false
  and an open tree for ${ \sim P }$ means it is not logically true. So if it is
  neither of these things, $P$ must be indeterminate.

  ### Logical equivalence

  Recall that $P$ and $Q$ are [logically equivalent](Logical%20equivalence.md)
  just if there is no truth assignment on which one is true and the other is
  false. We know from the
  [material biconditional shorthand](Corresponding%20material%20and%20biconditional.md#corresponding-material-biconditional)
  that this state of affairs can be expressed as $P \equiv Q$ and that if this
  compound sentence is true on every assignment then both simple sentences are
  equivalent. But ‘true on every assignment’ is another way of saying _logically
  true_ since there is no possibility of a false assignment. We already know what
  logical truth looks like as a truth tree: it is a closed tree for the negation
  of the sentence being tested. Therefore, to test the logical equivalence of two
  sentences it is necessary to construct a truth tree for the negation of the
  sentences conjoined by the biconditional (i.e. $\sim (P \equiv Q)$ )and see if
  this results in a closed tree. If it does, the two sentences are logically
  equivalent.

  > Sentences $P$ and $Q$ are truth-functionally equivalent if and only if the set
  > $\sim (P \equiv Q)$ has a closed tree

  ![logical-equivalence-tree.svg](logical-equivalence-tree.svg)

  ### Logical entailment and validity

  Let’s remind ourselves of the meaning of truth-functional
  [entailment](Validity%20and%20entailment.md#entailment) and
  [validity](Validity%20and%20entailment.md#validity) and the relation between the
  two. $\Gamma$ $\vdash$ $P$ is true if and only if there is no truth-assignment
  in which every member of $\Gamma$ is true and $P$ is false. Entailment is
  closely related to validity; it is really just a matter of emphasis: we say that
  $\Gamma$ are the premises and $P$ is the conclusion and that this is a valid
  argument if there is no assignment in which every member of $\Gamma$ is true and
  $P$ is false.

  As with the previous properties, to express validity and entailment in terms of
  truth trees we need to express these concepts in the language of logical
  consistency. $\Gamma$ entails $P$ just if one cannot consistently assert
  $\Gamma$ whilst denying $P$. This is to say that the set $\Gamma \cup {\sim P}$
  is inconsistent. So we just need a closed truth tree for $\Gamma \cup {\sim P}$
  to demonstrate the validity of this set.

  > A finite set of sentences $\Gamma$ truth-functionally entails a sentence $P$
  > if and only if the set $\Gamma \cup {\sim P}$ has a closed truth tree.

  > An argument is truth functionally valid if and only if the set consisting of
  > the premises and the negation of the conclusion has a closed truth tree.


path: zk/Tuples_in_Python.md
sortable_path: zkTuples_in_Python.md
title: Tuples in Python
lead: TODO: Exapand tuple notes - give more use cases
checksum: 030111bc7de9da55f9dfd32fbbf6212bc9095e33ae0e9729bc59f02b2410c4b6
created: 2024-03-30 09:03:21.65884988+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["python","data-structures"]}
body:
  TODO: Exapand tuple notes - give more use cases

  Tuples are one of the main data-structures or containers in Python. Tuples are
  useful in cases where you want to group related data and ensure that it will not
  change.

  Tuples have the following properties:

  - They are **ordered**
  - They have a **fixed size**
  - They are **immutable** and cannot be modified
  - **Allow duplicate** members
  - They are **indexed**

  > In essence a tuple is a list that is immutable.

  As with all containers in Python they permit any data type.

  > Tuples are denoted with `(...)`

  ## Basic usage

  ```python
  tup1 = (1, 3, 5, 7)
  print(tup1[2])

  # 5
  """
  ```

  ## Slicing

  ```python
  tup1 = (1, 3, 5, 7)

  print(tup1[1:3])
  print(tup1[:3])
  print(tup1[1:])
  print(tup1[::-1])

  """
  (3, 5)
  (1, 3, 5)
  (3, 5, 7)
  (7, 5, 3, 1)
  """
  ```

  ## Looping

  ```python
  tup3 = ('apple', 'pear', 'orange', 'plum', 'apple')
  for x in tup3:
      print(x)

  """
  apple
  pear
  orange
  plum
  apple
  """
  ```

  ## Useful methods and predicates

  ```python
  tup3 = ('apple', 'pear', 'orange', 'plum', 'apple')

  # Count instances of a member
  print(tup3.count('apple'))
  # 2

  # Get index of a member
  print(tup3.index('pear'))
  # 1

  # Check for membership
  if 'orange' in tup3:
      print('orange is in the Tuple')

  # orange is in the Tuple
  ```

  ## Nest tuples

  ```python

  tuple2 = ('John', 'Denise', 'Phoebe', 'Adam')
  tuple3 = (42, tuple1, tuple2, 5.5)
  print(tuple3)

  # (42, (1, 3, 5, 7), ('John', 'Denise', 'Phoebe', 'Adam'), 5.5)

  ```

  // TODO: How to flatten a tuple?

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

  # Tuples in Python

  TODO: Exapand tuple notes - give more use cases

  Tuples are one of the main data-structures or containers in Python. Tuples are
  useful in cases where you want to group related data and ensure that it will not
  change.

  Tuples have the following properties:

  - They are **ordered**
  - They have a **fixed size**
  - They are **immutable** and cannot be modified
  - **Allow duplicate** members
  - They are **indexed**

  > In essence a tuple is a list that is immutable.

  As with all containers in Python they permit any data type.

  > Tuples are denoted with `(...)`

  ## Basic usage

  ```python
  tup1 = (1, 3, 5, 7)
  print(tup1[2])

  # 5
  """
  ```

  ## Slicing

  ```python
  tup1 = (1, 3, 5, 7)

  print(tup1[1:3])
  print(tup1[:3])
  print(tup1[1:])
  print(tup1[::-1])

  """
  (3, 5)
  (1, 3, 5)
  (3, 5, 7)
  (7, 5, 3, 1)
  """
  ```

  ## Looping

  ```python
  tup3 = ('apple', 'pear', 'orange', 'plum', 'apple')
  for x in tup3:
      print(x)

  """
  apple
  pear
  orange
  plum
  apple
  """
  ```

  ## Useful methods and predicates

  ```python
  tup3 = ('apple', 'pear', 'orange', 'plum', 'apple')

  # Count instances of a member
  print(tup3.count('apple'))
  # 2

  # Get index of a member
  print(tup3.index('pear'))
  # 1

  # Check for membership
  if 'orange' in tup3:
      print('orange is in the Tuple')

  # orange is in the Tuple
  ```

  ## Nest tuples

  ```python

  tuple2 = ('John', 'Denise', 'Phoebe', 'Adam')
  tuple3 = (42, tuple1, tuple2, 5.5)
  print(tuple3)

  # (42, (1, 3, 5, 7), ('John', 'Denise', 'Phoebe', 'Adam'), 5.5)

  ```

  // TODO: How to flatten a tuple?


path: zk/Turing_machines.md
sortable_path: zkTuring_machines.md
title: Turing Machines
lead: ## What is a Turing Machine?
checksum: 77c4c42bdf565593e4df5a982bb47a1527efcfc69dd5cf0904e13b214dea73d8
created: 2024-03-30 09:03:21.664596419+00:00
modified: 2024-10-19 09:00:57.414852952+00:00
metadata: {"tags":["theory-of-computation","Turing"]}
body:
  ## What is a Turing Machine?

  Turing Machine is a machine that contains mutable state, executes sequences of
  simple instructions that read and write that state, and can pick different
  execution paths depending on the state (via conditional branch instructions.)

  This is a general expression, it is typically expanded as follows.

  The machine consists of an infinitely long tape, that has been divided up into
  cells. Each cell can contain either a 1, a 0 or an empty space. Above one cell
  of the tape is a head, which can either move left or right, and can read the
  symbols written in the cells. The head is also capable of erasing symbols and
  writing new symbols into the cells.

  ![Turing_machines_01.gif](Turing_machines_01.gif)

  The direction that the head moves, which values it erases, and which values it
  writes in, are dependent on a set of instructions provided to the machine.

  Different sets of instructions can be divided into **states.** States are like
  sub-routines and can themselves feature as part of instructions.

  For example:

  ### State 2

  - If 0 then erase
  - Write 1 then move right
  - Go to state 5

  ### State 5

  - If 1, then erase
  - Write 0 then move left
  - Go to state _n_

  Alan Turing proved that **any problem that is computable** can be computed by a
  Turing Machine using this simple system.

raw_content:
  ---
  tags:
    - theory-of-computation
    - Turing
  ---

  # Turing Machines

  ## What is a Turing Machine?

  Turing Machine is a machine that contains mutable state, executes sequences of
  simple instructions that read and write that state, and can pick different
  execution paths depending on the state (via conditional branch instructions.)

  This is a general expression, it is typically expanded as follows.

  The machine consists of an infinitely long tape, that has been divided up into
  cells. Each cell can contain either a 1, a 0 or an empty space. Above one cell
  of the tape is a head, which can either move left or right, and can read the
  symbols written in the cells. The head is also capable of erasing symbols and
  writing new symbols into the cells.

  ![Turing_machines_01.gif](Turing_machines_01.gif)

  The direction that the head moves, which values it erases, and which values it
  writes in, are dependent on a set of instructions provided to the machine.

  Different sets of instructions can be divided into **states.** States are like
  sub-routines and can themselves feature as part of instructions.

  For example:

  ### State 2

  - If 0 then erase
  - Write 1 then move right
  - Go to state 5

  ### State 5

  - If 1, then erase
  - Write 0 then move left
  - Go to state _n_

  Alan Turing proved that **any problem that is computable** can be computed by a
  Turing Machine using this simple system.


path: zk/Twos_complement.md
sortable_path: zkTwos_complement.md
title: Two's complement
lead: ## Summary
checksum: 6fb7bb890f9352267a3cd807838ba5cc81c5affae5d5d6fc0528d34dde324d4d
created: 2024-03-30 09:03:21.670206681+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"created":"Tuesday, March 19, 2024","id":"gktb","tags":["binary"]}
body:
  ## Summary

  - _Two's complement_ is a method for representing signed numbers (negative
    integers) in binary.

  - It is derived by inverting the values of an unsigned binary integer to create
    its signed equivalent.

  - A benefit is that hardware implementation of the binary arithmetic of signed
    and unsigned numbers can be handled in the same manner as unsigned numbers,
    requiring no additional handling. A drawback is that it halves the
    informational capacity of the given word length for the binary system.

  ## Detail

  ### Procedural steps

  Two's complement divides the available word length (see
  [[Binary_encoding|binary encoding]]) into two subsets: one for negative integrs
  and one for positive integers.

  Take the binary encoding of decimal five (`0101`). Its complement is `1011`.

  The procedure for deriving the complement is as follows.

  To derive the complement of an unsigned number:

  1. Take the unsigned number and invert its digits: `0` becomes `1`, `1` becomes
     `0`
  2. Add one

  ![](/img/unsigned-to-signed.png)

  To derive the unsigned equivalent of a signed number you invert the process but
  still make the smallest digit `1`:

  ![](/img/signed-to-unsigned.png)

  ### Formal expression

  $$
      2^n - x
  $$

  - where $x$ is the negative integer in binary that we wish to derive
  - where $n$ is the word length of the binary system in bits.

  Applied to the earlier example we have $2^4 -5$ which is:

  $$
      16 - 5 = 11
  $$

  When we convert the decimal `11` to binary we get `1011` which is identical to
  the signed version of the unsigned integer.

  We can confirm the correctness of the derviation by summing the signed and
  unsigned binary values. If this results in zeros (ignoring the overflow bit),
  the derivation is correct as the two values effectively cancel each other out:

  $$

    1011 + 0101 = 0000
  $$

  ### Advantages

  - The circuit implementation of arithmetic involving positive and negative
    integers is the same as the implementation of positive integers. There is no
    need for additional harware or special handling of the values.
  - This can be contrasted with the alternative approaches to signing numbers such
    as **signed magnitude representation** which uses certain bits as designators
    of negative/positive status.

  ### Limitations

  - Two's complement reduces the overall informational capacity of the given
    binary word length, effectively halving the total number of unique values.

  - In a 4-bit system instead of 16 total unique encodings of integers you have 8
    encodings for positive integers and 8 encodings for the their signed
    equivalent. For integers larger than denary 8 you would need to increase the
    bit length of the system

  - Consequently two's complement can necessitate larger overall word lengths.

  ## Related notes

  [[Signed_and_unsigned_numbers|signed_and_unsigned_numbers]],
  [[Binary_addition|binary addition]], [[Signed_magnitude_representation]]

raw_content:
  ---
  id: gktb
  tags:
    - binary
  created: Tuesday, March 19, 2024
  ---

  # Two's complement

  ## Summary

  - _Two's complement_ is a method for representing signed numbers (negative
    integers) in binary.

  - It is derived by inverting the values of an unsigned binary integer to create
    its signed equivalent.

  - A benefit is that hardware implementation of the binary arithmetic of signed
    and unsigned numbers can be handled in the same manner as unsigned numbers,
    requiring no additional handling. A drawback is that it halves the
    informational capacity of the given word length for the binary system.

  ## Detail

  ### Procedural steps

  Two's complement divides the available word length (see
  [[Binary_encoding|binary encoding]]) into two subsets: one for negative integrs
  and one for positive integers.

  Take the binary encoding of decimal five (`0101`). Its complement is `1011`.

  The procedure for deriving the complement is as follows.

  To derive the complement of an unsigned number:

  1. Take the unsigned number and invert its digits: `0` becomes `1`, `1` becomes
     `0`
  2. Add one

  ![](/img/unsigned-to-signed.png)

  To derive the unsigned equivalent of a signed number you invert the process but
  still make the smallest digit `1`:

  ![](/img/signed-to-unsigned.png)

  ### Formal expression

  $$
      2^n - x
  $$

  - where $x$ is the negative integer in binary that we wish to derive
  - where $n$ is the word length of the binary system in bits.

  Applied to the earlier example we have $2^4 -5$ which is:

  $$
      16 - 5 = 11
  $$

  When we convert the decimal `11` to binary we get `1011` which is identical to
  the signed version of the unsigned integer.

  We can confirm the correctness of the derviation by summing the signed and
  unsigned binary values. If this results in zeros (ignoring the overflow bit),
  the derivation is correct as the two values effectively cancel each other out:

  $$

    1011 + 0101 = 0000
  $$

  ### Advantages

  - The circuit implementation of arithmetic involving positive and negative
    integers is the same as the implementation of positive integers. There is no
    need for additional harware or special handling of the values.
  - This can be contrasted with the alternative approaches to signing numbers such
    as **signed magnitude representation** which uses certain bits as designators
    of negative/positive status.

  ### Limitations

  - Two's complement reduces the overall informational capacity of the given
    binary word length, effectively halving the total number of unique values.

  - In a 4-bit system instead of 16 total unique encodings of integers you have 8
    encodings for positive integers and 8 encodings for the their signed
    equivalent. For integers larger than denary 8 you would need to increase the
    bit length of the system

  - Consequently two's complement can necessitate larger overall word lengths.

  ## Related notes

  [[Signed_and_unsigned_numbers|signed_and_unsigned_numbers]],
  [[Binary_addition|binary addition]], [[Signed_magnitude_representation]]


path: zk/Type_hinting.md
sortable_path: zkType_hinting.md
title: Type hinting in Python
checksum: 2edd704a1bbc701030556aa35823683193b46331daf39f1bed8c62a50c08a78b
created: 2024-03-30 09:03:21.720533103+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["python","data-types"]}
lead:
  With type hinting we can add type information to variables, functions, and
  classes. This is not enforced by the Python interpreter but can be used by
  external tools like `mypy` to check the code.

body:
  With type hinting we can add type information to variables, functions, and
  classes. This is not enforced by the Python interpreter but can be used by
  external tools like `mypy` to check the code.

  Prior to 3.10, to use types you would have to import the types you wanted `from`
  the `typing` module. With later versions you can use them directly without
  importing from `typing`.

  ## Basic syntax

  ### Variables

  ```py
  name: str = "John"
  age: int = 30
  ```

  ### Functions

  ```py
  def greet(name: str) -> str:
      return "Hello, " + name
  ```

  ### Classes

  // TODO: Add examples

  ## Complex data types

  We can type complex data types such as dictionaries, lists, tuples etc.

  The following function takes a tuple comprising two integers as a parameter and
  returns a list of ints:

  ```py
  def populate_pair_values(pair: tuple[int, int]) -> list[int]:
      return [i for i in range(pair[0], pair[1] + 1)]
  ```

  ## Custom dictionary types

  We can create custom dictionary types in the manner of TypeScript types and
  interfaces as follows:

  ```py
  class ArticleInfo(TypedDict):
      timestamp: str
      article_    link: str
  ```

  We could then use it thus:

  ```py
  def parse_articles() -> list[ArticleInfo]:
  ```

  ## Optional types

  `Optional` can be used to indicate that a variable can be `None` or the
  specified type.

  ```py
  from typing import Optional

  def find_index(numbers: list[int], target: int) -> Optional[int]:
      try:
          return numbers.index(target)
      except ValueError:
          return None
  ```

  The function above returns an `int` or `None`.

  Post 3.10, we don't need to use `Optional`, we can use a union to cover the
  `None` case. Refactoring the previous example:

  ```py
  def find_index(numbers: list[int], target: int) -> int | None:
      try:
          return numbers.index(target)
      except ValueError:
          return None
  ```

  ## Union types

  `Union` can be used to indicate that a variable can be one of several types.

  ```py
  from typing import Union

  def add(a: Union[int, float], b: Union[int, float]) -> Union[int, float]:
      return a + b
  ```

  The function above returns an `int` or `float`.

  > Note that in the case of `Optional` and `Union` the type is specified using
  > square brackets. This is a syntactic covnention and does not denote that the
  > return type is a list. Below is an example of how we would use type hinting to
  > indicate that the return type actually is a list:

  ```py
  from typing import List, Union
  def my_function() -> List[int]:
      return [1, 2, 3]

  # And to show a return type of a list of integers or floats:

  def my_function() -> List[Union[int, float]]:
      return [1, 2, 3]
  ```

  Post 3.10 we can use `|` syntax instead of importing `Union`. E.g:

  ```py

  def add(a: int | float, b: int | float) -> int | float:
      return a + b
  ```

raw_content:
  ---
  tags:
    - python
    - data-types
  ---

  # Type hinting in Python

  With type hinting we can add type information to variables, functions, and
  classes. This is not enforced by the Python interpreter but can be used by
  external tools like `mypy` to check the code.

  Prior to 3.10, to use types you would have to import the types you wanted `from`
  the `typing` module. With later versions you can use them directly without
  importing from `typing`.

  ## Basic syntax

  ### Variables

  ```py
  name: str = "John"
  age: int = 30
  ```

  ### Functions

  ```py
  def greet(name: str) -> str:
      return "Hello, " + name
  ```

  ### Classes

  // TODO: Add examples

  ## Complex data types

  We can type complex data types such as dictionaries, lists, tuples etc.

  The following function takes a tuple comprising two integers as a parameter and
  returns a list of ints:

  ```py
  def populate_pair_values(pair: tuple[int, int]) -> list[int]:
      return [i for i in range(pair[0], pair[1] + 1)]
  ```

  ## Custom dictionary types

  We can create custom dictionary types in the manner of TypeScript types and
  interfaces as follows:

  ```py
  class ArticleInfo(TypedDict):
      timestamp: str
      article_    link: str
  ```

  We could then use it thus:

  ```py
  def parse_articles() -> list[ArticleInfo]:
  ```

  ## Optional types

  `Optional` can be used to indicate that a variable can be `None` or the
  specified type.

  ```py
  from typing import Optional

  def find_index(numbers: list[int], target: int) -> Optional[int]:
      try:
          return numbers.index(target)
      except ValueError:
          return None
  ```

  The function above returns an `int` or `None`.

  Post 3.10, we don't need to use `Optional`, we can use a union to cover the
  `None` case. Refactoring the previous example:

  ```py
  def find_index(numbers: list[int], target: int) -> int | None:
      try:
          return numbers.index(target)
      except ValueError:
          return None
  ```

  ## Union types

  `Union` can be used to indicate that a variable can be one of several types.

  ```py
  from typing import Union

  def add(a: Union[int, float], b: Union[int, float]) -> Union[int, float]:
      return a + b
  ```

  The function above returns an `int` or `float`.

  > Note that in the case of `Optional` and `Union` the type is specified using
  > square brackets. This is a syntactic covnention and does not denote that the
  > return type is a list. Below is an example of how we would use type hinting to
  > indicate that the return type actually is a list:

  ```py
  from typing import List, Union
  def my_function() -> List[int]:
      return [1, 2, 3]

  # And to show a return type of a list of integers or floats:

  def my_function() -> List[Union[int, float]]:
      return [1, 2, 3]
  ```

  Post 3.10 we can use `|` syntax instead of importing `Union`. E.g:

  ```py

  def add(a: int | float, b: int | float) -> int | float:
      return a + b
  ```


path: zk/Update_an_SQL_table.md
sortable_path: zkUpdate_an_SQL_table.md
title: Updating an SQL table
checksum: 9645a802e36a0c8433bba7eea42e93ce70bebe748378d2896fd9a1f511bf77c5
created: 2024-03-30 09:03:21.7468863+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["SQL","databases"]}
lead:
  We use the `ALTER` query to add, remove and otherwise change the structural
  properties of a table in the database.

body:
  We use the `ALTER` query to add, remove and otherwise change the structural
  properties of a table in the database.

  ## Add an additional field to existing table

  This adds a `price` field to the `sales` table. The `price` field accepts data
  of the type `real`.

  ```sql
  ALTER TABLE sales ADD price real;
  ```

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

  # Updating an SQL table

  We use the `ALTER` query to add, remove and otherwise change the structural
  properties of a table in the database.

  ## Add an additional field to existing table

  This adds a `price` field to the `sales` table. The `price` field accepts data
  of the type `real`.

  ```sql
  ALTER TABLE sales ADD price real;
  ```


path: zk/Update_existing_data_in_SQL_table.md
sortable_path: zkUpdate_existing_data_in_SQL_table.md
title: Update existing data with the SQL UPDATE command
lead: We use `UPDATE` to modify existing records in our table.
checksum: 8d899c4df6b07aa260bf8286610be856b821f2fdc69aaebbd7557a866cf15cf5
created: 2024-03-30 09:03:21.766801302+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["SQL","databases"]}
body:
  We use `UPDATE` to modify existing records in our table.

  ## Schematic syntax

  ```sql
  UPDATE [table_name]
  SET [field]
  WHERE [conditional expression/filter]
  ```

  ## Example

  ```sql
  UPDATE manufacturer
  SET url = '<http://www.hp.co.uk>'
  WHERE manufacturer_id = 4; --typically this will be the primary key as you are updating and existing record and need to identify it uniquely
  ```

  ## Update multiple fields

  ```sql
  UPDATE manufacturer
  SET url = '<http://www.apple.co.uk>',
      year_founded = 1977
  WHERE manufacturer_id = 2;
  ```

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

  # Update existing data with the SQL `UPDATE` command

  We use `UPDATE` to modify existing records in our table.

  ## Schematic syntax

  ```sql
  UPDATE [table_name]
  SET [field]
  WHERE [conditional expression/filter]
  ```

  ## Example

  ```sql
  UPDATE manufacturer
  SET url = '<http://www.hp.co.uk>'
  WHERE manufacturer_id = 4; --typically this will be the primary key as you are updating and existing record and need to identify it uniquely
  ```

  ## Update multiple fields

  ```sql
  UPDATE manufacturer
  SET url = '<http://www.apple.co.uk>',
      year_founded = 1977
  WHERE manufacturer_id = 2;
  ```


path: zk/Useful_operators_in_SQL.md
sortable_path: zkUseful_operators_in_SQL.md
title: Useful operators in SQL
lead: ## True and false
checksum: 378ed8a4886205275eb46560a2a2acab4e6a008ca30aa641e5a9529d3500620c
created: 2024-03-30 09:03:21.771065785+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["SQL","databases"]}
body:
  ## True and false

  > In SQL false results are represented by `0` and true results are represented
  > by `1`. Any zero value is false and any non-zero value is true.

  > When comparing operators we can use `SELECT` as a general "execute" function
  > for SQL to interpret

  ```sql
  SELECT 0 = 0 -- 1
  SELECT 0.0 = 0 -- 1
  SELECT 9 >= 12 -- 0
  ```

  ## Boolean operators

  SQL supports all Boolean operators:

  ```sql
  SELECT (9 > 7) AND (3 < 5) -- 1
  SELECT (3 > 1) OR (2 > 5) -- 1
  SELECT (3 > 1) XOR (5 > 1) -- 0
  ```

  We also have `IS` and `IS NOT` for testing Boolean values:

  ```sql
  SELECT (9 > 7) IS TRUE -- 1
  SELECT (9 > 7) IS NOT TRUE -- 0
  ```

  ## `NULL`

  - `NULL` is neither true (1) or false (0). It has no value.
  - An empty string (`""`) is **not** `NULL`

  ```sql
  SELECT 1 IS NULL -- 0
  SELECT 1 IS NOT NULL -- 1
  SELECT NULL IS NULL -- 1
  SELECT "" IS NULL -- 0
  ```

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

  # Useful operators in SQL

  ## True and false

  > In SQL false results are represented by `0` and true results are represented
  > by `1`. Any zero value is false and any non-zero value is true.

  > When comparing operators we can use `SELECT` as a general "execute" function
  > for SQL to interpret

  ```sql
  SELECT 0 = 0 -- 1
  SELECT 0.0 = 0 -- 1
  SELECT 9 >= 12 -- 0
  ```

  ## Boolean operators

  SQL supports all Boolean operators:

  ```sql
  SELECT (9 > 7) AND (3 < 5) -- 1
  SELECT (3 > 1) OR (2 > 5) -- 1
  SELECT (3 > 1) XOR (5 > 1) -- 0
  ```

  We also have `IS` and `IS NOT` for testing Boolean values:

  ```sql
  SELECT (9 > 7) IS TRUE -- 1
  SELECT (9 > 7) IS NOT TRUE -- 0
  ```

  ## `NULL`

  - `NULL` is neither true (1) or false (0). It has no value.
  - An empty string (`""`) is **not** `NULL`

  ```sql
  SELECT 1 IS NULL -- 0
  SELECT 1 IS NOT NULL -- 1
  SELECT NULL IS NULL -- 1
  SELECT "" IS NULL -- 0
  ```


path: zk/User_Space.md
sortable_path: zkUser_Space.md
title: User space
lead:
  User space is the portion of the main memory that the kernel allocates for user
  processes.

checksum: 9fda30f69fca66abe1243711ac15679517d775e629de2c0d9175dff66ed21cc5
created: 2024-03-30 09:03:21.777096563+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["operating-systems"]}
body:
  User space is the portion of the main memory that the kernel allocates for user
  processes.

  There is a hierarchy within the user space with several different groups of user
  processes:

  <dl>

  <dt>Basic services</dt>
  <dd>Run at the bottom level and therefore are closest to ther kernel. Comprises small components that perform single, uncomplicated tasks. Examples include network configuration, the communication bus, and diagnostic logging</dd> 
  <dt>Utiility services</dt>
  <dd>The middle level: larger components such as mail, print, and database services</dd>
  <dt>Services controllable by the user</dt>
  <dd>The top level: complicated tasks that the user controls directly such as using a web-browser, editing a text file</dd>
  </dl>

  ## What is a user?

  > A user is an entity that can run processes and own files.

  At the level of users, a user is associated with a user name however the kernel
  does not manage usernames, it identifies users via numeric identifiers called
  **user IDs**.

  Users exist in order to support permissions and access boundaries. A user may
  terminate or modify the behaviour of its own processes (within limits) but
  cannot interfere with another user's proceses. In addition, users may own files
  and choose whether to share them with other users.

  ### Root user (superuser)

  A Linux system normally has a number of users in addition to the ones that
  correspond to the real human users. The most important is **root**. The root
  user _can_ terminate and alter another user's processes and access any file on
  the local system.

  A person who can operate as root is an administrator on a typical user system.

raw_content:
  ---
  tags:
    - operating-systems
  ---

  # User space

  User space is the portion of the main memory that the kernel allocates for user
  processes.

  There is a hierarchy within the user space with several different groups of user
  processes:

  <dl>

  <dt>Basic services</dt>
  <dd>Run at the bottom level and therefore are closest to ther kernel. Comprises small components that perform single, uncomplicated tasks. Examples include network configuration, the communication bus, and diagnostic logging</dd> 
  <dt>Utiility services</dt>
  <dd>The middle level: larger components such as mail, print, and database services</dd>
  <dt>Services controllable by the user</dt>
  <dd>The top level: complicated tasks that the user controls directly such as using a web-browser, editing a text file</dd>
  </dl>

  ## What is a user?

  > A user is an entity that can run processes and own files.

  At the level of users, a user is associated with a user name however the kernel
  does not manage usernames, it identifies users via numeric identifiers called
  **user IDs**.

  Users exist in order to support permissions and access boundaries. A user may
  terminate or modify the behaviour of its own processes (within limits) but
  cannot interfere with another user's proceses. In addition, users may own files
  and choose whether to share them with other users.

  ### Root user (superuser)

  A Linux system normally has a number of users in addition to the ones that
  correspond to the real human users. The most important is **root**. The root
  user _can_ terminate and alter another user's processes and access any file on
  the local system.

  A person who can operate as root is an administrator on a typical user system.


path: zk/User_management_on_AWS.md
sortable_path: zkUser_management_on_AWS.md
title: User management and roles
lead: ## IAM
checksum: 173c1b3adbc04dfc09924b34e135e231e739c359cede8053c343a03bc5c2f4a8
created: 2024-03-30 09:03:21.791840628+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["AWS"]}
body:
  ## IAM

  > Identity and Access Management

  An IAM user is an identity with long-term credentials that is used to interact
  with AWS in an account. An IAM login is different from a root login. The root
  user is the one who creates the AWS account and who has the power to delete it.
  The root user can create IAM logins so that other users can log in through
  dedicated IAM portals. The root user should also create an IAM login for himself
  and use that, set to administrator policies, for most activities, reserving root
  for the most consequential stuff like adding users etc.

  ## Users and user groups

  - Use to create users and access rights accross the AWS services
  - Users get permissions through policies (use template or create your own)
  - You can also create user groups and add users to them to simplify role
    attribution

  ## Roles

  Roles are adopted by _services_ rather than users. They run either periodically
  (from a `cronjob` say) or when they are hit from an API.

  This allows you to assign policies to a role that a service can adopt when
  running. A good example is a lambda. Lambdas often need access to other services
  such as an S3 bucket or a DynamoDB table. Say you have a service that combines a
  lambda with a DynamoDB database. You could assign a role to the lambda and it
  would have access to the database.

  ## Distinction between _principal_ and _identity_

  Both “principal” and “identity” refer to entities that can perform actions and
  interact with AWS resources. However, there is a subtle difference in their
  usage:

  > a principal is a specific type of entity that can take actions in AWS, while
  > an identity is the unique identifier associated with that principal.

  1. Principal: In the context of IAM policies, a principal represents the entity
     that is allowed or denied access to AWS rThe principal is specified in the
     policy statement as the entity to which the permissions are granted or
     deniedesources. It can be an IAM user, an IAM role, an AWS service. The
     principal is specified in the policy statement as the entity to which the
     permissions are granted or denied.
  2. Identity: An identity, on the other hand, is a broader term that encompasses
     both the principal and the authentication credentials associated with that
     principal. It refers to the entity’s unique identifier, such as an IAM user’s
     username or an IAM role’s ARN (Amazon Resource Name). An identity is used for
     authentication purposes to verify the entity’s identity and determine its
     permissions.

  ## Cognito

  > Amazon Cognito provides authentication, authorization, and user management for
  > your web and mobile apps.

  There are **user pools** and **identity pools**.

  User pools provide sign-up and sign-in options for your app users. Identity
  pools provide AWS credentials to grant your users access to other AWS services.

  You would use identity pools to manage access to API endpoints or S3 buckets.
  They can be authenticated or unauthenticated. For example if you want to use an
  image from S3 in a website you would authenticate for that, but you would
  explicitly have to specify that it is general access by applying the policy
  `AmazonS3ReadOnlyAccess`.

raw_content:
  ---
  tags:
    - AWS
  ---

  # User management and roles

  ## IAM

  > Identity and Access Management

  An IAM user is an identity with long-term credentials that is used to interact
  with AWS in an account. An IAM login is different from a root login. The root
  user is the one who creates the AWS account and who has the power to delete it.
  The root user can create IAM logins so that other users can log in through
  dedicated IAM portals. The root user should also create an IAM login for himself
  and use that, set to administrator policies, for most activities, reserving root
  for the most consequential stuff like adding users etc.

  ## Users and user groups

  - Use to create users and access rights accross the AWS services
  - Users get permissions through policies (use template or create your own)
  - You can also create user groups and add users to them to simplify role
    attribution

  ## Roles

  Roles are adopted by _services_ rather than users. They run either periodically
  (from a `cronjob` say) or when they are hit from an API.

  This allows you to assign policies to a role that a service can adopt when
  running. A good example is a lambda. Lambdas often need access to other services
  such as an S3 bucket or a DynamoDB table. Say you have a service that combines a
  lambda with a DynamoDB database. You could assign a role to the lambda and it
  would have access to the database.

  ## Distinction between _principal_ and _identity_

  Both “principal” and “identity” refer to entities that can perform actions and
  interact with AWS resources. However, there is a subtle difference in their
  usage:

  > a principal is a specific type of entity that can take actions in AWS, while
  > an identity is the unique identifier associated with that principal.

  1. Principal: In the context of IAM policies, a principal represents the entity
     that is allowed or denied access to AWS rThe principal is specified in the
     policy statement as the entity to which the permissions are granted or
     deniedesources. It can be an IAM user, an IAM role, an AWS service. The
     principal is specified in the policy statement as the entity to which the
     permissions are granted or denied.
  2. Identity: An identity, on the other hand, is a broader term that encompasses
     both the principal and the authentication credentials associated with that
     principal. It refers to the entity’s unique identifier, such as an IAM user’s
     username or an IAM role’s ARN (Amazon Resource Name). An identity is used for
     authentication purposes to verify the entity’s identity and determine its
     permissions.

  ## Cognito

  > Amazon Cognito provides authentication, authorization, and user management for
  > your web and mobile apps.

  There are **user pools** and **identity pools**.

  User pools provide sign-up and sign-in options for your app users. Identity
  pools provide AWS credentials to grant your users access to other AWS services.

  You would use identity pools to manage access to API endpoints or S3 buckets.
  They can be authenticated or unauthenticated. For example if you want to use an
  image from S3 in a website you would authenticate for that, but you would
  explicitly have to specify that it is general access by applying the policy
  `AmazonS3ReadOnlyAccess`.


path: zk/Using_GraphQL_with_Node.md
sortable_path: zkUsing_GraphQL_with_Node.md
title: Using GraphQL with Node.js
lead: ## Create a basic Express server
checksum: 685307a804f6e41606f8a1dacebf338a95dd7476b1496eced0506b8f73f5f9df
created: 2024-03-30 09:03:21.797366505+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["graphql","node-js"]}
body:
  ## Create a basic Express server

  First we create a basic server in Node using Express:

  ```js
  import express from "express";

  app.get("/", (req, res) => {
    res.send("Graph QL test project");
  });

  app.listen(8080, () =>
    console.log("Running server on port localhost:8080/graphql")
  );
  ```

  ## Add GraphQL as middlewear

  Next we introduce GraphQL as a piece of Node.js
  [middlewear](Middleware_in_NodeJS.md), with the
  `app.use()` method.

  ```js
  import { graphqlHTTP } from "express-graphql";

  app.use(
    "/graphql",
    graphqlHTTP({
      schema: schema,
      rootValue: resolvers,
      graphiql: true,
    })
  );
  ```

  - `schema` is a reference to our GraphQL schema - the structure of the fields
    that define our server.
  - `rootValue` is a reference to our resolvers.
  - `graphiql` is the GUI tool that will be served from the GraphQL endpoint at
    `localhost:8080/graphql`. This tool enables us to interrogate our data using
    the defined schema and see what data we would get back from frontend queries.

raw_content:
  ---
  tags: [graphql, node-js]
  ---

  # Using GraphQL with Node.js

  ## Create a basic Express server

  First we create a basic server in Node using Express:

  ```js
  import express from "express";

  app.get("/", (req, res) => {
    res.send("Graph QL test project");
  });

  app.listen(8080, () =>
    console.log("Running server on port localhost:8080/graphql")
  );
  ```

  ## Add GraphQL as middlewear

  Next we introduce GraphQL as a piece of Node.js
  [middlewear](Middleware_in_NodeJS.md), with the
  `app.use()` method.

  ```js
  import { graphqlHTTP } from "express-graphql";

  app.use(
    "/graphql",
    graphqlHTTP({
      schema: schema,
      rootValue: resolvers,
      graphiql: true,
    })
  );
  ```

  - `schema` is a reference to our GraphQL schema - the structure of the fields
    that define our server.
  - `rootValue` is a reference to our resolvers.
  - `graphiql` is the GUI tool that will be served from the GraphQL endpoint at
    `localhost:8080/graphql`. This tool enables us to interrogate our data using
    the defined schema and see what data we would get back from frontend queries.


path: zk/Using_arguments_with_Apollo_Client.md
sortable_path: zkUsing_arguments_with_Apollo_Client.md
title: Using arguments with Apollo Client
lead: ## What are arguments?
checksum: 3d64509966807a581a0b74dcb400372cbc2441e505fe91173b05d7fed2473ed1
created: 2024-03-30 09:03:21.808215412+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["graphql"]}
body:
  ## What are arguments?

  - An argument is a value you provide for a particular field in a query.
  - The schema must define the arguments that a field accepts (if any)
  - The resolver invokes a field's provided arguments to determine how to return
    specific data
  - Some example use cases of arguments:
    - retrieve specific objects
    - filter through a set of objects
    - transform a field's returned value

  ## Updated schema

  In order to demonstrate arguments we need to expand the
  [original schema](Apollo_Server.md#example-schema).

  Remember a Track is a group of modules that teaches about a specific topic. We
  are going to add:

  - `description` and `numberOfViews` fields to the original `Track` type
  - A new `Module` type
  - A field `modules` to the `Track` type that will be an array of type `Module`

  ```js
  type Query {
    tracksForHome: [Track!]!
  }

  type Track {
      id: ID!
          authorId: String! // They added this on the sly
      thumbnail: String
      length: Int
      modulesCount: Int
      description: String
      numberOfViews: Int
      modules: [Module!]!
  }

  type Module {
      id: ID!
          length: Int
  }

  type Author {
      id: ID!
      name: String!
      photo: String
  }
  ```

  ## Adding additional query

  > Currently we only have one query in the schema (`tracksForHome`) and this
  > returns an array of all the tracks. To demonstrate arguments we want to return
  > a specific track by its `id`. We are going to add a query that enables this.

  ```js
  type Query {
      track(id: ID!):  Track
  }
  ```

  This query will take an `id` as an argument and the resolver will match the `id`
  to a given track and return that `Track`.

  To define an argument for a field in the schema we add parentheses after the
  field name. In the parentheses we write the name of the argument and its type.
  If we have more than one argument we can separate them with commas.

  ## Create resolver for new query

  Now we have to create a resolver for our new `track` query. We will quickly run
  through the [server-side process](Apollo_Server.md).

  ### Update our `RESTDataSource`

  ```js
  class TrackAPI extends RESTDataSource {
    constructor() {...}
    getTracksForHome() {...}
    getAuthor(authorId) {...}

    getTrack(trackId){
      return this.get(`track/${trackId}`);
    }
  }

  ```

  ### Add resolver to `resolvers.js`

  ```js
  const resolvers = {
    Query: {
      tracksForHome: (_, __, { dataSources }) => {
        return dataSources.trackApi.getTracksForHome();
      },
      track: (_, { id }, { dataSources }) => {
        return dataSources.trackAPI.getTrack(id);
      },
    },
    Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
    },
  };
  ```

  ### Making a query

  With the server changes complete, we can now issue a query with an argument from
  the client:

  ```js
  query track(id: 'xyz'){
    title
  }
  ```

  This will return the `title` field from the track with the specific id. This
  query complies with the schema since the `Track` type has the field `title`

  ### Resolver chains

  What about the following query:

  ```js
  query track(id: 'xyz'){
    title
    author {
      name
    }
  }
  ```

  It's not obvious how the resolver should respond to the nested query here since
  author name is not a field on `Track`, it is a field on `Author`:

  ```js
  type Track {
    id: ID!
      authorId: String!
    thumbnail: String
    length: Int
    modulesCount: Int
    description: String
    numberOfViews: Int
    modules: [Module!]!
  }

  type Author {
    id: ID!
    name: String!
    photo: String
  }
  ```

  This is where we use a **resolver chain**. Because `authorId` exists on `Track`
  we can use this to get the `name` part of the query. We already have a resolver
  for `author` under the `Track` resolver:

  ```js
  Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
    },
  ```

  Notice that `authorId` is used in the place of the `parent` parameter. It
  already exists on the `Track` type that wraps the resolver. So this can be
  invoked to fulfill `author` and thereby access the author name from the graph.

  This process is also required for our extended schema. The `Track` type now has
  a `modules` field that comprises an array of the `Module` type.

  Here's a reminder of the `Module` type:

  ```js
  type Module {
      id: ID!
          length: Int
  }
  ```

  The modules for a `Track` can be sourced from the track `id`. So we can again
  add a resolver chain function to the resolvers file:

  ```js
  const resolvers = {
    Query: {
      tracksForHome: (_, __, { dataSources }) => {
        return dataSources.trackApi.getTracksForHome();
      },
      track: (_, { id }, { dataSources }) => {
        return dataSources.trackAPI.getTrack(id);
      },
    },
    Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
      modules: ({ id }, _, { dataSources }) => {
        return dataSources.trackAPI.getTrackModules(id);
      },
    },
  };
  ```

  Once again we are using a property on the parent `Track` type to use as a
  parameter in the resolver function.

  This setup would enables like the following to be run:

  ```js
  query track(id: 'xyz'){
    title
    modules
  }
  ```

  The `id` parameter would be used by the `modules` resolver to return the array
  of the `Modules` type.

  ## Variables in arguments

  Instead of writing the following within our query constants on the client-side:

  ```js
  query GetTrack {
    track(id: 'xyz'){
      title
    }
  }
  ```

  We can make the code more reusable by using variables instead of the hardcoded
  `id` argument:

  ```js
  query GetTrack($trackId: ID!) {
    track(id: $trackId){
      title
    }
  }
  ```

  This way we do not need to write a new query constant every time we want to
  return a specific track.

  ## Send query with arguments using Apollo `useQuery`

  We can now write a proper query using the
  [useQuery hook](Apollo_Client.md#usequery-hook) from
  Apollo Client, with variables.

  First define our query constant:

  ```js
  export const GET_TRACK = gql`
    query GetTrack($trackId: ID!) {
      track(id: $trackId) {
        id
        title
        author {
          name
        }
        description
        modules {
          id
          title
          length
        }
      }
    }
  `;
  ```

  Then to employ in React:

  ```jsx
  const trackId = "xyz";

  const { loading, error, data } = useQuery(GET_TRACK, {
    variables: {
      id: trackId,
    },
  });
  ```

  Note that in contrast to the
  [simple example](Apollo_Client.md#query-constants)
  because we are using variables, we have to pass-in an additional options object
  with the query constant that specifies our variables.

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

  # Using arguments with Apollo Client

  ## What are arguments?

  - An argument is a value you provide for a particular field in a query.
  - The schema must define the arguments that a field accepts (if any)
  - The resolver invokes a field's provided arguments to determine how to return
    specific data
  - Some example use cases of arguments:
    - retrieve specific objects
    - filter through a set of objects
    - transform a field's returned value

  ## Updated schema

  In order to demonstrate arguments we need to expand the
  [original schema](Apollo_Server.md#example-schema).

  Remember a Track is a group of modules that teaches about a specific topic. We
  are going to add:

  - `description` and `numberOfViews` fields to the original `Track` type
  - A new `Module` type
  - A field `modules` to the `Track` type that will be an array of type `Module`

  ```js
  type Query {
    tracksForHome: [Track!]!
  }

  type Track {
      id: ID!
          authorId: String! // They added this on the sly
      thumbnail: String
      length: Int
      modulesCount: Int
      description: String
      numberOfViews: Int
      modules: [Module!]!
  }

  type Module {
      id: ID!
          length: Int
  }

  type Author {
      id: ID!
      name: String!
      photo: String
  }
  ```

  ## Adding additional query

  > Currently we only have one query in the schema (`tracksForHome`) and this
  > returns an array of all the tracks. To demonstrate arguments we want to return
  > a specific track by its `id`. We are going to add a query that enables this.

  ```js
  type Query {
      track(id: ID!):  Track
  }
  ```

  This query will take an `id` as an argument and the resolver will match the `id`
  to a given track and return that `Track`.

  To define an argument for a field in the schema we add parentheses after the
  field name. In the parentheses we write the name of the argument and its type.
  If we have more than one argument we can separate them with commas.

  ## Create resolver for new query

  Now we have to create a resolver for our new `track` query. We will quickly run
  through the [server-side process](Apollo_Server.md).

  ### Update our `RESTDataSource`

  ```js
  class TrackAPI extends RESTDataSource {
    constructor() {...}
    getTracksForHome() {...}
    getAuthor(authorId) {...}

    getTrack(trackId){
      return this.get(`track/${trackId}`);
    }
  }

  ```

  ### Add resolver to `resolvers.js`

  ```js
  const resolvers = {
    Query: {
      tracksForHome: (_, __, { dataSources }) => {
        return dataSources.trackApi.getTracksForHome();
      },
      track: (_, { id }, { dataSources }) => {
        return dataSources.trackAPI.getTrack(id);
      },
    },
    Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
    },
  };
  ```

  ### Making a query

  With the server changes complete, we can now issue a query with an argument from
  the client:

  ```js
  query track(id: 'xyz'){
    title
  }
  ```

  This will return the `title` field from the track with the specific id. This
  query complies with the schema since the `Track` type has the field `title`

  ### Resolver chains

  What about the following query:

  ```js
  query track(id: 'xyz'){
    title
    author {
      name
    }
  }
  ```

  It's not obvious how the resolver should respond to the nested query here since
  author name is not a field on `Track`, it is a field on `Author`:

  ```js
  type Track {
    id: ID!
      authorId: String!
    thumbnail: String
    length: Int
    modulesCount: Int
    description: String
    numberOfViews: Int
    modules: [Module!]!
  }

  type Author {
    id: ID!
    name: String!
    photo: String
  }
  ```

  This is where we use a **resolver chain**. Because `authorId` exists on `Track`
  we can use this to get the `name` part of the query. We already have a resolver
  for `author` under the `Track` resolver:

  ```js
  Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
    },
  ```

  Notice that `authorId` is used in the place of the `parent` parameter. It
  already exists on the `Track` type that wraps the resolver. So this can be
  invoked to fulfill `author` and thereby access the author name from the graph.

  This process is also required for our extended schema. The `Track` type now has
  a `modules` field that comprises an array of the `Module` type.

  Here's a reminder of the `Module` type:

  ```js
  type Module {
      id: ID!
          length: Int
  }
  ```

  The modules for a `Track` can be sourced from the track `id`. So we can again
  add a resolver chain function to the resolvers file:

  ```js
  const resolvers = {
    Query: {
      tracksForHome: (_, __, { dataSources }) => {
        return dataSources.trackApi.getTracksForHome();
      },
      track: (_, { id }, { dataSources }) => {
        return dataSources.trackAPI.getTrack(id);
      },
    },
    Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
      modules: ({ id }, _, { dataSources }) => {
        return dataSources.trackAPI.getTrackModules(id);
      },
    },
  };
  ```

  Once again we are using a property on the parent `Track` type to use as a
  parameter in the resolver function.

  This setup would enables like the following to be run:

  ```js
  query track(id: 'xyz'){
    title
    modules
  }
  ```

  The `id` parameter would be used by the `modules` resolver to return the array
  of the `Modules` type.

  ## Variables in arguments

  Instead of writing the following within our query constants on the client-side:

  ```js
  query GetTrack {
    track(id: 'xyz'){
      title
    }
  }
  ```

  We can make the code more reusable by using variables instead of the hardcoded
  `id` argument:

  ```js
  query GetTrack($trackId: ID!) {
    track(id: $trackId){
      title
    }
  }
  ```

  This way we do not need to write a new query constant every time we want to
  return a specific track.

  ## Send query with arguments using Apollo `useQuery`

  We can now write a proper query using the
  [useQuery hook](Apollo_Client.md#usequery-hook) from
  Apollo Client, with variables.

  First define our query constant:

  ```js
  export const GET_TRACK = gql`
    query GetTrack($trackId: ID!) {
      track(id: $trackId) {
        id
        title
        author {
          name
        }
        description
        modules {
          id
          title
          length
        }
      }
    }
  `;
  ```

  Then to employ in React:

  ```jsx
  const trackId = "xyz";

  const { loading, error, data } = useQuery(GET_TRACK, {
    variables: {
      id: trackId,
    },
  });
  ```

  Note that in contrast to the
  [simple example](Apollo_Client.md#query-constants)
  because we are using variables, we have to pass-in an additional options object
  with the query constant that specifies our variables.


path: zk/Validating_Mongoose_schemas.md
sortable_path: zkValidating_Mongoose_schemas.md
title: Validating Mongoose schemas
lead: Let's say we have the following schema:
checksum: 847b00e5d4138313509f38e42afa01586c9e0fa1e3cd708baf6a709aafe2702a
created: 2024-03-30 09:03:21.840865001+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["mongo-db","mongoose","node-js","databases"]}
body:
  Let's say we have the following schema:

  ```js
  const courseSchema = new mongoose.Schema({
    name: { type: String, required: true, minlength: 5, maxlength: 255 },
    author: String,
    tags: [String],
    data: { type: Date, default: Date.now }, // if unspecified, entry will default to current date
    isPublished: Boolean,
  });
  ```

  We set our validation criteria as the second property for each schema value. If
  it does not obtain, an exception will be thrown when we attempt to modify the
  database. This will most likely take the form of a Promise rejection.

  Note that this validation only occurs at the level of Mongoose. There is no
  mechanism for running the validation within the database itself. This is one of
  the ways that Mongo differs from an SQL database where you would be able to set
  the property of `required` for a cell in the table. If we didn't set any
  validation via Mongoose, Mongo would accept whatever we sent to it.

  What is the relationship between this Mongoose validation and the
  [Joi](Validation_in_NodeJS.md) validation that we
  use when validating API requests in Node/Express? They complement each other. We
  use Joi to validate the client request to the API. If this is valid, the process
  would then move onto the next stage which would be transforming the data from a
  REST request to a database request implemented through Mongoose, where a second
  layer of validation would appy. We would also have validation on the
  client-side.

  ## Validator functions

  Instead of simply applying a built-in validator like `required`, we can instead
  pass a function that validates agains the validation status of another property
  in the schema. For instance: make the `price` property required only if the
  entry has `true` for `isPublished`:

  ```js
  const courseSchema = new mongoose.Schema({
    ...
    isPublished: Boolean,
    price: {
      type: Number,
      required: function() {return this.isPublished}
    }
  });
  ```

  ## Enum validators

  An enum validator enables us to ensure that whatever value is passed when
  instantiating a document that is of a given schema type, it must be one of a
  finite set of values:

  ```js
  const anotherSchema = new mongoose.Schema({
    sex: {
      type: String,
      enum: ["male", "female"],
    },
  });
  ```

raw_content:
  ---
  tags:
    - mongo-db
    - mongoose
    - node-js
    - databases
  ---

  # Validating Mongoose schemas

  Let's say we have the following schema:

  ```js
  const courseSchema = new mongoose.Schema({
    name: { type: String, required: true, minlength: 5, maxlength: 255 },
    author: String,
    tags: [String],
    data: { type: Date, default: Date.now }, // if unspecified, entry will default to current date
    isPublished: Boolean,
  });
  ```

  We set our validation criteria as the second property for each schema value. If
  it does not obtain, an exception will be thrown when we attempt to modify the
  database. This will most likely take the form of a Promise rejection.

  Note that this validation only occurs at the level of Mongoose. There is no
  mechanism for running the validation within the database itself. This is one of
  the ways that Mongo differs from an SQL database where you would be able to set
  the property of `required` for a cell in the table. If we didn't set any
  validation via Mongoose, Mongo would accept whatever we sent to it.

  What is the relationship between this Mongoose validation and the
  [Joi](Validation_in_NodeJS.md) validation that we
  use when validating API requests in Node/Express? They complement each other. We
  use Joi to validate the client request to the API. If this is valid, the process
  would then move onto the next stage which would be transforming the data from a
  REST request to a database request implemented through Mongoose, where a second
  layer of validation would appy. We would also have validation on the
  client-side.

  ## Validator functions

  Instead of simply applying a built-in validator like `required`, we can instead
  pass a function that validates agains the validation status of another property
  in the schema. For instance: make the `price` property required only if the
  entry has `true` for `isPublished`:

  ```js
  const courseSchema = new mongoose.Schema({
    ...
    isPublished: Boolean,
    price: {
      type: Number,
      required: function() {return this.isPublished}
    }
  });
  ```

  ## Enum validators

  An enum validator enables us to ensure that whatever value is passed when
  instantiating a document that is of a given schema type, it must be one of a
  finite set of values:

  ```js
  const anotherSchema = new mongoose.Schema({
    sex: {
      type: String,
      enum: ["male", "female"],
    },
  });
  ```


path: zk/Validity_and_entailment.md
sortable_path: zkValidity_and_entailment.md
title: Validity and entailment
lead: ### Informal definition
checksum: c1ea50298640625de41a08e73e8852203b6a59a2f392299a43dc5ef7465fdb2b
created: 2024-03-30 09:03:21.866073262+00:00
modified: 2024-10-19 09:01:40.438998017+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  ### Informal definition

  In order to say whether an argument is 'good' or 'bad' we must have criteria of
  evaluation. in logic there are different criteria of evaluation:

  - **Deductive validity**

    An **argument is deductively valid if and only if it is not possible for the
    premises to be true and the conclusion false**. Linking to consistency: it is
    not possible to consistently assert all of the premises but deny the
    conclusion.

  - **Inductive strength**

    We do not say that inductive arguments have 'validity' because despite
    inductive premises being true, the conclusion may be falsifiable. Therefore we
    say inductive 'strength' rather than 'validity'. An argument is inductively
    strong if and only if the conclusion is probably true given the premises.

  #### Demonstration

  The Socrates demonstration above is an example of deductive validity.

  The following is an example of an argument that is inductively strong:

  ```
  99% of deaf persons have no musical talent.
  Beethoven was deaf.
  ___________________________________________
  Beethoven had no musical talent.
  ```

  The test for a strong inductive argument is not whether the conclusion is true,
  rather it concerns the evidence the premises provide in support of the
  conclusion.

  > In propositional logic we are concerned solely with deductive validity or
  > invalidity.

  ### Formal definition

  > An argument is truth-functionally valid if and only if there is no
  > truth-assignment on which all the premises are true and the conclusion is
  > false.

  Linking this to derivation, we say:

  > In a system of derivation in propositional logic, an argument is valid if the
  > conclusion of the argument is derivable within the system of derivation from
  > the set consisting of the premises, and invalid otherwise.

  ### Demonstration

  The inference from the set ${P, P \rightarrow Q}$ to $Q$ is valid

  ### Truth-table

  | $P$ | $Q$ | $P \rightarrow Q$ | $P$ | $Q$ | Assessment |
  | --- | --- | ----------------- | --- | --- | ---------- |
  | T   | T   | T                 | T   | T   | Valid      |
  | T   | F   | F                 | T   | F   |            |
  | F   | T   | T                 | F   | T   |            |

  ## Entailment

  ### Informal definition

  Entailment as a concept is almost identical to validity. We say that a
  proposition is entailed by a set of propositions if it is not possible for every
  member of this set to be true and the proposition to be false.

  The difference with validity resides in the fact that the propositions are
  distinguished in terms of whether they are premises or a conclusion. So,
  technically, validity is a subclass of entailment. A case of entailment where we
  distinguish propositions in terms of whether they are premises or conclusions. A
  proposition may be entailed by a given set without that proposition being the
  _conclusion_ of the set and where the set is a syllogism.

  ### Formal definition

  > A finite set of sentences $\Gamma$ $\vdash$ $P$ if and only if there is no
  > truth-assignment in which every member of $\Gamma$ is true and $P$ is false.

  #### Informal demonstration

  ```
  It is raining.
  If it is raining then the pavement will be wet.
  The pavement is wet.
  ```

  #### Formal demonstration

  $$
    \{ P, P\rightarrow Q   \} \vdash Q
  $$

  #### Truth-table

  | $P$ | $Q$ | $P \rightarrow Q$ | $P$ | $Q$ | Assessment |
  | --- | --- | ----------------- | --- | --- | ---------- |
  | T   | T   | T                 | T   | T   | Valid      |
  | T   | F   | F                 | T   | F   |            |
  | F   | T   | T                 | F   | T   |            |

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

  # Validity and entailment

  ### Informal definition

  In order to say whether an argument is 'good' or 'bad' we must have criteria of
  evaluation. in logic there are different criteria of evaluation:

  - **Deductive validity**

    An **argument is deductively valid if and only if it is not possible for the
    premises to be true and the conclusion false**. Linking to consistency: it is
    not possible to consistently assert all of the premises but deny the
    conclusion.

  - **Inductive strength**

    We do not say that inductive arguments have 'validity' because despite
    inductive premises being true, the conclusion may be falsifiable. Therefore we
    say inductive 'strength' rather than 'validity'. An argument is inductively
    strong if and only if the conclusion is probably true given the premises.

  #### Demonstration

  The Socrates demonstration above is an example of deductive validity.

  The following is an example of an argument that is inductively strong:

  ```
  99% of deaf persons have no musical talent.
  Beethoven was deaf.
  ___________________________________________
  Beethoven had no musical talent.
  ```

  The test for a strong inductive argument is not whether the conclusion is true,
  rather it concerns the evidence the premises provide in support of the
  conclusion.

  > In propositional logic we are concerned solely with deductive validity or
  > invalidity.

  ### Formal definition

  > An argument is truth-functionally valid if and only if there is no
  > truth-assignment on which all the premises are true and the conclusion is
  > false.

  Linking this to derivation, we say:

  > In a system of derivation in propositional logic, an argument is valid if the
  > conclusion of the argument is derivable within the system of derivation from
  > the set consisting of the premises, and invalid otherwise.

  ### Demonstration

  The inference from the set ${P, P \rightarrow Q}$ to $Q$ is valid

  ### Truth-table

  | $P$ | $Q$ | $P \rightarrow Q$ | $P$ | $Q$ | Assessment |
  | --- | --- | ----------------- | --- | --- | ---------- |
  | T   | T   | T                 | T   | T   | Valid      |
  | T   | F   | F                 | T   | F   |            |
  | F   | T   | T                 | F   | T   |            |

  ## Entailment

  ### Informal definition

  Entailment as a concept is almost identical to validity. We say that a
  proposition is entailed by a set of propositions if it is not possible for every
  member of this set to be true and the proposition to be false.

  The difference with validity resides in the fact that the propositions are
  distinguished in terms of whether they are premises or a conclusion. So,
  technically, validity is a subclass of entailment. A case of entailment where we
  distinguish propositions in terms of whether they are premises or conclusions. A
  proposition may be entailed by a given set without that proposition being the
  _conclusion_ of the set and where the set is a syllogism.

  ### Formal definition

  > A finite set of sentences $\Gamma$ $\vdash$ $P$ if and only if there is no
  > truth-assignment in which every member of $\Gamma$ is true and $P$ is false.

  #### Informal demonstration

  ```
  It is raining.
  If it is raining then the pavement will be wet.
  The pavement is wet.
  ```

  #### Formal demonstration

  $$
    \{ P, P\rightarrow Q   \} \vdash Q
  $$

  #### Truth-table

  | $P$ | $Q$ | $P \rightarrow Q$ | $P$ | $Q$ | Assessment |
  | --- | --- | ----------------- | --- | --- | ---------- |
  | T   | T   | T                 | T   | T   | Valid      |
  | T   | F   | F                 | T   | F   |            |
  | F   | T   | T                 | F   | T   |            |


path: zk/Viewing_remote_changes_without_merging.md
sortable_path: zkViewing_remote_changes_without_merging.md
title: Viewing remote changes without merging
lead:
  ```
  git fetch origin/master
  git log origin/main
  git show origin/master
  ```

body:
  ```
  git fetch origin/master
  git log origin/main
  git show origin/master
  ```

checksum: 1a5f587f96e84a733ebdf522999e5efdca8e55052d1f846baa71e7e768081e7c
created: 2024-03-30 09:03:21.882007116+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["git"]}
raw_content:
  ---
  tags: [git]
  ---

  # Viewing remote changes without merging

  ```
  git fetch origin/master
  git log origin/main
  git show origin/master
  ```


path: zk/Views_in_relational_databases.md
sortable_path: zkViews_in_relational_databases.md
title: Views in relational databases
checksum: f48de8e2b132aa72d28eefba0fb3508f1baac92593009b7f0bd7be27e3a539ed
created: 2024-03-30 09:03:21.885545399+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["databases"]}
lead:
  A view is a virtual table that is based on the result-set of a query. Although
  it doesn't actually exist in the same manner as an actual table it has exactly
  the same properties and functionality. Rather than adding the raw data manually,
  you derive it from logic applied to existing actual tables.

body:
  A view is a virtual table that is based on the result-set of a query. Although
  it doesn't actually exist in the same manner as an actual table it has exactly
  the same properties and functionality. Rather than adding the raw data manually,
  you derive it from logic applied to existing actual tables.

  This is a good way of dynamically generating data and is often employed as a way
  of creating mutations without changing the underlying data structure. In SQL,
  views can be created by combining `PRIMARY` and `FOREIGN` keys.

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

  # Views in relational databases

  A view is a virtual table that is based on the result-set of a query. Although
  it doesn't actually exist in the same manner as an actual table it has exactly
  the same properties and functionality. Rather than adding the raw data manually,
  you derive it from logic applied to existing actual tables.

  This is a good way of dynamically generating data and is often employed as a way
  of creating mutations without changing the underlying data structure. In SQL,
  views can be created by combining `PRIMARY` and `FOREIGN` keys.


path: zk/Voltage.md
sortable_path: zkVoltage.md
title: Voltage
lead: ## Difference of potential and the transfer of energy
checksum: aad60b4e37e2791658c5676cb4e0c66e309094763d8e8260822ed70057858666
created: 2024-03-30 09:03:21.904807011+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["physics","electricity"]}
body:
  ## Difference of potential and the transfer of energy

  We noted in the discussion of [current](Current.md) that current flows when
  there is a difference of potential between two points with negatively charged
  atoms at one point and positively charged atoms at the other.

  _Difference of potential_ is the same thing as voltage. Voltage is essential to
  current because it is the force that enables the current to flow.

  Without voltage there can be no current because in their natural state, the
  electrons in an atom are **in random motion with no direction. To produce a
  current, energy must be imparted to the electrons so that they all flow in the
  same direction.**

  Voltage is the application of this energy. Any
  [form of energy](Voltage_sources.md) that dislodges electrons from atoms can be
  used to produce current. Thus:

  > Voltage is the work required per coulomb to move a charge from one point to
  > another.

  ### Voltage exists even without current

  Given that voltage is the force that generates current, it would be natural to
  think that voltage only exists when a voltage source (such as a
  [battery](Cells_and_batteries.md)) is connected to a circuit. This however is
  not the case. Even if a 9V battery isn't connected to anything it still has a
  difference of potential of 9-volts accross its terminals. Remember voltage is
  _potential energy_ not just the actualisation of that energy.

  ## Voltage rise and voltage drops

  In circuits there are actually two types of voltage:

  1. Voltage rise
  2. Voltage drop

  ### Voltage rise

  When we introduce potential energy into a ciruit in the form of voltage, this is
  a voltage rise. The current flows from the negative terminal of the voltage
  source and returns to the positive terminal of the voltage source.

  A 12V battery connected to a circuit gives it a voltage rise of 12 volts.

  ### Voltage drop

  Voltage drop is the corrolary to voltage rise. It is the loss of energy that the
  electrons of the circuit current experience as a result of encountering
  resistance.

  As they move through the circuit the electrons encounter a **load** which is
  what we call resistance to the flow of current. As they run into this, they give
  up their energy. The relinquishing of energy happens in the form of a conversion
  of electrical energy to heat (hence why computers heat up). The amount lost is
  equal to the amount of energy imparted by the voltage rise.

  > The voltage drop in a circuit equals the the voltage rise of the circuit
  > because energy cannot be created or destroyed, only changed to another form.
  > When a voltage rise is converted to a voltage drop we say that **the energy
  > has been _consumed_ by the circuit**.

  #### Examples

  - If a 12V source is connected to a 12V lamp, the source supplies a 12V voltage
    rise and the lamp produces a 12V voltage drop.
  - If two identical 6V lamps are connected in series to the same 12V source, each
    lamp produces a 6V drop for a total of 12 volts
  - If two different lamps are connected in series to to a 12V source: a 3V and a
    9V lamp, the 9V lamp produces a drop of 9V and the 3V lamp produces a drop of
    3V. The sum of the voltage drops equals the voltage rise of 12 volts.

  These examples demonstrate that the voltage rise: voltage drop ratio always
  evens out.

  ### Kirchoff's Voltage Law

  The relationship between voltage rise and voltage drop is expressed in
  Kirchoff's Voltage Law:

  > The sum of the voltages around any closed loop in a circuit must be zero.

  The application of the Law is illustrated in the following diagram:

  ![](/img/voltage-drop.png)

  The explanation for the voltage drop at the positions $V^{A}$ and $V^{D}$ are
  obvious enough: they are at the beginning and end of the loop so are equal to
  the maximal voltage rise and minimal voltage drop, respectively.

  We can work out the voltage of the remaining voltage points by inverting
  [Ohm's Law](Ohms_Law.md): $V = I \times R$:

  For the voltage at $V^{B}$:

  $$
    0.5\textsf{mA} \times \textsf{4}k\Omega = 2 \textsf{V}
  $$

  For the voltage at $V^{C}$:

  $$
    0.5\textsf{mA} \times \textsf{6}k\Omega = 3 \textsf{V}
  $$

  The total drops (2V, 3V, 5V) are equal to the initial volatage rise (10V)

  ## Distinguishing _voltage_ from _electric field_

  It can be confusing that two different symbols often seemed to be used
  interchangeably when talking about voltage: $V$ and $E$. However, while they
  broadly point to the same phenomenon there is a difference in emphasis.

  - $V$ stands for volts or voltage conceived purely in terms of the difference in
    potential between two points: the positive and negative terminals
  - $E$ stands for electric field. This is the field that surround each electric
    charge and exerts force on all other charges in the field, attracting or
    repelling them. So it is more the physical process that the volatage
    represents rather than the numerical representation of the potential between
    the terminals.

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

  # Voltage

  ## Difference of potential and the transfer of energy

  We noted in the discussion of [current](Current.md) that current flows when
  there is a difference of potential between two points with negatively charged
  atoms at one point and positively charged atoms at the other.

  _Difference of potential_ is the same thing as voltage. Voltage is essential to
  current because it is the force that enables the current to flow.

  Without voltage there can be no current because in their natural state, the
  electrons in an atom are **in random motion with no direction. To produce a
  current, energy must be imparted to the electrons so that they all flow in the
  same direction.**

  Voltage is the application of this energy. Any
  [form of energy](Voltage_sources.md) that dislodges electrons from atoms can be
  used to produce current. Thus:

  > Voltage is the work required per coulomb to move a charge from one point to
  > another.

  ### Voltage exists even without current

  Given that voltage is the force that generates current, it would be natural to
  think that voltage only exists when a voltage source (such as a
  [battery](Cells_and_batteries.md)) is connected to a circuit. This however is
  not the case. Even if a 9V battery isn't connected to anything it still has a
  difference of potential of 9-volts accross its terminals. Remember voltage is
  _potential energy_ not just the actualisation of that energy.

  ## Voltage rise and voltage drops

  In circuits there are actually two types of voltage:

  1. Voltage rise
  2. Voltage drop

  ### Voltage rise

  When we introduce potential energy into a ciruit in the form of voltage, this is
  a voltage rise. The current flows from the negative terminal of the voltage
  source and returns to the positive terminal of the voltage source.

  A 12V battery connected to a circuit gives it a voltage rise of 12 volts.

  ### Voltage drop

  Voltage drop is the corrolary to voltage rise. It is the loss of energy that the
  electrons of the circuit current experience as a result of encountering
  resistance.

  As they move through the circuit the electrons encounter a **load** which is
  what we call resistance to the flow of current. As they run into this, they give
  up their energy. The relinquishing of energy happens in the form of a conversion
  of electrical energy to heat (hence why computers heat up). The amount lost is
  equal to the amount of energy imparted by the voltage rise.

  > The voltage drop in a circuit equals the the voltage rise of the circuit
  > because energy cannot be created or destroyed, only changed to another form.
  > When a voltage rise is converted to a voltage drop we say that **the energy
  > has been _consumed_ by the circuit**.

  #### Examples

  - If a 12V source is connected to a 12V lamp, the source supplies a 12V voltage
    rise and the lamp produces a 12V voltage drop.
  - If two identical 6V lamps are connected in series to the same 12V source, each
    lamp produces a 6V drop for a total of 12 volts
  - If two different lamps are connected in series to to a 12V source: a 3V and a
    9V lamp, the 9V lamp produces a drop of 9V and the 3V lamp produces a drop of
    3V. The sum of the voltage drops equals the voltage rise of 12 volts.

  These examples demonstrate that the voltage rise: voltage drop ratio always
  evens out.

  ### Kirchoff's Voltage Law

  The relationship between voltage rise and voltage drop is expressed in
  Kirchoff's Voltage Law:

  > The sum of the voltages around any closed loop in a circuit must be zero.

  The application of the Law is illustrated in the following diagram:

  ![](/img/voltage-drop.png)

  The explanation for the voltage drop at the positions $V^{A}$ and $V^{D}$ are
  obvious enough: they are at the beginning and end of the loop so are equal to
  the maximal voltage rise and minimal voltage drop, respectively.

  We can work out the voltage of the remaining voltage points by inverting
  [Ohm's Law](Ohms_Law.md): $V = I \times R$:

  For the voltage at $V^{B}$:

  $$
    0.5\textsf{mA} \times \textsf{4}k\Omega = 2 \textsf{V}
  $$

  For the voltage at $V^{C}$:

  $$
    0.5\textsf{mA} \times \textsf{6}k\Omega = 3 \textsf{V}
  $$

  The total drops (2V, 3V, 5V) are equal to the initial volatage rise (10V)

  ## Distinguishing _voltage_ from _electric field_

  It can be confusing that two different symbols often seemed to be used
  interchangeably when talking about voltage: $V$ and $E$. However, while they
  broadly point to the same phenomenon there is a difference in emphasis.

  - $V$ stands for volts or voltage conceived purely in terms of the difference in
    potential between two points: the positive and negative terminals
  - $E$ stands for electric field. This is the field that surround each electric
    charge and exerts force on all other charges in the field, attracting or
    repelling them. So it is more the physical process that the volatage
    represents rather than the numerical representation of the potential between
    the terminals.


path: zk/Voltage_sources.md
sortable_path: zkVoltage_sources.md
title: Voltage sources
lead: The following phenomena are the main sources that we use to generate voltage:
checksum: 88d7509073e3453a58af7a34f89f36e3de1f0212048f3255ad57489a3e2bf0a2
created: 2024-03-30 09:03:21.931596527+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["physics","electricity"]}
body:
  The following phenomena are the main sources that we use to generate voltage:

  - friction
  - magnetism
  - chemicals
  - light
  - heat
  - pressure

  > In each case, energy in one state is transferred into energy as current. No
  > energy is lost or gained, it simply changes its form.

  ### Magentism (electrical generators)

  Magnetism is used as the voltage source in electrical generators and is by far
  the most common method of producing powerful and large currents at scale.

  If a conductive wire is passed through a magnetic field voltage will be produced
  so long as there is motion between the magnetic field and the conductor. A
  **generator** is a device that generates current in this manner. Generators
  themselves need to be powered. They can be powered by steam from a nuclear power
  plant, water, wind, coal or other fossil fuels.

  #### AC/DC

  Depending on how it is wired, a generator can produce **directed current** (DC)
  or **alternating current** (AC):

  - **Directed current**

    - The electrons flow in only one direction

  - **Alternating current**
    - The electrons flow in one direction and then the other

  ### Chemicals (cells and batteries)

  The chemical creation of current is the physics behind
  [batteries](Cells_and_batteries.md). Chemical current production produces
  currents on a smaller and less industrial scale than generators.

  A chemical cell consists in two dissimilar metals such as copper and zinc. We
  call these the **electrodes**. They are immersed in a salt, acid or alkaline
  solution. We call these the **electrolytes**. The electrolyte pulls the free
  electrons from the copper electrode which leaves it imbalanced with a positive
  charge. The zinc electrode attracts the free electrons from the electrolyte
  giving it a negative charge, thus a difference of potential is achieved.

  ### Light (photovoltaic cells)

  Solar energy can be converted to electrical energy through solar panels which
  are large collections of **photovoltaic cells**.

  When the surfaces of these cells are exposed to light, it dislodges electrons
  from their orbits around the surface atoms of the cell material. For each cell
  this only produces a very small amount of energy, therefore large quantities
  must be used.

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

  # Voltage sources

  The following phenomena are the main sources that we use to generate voltage:

  - friction
  - magnetism
  - chemicals
  - light
  - heat
  - pressure

  > In each case, energy in one state is transferred into energy as current. No
  > energy is lost or gained, it simply changes its form.

  ### Magentism (electrical generators)

  Magnetism is used as the voltage source in electrical generators and is by far
  the most common method of producing powerful and large currents at scale.

  If a conductive wire is passed through a magnetic field voltage will be produced
  so long as there is motion between the magnetic field and the conductor. A
  **generator** is a device that generates current in this manner. Generators
  themselves need to be powered. They can be powered by steam from a nuclear power
  plant, water, wind, coal or other fossil fuels.

  #### AC/DC

  Depending on how it is wired, a generator can produce **directed current** (DC)
  or **alternating current** (AC):

  - **Directed current**

    - The electrons flow in only one direction

  - **Alternating current**
    - The electrons flow in one direction and then the other

  ### Chemicals (cells and batteries)

  The chemical creation of current is the physics behind
  [batteries](Cells_and_batteries.md). Chemical current production produces
  currents on a smaller and less industrial scale than generators.

  A chemical cell consists in two dissimilar metals such as copper and zinc. We
  call these the **electrodes**. They are immersed in a salt, acid or alkaline
  solution. We call these the **electrolytes**. The electrolyte pulls the free
  electrons from the copper electrode which leaves it imbalanced with a positive
  charge. The zinc electrode attracts the free electrons from the electrolyte
  giving it a negative charge, thus a difference of potential is achieved.

  ### Light (photovoltaic cells)

  Solar energy can be converted to electrical energy through solar panels which
  are large collections of **photovoltaic cells**.

  When the surfaces of these cells are exposed to light, it dislodges electrons
  from their orbits around the surface atoms of the cell material. For each cell
  this only produces a very small amount of energy, therefore large quantities
  must be used.


path: zk/What_are_disks.md
sortable_path: zkWhat_are_disks.md
title: What are disks?
lead:
  A disk is a mass storage [block_device](Devices.md) which we
  can write to and read from.

checksum: e2afba648e34963a7b5c9f4a8b9bb857ce1540de0997c2f7291479de9d905c5b
created: 2024-03-30 09:03:21.942804056+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["disks","computer-architecture"]}
body:
  A disk is a mass storage [block_device](Devices.md) which we
  can write to and read from.

  ## SCSI

  - Small Computer System Interface, responsible for handling disk access on most
    Linux systems.
  - It is a protocol that allows communicaton between printers, scanners and other
    peripherals in addition to harddisks.

  ## Disk schematic

  The following diagram represents the basic anatomy of a disk device.

  ![](/img/harddisk.png)

  - A disk is divided up into [partitions](Partitions.md)
    which are subsections of the overall disk. The kernel presents each partition
    as a [block device](Devices.md) 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.
  - The **filesystem** is a database of files and directories: this comprises the
    bulk of the partition and is what you interact with in
    [user space](User_Space.md) when reading and writing data.

raw_content:
  ---
  tags:
    - disks
    - computer-architecture
  ---

  # What are disks?

  A disk is a mass storage [block_device](Devices.md) which we
  can write to and read from.

  ## SCSI

  - Small Computer System Interface, responsible for handling disk access on most
    Linux systems.
  - It is a protocol that allows communicaton between printers, scanners and other
    peripherals in addition to harddisks.

  ## Disk schematic

  The following diagram represents the basic anatomy of a disk device.

  ![](/img/harddisk.png)

  - A disk is divided up into [partitions](Partitions.md)
    which are subsections of the overall disk. The kernel presents each partition
    as a [block device](Devices.md) 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.
  - The **filesystem** is a database of files and directories: this comprises the
    bulk of the partition and is what you interact with in
    [user space](User_Space.md) when reading and writing data.


path: zk/Whole_numbers.md
sortable_path: zkWhole_numbers.md
title: The set of whole numbers
lead: We recall the set of whole numbers:
checksum: 55a213e7c8d3fd57417fb5291e2452abb12fdc19266c9a6c00130544e5b85789
created: 2024-03-30 09:03:21.964186692+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["prealgebra"]}
body:
  We recall the set of whole numbers:

  $$ \mathbb{W} = {0, 1, 2, 3, ...} $$

  # The properties of $\mathbb{W}$

  > In mathematics, a **property** is any characteristic that applies to a given
  > set.

  ## The commutative property

  ### Addition

  When **adding** whole numbers, the placement of the addends does not affect the
  sum.

  Let **a**, **b** represent whole numbers, then:

  $$ a + b = b + a $$

  ### Multiplication

  When **multiplying** whole numbers the placement of the
  [multiplicands](Symbols-and-formal-conventions-80aeaf1872f94a0d97a2e8d07e3855bd)
  does not affect the
  [product](Symbols-and-formal-conventions-80aeaf1872f94a0d97a2e8d07e3855bd).

  Let **a, b** represent whole numbers, then:

  $$ a \cdot b = b \cdot a $$

  ### Subtraction

  **Subtraction** is not commutative, viz:

  $$ a - b \neq b - a $$

  ### Division

  Division is not commutative, viz:

  $$ a \div b \neq b \div a $$

  ## The associative property

  ### Addition

  When grouping symbols (parentheses, brackets, braces) are used with addition,
  the particular placement of the grouping symbols relative to each of the addends
  does not change the sum.

  Let **a**, **b, c** represent whole numbers, then:

  $$ (a + b) + c = a + (b + c) $$

  ### Multiplication

  Let **a, b, c** represent whole numbers, then:

  $$ a \cdot (b \cdot c) = (a \cdot b) \cdot c $$

  ### Subtraction

  Subtraction is not associative, viz:

  $$ (a - b) - c \neq a - (b - c) $$

  ### Division

  Division is not associative

  $$ (a \div b) \div c \neq a \div (b \div c) $$

  ## The property of additive identity

  If **a** is any whole number, then:

  $$ a + 0 = a $$

  We therefore call zero the additive identity: whenever we add zero to a whole
  number, the sum is equal to the whole number itself.

  ## The property of multiplicative identity

  If **a** is any whole number, then:

  $$ (a \cdot 1 = a) = (1 \cdot a = a) $$

  ## Multiplication by zero

  If **a** is any whole number, then:

  $$ (a \cdot 0 = 0) = (0 \cdot a = 0) $$

  ## Division by zero

  Division by zero is **undefined** but zero divided is zero.

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

  # The set of whole numbers

  We recall the set of whole numbers:

  $$ \mathbb{W} = {0, 1, 2, 3, ...} $$

  # The properties of $\mathbb{W}$

  > In mathematics, a **property** is any characteristic that applies to a given
  > set.

  ## The commutative property

  ### Addition

  When **adding** whole numbers, the placement of the addends does not affect the
  sum.

  Let **a**, **b** represent whole numbers, then:

  $$ a + b = b + a $$

  ### Multiplication

  When **multiplying** whole numbers the placement of the
  [multiplicands](Symbols-and-formal-conventions-80aeaf1872f94a0d97a2e8d07e3855bd)
  does not affect the
  [product](Symbols-and-formal-conventions-80aeaf1872f94a0d97a2e8d07e3855bd).

  Let **a, b** represent whole numbers, then:

  $$ a \cdot b = b \cdot a $$

  ### Subtraction

  **Subtraction** is not commutative, viz:

  $$ a - b \neq b - a $$

  ### Division

  Division is not commutative, viz:

  $$ a \div b \neq b \div a $$

  ## The associative property

  ### Addition

  When grouping symbols (parentheses, brackets, braces) are used with addition,
  the particular placement of the grouping symbols relative to each of the addends
  does not change the sum.

  Let **a**, **b, c** represent whole numbers, then:

  $$ (a + b) + c = a + (b + c) $$

  ### Multiplication

  Let **a, b, c** represent whole numbers, then:

  $$ a \cdot (b \cdot c) = (a \cdot b) \cdot c $$

  ### Subtraction

  Subtraction is not associative, viz:

  $$ (a - b) - c \neq a - (b - c) $$

  ### Division

  Division is not associative

  $$ (a \div b) \div c \neq a \div (b \div c) $$

  ## The property of additive identity

  If **a** is any whole number, then:

  $$ a + 0 = a $$

  We therefore call zero the additive identity: whenever we add zero to a whole
  number, the sum is equal to the whole number itself.

  ## The property of multiplicative identity

  If **a** is any whole number, then:

  $$ (a \cdot 1 = a) = (1 \cdot a = a) $$

  ## Multiplication by zero

  If **a** is any whole number, then:

  $$ (a \cdot 0 = 0) = (0 \cdot a = 0) $$

  ## Division by zero

  Division by zero is **undefined** but zero divided is zero.


path: zk/Why_computers_use_binary.md
sortable_path: zkWhy_computers_use_binary.md
title: Why computers use binary
lead: ## Now we know how binary works, how does it relate to computing?
checksum: f1e48d431648ff5f081d982bd3cde7673eb114fa43c6c62f39f4d8e9f23817f3
created: 2024-03-30 09:03:22.004239333+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["binary","computer-architecture"]}
body:
  ## Now we know how binary works, how does it relate to computing?

  The reason is straight forward: it is the simplest way on the level of pure
  engineering of representing numerical and logical values; both of which are the
  basic foundations of programming. An electronic circuit or transistor only needs
  to represent two states: on (1) and off (0) which corresponds to the switch on
  an electrical circuit. A single circuit representing the binary values of 1 and
  0:

  ![multi_on_off 1.gif](multi_on_off.gif)

  It would be much more complicated to have to represent ten different states
  under the decimal number system, although denary computers do exist.

  If we want more digits, we just need to add in more circuits, and we can
  represent as large a binary number as we need. We just need one switch for every
  digit we want to represent. The switches used in modern computers are so cheap
  and so small that billions can be fitted on a single circuit board.

  ![multiple_circuits.gif](multiple_circuits.gif)

  When we use the term 'switch' we actually mean the transistor components of a
  circuit. We don't need to know the physical details at this level but we can say
  that a transistor turns a current on and off. They can also be used to amplify
  the current.

  On the level of electrical engineering and in the subsequent examples using a
  light bulb and a simple circuit, the switch being OFF corresponds to the light
  being ON, this is because the switch breaks the current. So when it is unbroken,
  the current passes to the bulb. This also means that 1 corresponds to the switch
  being OFF and 0 corresponds to the switch being ON which can be confusing at
  first.

  ## From circuits to programs

  The following breaks down how we get from the binary number system to electrical
  circuits to computer programs:

  1. ”Data”= a piece or pieces of **information**

  1. In computing all data is represented in **binary form:**

     2.1. ”Binary” means that the value of a piece of data is exclusively one
     thing or another

     2.2. The values in binary code are exclusively 1 and 0 (either a piece of
     data is a 1 or it is a 0, it can never be both). Binary can also be expressed
     in logical terms where 1 is equal to `true` and 0 is equal to `false`

  1. The smallest piece of data is a **bit**

  1. We apply the binary number system to bits

  1. Thus by 1-4, **a bit is either 1 or 0**

  1. Binary form bears an isomorphic relation to switches on electrical circuits
     (the hardware of computers). Thus binary code can be _mapped onto circuits_.

     - Circuits are controlled by switches
     - A switch is a binary function: it is either on or off ”On/off”
       corresponding to 1/0.
     - In this way, switches control the flow of electric current

  1. Thus by the above, hardware (which is a physical structure made up of
     electric circuits) is **readily programmable in binary terms**

  1. Restatement of 6: in order for hardware to be programmed the program must
     provide the hardware with something that it can actually perform. The program
     must be formulated in binary code.

raw_content:
  ---
  tags:
    - binary
    - computer-architecture
  ---

  # Why computers use binary

  ## Now we know how binary works, how does it relate to computing?

  The reason is straight forward: it is the simplest way on the level of pure
  engineering of representing numerical and logical values; both of which are the
  basic foundations of programming. An electronic circuit or transistor only needs
  to represent two states: on (1) and off (0) which corresponds to the switch on
  an electrical circuit. A single circuit representing the binary values of 1 and
  0:

  ![multi_on_off 1.gif](multi_on_off.gif)

  It would be much more complicated to have to represent ten different states
  under the decimal number system, although denary computers do exist.

  If we want more digits, we just need to add in more circuits, and we can
  represent as large a binary number as we need. We just need one switch for every
  digit we want to represent. The switches used in modern computers are so cheap
  and so small that billions can be fitted on a single circuit board.

  ![multiple_circuits.gif](multiple_circuits.gif)

  When we use the term 'switch' we actually mean the transistor components of a
  circuit. We don't need to know the physical details at this level but we can say
  that a transistor turns a current on and off. They can also be used to amplify
  the current.

  On the level of electrical engineering and in the subsequent examples using a
  light bulb and a simple circuit, the switch being OFF corresponds to the light
  being ON, this is because the switch breaks the current. So when it is unbroken,
  the current passes to the bulb. This also means that 1 corresponds to the switch
  being OFF and 0 corresponds to the switch being ON which can be confusing at
  first.

  ## From circuits to programs

  The following breaks down how we get from the binary number system to electrical
  circuits to computer programs:

  1. ”Data”= a piece or pieces of **information**

  1. In computing all data is represented in **binary form:**

     2.1. ”Binary” means that the value of a piece of data is exclusively one
     thing or another

     2.2. The values in binary code are exclusively 1 and 0 (either a piece of
     data is a 1 or it is a 0, it can never be both). Binary can also be expressed
     in logical terms where 1 is equal to `true` and 0 is equal to `false`

  1. The smallest piece of data is a **bit**

  1. We apply the binary number system to bits

  1. Thus by 1-4, **a bit is either 1 or 0**

  1. Binary form bears an isomorphic relation to switches on electrical circuits
     (the hardware of computers). Thus binary code can be _mapped onto circuits_.

     - Circuits are controlled by switches
     - A switch is a binary function: it is either on or off ”On/off”
       corresponding to 1/0.
     - In this way, switches control the flow of electric current

  1. Thus by the above, hardware (which is a physical structure made up of
     electric circuits) is **readily programmable in binary terms**

  1. Restatement of 6: in order for hardware to be programmed the program must
     provide the hardware with something that it can actually perform. The program
     must be formulated in binary code.


path: zk/Wildcards_in_SQL.md
sortable_path: zkWildcards_in_SQL.md
title: Wildcards in SQL
lead:
  SQL does not use Regex. Instead it has a simpler glob-like syntax for carrying
  out string matching.

checksum: b761196c7c6f74fc89c73b5473e5d14148d6db91415904bc5456d190063aa12f
created: 2024-03-30 09:03:22.009604092+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["SQL","databases"]}
body:
  SQL does not use Regex. Instead it has a simpler glob-like syntax for carrying
  out string matching.

  In order to signal that you wish to compare by a wildcard and not a value, you
  have to use the `LIKE` keyword. The actual wildcard operator is `%` .

  In an SQL statement, the `%` wild card will match any number of occurrences of
  any character. Any characters can appear before or after ‘MacBook’ and the
  record will still be returned:

  ```sql
  SELECT name, cores, release_date
  FROM model
  WHERE name LIKE '%MacBook%';
  ```

  This wildcard only filters characters that come after `Raspberry` :

  ```sql
  SELECT name, cores, release_date
  FROM model
  WHERE name LIKE 'Raspberry%';
  ```

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

  # Wildcards in SQL

  SQL does not use Regex. Instead it has a simpler glob-like syntax for carrying
  out string matching.

  In order to signal that you wish to compare by a wildcard and not a value, you
  have to use the `LIKE` keyword. The actual wildcard operator is `%` .

  In an SQL statement, the `%` wild card will match any number of occurrences of
  any character. Any characters can appear before or after ‘MacBook’ and the
  record will still be returned:

  ```sql
  SELECT name, cores, release_date
  FROM model
  WHERE name LIKE '%MacBook%';
  ```

  This wildcard only filters characters that come after `Raspberry` :

  ```sql
  SELECT name, cores, release_date
  FROM model
  WHERE name LIKE 'Raspberry%';
  ```


path: zk/Working_with_numbers_in_Bash.md
sortable_path: zkWorking_with_numbers_in_Bash.md
title: Working with numbers in Bash
lead: We distinguish:
checksum: d793b757573d5c77ece45d9b81efbc9a285dfe699f8221faf4f0ab9f095c96c5
created: 2024-03-30 09:03:22.015441917+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["shell"]}
body:
  We distinguish:

  - **arithmetic expansion** `$(( ))`
    - returns the result of literal numbers that can then be stored in a variable
  - **artihmetic evaluation** `(( ))`
    - perform calculations on _existing_ variables

  An example of expansion:

  ```bash
  a=3
  ((a += 3))
  echo $a
  # 6
  ((a++))
  # 7
  ```

  > Note: we do not use a dollar-sign when referring to variables within
  > arithmetic evaluation, there is no need. If we do, we get an error. This is
  > because we are using an
  > [expansion](Expansions_and_substitutions.md),
  > therefore the variables are already being interpreted as variables.

  ## Declaring variables as integers

  It is good practice to safeguard against Bash treating numbers as strings to
  declare them as integers in addition to using arithmetic evaluation, e.g:

  ```bash
  declare -i b=3
  ```

  Whilst this isn't a strict type, it means we can do this:

  ```bash
  b=$b+4
  echo b
  # 7
  ```

  Without getting `3+4` in return

  ## No decimals in bash

  Bash does not support decimal calculations natively. This is what you'd get for
  example:

  ```bash
  echo $(( 1/3 ))
  # 0 (not 0.33)
  ```

  So work with decimals you should use `awk` or `bc` ("basic calculations").

  Example of using `bc`:

  ```bash
  declare -i c=1
  declare -i d=3
  e=$(echo "scale=3; $c/$d" | bc)
  # 0.333
  ```

  ## Random numbers

  Generate a pseudo-random number between 1 and 20:

  ```bash
  echo $(( 1 + RANDOM % 10))
  # 18
  ```

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

  # Working with numbers in Bash

  We distinguish:

  - **arithmetic expansion** `$(( ))`
    - returns the result of literal numbers that can then be stored in a variable
  - **artihmetic evaluation** `(( ))`
    - perform calculations on _existing_ variables

  An example of expansion:

  ```bash
  a=3
  ((a += 3))
  echo $a
  # 6
  ((a++))
  # 7
  ```

  > Note: we do not use a dollar-sign when referring to variables within
  > arithmetic evaluation, there is no need. If we do, we get an error. This is
  > because we are using an
  > [expansion](Expansions_and_substitutions.md),
  > therefore the variables are already being interpreted as variables.

  ## Declaring variables as integers

  It is good practice to safeguard against Bash treating numbers as strings to
  declare them as integers in addition to using arithmetic evaluation, e.g:

  ```bash
  declare -i b=3
  ```

  Whilst this isn't a strict type, it means we can do this:

  ```bash
  b=$b+4
  echo b
  # 7
  ```

  Without getting `3+4` in return

  ## No decimals in bash

  Bash does not support decimal calculations natively. This is what you'd get for
  example:

  ```bash
  echo $(( 1/3 ))
  # 0 (not 0.33)
  ```

  So work with decimals you should use `awk` or `bc` ("basic calculations").

  Example of using `bc`:

  ```bash
  declare -i c=1
  declare -i d=3
  e=$(echo "scale=3; $c/$d" | bc)
  # 0.333
  ```

  ## Random numbers

  Generate a pseudo-random number between 1 and 20:

  ```bash
  echo $(( 1 + RANDOM % 10))
  # 18
  ```


path: zk/Zero_property_of_multiplication.md
sortable_path: zkZero_property_of_multiplication.md
title: Zero property of multiplication
lead: **Let $a$ represent any member of $\mathbb{W}$ or $\mathbb{Z}$ then:**
body:
  **Let $a$ represent any member of $\mathbb{W}$ or $\mathbb{Z}$ then:**

  $$ a \cdot 0 = 0 $$

checksum: c547a0d6c08c9a206ef1caaadd2811d792cf21109b64772a860ee712d21a01f0
created: 2024-03-30 09:03:22.025204158+00:00
modified: 2024-10-19 09:02:28.143224923+00:00
metadata: {"tags":["prealgebra","theorems"]}
raw_content:
  ---
  tags:
    - prealgebra
    - theorems
  ---

  # Zero property of multiplication

  **Let $a$ represent any member of $\mathbb{W}$ or $\mathbb{Z}$ then:**

  $$ a \cdot 0 = 0 $$


path: zk/Zip_function_in_Python.md
sortable_path: zkZip_function_in_Python.md
title: zip
checksum: 0e7191e6c8fbb369de0479b6d8f0ebcd60161e6cf2f4f978eb878aa8436e54a1
created: 2024-03-30 09:03:22.028631364+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["python","data-structures"]}
lead:
  The `zip` function returns a zip object when you pass in two iterator data types
  (typically two lists). This is an iterator of tuples where the first item in
  each passed iterator is paired together, and then the second item in each passed
  iterator are paired together, and so on.

body:
  The `zip` function returns a zip object when you pass in two iterator data types
  (typically two lists). This is an iterator of tuples where the first item in
  each passed iterator is paired together, and then the second item in each passed
  iterator are paired together, and so on.

  ## Example

  ```py
  a = ("John", "Charles", "Mike")
  b = ("Jenny", "Christy", "Monica", "Vicky")

  x = zip(a,b)

  print(x)
  # <zip object at 0x1514af34c340>

  # To display the resulting tuple in a readable format:

  print(tuple(x))

  (('John', 'Jenny'), ('Charles', 'Christy'), ('Mike', 'Monica'))
  ```

  As indicated above, if the input iterators are of equal lengths, Python will
  ignore the value in iterator B that doesn't have an equivalent index in iterator
  A.

  ## Real-life example

  ```py
  prompts = [
      "Enter title",
      "Enter author",
      "Enter publication date",
      "Enter year you read the book"
  ]

  keys = ["Title", "Author", "Publication_date", "Date_read"]

  book = {}

  for key, prompt in zip(keys,prompts):
      book[key] = input(prompt + ": ")

  ```

  Here I take the `prompts` and `keys` lists, collate them via `zip` and then loop
  through them to populate the `book` dictionary, which contains properties
  sourced from the `keys` list and values from the user's answers to the
  `prompts`.

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

  # `zip`

  The `zip` function returns a zip object when you pass in two iterator data types
  (typically two lists). This is an iterator of tuples where the first item in
  each passed iterator is paired together, and then the second item in each passed
  iterator are paired together, and so on.

  ## Example

  ```py
  a = ("John", "Charles", "Mike")
  b = ("Jenny", "Christy", "Monica", "Vicky")

  x = zip(a,b)

  print(x)
  # <zip object at 0x1514af34c340>

  # To display the resulting tuple in a readable format:

  print(tuple(x))

  (('John', 'Jenny'), ('Charles', 'Christy'), ('Mike', 'Monica'))
  ```

  As indicated above, if the input iterators are of equal lengths, Python will
  ignore the value in iterator B that doesn't have an equivalent index in iterator
  A.

  ## Real-life example

  ```py
  prompts = [
      "Enter title",
      "Enter author",
      "Enter publication date",
      "Enter year you read the book"
  ]

  keys = ["Title", "Author", "Publication_date", "Date_read"]

  book = {}

  for key, prompt in zip(keys,prompts):
      book[key] = input(prompt + ": ")

  ```

  Here I take the `prompts` and `keys` lists, collate them via `zip` and then loop
  through them to populate the `book` dictionary, which contains properties
  sourced from the `keys` list and values from the user's answers to the
  `prompts`.


path: zk/fs.md
sortable_path: zkfs.md
title: fs module
checksum: acd2f1a9993165caa5ecf4a56b9e9dee29e00696a1f03582e784d576712099d4
created: 2024-03-30 09:03:22.049680323+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["node-js"]}
lead:
  File System is an essential built-in module of Node that contains utility
  methods for working with files and directories.

body:
  File System is an essential built-in module of Node that contains utility
  methods for working with files and directories.

  Every method associated with `fs` has a _blocking_ and _asynchronous_
  implementation. The former obviously blocks the
  [event queue](Event_loop.md), the
  latter does not.

  The synchronous methods are useful to have in some contexts but in general and
  with real-world applications, you should be using the async implementation so as
  to accord with the single-threaded event-driven architecture of Node.

  ## Common I/0 methods

  ### Read directory

  Return a string array of all files in the current directory.

  ```js
  fs.readdir("./", function (err, files) {
    if (err) {
      console.error(err);
    } else {
      console.log(files);
    }
  });
  ```

  ### Read from file

  ```js
  fs.readFile("./lorem.md", "UTF-8", function (err, fileContents) {
    console.log(fileContents);
  });
  ```

  ### Write to file

  ```js
  let md = `A new file`;

  fs.writeFile("testFile.md", md.trim(), function () {
    console.log("File was created");
  });
  ```

  ### Appending to file

  ```js
  fs.appendFile("testFile.md", "new content");
  ```

  ### Create directory

  ```js
  if (!fs.existsSync("directory_name")) {
    fs.mkdir("directory_name", function (err) {
      if (err) {
        console.err(err);
      }
    });
  } else {
    console.warn("Directory already exists");
  }
  ```

  ### Rename and remove files

  ```js
  fs.rename("./filename.js", "./newname.js", function () {
    console.log("file renamed");
  });
  ```

  ```js
  // Remove file
  fs.unlink("./file-to-delete", function (err) {
    if (err) {
      console.error(err);
    }
  });
  ```

  ### Rename and remove directories

  ```js
  // Best to use synchronous method here
  fs.rmSync("/dir", { recursive: true, force: true });
  ```

  ## Streams

  See [Handling streams with fs](Streams_in_NodeJS.md)

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

  # fs module

  File System is an essential built-in module of Node that contains utility
  methods for working with files and directories.

  Every method associated with `fs` has a _blocking_ and _asynchronous_
  implementation. The former obviously blocks the
  [event queue](Event_loop.md), the
  latter does not.

  The synchronous methods are useful to have in some contexts but in general and
  with real-world applications, you should be using the async implementation so as
  to accord with the single-threaded event-driven architecture of Node.

  ## Common I/0 methods

  ### Read directory

  Return a string array of all files in the current directory.

  ```js
  fs.readdir("./", function (err, files) {
    if (err) {
      console.error(err);
    } else {
      console.log(files);
    }
  });
  ```

  ### Read from file

  ```js
  fs.readFile("./lorem.md", "UTF-8", function (err, fileContents) {
    console.log(fileContents);
  });
  ```

  ### Write to file

  ```js
  let md = `A new file`;

  fs.writeFile("testFile.md", md.trim(), function () {
    console.log("File was created");
  });
  ```

  ### Appending to file

  ```js
  fs.appendFile("testFile.md", "new content");
  ```

  ### Create directory

  ```js
  if (!fs.existsSync("directory_name")) {
    fs.mkdir("directory_name", function (err) {
      if (err) {
        console.err(err);
      }
    });
  } else {
    console.warn("Directory already exists");
  }
  ```

  ### Rename and remove files

  ```js
  fs.rename("./filename.js", "./newname.js", function () {
    console.log("file renamed");
  });
  ```

  ```js
  // Remove file
  fs.unlink("./file-to-delete", function (err) {
    if (err) {
      console.error(err);
    }
  });
  ```

  ### Rename and remove directories

  ```js
  // Best to use synchronous method here
  fs.rmSync("/dir", { recursive: true, force: true });
  ```

  ## Streams

  See [Handling streams with fs](Streams_in_NodeJS.md)


path: zk/journald.md
sortable_path: zkjournald.md
title: journald
checksum: 44545669b98d5fb966e0e5acedac5d552eeae71380c97350c99984f74f7fcb04
created: 2024-03-30 09:03:22.082651017+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["systems-programming","Linux","procedural"]}
lead:
  `journald` is a program that comes as default with [systemd](systemd.md). It is
  a service for collecting and storing system-level log data. I keeps a track of
  all [kernel](The_kernel.md) processes. It is invaluable when tracing the source
  of problems and errors that may arise on the system level. It keeps a track of
  all kernal processes.

body:
  `journald` is a program that comes as default with [systemd](systemd.md). It is
  a service for collecting and storing system-level log data. I keeps a track of
  all [kernel](The_kernel.md) processes. It is invaluable when tracing the source
  of problems and errors that may arise on the system level. It keeps a track of
  all kernal processes.

  ![](/img/journald.png)

  ## `journalctl`

  We use `journalctl` to access the logs. The command by itself outputs the entire
  log which will be huge and hard to scroll through. We can refine the results
  with modifiers.

  ### View logs for a specific process with pid

  ```bash
  journalctl _PID=1234
  ```

  ### View logs for a specific time period

  This can be really helpful since you can bracket the most recent events which
  will be more memorable.

  ```bash
  journalctl -S -1h
  ```

  ### View logs for a specfic systemd unit

  ```bash
  journalctl -u [unit_name] -e
  ```

  ### View boot logs

  ```bash
  journalctl -b
  ```

  #### Identify specific boot

  ```bash
  journalctl --list-boots

  ```

  ### List only kernel entries to the journal

  ```bash
  journalctl -k

  ```

raw_content:
  ---
  tags:
    - systems-programming
    - Linux
    - procedural
  ---

  # `journald`

  `journald` is a program that comes as default with [systemd](systemd.md). It is
  a service for collecting and storing system-level log data. I keeps a track of
  all [kernel](The_kernel.md) processes. It is invaluable when tracing the source
  of problems and errors that may arise on the system level. It keeps a track of
  all kernal processes.

  ![](/img/journald.png)

  ## `journalctl`

  We use `journalctl` to access the logs. The command by itself outputs the entire
  log which will be huge and hard to scroll through. We can refine the results
  with modifiers.

  ### View logs for a specific process with pid

  ```bash
  journalctl _PID=1234
  ```

  ### View logs for a specific time period

  This can be really helpful since you can bracket the most recent events which
  will be more memorable.

  ```bash
  journalctl -S -1h
  ```

  ### View logs for a specfic systemd unit

  ```bash
  journalctl -u [unit_name] -e
  ```

  ### View boot logs

  ```bash
  journalctl -b
  ```

  #### Identify specific boot

  ```bash
  journalctl --list-boots

  ```

  ### List only kernel entries to the journal

  ```bash
  journalctl -k

  ```


path: zk/jq.md
sortable_path: zkjq.md
title: jq
lead: ## Remove property
checksum: 676854d84ae30e105cfd98da7b65640c36c42493225fcdd94a44f13d3be84d41
created: 2024-03-30 09:03:22.101647543+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["json","shell"]}
body:
  ## Remove property

  Remove property from a JSON array of objects:

  ```json
  {
    "member_inputs": [
      {
        "name": "Thomas",
        "input_type": "select"
      },
      {
        "name": "Martha",
        "input_type": "text"
      }
    ]
  }
  ```

  ```sh
  jq '.member_details |= map(del(.input_type))' memberDetails.json
  ```

  > Add '-i' to modify the source file directly (in-place)

  > Multiple properties can be deleted simply by chaining,
  > e.g.`(del(.input_type, .another_property))`

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

  # jq

  ## Remove property

  Remove property from a JSON array of objects:

  ```json
  {
    "member_inputs": [
      {
        "name": "Thomas",
        "input_type": "select"
      },
      {
        "name": "Martha",
        "input_type": "text"
      }
    ]
  }
  ```

  ```sh
  jq '.member_details |= map(del(.input_type))' memberDetails.json
  ```

  > Add '-i' to modify the source file directly (in-place)

  > Multiple properties can be deleted simply by chaining,
  > e.g.`(del(.input_type, .another_property))`


