# notes

path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Lifecycle_methods.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Lifecycle_methods.md
title: Lifecycle methods
checksum: 8ab9583e6fd753252c7a902a5e10f3fa48982e300b0685fd4decec37a023a147
created: 2024-10-22 05:37:59.416996145+00:00
modified: 2024-10-21 22:11:47.061979786+00:00
metadata: {"tags":["javascript","react"]}
lead:
  Lifecycles are familiar from nature: something is born, it grows and reaches
  maturity and then dies and the process continues as what dies is broken down
  into constituent parts which form the basis of new life. And so on and so on.

body:
  Lifecycles are familiar from nature: something is born, it grows and reaches
  maturity and then dies and the process continues as what dies is broken down
  into constituent parts which form the basis of new life. And so on and so on.

  The same metaphor can be applied to React components relative to the document
  object model:

  - Components are 'born' (created and mounted on the DOM)
  - They 'grow' by changing and updating their state
  - They 'die' by being unmounted from the DOM

  ## Specific lifecycle phases

  The outline above corresponds to specific methods that execute when React code
  runs:

  1. `componentWillMount()` : this method is called just before the `render()`
     method is called
  2. `componentDidMount()` : this method is called after the component is rendered
     in the DOM

  The two methods above only occur once in a lifecycle.

  The 'updating' phase is achieved with the following methods. This is when the
  data of the component, in the form of state and props updates in response to
  user interaction. We call this process 're-rendering':

  3. `shouldComponentUpdate()` : determines whether component should update

  4. `componentWillUpdate()` : called before re-rerendering occurs

  5. `componentDidUpdate()` : called after re-rendering occurs

  The final phase is unmounting: when the component is removed from the DOM:

  6. `componentWillUnmount()`

  ![React component lifecycle](static/react-lifecycle.png)

  ## Side-effects: why lifecycle phases matter

  > React is built based on the principles of functional programming. In this
  > paradigm, a side-effect (something that is generally discouraged) is any
  > process that is not the direct result of a function executing or anything that
  > is not covered by the `return` keyword.

  Many of the methods detailed in the previous section will rarely be used in
  React development since they pertain to the inner engineering of the library,
  rather than its application in custom contexts. However sometimes you do need
  access to these methods, depending on the background context of what you are
  trying to achieving. This context could include external API and database
  requests or **anything that does not directly pertain to the rendering of the
  component on the screen**.

  React groups these processes under the heading of **side-effects.**

  ### Examples of side-effects and where in the lifecycle they should run

  - `componentWillMount` : It's a good idea to run set-up scripts at this point,
    such as connecting to and authenticating access to a database. This process
    runs before the `render` method is called so it is an optimal point to
    establish these connections
    - You should not however make data requests at this point since data is
      typically returned asynchronously. You cannot therefore be sure that the
      data will be returned before mounting occurs.
  - `componentDidMount` : This is the time to do things that can't occur without
    the DOM:
    - retrieve data
    - add event listeners
    - set timers using `setTimeout` or `setInterval`
  - Updating methods (`shouldComponentUpdate`, `componentWillUpdate` etc.)
    - As these precede or take place concurrently with the rerendering process,
      this phase is a good point to retrieve new data or run necessary external
      processes that are required by the re-rendering.

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

  # Lifecycle methods

  Lifecycles are familiar from nature: something is born, it grows and reaches
  maturity and then dies and the process continues as what dies is broken down
  into constituent parts which form the basis of new life. And so on and so on.

  The same metaphor can be applied to React components relative to the document
  object model:

  - Components are 'born' (created and mounted on the DOM)
  - They 'grow' by changing and updating their state
  - They 'die' by being unmounted from the DOM

  ## Specific lifecycle phases

  The outline above corresponds to specific methods that execute when React code
  runs:

  1. `componentWillMount()` : this method is called just before the `render()`
     method is called
  2. `componentDidMount()` : this method is called after the component is rendered
     in the DOM

  The two methods above only occur once in a lifecycle.

  The 'updating' phase is achieved with the following methods. This is when the
  data of the component, in the form of state and props updates in response to
  user interaction. We call this process 're-rendering':

  3. `shouldComponentUpdate()` : determines whether component should update

  4. `componentWillUpdate()` : called before re-rerendering occurs

  5. `componentDidUpdate()` : called after re-rendering occurs

  The final phase is unmounting: when the component is removed from the DOM:

  6. `componentWillUnmount()`

  ![React component lifecycle](static/react-lifecycle.png)

  ## Side-effects: why lifecycle phases matter

  > React is built based on the principles of functional programming. In this
  > paradigm, a side-effect (something that is generally discouraged) is any
  > process that is not the direct result of a function executing or anything that
  > is not covered by the `return` keyword.

  Many of the methods detailed in the previous section will rarely be used in
  React development since they pertain to the inner engineering of the library,
  rather than its application in custom contexts. However sometimes you do need
  access to these methods, depending on the background context of what you are
  trying to achieving. This context could include external API and database
  requests or **anything that does not directly pertain to the rendering of the
  component on the screen**.

  React groups these processes under the heading of **side-effects.**

  ### Examples of side-effects and where in the lifecycle they should run

  - `componentWillMount` : It's a good idea to run set-up scripts at this point,
    such as connecting to and authenticating access to a database. This process
    runs before the `render` method is called so it is an optimal point to
    establish these connections
    - You should not however make data requests at this point since data is
      typically returned asynchronously. You cannot therefore be sure that the
      data will be returned before mounting occurs.
  - `componentDidMount` : This is the time to do things that can't occur without
    the DOM:
    - retrieve data
    - add event listeners
    - set timers using `setTimeout` or `setInterval`
  - Updating methods (`shouldComponentUpdate`, `componentWillUpdate` etc.)
    - As these precede or take place concurrently with the rerendering process,
      this phase is a good point to retrieve new data or run necessary external
      processes that are required by the re-rendering.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Link_Layer_of_Internet_Protocol.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Link_Layer_of_Internet_Protocol.md
title: Link Layer of the Internet Protocol Suite
checksum: 3bf8acaf0b0c9cca2f8a3ad4a0f8e4ac4a3e919c3aaa4c6009a4011f8161dad3
created: 2024-10-22 05:37:59.437648329+00:00
modified: 2024-10-21 22:11:47.061979786+00:00
metadata: {"created":"Tuesday, August 06, 2024","tags":["networks","internet"]}
lead:
  > The Link Layer is the lowest level of the network stack that makes up the
  > Internet Protocol Suite. It is concerned with the physical and logical
  > connections between [hosts](./Network_hosts.md) on the same local network.

body:
  > The Link Layer is the lowest level of the network stack that makes up the
  > Internet Protocol Suite. It is concerned with the physical and logical
  > connections between [hosts](./Network_hosts.md) on the same local network.

  The physical and logical connections are known as **Links**.

  Link Layer protocols goven communication between devices. Examples include:

  - **WiFi**
    - For wireless communication between devices using radio waves
  - **Ethernet**
    - Wired communication

  All devices connected to the internet participate in the Link Layer since it is
  the base level and the gateway out of the local network to other networks. The
  same is not true for other layers. For example a device like a laptop
  participates in all four layers up to the Application Layer but a network router
  does not:

  ![Link Layer diagram](static/link-layer-frame-revised.png)

  ## MAC addresses

  Each device on a link has a network address that uniquely identifies it. For
  most Link Layer Protocols this is a
  [Media Access Control (MAC) address](MAC_addresses.md).

  A device receives data addressed to its MAC address.

  MAC addresses are limited to local networks. Remote networks cannot send data to
  a specific MAC address. This is off-limits from the outside.

  ## Frames

  Link Layer data is divided into small units called "frames". The anatomy of a
  frame is as follows:

  ![Link Layer frame](static/link-layer-frame-revised.png)

  The header contains the source and destination MAC address plus a descriptor of
  the type of data it contains. The data is the payload. The footer is used to
  detect errors.

raw_content:
  ---
  tags: [networks, internet]
  created: Tuesday, August 06, 2024
  ---

  # Link Layer of the Internet Protocol Suite

  > The Link Layer is the lowest level of the network stack that makes up the
  > Internet Protocol Suite. It is concerned with the physical and logical
  > connections between [hosts](./Network_hosts.md) on the same local network.

  The physical and logical connections are known as **Links**.

  Link Layer protocols goven communication between devices. Examples include:

  - **WiFi**
    - For wireless communication between devices using radio waves
  - **Ethernet**
    - Wired communication

  All devices connected to the internet participate in the Link Layer since it is
  the base level and the gateway out of the local network to other networks. The
  same is not true for other layers. For example a device like a laptop
  participates in all four layers up to the Application Layer but a network router
  does not:

  ![Link Layer diagram](static/link-layer-frame-revised.png)

  ## MAC addresses

  Each device on a link has a network address that uniquely identifies it. For
  most Link Layer Protocols this is a
  [Media Access Control (MAC) address](MAC_addresses.md).

  A device receives data addressed to its MAC address.

  MAC addresses are limited to local networks. Remote networks cannot send data to
  a specific MAC address. This is off-limits from the outside.

  ## Frames

  Link Layer data is divided into small units called "frames". The anatomy of a
  frame is as follows:

  ![Link Layer frame](static/link-layer-frame-revised.png)

  The header contains the source and destination MAC address plus a descriptor of
  the type of data it contains. The data is the payload. The footer is used to
  detect errors.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Linux_disk_partitions.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Linux_disk_partitions.md
title: Linux disk partitions
checksum: 127774e0df1e696c8514fe18a4a53ffeafdf418cf3c0e7ff7be807774e12f020
created: 2024-10-22 05:37:59.534957775+00:00
modified: 2024-10-21 22:11:47.061979786+00:00
metadata: {"tags":["disks","Linux","procedural"]}
lead:
  A disk is divided up into partitions which are subsections of the overall disk.
  The kernel presents each partition as a [block device](Devices.md#Devices) as it
  would with an entire disk.

body:
  A disk is divided up into partitions which are subsections of the overall disk.
  The kernel presents each partition as a [block device](Devices.md#Devices) as it
  would with an entire disk.

  The disk dedicates a small part of its contents to a **partition table**: this
  defines the different partitions that comprise the total disk space.

  ## Viewing current partitions

  Whenever you install a Linux distribution on a real or virtual machine, you must
  partition the drive. There are three main tools to choose from: `parted`,
  `g(raphical)parted`, `fdisk`.

  For a top-level overview of your disks and their main partitions you can run
  `lsblk` (_list block devices_):

  ```bash
  $ lsblk

  NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
  sda           8:0    0 465.7G  0 disk
  ├─sda1        8:1    0   200M  0 part
  └─sda2        8:2    0 465.4G  0 part /run/media/thomas/ganesh
  nvme0n1     259:0    0 476.9G  0 disk
  ├─nvme0n1p1 259:1    0   488M  0 part
  ├─nvme0n1p2 259:2    0  27.9G  0 part /
  └─nvme0n1p3 259:3    0 448.5G  0 part /home
  ```

  We can use `parted -l` to view the partition table for the current machine:

  ```bash
  Model: SKHynix_HFS512GDE9X081N (nvme)
  Disk /dev/nvme0n1: 512GB
  Sector size (logical/physical): 512B/512B
  Partition Table: gpt
  Disk Flags:

  Number  Start   End     Size    File system  Name  Flags
   1      1049kB  513MB   512MB   fat32              boot, esp
   2      513MB   30.5GB  30.0GB  ext4
   3      30.5GB  512GB   482GB   ext4
  ```

  We can use `fdisk -l` to get slightly more info:

  ```bash
  disk /dev/nvme0n1: 476.94 GiB, 512110190592 bytes, 1000215216 sectors
  Disk model: SKHynix_HFS512GDE9X081N
  Units: sectors of 1 * 512 = 512 bytes
  Sector size (logical/physical): 512 bytes / 512 bytes
  I/O size (minimum/optimal): 512 bytes / 512 bytes
  Disklabel type: gpt
  Disk identifier: 08175E77-CB9F-C34A-9032-DF29A3F8F0FE

  Device            Start        End   Sectors   Size Type
  /dev/nvme0n1p1     2048    1001471    999424   488M EFI System
  /dev/nvme0n1p2  1001472   59594751  58593280  27.9G Linux filesystem
  /dev/nvme0n1p3 59594752 1000214527 940619776 448.5G Linux filesystem
  ```

  ## Standard Linux partitions

  The two tools disclose that the main harddrive is `/dev/nvme0n1` (equivalent to
  `sda` on older machines running Linux) and it has the standard three partitions:

  - Boot partition (`/dev/nvme0n1p1`)
  - Root dir (`/dev/nvme0n1p2`)
  - Home dir (`/dev/nvme0n1p3`)

  ### Boot partition

  - Takes up the smallest amount of space
  - Contains the Linux kernel and the bootloader (GRUB, usually) and any other
    files required for booting.
  - Once the BIOS has initialized the hardware components it hands control to the
    bootloader stored in this partition.
  - The bootloader loads the kernel contained in its partition, into memory

  ### Root partition (`/`)

  - This houses everything to do with the operation of the operating system apart
    from the initial booting process. Once the kernel is in memory it accesses the
    resources in the root partition to get the OS up and running. These resources
    include system libraries, utilities, system-wide configs and application
    software.

  - The root partition is vital for the system's functionality. Without it, the
    system cannot boot or operate. It needs to be  
     large enough to accommodate the operating system, installed programs, and system
    libraries, but it doesn't need to store user data or  
     additional applications, which can be located on other partitions.

  - This is the domain of the [superuser](User_Space.md#root-user-superuser). The
    part of the filesystem that you need sudo priveleges to access and where you
    manage users

  ### Home partition

  - This is presented as `/home/`, i.e. that it is a descendent of `/`. But this
    is because the Linux file system is hierarchical. Typically `/home/` is
    usually its own partition.
  - Separating user data and system data in this way protects user data during
    upgrades or reinstallation. Reinstalling or upgrading the OS can be done
    without affecting these files.
  - Each user has their own directory off of `/home`, e.g `/home/thomas`.
  - While the root partition (`/`) is essential for the operation of the OS,
    placing `/home` on a separate partition allows for better management of user
    data, enhancing the system's flexibility and the user's control over their own
    files, without compromising the integrity or performance of the OS. This
    approach exemplifies the Linux philosophy of giving the user control while
    maintaining a clear distinction between system  
    resources and user data.

  ## Types of partition table

  In the Linux world there are two main types: MBR and GPT. The type of table used
  determines how the OS boots. So although partition tables are also responsible
  for the partitioning of non-bootable sectors of a disk, **they are distinguished
  by the boot system they implement**. If we look at the output from `parted` and
  `fdisk` above we see that the harddrive uses the GPT partition type.

  #### Primary, extended and logical partitions

  Most standard partition tables allow for primary, extended and logical
  partitions. The primary partition is the part of the harddisk that contains the
  operating system and is thus described as 'bootable' and may be called the 'boot
  partition'. During the bootstrapping process this is injected into memory as the
  [kernel](The_kernel.md).

  The extended partition is basically everything other than the primary partition.
  This is typically subdivided into other partitions that are called _logical_
  partitions. This is because they physically reside in the same sector of the
  disk (the extended partition) but are treated as virtual and independent disks.

  In our example above:

  - `/dev/nvme0n1p1` is the primary/boot partition
  - `/dev/nvme0n1p2` and `/dev/nvme0n1p3` comprise the extended partition and by
    themselves are each logical partitions.

  <dl>
    <dt>MBR</dt>
    <dd> 
      <ul>
        <li>Stands for Master Boot Record</li> 
        <li>Uses BIOS in the boot process</li> 
        <li>Can only works with disks up to 2TB in size</li> 
        <li>Only supports 4 primary partitions. This means the number of operating systems you install is limitied to this number.</li> 
        <li>This is the first 512 bytes of a storage device, preceding the first partition.</li>
      </ul> 
    </dd>
    <dt>GPT</dt>
    <dd>
    <ul>
        <li>Stands for GUID Partition Table </li>
        <li>Gradually replacing MBR</li>
        <li>Uses UEFI instead of BIOS</li>
        <li>As name indicates, every partition on disk has its own globally-unique identifier</li>
        <li>Vastly more partitions available than with MBR (dependent on operating system)</li>
        <li>Offers greater recovery options and anti-corruption safeguards</li> 
      </ul>
  </dd>
  </dl>

  ## Related notes

  ![Creating a Linux partition table](static/Creating_a_Linux_partition_table.md)

raw_content:
  ---
  tags:
    - disks
    - Linux
    - procedural
  ---

  # Linux disk partitions

  A disk is divided up into partitions which are subsections of the overall disk.
  The kernel presents each partition as a [block device](Devices.md#Devices) as it
  would with an entire disk.

  The disk dedicates a small part of its contents to a **partition table**: this
  defines the different partitions that comprise the total disk space.

  ## Viewing current partitions

  Whenever you install a Linux distribution on a real or virtual machine, you must
  partition the drive. There are three main tools to choose from: `parted`,
  `g(raphical)parted`, `fdisk`.

  For a top-level overview of your disks and their main partitions you can run
  `lsblk` (_list block devices_):

  ```bash
  $ lsblk

  NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
  sda           8:0    0 465.7G  0 disk
  ├─sda1        8:1    0   200M  0 part
  └─sda2        8:2    0 465.4G  0 part /run/media/thomas/ganesh
  nvme0n1     259:0    0 476.9G  0 disk
  ├─nvme0n1p1 259:1    0   488M  0 part
  ├─nvme0n1p2 259:2    0  27.9G  0 part /
  └─nvme0n1p3 259:3    0 448.5G  0 part /home
  ```

  We can use `parted -l` to view the partition table for the current machine:

  ```bash
  Model: SKHynix_HFS512GDE9X081N (nvme)
  Disk /dev/nvme0n1: 512GB
  Sector size (logical/physical): 512B/512B
  Partition Table: gpt
  Disk Flags:

  Number  Start   End     Size    File system  Name  Flags
   1      1049kB  513MB   512MB   fat32              boot, esp
   2      513MB   30.5GB  30.0GB  ext4
   3      30.5GB  512GB   482GB   ext4
  ```

  We can use `fdisk -l` to get slightly more info:

  ```bash
  disk /dev/nvme0n1: 476.94 GiB, 512110190592 bytes, 1000215216 sectors
  Disk model: SKHynix_HFS512GDE9X081N
  Units: sectors of 1 * 512 = 512 bytes
  Sector size (logical/physical): 512 bytes / 512 bytes
  I/O size (minimum/optimal): 512 bytes / 512 bytes
  Disklabel type: gpt
  Disk identifier: 08175E77-CB9F-C34A-9032-DF29A3F8F0FE

  Device            Start        End   Sectors   Size Type
  /dev/nvme0n1p1     2048    1001471    999424   488M EFI System
  /dev/nvme0n1p2  1001472   59594751  58593280  27.9G Linux filesystem
  /dev/nvme0n1p3 59594752 1000214527 940619776 448.5G Linux filesystem
  ```

  ## Standard Linux partitions

  The two tools disclose that the main harddrive is `/dev/nvme0n1` (equivalent to
  `sda` on older machines running Linux) and it has the standard three partitions:

  - Boot partition (`/dev/nvme0n1p1`)
  - Root dir (`/dev/nvme0n1p2`)
  - Home dir (`/dev/nvme0n1p3`)

  ### Boot partition

  - Takes up the smallest amount of space
  - Contains the Linux kernel and the bootloader (GRUB, usually) and any other
    files required for booting.
  - Once the BIOS has initialized the hardware components it hands control to the
    bootloader stored in this partition.
  - The bootloader loads the kernel contained in its partition, into memory

  ### Root partition (`/`)

  - This houses everything to do with the operation of the operating system apart
    from the initial booting process. Once the kernel is in memory it accesses the
    resources in the root partition to get the OS up and running. These resources
    include system libraries, utilities, system-wide configs and application
    software.

  - The root partition is vital for the system's functionality. Without it, the
    system cannot boot or operate. It needs to be  
     large enough to accommodate the operating system, installed programs, and system
    libraries, but it doesn't need to store user data or  
     additional applications, which can be located on other partitions.

  - This is the domain of the [superuser](User_Space.md#root-user-superuser). The
    part of the filesystem that you need sudo priveleges to access and where you
    manage users

  ### Home partition

  - This is presented as `/home/`, i.e. that it is a descendent of `/`. But this
    is because the Linux file system is hierarchical. Typically `/home/` is
    usually its own partition.
  - Separating user data and system data in this way protects user data during
    upgrades or reinstallation. Reinstalling or upgrading the OS can be done
    without affecting these files.
  - Each user has their own directory off of `/home`, e.g `/home/thomas`.
  - While the root partition (`/`) is essential for the operation of the OS,
    placing `/home` on a separate partition allows for better management of user
    data, enhancing the system's flexibility and the user's control over their own
    files, without compromising the integrity or performance of the OS. This
    approach exemplifies the Linux philosophy of giving the user control while
    maintaining a clear distinction between system  
    resources and user data.

  ## Types of partition table

  In the Linux world there are two main types: MBR and GPT. The type of table used
  determines how the OS boots. So although partition tables are also responsible
  for the partitioning of non-bootable sectors of a disk, **they are distinguished
  by the boot system they implement**. If we look at the output from `parted` and
  `fdisk` above we see that the harddrive uses the GPT partition type.

  #### Primary, extended and logical partitions

  Most standard partition tables allow for primary, extended and logical
  partitions. The primary partition is the part of the harddisk that contains the
  operating system and is thus described as 'bootable' and may be called the 'boot
  partition'. During the bootstrapping process this is injected into memory as the
  [kernel](The_kernel.md).

  The extended partition is basically everything other than the primary partition.
  This is typically subdivided into other partitions that are called _logical_
  partitions. This is because they physically reside in the same sector of the
  disk (the extended partition) but are treated as virtual and independent disks.

  In our example above:

  - `/dev/nvme0n1p1` is the primary/boot partition
  - `/dev/nvme0n1p2` and `/dev/nvme0n1p3` comprise the extended partition and by
    themselves are each logical partitions.

  <dl>
    <dt>MBR</dt>
    <dd> 
      <ul>
        <li>Stands for Master Boot Record</li> 
        <li>Uses BIOS in the boot process</li> 
        <li>Can only works with disks up to 2TB in size</li> 
        <li>Only supports 4 primary partitions. This means the number of operating systems you install is limitied to this number.</li> 
        <li>This is the first 512 bytes of a storage device, preceding the first partition.</li>
      </ul> 
    </dd>
    <dt>GPT</dt>
    <dd>
    <ul>
        <li>Stands for GUID Partition Table </li>
        <li>Gradually replacing MBR</li>
        <li>Uses UEFI instead of BIOS</li>
        <li>As name indicates, every partition on disk has its own globally-unique identifier</li>
        <li>Vastly more partitions available than with MBR (dependent on operating system)</li>
        <li>Offers greater recovery options and anti-corruption safeguards</li> 
      </ul>
  </dd>
  </dl>

  ## Related notes

  ![Creating a Linux partition table](static/Creating_a_Linux_partition_table.md)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Local_AWS_development_with_SAM.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Local_AWS_development_with_SAM.md
title: Local AWS development with SAM
checksum: 7574fab5c2296a6f820160136c4ccc4e7925b9fd335609a49cb0a0455b5cef1e
created: 2024-10-22 05:37:59.94581274+00:00
modified: 2024-10-21 22:11:47.065313176+00:00
metadata: {"tags":["AWS","docker"]}
lead:
  We can run a local instance of our SAM stack for a given application without
  sending requests to the cloud. This is implemented through Docker.

body:
  We can run a local instance of our SAM stack for a given application without
  sending requests to the cloud. This is implemented through Docker.

  The SAM CLI handles all the Docker-related tasks, such as pulling the required
  Lambda runtime images, creating containers, mounting your code and dependencies,
  and running your Lambda functions inside those containers.

  ## Basic set up

  Enter your project directory.

  First build your SAM application:

  ```sh
  sam build
  ```

  We then run:

  ```sh
  sam local start-api
  ```

  If you have an API Gateway endpoint that you want to call over the local server.
  You will be able to call it after executing the above.

  This will be indicated by:

  ![](static/local-sam-docker.png)

  If we want to invoke the function directly we use:

  ```sh
   sam local invoke [FunctionName]
  ```

  ## Using environment variables

  If you have an API key or database credentials, you are going to typically want
  to use different values dependent on environment.

  Even if the values are the same accross environments, it's a good idea to not
  call a secret when working locally since this request is billable.

  In the example below I show how to set up environment variables for an API key
  locally and in production.

  ## Create secret

  Go to AWS SecretsManager and add the API key as a secret. This will be sourced
  in production.

  ## Create local ENV file

  > These must be in JSON to work with SAM:

  ### Local env

  ```json
  // local-env.json
  {
    "FunctionName": {
      "API_KEY": "xxx-yyy-xxx",
      "NODE_ENV": "development"
    }
  }
  ```

  We save these to the root of the given function's directory not at the global
  repo level.

  > Be sure to add this to `.gitignore` so that it does not become public

  ### Update `template.yaml`

  Every environment variable you intend to use, must exist in the `template.yaml`,
  otherwise it will not be sourced at runtime:

  ```yaml
  ...
  Resources:
    Properties:
      Environment:
        Variables:
          SECRET_ARN: "arn:aws:secretsmanager:eu-west-2:885135949562:secret:wakatime-api-key-X9oF3v",
          NODE_ENV: production
          API_KEY:
  ...
  ```

  > We go ahead and populate the values for production. But we leave the variable
  > we use in development blank, since we don't want it committed and we will
  > source it at the SAM invocation. It still needs to exist though.

  ### Pass in the environment variable at invocation:

  ```sh
  sam local start-api --env-vars /home/thomas/repos/lambdas/wakatime-api/get-coding-stats/local-env.json
  ```

  In production, the variables required will be automatically sourced from the
  `template.yaml`

  ### Create handler within the Lambda itself

  You are obviously going to need to distinguish between the different deployments
  when the Lambda executes. Here is an example helper function:

  ```ts
  import * as AWS from "aws-sdk";

  const secretsManager = new AWS.SecretsManager();

  async function getApiKey(): Promise<string> {
    if (process.env.NODE_ENV === "production") {
      const response = await secretsManager
        .getSecretValue({ SecretId: process.env.SECRET_ARN as string })
        .promise();
      const secretValues = JSON.parse(response.SecretString as string);
      return secretValues.API_KEY;
    } else {
      return process.env.API_KEY as string;
    }
  }
  ```

raw_content:
  ---
  tags: [AWS, docker]
  ---

  # Local AWS development with SAM

  We can run a local instance of our SAM stack for a given application without
  sending requests to the cloud. This is implemented through Docker.

  The SAM CLI handles all the Docker-related tasks, such as pulling the required
  Lambda runtime images, creating containers, mounting your code and dependencies,
  and running your Lambda functions inside those containers.

  ## Basic set up

  Enter your project directory.

  First build your SAM application:

  ```sh
  sam build
  ```

  We then run:

  ```sh
  sam local start-api
  ```

  If you have an API Gateway endpoint that you want to call over the local server.
  You will be able to call it after executing the above.

  This will be indicated by:

  ![](static/local-sam-docker.png)

  If we want to invoke the function directly we use:

  ```sh
   sam local invoke [FunctionName]
  ```

  ## Using environment variables

  If you have an API key or database credentials, you are going to typically want
  to use different values dependent on environment.

  Even if the values are the same accross environments, it's a good idea to not
  call a secret when working locally since this request is billable.

  In the example below I show how to set up environment variables for an API key
  locally and in production.

  ## Create secret

  Go to AWS SecretsManager and add the API key as a secret. This will be sourced
  in production.

  ## Create local ENV file

  > These must be in JSON to work with SAM:

  ### Local env

  ```json
  // local-env.json
  {
    "FunctionName": {
      "API_KEY": "xxx-yyy-xxx",
      "NODE_ENV": "development"
    }
  }
  ```

  We save these to the root of the given function's directory not at the global
  repo level.

  > Be sure to add this to `.gitignore` so that it does not become public

  ### Update `template.yaml`

  Every environment variable you intend to use, must exist in the `template.yaml`,
  otherwise it will not be sourced at runtime:

  ```yaml
  ...
  Resources:
    Properties:
      Environment:
        Variables:
          SECRET_ARN: "arn:aws:secretsmanager:eu-west-2:885135949562:secret:wakatime-api-key-X9oF3v",
          NODE_ENV: production
          API_KEY:
  ...
  ```

  > We go ahead and populate the values for production. But we leave the variable
  > we use in development blank, since we don't want it committed and we will
  > source it at the SAM invocation. It still needs to exist though.

  ### Pass in the environment variable at invocation:

  ```sh
  sam local start-api --env-vars /home/thomas/repos/lambdas/wakatime-api/get-coding-stats/local-env.json
  ```

  In production, the variables required will be automatically sourced from the
  `template.yaml`

  ### Create handler within the Lambda itself

  You are obviously going to need to distinguish between the different deployments
  when the Lambda executes. Here is an example helper function:

  ```ts
  import * as AWS from "aws-sdk";

  const secretsManager = new AWS.SecretsManager();

  async function getApiKey(): Promise<string> {
    if (process.env.NODE_ENV === "production") {
      const response = await secretsManager
        .getSecretValue({ SecretId: process.env.SECRET_ARN as string })
        .promise();
      const secretValues = JSON.parse(response.SecretString as string);
      return secretValues.API_KEY;
    } else {
      return process.env.API_KEY as string;
    }
  }
  ```


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Logic_gates.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Logic_gates.md
title: Logic gates
checksum: b91d35e2a0d9dd88b0b453e19b45de33ee89bb287d0612b5349464505b7875b3
created: 2024-10-22 05:37:59.986437254+00:00
modified: 2024-10-21 22:11:47.065313176+00:00
metadata: {"tags":["logic-gates","binary"]}
lead:
  > [A logic gate consists in] three connections where there may or may not be
  > some electricity. Two of those connections are places where electricity may be
  > put into the device, and the third connection is a place where electricity may
  > come out of the device.

body:
  > [A logic gate consists in] three connections where there may or may not be
  > some electricity. Two of those connections are places where electricity may be
  > put into the device, and the third connection is a place where electricity may
  > come out of the device.

  [J.C. Scott. 2009. **But How Do It Know? The Basics of Computers for Everyone**,
  21]

  Logic gates are the basic building blocks of digital computing. **A logic gate
  is an electrical circuit that has one or more than one input and only one
  output.** The input and output points of the gate are
  [pins](Integrated_circuits.md) The
  input controls the output and the logic determining which types of input
  (on/off) lead to specific outputs (on/off) is isomorphic with the
  truth-conditions of the
  [Boolean connectives](Truth-functional_connectives.md) specifiable in
  terms of [truth tables](Truth-tables.md).

  Physically, what 'travels through' the gates is electrical current and what
  constitutes the 'gate' is a
  [transistor](Transistors.md)
  responding to the current. Going up a level of abstraction, the current/ charge
  is identified with a
  [bit](Binary_units_of_measurement.md#binary-units-of-measurement).
  It is bits that go into the gate and bits which come out: binary information
  that may be either 1 or 0.

  ## Elementary and composite gates

  We distinguish elementary from composite logic gates. An elementary gate is a
  single gate embodying a single logical connective. It cannot be reduced any
  lower as a logical abstraction. A composite gate is a gate made up of more than
  one elemen>tary gate and/or other composite gates.

  An example of a composite gate would be a three-way AND. An AND with three
  inputs rather than the standard two that furnish the elementary AND gate. This
  gate would output 1 when all three gates have the value 1 and 0 otherwise.
  [Adders](Half_adder_and_full_adder.md)
  and [latche>s](Latches.md) whilst
  being
  [integrated circuits](Integrated_circuits.md)
  are also, technically speaking, composite gates.

  ## Gate interface / gate implementation

  The gate _interface_ is an abstraction that the enables the user to think of the
  gate simply in terms of inputs and outputs, without being conc>erned with the
  technical details of how this is achieved. How it is achieved is the gate
  _implementation_.

  We can demonstrate this with the earlier example of a three-way AND. The diagram
  below represents the gate as an interface:

  // Add: Interface diagram

  Whereas this diagram presents the implementation of the gate: it shows the
  specific combination of gates which creates the enables the behaviour
  represented in the interface diagram.

  // Add: Implementation diagram

  > Importantly, a single interface may be implemented in a variety of ways. There
  > is a one-to-many relationship at work here. From the point of view of the user
  > interface these differences should not be detectable. This is another example
  > of
  > [hardware abstraction](Hardware_abstraction_and_modularity.md)

  ## NOT gate

  > The NOT gate inverts the value of whatever input it receives

  ### Truth conditions

  | $P$ | $\lnot P$ |
  | --- | --------- |
  | 1   | 0         |
  | 0   | 1         |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/not-gate-aeb5f9e5-9f58-4883-b8e5-d70f6d023185?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginhe

  ### Truth conditions

  tion) truth functional connective

  ### Symbol

  ![](static/and-gate-new-2.png)

  ### Truth conditions

  ### Truth conditions

  | 1 | 0 | 0 | | 0 | 0 | 0 | | 0 | 0 | 0 |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/and-gate-b6937338-e83f-474b-af79-854b39c151a3?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## NAND gate

  > The NAND gate inverts the truth conditions of AND.

  ### Symbol

  ![](static/nand-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $\lnot(P \land Q)$ |
  | --- | --- | ------------------ |
  | 1   | 1   | 0                  |
  | 1   | 0   | 0                  |
  | 0   | 1   | 0                  |
  | 0   | 0   | 1                  |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/nand-gate-60613ab9-9562-445e-9883-c4ea1920e206?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  NAND is a **universal logic gate**: equipped with just a NAND we can represent
  every other possible logical condition. In practice with circuits, it is more
  efficient to use specific dedicated gates (i.e OR, AND, NOT etc) for the other
  Boolean connectives but in principle the same output can be achieved through
  NANDs alone.

  ## OR gate

  > The OR gate represents the truth conditions of the
  > [disjunction](Truth-functional_connectives### Truth
  > conditions.md#disjunction) truth functional connective

  ### Symbol

  ![](static/or-gate-new.png)

  ### Truth condition

  ### Truth conditions

  s

  | $P$ | $Q$ | $P \lor Q$ |
  | --- | --- | ---------- |
  | 1   | 1   | 1          |
  | 1   | 0   | 1          |
  | 0   | 1   | 1          |
  | 0   | 0   | 0          |

  ### Interactive circui

  ### Truth conditions

  t

  <iframe src="https://circuitverse.org/simulator/embed/or-gate-087e4933-7963-482d-b4bf-9e130ef05706?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## XOR gate

  > The OR gate represents the truth conditions of the exclusive OR

  ### Symbol

  ![](static/xor-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $\lnot(P \Leftrightarrow Q)$ |
  | --- | --- | ---------------------------- |
  | 1   | 1   | 0                            |
  | 1   | 0   | 1                            |
  | 0   | 1   | 1                            |
  | 0   | 0   | 0                            |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/xor-gate-a240131e-a9c3-4240-a52b-e14412fdd654?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## NOR gate

  > The NOR gate inverts the function of an OR gate

  ### Symbol

  ![](static/nor-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $P \lor Q$ |
  | --- | --- | ---------- |
  | 1   | 1   | 0          |
  | 1   | 0   | 0          |
  | 0   | 1   | 0          |
  | 0   | 0   | 1          |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/nor-gate-ac7946b4-f5d0-4c87-afd1-a2e92326d006?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

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

  # Logic gates

  > [A logic gate consists in] three connections where there may or may not be
  > some electricity. Two of those connections are places where electricity may be
  > put into the device, and the third connection is a place where electricity may
  > come out of the device.

  [J.C. Scott. 2009. **But How Do It Know? The Basics of Computers for Everyone**,
  21]

  Logic gates are the basic building blocks of digital computing. **A logic gate
  is an electrical circuit that has one or more than one input and only one
  output.** The input and output points of the gate are
  [pins](Integrated_circuits.md) The
  input controls the output and the logic determining which types of input
  (on/off) lead to specific outputs (on/off) is isomorphic with the
  truth-conditions of the
  [Boolean connectives](Truth-functional_connectives.md) specifiable in
  terms of [truth tables](Truth-tables.md).

  Physically, what 'travels through' the gates is electrical current and what
  constitutes the 'gate' is a
  [transistor](Transistors.md)
  responding to the current. Going up a level of abstraction, the current/ charge
  is identified with a
  [bit](Binary_units_of_measurement.md#binary-units-of-measurement).
  It is bits that go into the gate and bits which come out: binary information
  that may be either 1 or 0.

  ## Elementary and composite gates

  We distinguish elementary from composite logic gates. An elementary gate is a
  single gate embodying a single logical connective. It cannot be reduced any
  lower as a logical abstraction. A composite gate is a gate made up of more than
  one elemen>tary gate and/or other composite gates.

  An example of a composite gate would be a three-way AND. An AND with three
  inputs rather than the standard two that furnish the elementary AND gate. This
  gate would output 1 when all three gates have the value 1 and 0 otherwise.
  [Adders](Half_adder_and_full_adder.md)
  and [latche>s](Latches.md) whilst
  being
  [integrated circuits](Integrated_circuits.md)
  are also, technically speaking, composite gates.

  ## Gate interface / gate implementation

  The gate _interface_ is an abstraction that the enables the user to think of the
  gate simply in terms of inputs and outputs, without being conc>erned with the
  technical details of how this is achieved. How it is achieved is the gate
  _implementation_.

  We can demonstrate this with the earlier example of a three-way AND. The diagram
  below represents the gate as an interface:

  // Add: Interface diagram

  Whereas this diagram presents the implementation of the gate: it shows the
  specific combination of gates which creates the enables the behaviour
  represented in the interface diagram.

  // Add: Implementation diagram

  > Importantly, a single interface may be implemented in a variety of ways. There
  > is a one-to-many relationship at work here. From the point of view of the user
  > interface these differences should not be detectable. This is another example
  > of
  > [hardware abstraction](Hardware_abstraction_and_modularity.md)

  ## NOT gate

  > The NOT gate inverts the value of whatever input it receives

  ### Truth conditions

  | $P$ | $\lnot P$ |
  | --- | --------- |
  | 1   | 0         |
  | 0   | 1         |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/not-gate-aeb5f9e5-9f58-4883-b8e5-d70f6d023185?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginhe

  ### Truth conditions

  tion) truth functional connective

  ### Symbol

  ![](static/and-gate-new-2.png)

  ### Truth conditions

  ### Truth conditions

  | 1 | 0 | 0 | | 0 | 0 | 0 | | 0 | 0 | 0 |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/and-gate-b6937338-e83f-474b-af79-854b39c151a3?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## NAND gate

  > The NAND gate inverts the truth conditions of AND.

  ### Symbol

  ![](static/nand-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $\lnot(P \land Q)$ |
  | --- | --- | ------------------ |
  | 1   | 1   | 0                  |
  | 1   | 0   | 0                  |
  | 0   | 1   | 0                  |
  | 0   | 0   | 1                  |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/nand-gate-60613ab9-9562-445e-9883-c4ea1920e206?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  NAND is a **universal logic gate**: equipped with just a NAND we can represent
  every other possible logical condition. In practice with circuits, it is more
  efficient to use specific dedicated gates (i.e OR, AND, NOT etc) for the other
  Boolean connectives but in principle the same output can be achieved through
  NANDs alone.

  ## OR gate

  > The OR gate represents the truth conditions of the
  > [disjunction](Truth-functional_connectives### Truth
  > conditions.md#disjunction) truth functional connective

  ### Symbol

  ![](static/or-gate-new.png)

  ### Truth condition

  ### Truth conditions

  s

  | $P$ | $Q$ | $P \lor Q$ |
  | --- | --- | ---------- |
  | 1   | 1   | 1          |
  | 1   | 0   | 1          |
  | 0   | 1   | 1          |
  | 0   | 0   | 0          |

  ### Interactive circui

  ### Truth conditions

  t

  <iframe src="https://circuitverse.org/simulator/embed/or-gate-087e4933-7963-482d-b4bf-9e130ef05706?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## XOR gate

  > The OR gate represents the truth conditions of the exclusive OR

  ### Symbol

  ![](static/xor-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $\lnot(P \Leftrightarrow Q)$ |
  | --- | --- | ---------------------------- |
  | 1   | 1   | 0                            |
  | 1   | 0   | 1                            |
  | 0   | 1   | 1                            |
  | 0   | 0   | 0                            |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/xor-gate-a240131e-a9c3-4240-a52b-e14412fdd654?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>

  ## NOR gate

  > The NOR gate inverts the function of an OR gate

  ### Symbol

  ![](static/nor-gate-new.png)

  ### Truth conditions

  | $P$ | $Q$ | $P \lor Q$ |
  | --- | --- | ---------- |
  | 1   | 1   | 0          |
  | 1   | 0   | 0          |
  | 0   | 1   | 0          |
  | 0   | 0   | 1          |

  ### Interactive circuit

  <iframe src="https://circuitverse.org/simulator/embed/nor-gate-ac7946b4-f5d0-4c87-afd1-a2e92326d006?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="250" width="500" allowFullScreen></iframe>


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Logical_consistency.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Logical_consistency.md
title: Logical consistency
lead: ## Informal definition
checksum: b7a052b9773a7b6eebee344c7fe24b2623527f9f014fdd2952d7e4a9d983594e
created: 2024-10-22 05:38:00.27768599+00:00
modified: 2024-10-21 22:11:47.061979786+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  ## Informal definition

  A set of propositions is consistent if and only if **it is possible for all the
  members of the set to be true at the same time**. A set of propositions is
  inconsistent if and only if it is not consistent.

  ### Demonstration

  The following set of propositions form an inconsistent set:

  1. Anyone who takes astrology seriously is a lunatic.
  2. Alice is my sister and no sister of mine has a lunatic for a husband.
  3. David is Alice's husband and he read's the horoscope column every morning.
  4. Anyone who reads the horoscope column every morning takes astrology
     seriously.

  The set is inconsistent because not all of them can be true. If (1), (3), (4)
  are true, (2) cannot be. If (2), (3),(4) are true, (1) cannot be.

  ## Formal definition

  > A finite set of propositions $\Gamma$ is truth-functionally consistent if and
  > only if there is at least one truth-assignment in which all propositions of
  > $\Gamma$ are true.

  ### Informal expression

  ```
  The book is blue or the book is brown
  The book is brown
  ```

  ### Formal expression

  $$
  \{P \lor Q, Q\}
  $$

  ### Truth table

  $ \{P, Q\} $ form a consistent set because there is at least one assignment when
  both propositions are true. In fact there are two (corresponding to each
  disjunct) but one is sufficient.

  | $P$ | $Q$ | $ P \lor Q $ | $Q$ |
  | --- | --- | ------------ | --- |
  | T   | T   | T            | T   |
  | T   | F   | T            | F   |
  | F   | T   | T            | T   |
  | F   | F   | F            | F   |

  ## Derivation

  > In terms of logical derivation, a finite $\Gamma$ of propositions is
  > **inconsistent** in a system of derivation for propositional logic if and only
  > if a proposition of the form $P \& \lnot P$ is derivable from $\Gamma$. It is
  > **consistent** just if this is not the case.

  In other terms, if you can derive a contradiction from the set, the set is
  logically inconsistent.

  A
  [contradiction](Logical_truth_and_falsity.md#logical-falsity)
  has very important consequences for reasoning because if a set of propositions
  is inconsistent, any other proposition is derivable from it.

  ![](static/derivation_from_contradiction.png)

  _A demonstration of the the consequences of deriving a contradiction in a
  sequence of reasoning._

  Here we want to derive some proposition $Q$. If we can derive a contradiction
  from its negation as an assumption then, by the
  [negation elimination](Negation_Elimination.md)) rule, we can
  assert $Q$. This is why contradictions should be avoided in arguments, they
  'prove' everything which, by association, undermines any particular premise you
  are trying to assert.

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

  # Logical consistency

  ## Informal definition

  A set of propositions is consistent if and only if **it is possible for all the
  members of the set to be true at the same time**. A set of propositions is
  inconsistent if and only if it is not consistent.

  ### Demonstration

  The following set of propositions form an inconsistent set:

  1. Anyone who takes astrology seriously is a lunatic.
  2. Alice is my sister and no sister of mine has a lunatic for a husband.
  3. David is Alice's husband and he read's the horoscope column every morning.
  4. Anyone who reads the horoscope column every morning takes astrology
     seriously.

  The set is inconsistent because not all of them can be true. If (1), (3), (4)
  are true, (2) cannot be. If (2), (3),(4) are true, (1) cannot be.

  ## Formal definition

  > A finite set of propositions $\Gamma$ is truth-functionally consistent if and
  > only if there is at least one truth-assignment in which all propositions of
  > $\Gamma$ are true.

  ### Informal expression

  ```
  The book is blue or the book is brown
  The book is brown
  ```

  ### Formal expression

  $$
  \{P \lor Q, Q\}
  $$

  ### Truth table

  $ \{P, Q\} $ form a consistent set because there is at least one assignment when
  both propositions are true. In fact there are two (corresponding to each
  disjunct) but one is sufficient.

  | $P$ | $Q$ | $ P \lor Q $ | $Q$ |
  | --- | --- | ------------ | --- |
  | T   | T   | T            | T   |
  | T   | F   | T            | F   |
  | F   | T   | T            | T   |
  | F   | F   | F            | F   |

  ## Derivation

  > In terms of logical derivation, a finite $\Gamma$ of propositions is
  > **inconsistent** in a system of derivation for propositional logic if and only
  > if a proposition of the form $P \& \lnot P$ is derivable from $\Gamma$. It is
  > **consistent** just if this is not the case.

  In other terms, if you can derive a contradiction from the set, the set is
  logically inconsistent.

  A
  [contradiction](Logical_truth_and_falsity.md#logical-falsity)
  has very important consequences for reasoning because if a set of propositions
  is inconsistent, any other proposition is derivable from it.

  ![](static/derivation_from_contradiction.png)

  _A demonstration of the the consequences of deriving a contradiction in a
  sequence of reasoning._

  Here we want to derive some proposition $Q$. If we can derive a contradiction
  from its negation as an assumption then, by the
  [negation elimination](Negation_Elimination.md)) rule, we can
  assert $Q$. This is why contradictions should be avoided in arguments, they
  'prove' everything which, by association, undermines any particular premise you
  are trying to assert.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Logical_equivalence.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Logical_equivalence.md
title: Logical equivalence
checksum: d19c3eb0f3dddb1b0500d8e0c58d47d9f78d0a6dd9d1b3fec38ef5f3dbbd0f92
created: 2024-10-22 05:38:00.395653866+00:00
modified: 2024-10-21 22:11:47.061979786+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  > Two sentences, P and Q, are truth-functionally equivalent if and only if there
  > is no truth assignment in which P is true and Q is false

body:
  > Two sentences, P and Q, are truth-functionally equivalent if and only if there
  > is no truth assignment in which P is true and Q is false

  ### Informal expression

  P: If it is raining then the pavement will be wet.

  Q: The pavement is not wet unless it is raining.

  ### Formal expression

  $$
  (P \rightarrow Q) \longleftrightarrow (\lnot P \lor Q)
  $$

  ### Truth-tables

  | $P$ | $Q$ | $ P \rightarrow Q $ | $ \lnot P \lor Q$ |
  | --- | --- | ------------------- | ----------------- |
  | T   | T   | T                   | T                 |
  | T   | F   | T                   | F                 |
  | F   | T   | T                   | T                 |
  | F   | F   | F                   | T                 |

  ### Derivation

  > Propositions $P$ and $Q$ are equivalent in a system of
  > [derivation](Formal%20proofs%20in%20propositional%20logic.md) for
  > propositional logic if $Q$ is derivable from $P$ and $P$ is derivable from
  > $Q$.

  Note that the property of equivalence stated in terms of derivablity above is
  identical to the derivation rule for the
  [material biconditional](Biconditional_Introduction.md):

  ![bi-intro.png](static/bi-intro.png)

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

  # Logical equivalence

  > Two sentences, P and Q, are truth-functionally equivalent if and only if there
  > is no truth assignment in which P is true and Q is false

  ### Informal expression

  P: If it is raining then the pavement will be wet.

  Q: The pavement is not wet unless it is raining.

  ### Formal expression

  $$
  (P \rightarrow Q) \longleftrightarrow (\lnot P \lor Q)
  $$

  ### Truth-tables

  | $P$ | $Q$ | $ P \rightarrow Q $ | $ \lnot P \lor Q$ |
  | --- | --- | ------------------- | ----------------- |
  | T   | T   | T                   | T                 |
  | T   | F   | T                   | F                 |
  | F   | T   | T                   | T                 |
  | F   | F   | F                   | T                 |

  ### Derivation

  > Propositions $P$ and $Q$ are equivalent in a system of
  > [derivation](Formal%20proofs%20in%20propositional%20logic.md) for
  > propositional logic if $Q$ is derivable from $P$ and $P$ is derivable from
  > $Q$.

  Note that the property of equivalence stated in terms of derivablity above is
  identical to the derivation rule for the
  [material biconditional](Biconditional_Introduction.md):

  ![bi-intro.png](static/bi-intro.png)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/MAC_addresses.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887MAC_addresses.md
title: MAC addresses
checksum: 3f940977727e2eb820c5a78eb0e5279cdb2ea525f376596521b916bce1c1a647
created: 2024-10-22 05:38:00.932579909+00:00
modified: 2024-10-21 22:11:47.058646397+00:00
metadata: {"created":"Tuesday, August 06, 2024","tags":["networks"]}
lead:
  A Media Access Control (MAC) address is a physical address in contrast to, say,
  an IP address which is logical.

body:
  A Media Access Control (MAC) address is a physical address in contrast to, say,
  an IP address which is logical.

  Each device on a [local network](./Link_Layer_of_Internet_Protocol.md) has a MAC
  as a unique identifier. It is a hardware-based, physical attribute of the
  device, typically permanently encoded onto a non-volatile memory chip attached
  to the ![network_card](static/Network_card.md).

  MAC addresses consist of 6 bytes (48-bits) represented as 12
  [hexadecimal_digits](Hexadecimal_number_system.md).

raw_content:
  ---
  tags: [networks]
  created: Tuesday, August 06, 2024
  ---

  # MAC addresses

  A Media Access Control (MAC) address is a physical address in contrast to, say,
  an IP address which is logical.

  Each device on a [local network](./Link_Layer_of_Internet_Protocol.md) has a MAC
  as a unique identifier. It is a hardware-based, physical attribute of the
  device, typically permanently encoded onto a non-volatile memory chip attached
  to the ![network_card](static/Network_card.md).

  MAC addresses consist of 6 bytes (48-bits) represented as 12
  [hexadecimal_digits](Hexadecimal_number_system.md).


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Magnetic_core_memory.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Magnetic_core_memory.md
title: Magnetic core memory
lead: ![](static/magnetic-core-memory.jpg)
checksum: 9ff1f3e4f0e08afc5f70541a8260878d597ec93e6f74f81a52f3b2a4ad45b1b0
created: 2024-10-22 05:38:01.120607231+00:00
modified: 2024-10-21 22:11:47.061979786+00:00
metadata: {"created":"Friday, September 27, 2024","tags":["computer-history","memory"]}
body:
  ![](static/magnetic-core-memory.jpg)

  Like [Magnetic_drum_memory](Magnetic_drum_memory.md), magnetic core memory was
  faster and more reliable than [delay_line_memory](Delay_line_memory.md) and
  vacuum-tubes, and was also persistent. Thus it was used for volatile memory as
  well as storage.

  It worked as follows. Magnetic beads made of ferrite and threaded with copper
  were arranged in a crisscross grid. A bead would be placed at the overlap of two
  copper threads. This constitutes a "core".A pulse of electric current would
  magnetise/de-magnetise the cores.

  ![](static/magnetic-core-closeup.jpg)

raw_content:
  ---
  tags: [computer-history, memory]
  created: Friday, September 27, 2024
  ---

  # Magnetic core memory

  ![](static/magnetic-core-memory.jpg)

  Like [Magnetic_drum_memory](Magnetic_drum_memory.md), magnetic core memory was
  faster and more reliable than [delay_line_memory](Delay_line_memory.md) and
  vacuum-tubes, and was also persistent. Thus it was used for volatile memory as
  well as storage.

  It worked as follows. Magnetic beads made of ferrite and threaded with copper
  were arranged in a crisscross grid. A bead would be placed at the overlap of two
  copper threads. This constitutes a "core".A pulse of electric current would
  magnetise/de-magnetise the cores.

  ![](static/magnetic-core-closeup.jpg)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Magnetic_drum_memory.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Magnetic_drum_memory.md
title: Magnetic drum memory
checksum: a4227d97fd200005ba75b58b35c15ef5a10dab7ef31751aac46f2c69fa2eb85f
created: 2024-10-22 05:38:01.210062216+00:00
modified: 2024-10-21 22:11:47.065313176+00:00
metadata: {"created":"Tuesday, September 24, 2024","tags":["computer-history","memory"]}
lead:
  Along with the [Williams_Tube](Williams_Tube_memory.md), another early approach
  to RAM used in 1950s-1960s era of computing.

body:
  Along with the [Williams_Tube](Williams_Tube_memory.md), another early approach
  to RAM used in 1950s-1960s era of computing.

  ![Magnetic drum](static/magnetic-drum-memory.jpg)

  A magnetic drum was a metal cylinder coated with a magnetic material. Data was
  stored by magnetising small regions on the drum's surface. The drum would rotate
  at high speeds and read/write heads were positioned along the length of the drum
  to access data.

  Seen as a improvement on Williams Tubes and
  [delay line memory](Delay_line_memory.md) but superseded by magnetic core memory
  later. It's concept lived on in harddisk drives which became the dominant form
  of secondary storage.

  It had a larger capacity than the technologies that preceded it and it was also
  non-volatile - the data would remain intact when the power was turned off.

  It was used in the [IBM 650](The_History_of_Computing_Swade.md) (1953) and
  Ferranti Mark I (1951)

raw_content:
  ---
  tags: [computer-history, memory]
  created: Tuesday, September 24, 2024
  ---

  # Magnetic drum memory

  Along with the [Williams_Tube](Williams_Tube_memory.md), another early approach
  to RAM used in 1950s-1960s era of computing.

  ![Magnetic drum](static/magnetic-drum-memory.jpg)

  A magnetic drum was a metal cylinder coated with a magnetic material. Data was
  stored by magnetising small regions on the drum's surface. The drum would rotate
  at high speeds and read/write heads were positioned along the length of the drum
  to access data.

  Seen as a improvement on Williams Tubes and
  [delay line memory](Delay_line_memory.md) but superseded by magnetic core memory
  later. It's concept lived on in harddisk drives which became the dominant form
  of secondary storage.

  It had a larger capacity than the technologies that preceded it and it was also
  non-volatile - the data would remain intact when the power was turned off.

  It was used in the [IBM 650](The_History_of_Computing_Swade.md) (1953) and
  Ferranti Mark I (1951)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Magnetic_tape.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Magnetic_tape.md
title: Magnetic tape
checksum: 3fe544ef92e3de5885627fee6eb31642d7e86ef03a5b195140b31cf788a56139
created: 2024-10-22 05:38:01.34941967+00:00
modified: 2024-10-21 22:11:47.058646397+00:00
metadata: {"created":"Monday, September 23, 2024","tags":["computer-history","storage"]}
lead:
  Used for data storage in early digital computers (broadly from the
  [UNIVAC](./The_History_of_Computing_Swade.md)) era until the mini-computer era.

body:
  Used for data storage in early digital computers (broadly from the
  [UNIVAC](./The_History_of_Computing_Swade.md)) era until the mini-computer era.

  While the UNIVAC was not the first to use magnetic tape, it popularised the
  method due to its success and high profile.

  ![Magnetic tape reader in IBM computer](static/magnetic-tape-ibm.jpeg)

  It worked as follows. The tape was a long, narrow strip of plastic coated with a
  magnetic material. Data is recorded on the the tape by magnetising tiny
  particles on its surface. It was first used in the 1920s for audio recording.

  Data is accessed in a linear, sequential manner. To reach a specific piece of
  data, you often need to wind through all the preceding data

  It was used for long term storage rather than RAM memory due to its sequential
  nature. It stored data and program instructions that would be read into the
  machine's memory. Data was also written out to magenetic tape, in order to store
  and further process results.

  It could store more data than punched cards and its sequential nature
  accommodated batch processing well. However its sequential nature meant it was
  slow at retreiving specific pieces of data as it would have to cycle through all
  the values stored on the tape.

  ![Magnetic tape stored as casette](static/magnetic-tape-disk.jpeg)

  It developed from open-reel tapes to cartridges and cassettes. It was superseded
  by hard disk drives and solid state drives.

raw_content:
  ---
  tags: [computer-history, storage]
  created: Monday, September 23, 2024
  ---

  # Magnetic tape

  Used for data storage in early digital computers (broadly from the
  [UNIVAC](./The_History_of_Computing_Swade.md)) era until the mini-computer era.

  While the UNIVAC was not the first to use magnetic tape, it popularised the
  method due to its success and high profile.

  ![Magnetic tape reader in IBM computer](static/magnetic-tape-ibm.jpeg)

  It worked as follows. The tape was a long, narrow strip of plastic coated with a
  magnetic material. Data is recorded on the the tape by magnetising tiny
  particles on its surface. It was first used in the 1920s for audio recording.

  Data is accessed in a linear, sequential manner. To reach a specific piece of
  data, you often need to wind through all the preceding data

  It was used for long term storage rather than RAM memory due to its sequential
  nature. It stored data and program instructions that would be read into the
  machine's memory. Data was also written out to magenetic tape, in order to store
  and further process results.

  It could store more data than punched cards and its sequential nature
  accommodated batch processing well. However its sequential nature meant it was
  slow at retreiving specific pieces of data as it would have to cycle through all
  the values stored on the tape.

  ![Magnetic tape stored as casette](static/magnetic-tape-disk.jpeg)

  It developed from open-reel tapes to cartridges and cassettes. It was superseded
  by hard disk drives and solid state drives.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Matter_and_atoms.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Matter_and_atoms.md
title: Matter and atoms
lead: ## Matter, elements, compounds and mixtures
checksum: 613eb6a0125902eaef1e0a65702469a7bc0976c4582f5719cef4b2a7093959ee
created: 2024-10-22 05:38:01.69372188+00:00
modified: 2024-10-21 22:11:47.048646228+00:00
metadata: {"tags":["physics"]}
body:
  ## Matter, elements, compounds and mixtures

  - Matter is anything that occupies space and has weight. It may be found in any
    one of three states: liquid, solid, or gas.
  - Matter can be classed in terms of **elements**, **compounds** and
    **mixtures**.
    - An element is a substance that cannot be reduced to a simpler substance by
      chemical means.
    - The chemical combination of two or more elements is a compound. Compounds
      _can_ be separated or reduced by chemical means but they cannot be
      physically separated.
    - A _physical_ (note not _chemical_) combination of elements and compounds is
      a mixture.
    - For example "air" is a mixture which is made up of oxygen, nitrogen, carbon
      dioxide, and other gases.

  ## Atoms and molecules

  - Elements are made up of **atoms**.
  - A **molecule** is a type of compound where the atoms of two types of elements
    have joined together. For example sodium and chlorine are two elements that
    can join to form sodium chloride. We call this **chemical bonding**.

  ## Atomic particles
  ![](static/atom-diagram.svg)



  - There are three particles comprising atoms: **protons**, **neutrons** and
    **electrons**.

  - Protons and neutrons comprise the nucleus of the atom. A proton is positively
    charged. A neutron has no charge.

  - Electrons orbit around the nucleus in concentric rings. Electrons are
    negatively charged. Because of this protons and electrons attract each other.

  ### Atomic number

  - The number of protons in the nucleus is called the element's **atomic**
    number.
  - We use the atomic number of elements to distinguish one element from another.

  ### Atomic weight

  - The atomic weight of an atom is its mass.
  - The mass is determined by the number of protons and neutrons in the nucleus.
  - An electron $\frac{1}{1845}$ the mass of a proton. Its mass is so neglible
    that it makes no significant difference to the atomic weight.

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

  # Matter and atoms

  ## Matter, elements, compounds and mixtures

  - Matter is anything that occupies space and has weight. It may be found in any
    one of three states: liquid, solid, or gas.
  - Matter can be classed in terms of **elements**, **compounds** and
    **mixtures**.
    - An element is a substance that cannot be reduced to a simpler substance by
      chemical means.
    - The chemical combination of two or more elements is a compound. Compounds
      _can_ be separated or reduced by chemical means but they cannot be
      physically separated.
    - A _physical_ (note not _chemical_) combination of elements and compounds is
      a mixture.
    - For example "air" is a mixture which is made up of oxygen, nitrogen, carbon
      dioxide, and other gases.

  ## Atoms and molecules

  - Elements are made up of **atoms**.
  - A **molecule** is a type of compound where the atoms of two types of elements
    have joined together. For example sodium and chlorine are two elements that
    can join to form sodium chloride. We call this **chemical bonding**.

  ## Atomic particles
  ![](static/atom-diagram.svg)



  - There are three particles comprising atoms: **protons**, **neutrons** and
    **electrons**.

  - Protons and neutrons comprise the nucleus of the atom. A proton is positively
    charged. A neutron has no charge.

  - Electrons orbit around the nucleus in concentric rings. Electrons are
    negatively charged. Because of this protons and electrons attract each other.

  ### Atomic number

  - The number of protons in the nucleus is called the element's **atomic**
    number.
  - We use the atomic number of elements to distinguish one element from another.

  ### Atomic weight

  - The atomic weight of an atom is its mass.
  - The mass is determined by the number of protons and neutrons in the nucleus.
  - An electron $\frac{1}{1845}$ the mass of a proton. Its mass is so neglible
    that it makes no significant difference to the atomic weight.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Memory_Management_Unit.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Memory_Management_Unit.md
title: Memory Management Unit (MMU)
checksum: 50c24f65131b262fb84872c3963156155fd73e4298979f2d51663a1aa3d96658
created: 2024-10-22 05:38:01.777545357+00:00
modified: 2024-10-21 22:11:47.061979786+00:00
metadata: {"created":"Monday, July 08, 2024","tags":["memory","Linux"]}
lead:
  The MMU is the bridge between the physical memory devices of the machine and the
  virtual memory space. It is a chip that sits between the CPU and the RAM and
  determines the physical location of the memory requested by the kernel as
  virtual memory.

body:
  The MMU is the bridge between the physical memory devices of the machine and the
  virtual memory space. It is a chip that sits between the CPU and the RAM and
  determines the physical location of the memory requested by the kernel as
  virtual memory.

  ![Virtual memory diagram](static/virtual-memory-diagram.jpg)

  ## Pages

  We use the term **pages** to denote blocks of virtual memory and to distinguish
  them from **addresses** as physical blocks. The MMU possesses a **page table**
  which is registry logging which pages correspond to which physical blocks.

  ## Shared pages

  Virtual memory allows the sharing of files and memory by multiple processes.
  Crucially the shared data doesn't have to be within the address of each process,
  instead there is a reference in the page table that each process has access to
  the shared data.

  ## Page faults

  There are two kinds of error that can occur with relation to paged memory:

  - minor page faults
    - The desired page is in main memory but the MMU doesn't currently know where
      it is
  - major page faults
    - The desired page is not in main memory at all. Therefore the kernel must
      fetch it from disk

  Minor page faults are very common and are to be expected; they resolve quickly.
  On the other hand too many major page faults can slow the system down both
  because of the time-costly process of fetching data from disk and because it
  demands more kernel resources to locate the missing page, which puts other
  processes on hold.

raw_content:
  ---
  tags: [memory, Linux]
  created: Monday, July 08, 2024
  ---

  # Memory Management Unit (MMU)

  The MMU is the bridge between the physical memory devices of the machine and the
  virtual memory space. It is a chip that sits between the CPU and the RAM and
  determines the physical location of the memory requested by the kernel as
  virtual memory.

  ![Virtual memory diagram](static/virtual-memory-diagram.jpg)

  ## Pages

  We use the term **pages** to denote blocks of virtual memory and to distinguish
  them from **addresses** as physical blocks. The MMU possesses a **page table**
  which is registry logging which pages correspond to which physical blocks.

  ## Shared pages

  Virtual memory allows the sharing of files and memory by multiple processes.
  Crucially the shared data doesn't have to be within the address of each process,
  instead there is a reference in the page table that each process has access to
  the shared data.

  ## Page faults

  There are two kinds of error that can occur with relation to paged memory:

  - minor page faults
    - The desired page is in main memory but the MMU doesn't currently know where
      it is
  - major page faults
    - The desired page is not in main memory at all. Therefore the kernel must
      fetch it from disk

  Minor page faults are very common and are to be expected; they resolve quickly.
  On the other hand too many major page faults can slow the system down both
  because of the time-costly process of fetching data from disk and because it
  demands more kernel resources to locate the missing page, which puts other
  processes on hold.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/MongoDB_Introduction.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887MongoDB_Introduction.md
title: MongoDB: Introduction
checksum: 718913a0e1103f868ab9dac098860b8ac2dbab786ec25b95757f30a8f83f858b
created: 2024-10-22 05:38:02.361221967+00:00
modified: 2024-10-21 22:11:47.068646566+00:00
metadata: {"tags":["mongo-db","databases"]}
lead:
  MongoDB is not a relational database system like SQL, instead it is
  document-based which means you do not neeed to design database tables and
  schema, you are simply storing data as JSON and there is no transformation of
  the data.

body:
  MongoDB is not a relational database system like SQL, instead it is
  document-based which means you do not neeed to design database tables and
  schema, you are simply storing data as JSON and there is no transformation of
  the data.

  Most of the notes on Mongo will introduce it within the context of Node.js
  backend. We will be sending Javascript objects and arrays to Mongo and returning
  them as JSON.

  ## Databases, collections, documents

  Although Mongo is not a relational database it has a structure that we can
  understand in relation to that paradigm. A **database** is obviously the overall
  structure. It comprises **collections** which are organised sets of data that
  are analagous to [tables](Relational_database_architecture.md#table)
  in RDBs. Within each collection are a series of **documents** which we can think
  of as being equivalent to [rows](Relational_database_architecture.md)
  in RDB table: units that comprise the collection.

  A document is a container comprising key-value pairs in the manner of an object.

  ![](static/mongo-db-structure.svg)

raw_content:
  ---
  tags:
    - mongo-db
    - databases
  ---

  # MongoDB: Introduction

  MongoDB is not a relational database system like SQL, instead it is
  document-based which means you do not neeed to design database tables and
  schema, you are simply storing data as JSON and there is no transformation of
  the data.

  Most of the notes on Mongo will introduce it within the context of Node.js
  backend. We will be sending Javascript objects and arrays to Mongo and returning
  them as JSON.

  ## Databases, collections, documents

  Although Mongo is not a relational database it has a structure that we can
  understand in relation to that paradigm. A **database** is obviously the overall
  structure. It comprises **collections** which are organised sets of data that
  are analagous to [tables](Relational_database_architecture.md#table)
  in RDBs. Within each collection are a series of **documents** which we can think
  of as being equivalent to [rows](Relational_database_architecture.md)
  in RDB table: units that comprise the collection.

  A document is a container comprising key-value pairs in the manner of an object.

  ![](static/mongo-db-structure.svg)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Monitoring_processes_and_resources.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Monitoring_processes_and_resources.md
title: Monitoring processes and resources
lead: ## General purpose diagnostic programs (memory, CPU, I/O)
checksum: b1085549a3184f7ac7a519f4569ef5a6eb8b358363124ccc554c660648e4baca
created: 2024-10-22 05:38:02.489210145+00:00
modified: 2024-10-21 22:11:47.061979786+00:00
metadata: {"tags":["systems-programming","Linux","procedural"]}
body:
  ## General purpose diagnostic programs (memory, CPU, I/O)

  ### `top`/`htop`

  We can use [ps](Processes.md) to list the
  currently running processes but it does not provide much information about the
  resource metrics or how the process changes over time. We can use `top` to get
  more information.

  `top` provides an interactive interface for the information that `ps` displays.
  It updates in real time and shows the most active processes based on the CPU
  time that they are utilising. You can also order by memory usage.

  _Here I have pressed `u` to show only the processes associated with my user:_

  ![](static/htop.png)

  ### Main commands

  | Command | Action                          |
  | ------- | ------------------------------- |
  | -u      | Show processes by selected user |
  | M       | Sort by memory usage            |
  | P       | Sort by cumulative CPU usage    |
  | ?       | View key and explanation        |

  ### Understanding the categories

  - `Main/IO`
    - The first covers all processes. The second focuses on input/output processes
      (i.e. reading and writing to disks and other devices)
  - `PRI`
    - This stands for _priority_. This metric reflects the kernel's current
      schedule priority for the process. The higher the value, it is less likely
      that the kernel will schedule the process if there are competing processes
      that require CPU time. The lower the value, the greater priority this
      process has over others.
  - `NI`
    - This stands for _nice value_. This metric exists in order to allow
      administrators to nudge or influence the priority of a given process. You
      cannot directly tell the kernel to _do x now instead of y_ but you can make
      what are effectively suggestions by manipulating the nice value.
    - The kernel adds the nice value to the current priority value for the given
      process to determine its next time slot. When you increase the nice value of
      process _P_ you are being "nicer" to the other processes by influencing the
      priority of _P_ downwards so that the other processes receive greater
      precedence from the kernel.
    - By default, the nice value will be 0. To reduce priority of PID 1234, you
      would use:
      ```bash
      $ renice 20 1234
      ```
  - `VIRT`
    - The total amount of
      [virtual memory](Virtual_memory_and_the_MMU_in_Linux.md) used by
      the process including: program code, data, shared libraries, pages that have
      been swapped, pages that have been mapped but not used.
  - `RES`
    - Stands for _resident size_
    - The non swapped _physical_ memory the process has used
  - `SHR`
    - The size of the process's
      [shared pages](Virtual_memory_and_the_MMU_in_Linux.md#shared-pages)
  - `S`
    - Status:
      - S for sleeping (idle)
      - R for running
      - D for disk sleep

  ### `vmstat`

  `vmstat` provides similar metrics to `htop` but tells you more about the memory
  state and the activities of the kernel in a single row.

  The default output is a single line with the averages since boot. You can add a
  delay parameter (in secs) which will then output at that interval, allowing you
  to see memory usage in realtime, e.g:

  ```
  $ vmstat 5
  procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
   r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
   2  0      0 4326768 334228 5050952    0    0     8    19   80   10  4  1 94  0  0
   0  0      0 4365520 334260 5054468    0    0     0   125 2140 3434  4  1 94  0  0
   1  0      0 4382400 334276 5068940    0    0     0    77 2102 3988  3  1 95  0  0
   1  0      0 4434000 334288 5052908    0    0     0    25 2859 4278  6  1 92  0  0
   0  0      0 4391576 334304 5086484    0    0     0   110 2899 6480  8  3 90  0  0

  ```

  - `procs`
    - The number of runnable processes (`r`) and the number of blocked (`b`)
      processes
  - `memory`
    - The core memory output distinguishing:
      - Total kbs swapped to disk
      - Total kbs free
      - Total kbs currently in
        [buffer](Role_of_memory_in_computation.md#relation-between-cache-and-buffers)
        and not written
      - Total amount of virtual memory in the
        [cache](Role_of_memory_in_computation.md#relation-between-cache-and-buffers)
  - `swap`
    - Distinguishes amount of memory
      [swapped](Swap_space.md) in (`si`) to memory and
      swapped out (`so`) to disk
  - `io`
    - Disk actions
    - Amount of data read from harddisk (`bi`)
    - Amount of data written to harddisk (`bo`)
  - `system`
    - The number of times the kernel switches to kernel code
  - `cpu`
    - Percentage of the different CPU behaviours:
      - Responding to user tasks (`us`)
      - Time that it is idle (`id`)

  ## Files being used by active processes: `lsof`

  `lsof` stands for _list open files_. It lists opened files and the processes
  using them. Without modifiers it outputs a huge amount of data. The best way to
  use it is to execute it against a specific PID. For example the below output
  gives me some useful info about which files VS Code is using:

  ![](static/lsof.png)

  ## System calls: `strace`

  A system call is when a process requests a service from the
  [kernel](The_kernel.md), for instance an I/O operation to
  memory. We can trace these system calls with `strace`.

  ## CPU performance

  We can use the `uptime` program to assess overall CPU performance in the form of
  a load average.

  > Load average is the number of active processes currently ready to run. It is
  > an estimate of the number of processes that are capable of using the CPU at
  > any given time.

  `Uptime` gives you three load averages:

  ```bash
  $ uptime
  11:19:16 up 14 days,  3:53,  1 user,  load average: 0.84, 0.57, 0.50
  ```

  - The three numbers are load averages for the past 1 minute, 5 minutes and 15
    minutes respectively.

  - A load average close to 0 is usually a good sign because it means that your
    processor isn't being challenged and you are conserving power. Anything equal
    to or above 1 means that a single process is using the CPU nearly all the
    time. You can identify that process with `htop` and it will obviously be near
    to the top. (This is often caused by Chrome and Electron-based software.)

  ## Memory status

  We know that processes primarily interact with virtual memory in the form of
  pages which are then translated to physical blocks by the kernel via the
  [MMU](Virtual_memory_and_the_MMU_in_Linux.md). There are several tools
  which provide windows onto this process.

  ### System page size

  We can view the overall system page size which is a representation of the amount
  of virtual memory available:

  ```bash
  $ getconf PAGE_SIZE
  4096
  ```

  This will typically be the same for all Linux systems.

  ### `free` : available physical memory

  `free` displays the total amount of free and¬used physical and swap memory in
  the system, as well as the
  [buffers and caches](Role_of_memory_in_computation.md#relation-between-cache-and-buffers)
  used by the kernel.

  ```bash
  $ free
                total        used        free      shared  buff/cache   available
  Mem:        16099420     5931512     5039344     2046460     5128564     7781904
  Swap:        3145724           0     3145724
  ```

raw_content:
  ---
  tags:
    - systems-programming
    - Linux
    - procedural
  ---

  # Monitoring processes and resources

  ## General purpose diagnostic programs (memory, CPU, I/O)

  ### `top`/`htop`

  We can use [ps](Processes.md) to list the
  currently running processes but it does not provide much information about the
  resource metrics or how the process changes over time. We can use `top` to get
  more information.

  `top` provides an interactive interface for the information that `ps` displays.
  It updates in real time and shows the most active processes based on the CPU
  time that they are utilising. You can also order by memory usage.

  _Here I have pressed `u` to show only the processes associated with my user:_

  ![](static/htop.png)

  ### Main commands

  | Command | Action                          |
  | ------- | ------------------------------- |
  | -u      | Show processes by selected user |
  | M       | Sort by memory usage            |
  | P       | Sort by cumulative CPU usage    |
  | ?       | View key and explanation        |

  ### Understanding the categories

  - `Main/IO`
    - The first covers all processes. The second focuses on input/output processes
      (i.e. reading and writing to disks and other devices)
  - `PRI`
    - This stands for _priority_. This metric reflects the kernel's current
      schedule priority for the process. The higher the value, it is less likely
      that the kernel will schedule the process if there are competing processes
      that require CPU time. The lower the value, the greater priority this
      process has over others.
  - `NI`
    - This stands for _nice value_. This metric exists in order to allow
      administrators to nudge or influence the priority of a given process. You
      cannot directly tell the kernel to _do x now instead of y_ but you can make
      what are effectively suggestions by manipulating the nice value.
    - The kernel adds the nice value to the current priority value for the given
      process to determine its next time slot. When you increase the nice value of
      process _P_ you are being "nicer" to the other processes by influencing the
      priority of _P_ downwards so that the other processes receive greater
      precedence from the kernel.
    - By default, the nice value will be 0. To reduce priority of PID 1234, you
      would use:
      ```bash
      $ renice 20 1234
      ```
  - `VIRT`
    - The total amount of
      [virtual memory](Virtual_memory_and_the_MMU_in_Linux.md) used by
      the process including: program code, data, shared libraries, pages that have
      been swapped, pages that have been mapped but not used.
  - `RES`
    - Stands for _resident size_
    - The non swapped _physical_ memory the process has used
  - `SHR`
    - The size of the process's
      [shared pages](Virtual_memory_and_the_MMU_in_Linux.md#shared-pages)
  - `S`
    - Status:
      - S for sleeping (idle)
      - R for running
      - D for disk sleep

  ### `vmstat`

  `vmstat` provides similar metrics to `htop` but tells you more about the memory
  state and the activities of the kernel in a single row.

  The default output is a single line with the averages since boot. You can add a
  delay parameter (in secs) which will then output at that interval, allowing you
  to see memory usage in realtime, e.g:

  ```
  $ vmstat 5
  procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
   r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
   2  0      0 4326768 334228 5050952    0    0     8    19   80   10  4  1 94  0  0
   0  0      0 4365520 334260 5054468    0    0     0   125 2140 3434  4  1 94  0  0
   1  0      0 4382400 334276 5068940    0    0     0    77 2102 3988  3  1 95  0  0
   1  0      0 4434000 334288 5052908    0    0     0    25 2859 4278  6  1 92  0  0
   0  0      0 4391576 334304 5086484    0    0     0   110 2899 6480  8  3 90  0  0

  ```

  - `procs`
    - The number of runnable processes (`r`) and the number of blocked (`b`)
      processes
  - `memory`
    - The core memory output distinguishing:
      - Total kbs swapped to disk
      - Total kbs free
      - Total kbs currently in
        [buffer](Role_of_memory_in_computation.md#relation-between-cache-and-buffers)
        and not written
      - Total amount of virtual memory in the
        [cache](Role_of_memory_in_computation.md#relation-between-cache-and-buffers)
  - `swap`
    - Distinguishes amount of memory
      [swapped](Swap_space.md) in (`si`) to memory and
      swapped out (`so`) to disk
  - `io`
    - Disk actions
    - Amount of data read from harddisk (`bi`)
    - Amount of data written to harddisk (`bo`)
  - `system`
    - The number of times the kernel switches to kernel code
  - `cpu`
    - Percentage of the different CPU behaviours:
      - Responding to user tasks (`us`)
      - Time that it is idle (`id`)

  ## Files being used by active processes: `lsof`

  `lsof` stands for _list open files_. It lists opened files and the processes
  using them. Without modifiers it outputs a huge amount of data. The best way to
  use it is to execute it against a specific PID. For example the below output
  gives me some useful info about which files VS Code is using:

  ![](static/lsof.png)

  ## System calls: `strace`

  A system call is when a process requests a service from the
  [kernel](The_kernel.md), for instance an I/O operation to
  memory. We can trace these system calls with `strace`.

  ## CPU performance

  We can use the `uptime` program to assess overall CPU performance in the form of
  a load average.

  > Load average is the number of active processes currently ready to run. It is
  > an estimate of the number of processes that are capable of using the CPU at
  > any given time.

  `Uptime` gives you three load averages:

  ```bash
  $ uptime
  11:19:16 up 14 days,  3:53,  1 user,  load average: 0.84, 0.57, 0.50
  ```

  - The three numbers are load averages for the past 1 minute, 5 minutes and 15
    minutes respectively.

  - A load average close to 0 is usually a good sign because it means that your
    processor isn't being challenged and you are conserving power. Anything equal
    to or above 1 means that a single process is using the CPU nearly all the
    time. You can identify that process with `htop` and it will obviously be near
    to the top. (This is often caused by Chrome and Electron-based software.)

  ## Memory status

  We know that processes primarily interact with virtual memory in the form of
  pages which are then translated to physical blocks by the kernel via the
  [MMU](Virtual_memory_and_the_MMU_in_Linux.md). There are several tools
  which provide windows onto this process.

  ### System page size

  We can view the overall system page size which is a representation of the amount
  of virtual memory available:

  ```bash
  $ getconf PAGE_SIZE
  4096
  ```

  This will typically be the same for all Linux systems.

  ### `free` : available physical memory

  `free` displays the total amount of free and¬used physical and swap memory in
  the system, as well as the
  [buffers and caches](Role_of_memory_in_computation.md#relation-between-cache-and-buffers)
  used by the kernel.

  ```bash
  $ free
                total        used        free      shared  buff/cache   available
  Mem:        16099420     5931512     5039344     2046460     5128564     7781904
  Swap:        3145724           0     3145724
  ```


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Multiplexers_and_demultiplexers.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Multiplexers_and_demultiplexers.md
title: Multiplexers (MUX) and demultiplexers (DMUX)
lead: ## Multiplexer
checksum: c507306482232b7f7406d02480d1063463739c5677c49e0b450bd605d42a3365
created: 2024-10-22 05:38:03.11817435+00:00
modified: 2024-10-21 22:11:47.055313007+00:00
metadata: {"tags":["logic-gates","binary","nand-to-tetris"]}
body:
  ## Multiplexer

  > Multiplexing is the generic term used to describe the operation of sending one
  > or more analogue or digital signals over a common transmission line at
  > different times or speeds.

  A multiplexer selects one of several input signals and forwards ths selected
  input to a single output line.

  We have two inputs (A,B) plus a third input SEL (for "select"). Applying a value
  to SEL toggles the output between A and B.

  ![](static/MUX.png)

  Multiplexers can be used to build larger circuits by connecting the output of
  one multiplexer to the input of another. They are often used to implement data
  selection and switching in digital systems

  ### Programable gates

  One of the main use cases of multiplexers is to implement programmable gates.
  These are gates where the logic can be switched. For example an ANDMUXOR gate
  uses the SEL value to toggle the operation of a gate between AND and OR for its
  two inputs A and B

  ## Demultiplexer

  As the name suggests, a demultiplexer reverses the functionality of a
  multiplexer. It receives a single input and based on the selection of the SEL
  input it channels it to either an A or a B output.

  ![](static/DMUX.png)

  We can think of it as a distributor of a value into one of several possible
  channels.

raw_content:
  ---
  tags: [logic-gates, binary, nand-to-tetris]
  ---

  # Multiplexers (MUX) and demultiplexers (DMUX)

  ## Multiplexer

  > Multiplexing is the generic term used to describe the operation of sending one
  > or more analogue or digital signals over a common transmission line at
  > different times or speeds.

  A multiplexer selects one of several input signals and forwards ths selected
  input to a single output line.

  We have two inputs (A,B) plus a third input SEL (for "select"). Applying a value
  to SEL toggles the output between A and B.

  ![](static/MUX.png)

  Multiplexers can be used to build larger circuits by connecting the output of
  one multiplexer to the input of another. They are often used to implement data
  selection and switching in digital systems

  ### Programable gates

  One of the main use cases of multiplexers is to implement programmable gates.
  These are gates where the logic can be switched. For example an ANDMUXOR gate
  uses the SEL value to toggle the operation of a gate between AND and OR for its
  two inputs A and B

  ## Demultiplexer

  As the name suggests, a demultiplexer reverses the functionality of a
  multiplexer. It receives a single input and based on the selection of the SEL
  input it channels it to either an A or a B output.

  ![](static/DMUX.png)

  We can think of it as a distributor of a value into one of several possible
  channels.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Negation_Elimination.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Negation_Elimination.md
title: Negation Elimination
checksum: 06dafc27e2fab158f20916ac801e8735eaca986776af626147e430327f3e230e
created: 2024-10-22 05:38:03.322966236+00:00
modified: 2024-10-21 22:11:47.055313007+00:00
metadata: {"tags":["logic","propositional-logic"]}
lead:
  Like the [introduction](Negation_Introduction.md) rule for
  negation, the elimination rule also works by deriving a contradiction. It is
  basically _Negation Introduction_ in reverse. Instead of starting the sub-proof
  with a true proposition from which you derive a contradiction, you start with
  the negation of a proposition, derive a contradiction and then assert the
  positive of the negated proposition you started out with.

body:
  Like the [introduction](Negation_Introduction.md) rule for
  negation, the elimination rule also works by deriving a contradiction. It is
  basically _Negation Introduction_ in reverse. Instead of starting the sub-proof
  with a true proposition from which you derive a contradiction, you start with
  the negation of a proposition, derive a contradiction and then assert the
  positive of the negated proposition you started out with.

  ![](static/negate-elim.png)

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

  # Negation Elimination

  Like the [introduction](Negation_Introduction.md) rule for
  negation, the elimination rule also works by deriving a contradiction. It is
  basically _Negation Introduction_ in reverse. Instead of starting the sub-proof
  with a true proposition from which you derive a contradiction, you start with
  the negation of a proposition, derive a contradiction and then assert the
  positive of the negated proposition you started out with.

  ![](static/negate-elim.png)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Negation_Introduction.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Negation_Introduction.md
title: Negation Introduction
checksum: e87bda8419ed6c78993f2e04957db30da021da18b0ab3ccb16ed07d7e707022d
created: 2024-10-22 05:38:03.394741677+00:00
modified: 2024-10-21 22:11:47.065313176+00:00
metadata: {"tags":["logic","propositional-logic"]}
lead:
  This is also known as _proof by contradiction_. You start with an assumption
  declared in a sub-proof. If you can derive a contradiction from this assumption
  (typically from the introduction of another proposition and its negation), then
  you are permitted to derive the negation of the auxiliary assumption in the main
  proof.

body:
  This is also known as _proof by contradiction_. You start with an assumption
  declared in a sub-proof. If you can derive a contradiction from this assumption
  (typically from the introduction of another proposition and its negation), then
  you are permitted to derive the negation of the auxiliary assumption in the main
  proof.

  ![](static/negate-intro.png)

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

  # Negation Introduction

  This is also known as _proof by contradiction_. You start with an assumption
  declared in a sub-proof. If you can derive a contradiction from this assumption
  (typically from the introduction of another proposition and its negation), then
  you are permitted to derive the negation of the auxiliary assumption in the main
  proof.

  ![](static/negate-intro.png)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Network_debugging.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Network_debugging.md
title: Network debugging
lead: ## Related notes
body:
  ## Related notes

  ![Network scanning](static/Network_scanning.md)

checksum: 6b89a3a7bb9fa7cd690b9c6a434d2e4dd88e8891f9ab47dbf02c23109eea5867
created: 2024-10-22 05:38:03.532763457+00:00
modified: 2024-10-21 22:11:47.065313176+00:00
metadata: {"created":"Monday, June 17, 2024","id":"x44w","tags":["procedural","Linux","networks"]}
raw_content:
  ---
  id: x44w
  tags: [procedural, Linux, networks]
  created: Monday, June 17, 2024
  ---

  # Network debugging

  ## Related notes

  ![Network scanning](static/Network_scanning.md)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Network_hosts.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Network_hosts.md
title: Network hosts
checksum: 79fb76bad842c91432cb7cbbf4ae962e11cd37755442f2b7151b629a32de4824
created: 2024-10-22 05:38:03.616532772+00:00
modified: 2024-10-21 22:11:47.055313007+00:00
metadata: {"created":"Saturday, August 03, 2024","tags":["networks"]}
lead:
  A network **host** or **node** is a single computing device attached to a
  ![network](static/Network_fundamentals.md)

body:
  A network **host** or **node** is a single computing device attached to a
  ![network](static/Network_fundamentals.md)

  Hosts can act as servers or clients, or both.

  ## Server

  A network server is a host that listens for inbound network connections and
  provides services to other hosts. Examples include web and email servers.

  ## Client

  A network client is a host that makes outbound connections and requests servcies
  from network servers.

raw_content:
  ---
  tags: [networks]
  created: Saturday, August 03, 2024
  ---

  # Network hosts

  A network **host** or **node** is a single computing device attached to a
  ![network](static/Network_fundamentals.md)

  Hosts can act as servers or clients, or both.

  ## Server

  A network server is a host that listens for inbound network connections and
  provides services to other hosts. Examples include web and email servers.

  ## Client

  A network client is a host that makes outbound connections and requests servcies
  from network servers.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/NodeJS_scripts.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887NodeJS_scripts.md
title: NodeJS scripts
checksum: 74b8892642909854ed260a8ed48e669093d80ac5306efce3e6138cbb9f327c7f
created: 2024-10-22 05:38:03.822053498+00:00
modified: 2024-10-21 22:11:47.055313007+00:00
metadata: {"created":"Friday, April 26, 2024","tags":["node-js"]}
lead:
  If we want to use NodeJS as a scripting language without going to the trouble of
  setting up an NPM-based development environment, we can use the following
  architecture.

body:
  If we want to use NodeJS as a scripting language without going to the trouble of
  setting up an NPM-based development environment, we can use the following
  architecture.

  ```js
  #!/usr/bin/env node

  const process = require("process");

  const actualScript = (firstParam, secondParam) => {
    // Do something
  };

  if (require.main === module) {
    const [, param1, param2] = process.argv;
    actualScript(param1, param2).catch(console.error);
  }
  ```

  Basically we have a function that contains the main actions of the script. This
  is then invoked within `main` in the manner of a Bash or
  ![Python script](static/Single_file_Python_scripts.md)

  This obviously requires the Node binary to be in your path and the script must
  be run with executable privileges.

  If you use any module not in the Node core, you will need to have this installed
  globally for it to work. (There are
  [ways around this](https://thom4.net/2014/self-contained-node-scripts/) for Node
  scripts you publish but for personal scripts this doesn't matter.)

  ## `require.main`

  This construction checks that the script is being directy invoked via the 'node'
  command in the terminal rather than called by another module.

  This is equivalent to the [following construct]() in Python:

  ```py
  if __name__ == "__main__":
  ```

raw_content:
  ---
  tags: [node-js]
  created: Friday, April 26, 2024
  ---

  # NodeJS scripts

  If we want to use NodeJS as a scripting language without going to the trouble of
  setting up an NPM-based development environment, we can use the following
  architecture.

  ```js
  #!/usr/bin/env node

  const process = require("process");

  const actualScript = (firstParam, secondParam) => {
    // Do something
  };

  if (require.main === module) {
    const [, param1, param2] = process.argv;
    actualScript(param1, param2).catch(console.error);
  }
  ```

  Basically we have a function that contains the main actions of the script. This
  is then invoked within `main` in the manner of a Bash or
  ![Python script](static/Single_file_Python_scripts.md)

  This obviously requires the Node binary to be in your path and the script must
  be run with executable privileges.

  If you use any module not in the Node core, you will need to have this installed
  globally for it to work. (There are
  [ways around this](https://thom4.net/2014/self-contained-node-scripts/) for Node
  scripts you publish but for personal scripts this doesn't matter.)

  ## `require.main`

  This construction checks that the script is being directy invoked via the 'node'
  command in the terminal rather than called by another module.

  This is equivalent to the [following construct]() in Python:

  ```py
  if __name__ == "__main__":
  ```


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/OpenSearch.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887OpenSearch.md
title: OpenSearch
lead: ## Background
checksum: caefd221c50585c6e9be5e55282d1661e0bc9f480334a8f73d937ca21c74e64e
created: 2024-10-22 05:38:04.259975568+00:00
modified: 2024-10-21 22:11:47.071979955+00:00
metadata: {"tags":["AWS"]}
body:
  ## Background

  OpenSearch is the AWS implementation of the opean source Elasticsearch search
  engine. It was formally known as "AWS Elasticsearch".

  It has many features but the core usage is to create searchable indicies of a
  given content domain such as, for example, a website or content management
  system. This enables the quick search and retrieval of documents without using
  expensive database queries.

  ## Key concepts

  We will introduce the main concepts with the example of an internal intranet for
  which we want to create a searchable index. The intranet comprises hundreds of
  pages. Each page has the following metadata, conforming to the following
  example.

  ```json
  {
    "title": "Internal News",
    "author": "Jane Doe",
    "published_date": "2023-11-01T00:00:00Z",
    "tags": ["news", "internal"],
    "categories": ["communication"],
    "content": "Today's internal news and updates are..."
  }
  ```

  ### Create domain

  The OpenSearch domain is a managed environment which hosts OpenSearch
  **clusters**. It can contain one or more clusters.

  The domain provides network **endpoints** you use to communicate and send
  requests. Typical requests:

  - ingest data
  - index data
  - run search query and return matches

  #### Clusters and nodes

  A cluster is the highest level of organisation within an OpensSearch domain that
  contains your indexed data. It processes all the search queries and handles
  tasks like indexing, searching, and managing documents.

  ![](static/opensearch-architecture.drawio.svg)

  A cluster comprises **nodes**. Nodes are individual servers that hold part of
  the cluster's data. Each node participates in the indexing and searching of the
  cluster's data.

  - This distributed architecture helps in balancing the load and ensuring high
    availability.
  - Data can be replicated accross nodes, making the system resiliant against data
    loss
  - You can add more nodes to the cluster to handle increased data and traffic,
    making the system adaptable to changing needs.

  ### Define index and mappings

  Assuming the domain has been created. The next step is to create an index for
  the data, say `intranet_pages`. An index is basically a collection where the
  data is stored, similar to a database. Each entry is a **document** in this
  collection.

  Our index will store each webpage as a document.

  We specify the data that we want to store using an index mapping. For instance
  we may not want to store all the metadata for each page, preferring only to
  index a subset of the properties. In this example we will store all the data. We
  would achieve this with the following:

  ```json
  {
    "mappings": {
      "properties": {
        "title": { "type": "text" },
        "author": { "type": "keyword" },
        "published_date": { "type": "date" },
        "tags": { "type": "keyword" },
        "content": { "type": "text" }
      }
    }
  }
  ```

  The mapping will be utilised in the following scenarios:

  - storing data:
    - when a new document is added to the `intranet_pages` index, it will adhere
      to the defined mappings
    - it will have to have the properties specified in the mapping in order to be
      added
  - searching data:
    - when executing searches, OpenSearch utilizes the mappings to understand the
      type of data in each field and optimizes search queries accordingly
  - assessing relevance:
    - proper mappings allow OpenSearch to accurately score and rank search results
      based on relevance.

  ### Ingest data (bulk import and/or scraper)

  In order to create the index that we previously defined with a mapping it is
  necessary to implement some sort of mechanism for collating the metadata that
  matches the mapping. This would be a crawler or scraper that might be
  implemented with a lambda. This is a key part of the ingestion process.

  Alternatively the data could be bulk imported in a format that maps to the
  index.

  ### Querying and searching

  Having established the crawler (and some kind of search interface), we can now
  run queries against the OpenSearch domain.

  A basic example of the structure of a query would be as follows:

  ```sh
  GET /intranet_pages/_search
  ```

  ```json
  {
    "query": {
      "match": {
        "content": "project"
      }
    }
  }
  ```

  Here we search against the `content` mapping to find pages that contain the word
  "project".

  Example of the data returned:

  ```json
  {
    "hits": {
      "total": { "value": 100 },
      "hits": [
        {
          "_index": "intranet_pages",
          "_id": "1",
          "_score": 1.2,
          "_source": {
            "title": "Project ABC Launch",
            "author": "John Doe",
            "published_date": "2023-01-01T00:00:00Z",
            "tags": ["project", "launch"],
            "content": "Details about the launch of Project ABC..."
          }
        }
        // Additional results here
      ]
    }
  }
  ```

  ## Search patterns

  Below are further examples of commonly used search patterns.

  ### Multiple conditions

  Find documents that are authored by Jane Doe that contain the word "meeting".
  `must` stands for boolean AND:

  ```json
  {
    "query": {
      "bool": {
        "must": [
          { "match": { "content": "meeting" } },
          { "match": { "author": "Jane Doe" } }
        ]
      }
    }
  }
  ```

  Find documents that contain either the word "meeting" or the word "project" in
  their content. `should` stands for boolean OR:

  ```json
  {
    "query": {
      "bool": {
        "should": [
          { "match": { "content": "meeting" } },
          { "match": { "content": "project" } }
        ],
        "minimum_should_match": 1
      }
    }
  }
  ```

  `minimum_should_match` specifies the number of conditions that should match.

  ### Query by date ranges

  Find pages published after a certain date:

  ```json
  {
    "query": {
      "range": {
        "published_date": {
          "gte": "2023-01-01T00:00:00Z"
        }
      }
    }
  }
  ```

  ```json
  {
    "query": {
      "bool": {
        "should": [
          { "match": { "fileId": "val" } },
          { "match": { "programmeId": "val" } },
          { "match": { "guid": "val" } }
        ],
        "minimum_should_match": 1
      }
    }
  }
  ```

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

  # OpenSearch

  ## Background

  OpenSearch is the AWS implementation of the opean source Elasticsearch search
  engine. It was formally known as "AWS Elasticsearch".

  It has many features but the core usage is to create searchable indicies of a
  given content domain such as, for example, a website or content management
  system. This enables the quick search and retrieval of documents without using
  expensive database queries.

  ## Key concepts

  We will introduce the main concepts with the example of an internal intranet for
  which we want to create a searchable index. The intranet comprises hundreds of
  pages. Each page has the following metadata, conforming to the following
  example.

  ```json
  {
    "title": "Internal News",
    "author": "Jane Doe",
    "published_date": "2023-11-01T00:00:00Z",
    "tags": ["news", "internal"],
    "categories": ["communication"],
    "content": "Today's internal news and updates are..."
  }
  ```

  ### Create domain

  The OpenSearch domain is a managed environment which hosts OpenSearch
  **clusters**. It can contain one or more clusters.

  The domain provides network **endpoints** you use to communicate and send
  requests. Typical requests:

  - ingest data
  - index data
  - run search query and return matches

  #### Clusters and nodes

  A cluster is the highest level of organisation within an OpensSearch domain that
  contains your indexed data. It processes all the search queries and handles
  tasks like indexing, searching, and managing documents.

  ![](static/opensearch-architecture.drawio.svg)

  A cluster comprises **nodes**. Nodes are individual servers that hold part of
  the cluster's data. Each node participates in the indexing and searching of the
  cluster's data.

  - This distributed architecture helps in balancing the load and ensuring high
    availability.
  - Data can be replicated accross nodes, making the system resiliant against data
    loss
  - You can add more nodes to the cluster to handle increased data and traffic,
    making the system adaptable to changing needs.

  ### Define index and mappings

  Assuming the domain has been created. The next step is to create an index for
  the data, say `intranet_pages`. An index is basically a collection where the
  data is stored, similar to a database. Each entry is a **document** in this
  collection.

  Our index will store each webpage as a document.

  We specify the data that we want to store using an index mapping. For instance
  we may not want to store all the metadata for each page, preferring only to
  index a subset of the properties. In this example we will store all the data. We
  would achieve this with the following:

  ```json
  {
    "mappings": {
      "properties": {
        "title": { "type": "text" },
        "author": { "type": "keyword" },
        "published_date": { "type": "date" },
        "tags": { "type": "keyword" },
        "content": { "type": "text" }
      }
    }
  }
  ```

  The mapping will be utilised in the following scenarios:

  - storing data:
    - when a new document is added to the `intranet_pages` index, it will adhere
      to the defined mappings
    - it will have to have the properties specified in the mapping in order to be
      added
  - searching data:
    - when executing searches, OpenSearch utilizes the mappings to understand the
      type of data in each field and optimizes search queries accordingly
  - assessing relevance:
    - proper mappings allow OpenSearch to accurately score and rank search results
      based on relevance.

  ### Ingest data (bulk import and/or scraper)

  In order to create the index that we previously defined with a mapping it is
  necessary to implement some sort of mechanism for collating the metadata that
  matches the mapping. This would be a crawler or scraper that might be
  implemented with a lambda. This is a key part of the ingestion process.

  Alternatively the data could be bulk imported in a format that maps to the
  index.

  ### Querying and searching

  Having established the crawler (and some kind of search interface), we can now
  run queries against the OpenSearch domain.

  A basic example of the structure of a query would be as follows:

  ```sh
  GET /intranet_pages/_search
  ```

  ```json
  {
    "query": {
      "match": {
        "content": "project"
      }
    }
  }
  ```

  Here we search against the `content` mapping to find pages that contain the word
  "project".

  Example of the data returned:

  ```json
  {
    "hits": {
      "total": { "value": 100 },
      "hits": [
        {
          "_index": "intranet_pages",
          "_id": "1",
          "_score": 1.2,
          "_source": {
            "title": "Project ABC Launch",
            "author": "John Doe",
            "published_date": "2023-01-01T00:00:00Z",
            "tags": ["project", "launch"],
            "content": "Details about the launch of Project ABC..."
          }
        }
        // Additional results here
      ]
    }
  }
  ```

  ## Search patterns

  Below are further examples of commonly used search patterns.

  ### Multiple conditions

  Find documents that are authored by Jane Doe that contain the word "meeting".
  `must` stands for boolean AND:

  ```json
  {
    "query": {
      "bool": {
        "must": [
          { "match": { "content": "meeting" } },
          { "match": { "author": "Jane Doe" } }
        ]
      }
    }
  }
  ```

  Find documents that contain either the word "meeting" or the word "project" in
  their content. `should` stands for boolean OR:

  ```json
  {
    "query": {
      "bool": {
        "should": [
          { "match": { "content": "meeting" } },
          { "match": { "content": "project" } }
        ],
        "minimum_should_match": 1
      }
    }
  }
  ```

  `minimum_should_match` specifies the number of conditions that should match.

  ### Query by date ranges

  Find pages published after a certain date:

  ```json
  {
    "query": {
      "range": {
        "published_date": {
          "gte": "2023-01-01T00:00:00Z"
        }
      }
    }
  }
  ```

  ```json
  {
    "query": {
      "bool": {
        "should": [
          { "match": { "fileId": "val" } },
          { "match": { "programmeId": "val" } },
          { "match": { "guid": "val" } }
        ],
        "minimum_should_match": 1
      }
    }
  }
  ```


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Operating_system_API.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Operating_system_API.md
title: Operating system API
checksum: 4b0f9238a5755b353bb5595c90cd04c1c7eeaad07714b8d91884040317f12f9c
created: 2024-10-22 05:38:04.285093355+00:00
modified: 2024-10-21 22:11:47.068646566+00:00
metadata: {"created":"Wednesday, July 17, 2024","tags":["operating-systems"]}
lead:
  A user can interact with the operating system either via the GUI or the
  [shell](Intro_to_the_Unix_shell.md). (Both ultimately access the OS via the
  shell since the UI is an abstraction layer of the shell.)

body:
  A user can interact with the operating system either via the GUI or the
  [shell](Intro_to_the_Unix_shell.md). (Both ultimately access the OS via the
  shell since the UI is an abstraction layer of the shell.)

  When _programs_ interact with the operating system they use the **operating
  system's API** directly. The GUI and shell also invoke the OS API but the access
  is mediated.

  Every OS has an API. This specification details how a program should interact
  with the OS, exposing functions, properties and data types.

  Whether using the GUI or a terminal emulator, the shell translates the user's
  commands into API calls. The API the invokes internal operating system code to
  perform the action.

  ![OS API diagram](static/OS-api.svg)

  ## Example: opening a file

  A user can use a file explorer and the mouse to open a file. Or they can open
  the file by invoking it from the terminal.

  A running application doesn't have to do this. If it's remit includes opening
  files, it can do so by calling the OS API.

  On a Unix-like system the applicaton would use the `open` API function to open
  the file:

  ```
  open("foo.txt", 0_WRONLY|0_CREAT)
  ```

  This command makes the action write-only and creates the file because it does
  not yet exist. When the user opens a file in their File Explorer, the
  application translates their graphical actions into this API call.

  ## Different OSs have different APIs

  Unix-like and Linux operating systems use variations on the **Portable Operating
  System Interface** (POSIX) standard. This defines the OS API and also the rules
  for the shell's behaviour and its included utilities.

  Variations on this include _Cocoa_, macOS's API and Android's _Android Platform_
  API.

  Windows has its own API called _The Windows API_. The original version was
  16-bit called _Win16_, then when it moved to 32-bits it became _Win32_. When it
  moved to an x86 [instruction_set_architecture](Instruction_set_architectures.md)
  it became _Win64_. Starting with Windows 10, it introduced the _Universal
  Windows Platform_ which aims to make app development consistent accross
  different devices that run Windows.

raw_content:
  ---
  tags: [operating-systems]
  created: Wednesday, July 17, 2024
  ---

  # Operating system API

  A user can interact with the operating system either via the GUI or the
  [shell](Intro_to_the_Unix_shell.md). (Both ultimately access the OS via the
  shell since the UI is an abstraction layer of the shell.)

  When _programs_ interact with the operating system they use the **operating
  system's API** directly. The GUI and shell also invoke the OS API but the access
  is mediated.

  Every OS has an API. This specification details how a program should interact
  with the OS, exposing functions, properties and data types.

  Whether using the GUI or a terminal emulator, the shell translates the user's
  commands into API calls. The API the invokes internal operating system code to
  perform the action.

  ![OS API diagram](static/OS-api.svg)

  ## Example: opening a file

  A user can use a file explorer and the mouse to open a file. Or they can open
  the file by invoking it from the terminal.

  A running application doesn't have to do this. If it's remit includes opening
  files, it can do so by calling the OS API.

  On a Unix-like system the applicaton would use the `open` API function to open
  the file:

  ```
  open("foo.txt", 0_WRONLY|0_CREAT)
  ```

  This command makes the action write-only and creates the file because it does
  not yet exist. When the user opens a file in their File Explorer, the
  application translates their graphical actions into this API call.

  ## Different OSs have different APIs

  Unix-like and Linux operating systems use variations on the **Portable Operating
  System Interface** (POSIX) standard. This defines the OS API and also the rules
  for the shell's behaviour and its included utilities.

  Variations on this include _Cocoa_, macOS's API and Android's _Android Platform_
  API.

  Windows has its own API called _The Windows API_. The original version was
  16-bit called _Win16_, then when it moved to 32-bits it became _Win32_. When it
  moved to an x86 [instruction_set_architecture](Instruction_set_architectures.md)
  it became _Win64_. Starting with Windows 10, it introduced the _Universal
  Windows Platform_ which aims to make app development consistent accross
  different devices that run Windows.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Package_management_in_NodeJS.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Package_management_in_NodeJS.md
title: Package management
lead: ## List installed packages
checksum: db00aafa157a9cad44c0d24a2f990987205a80da8daa5d7b0f251aa613909a32
created: 2024-10-22 05:38:04.38210175+00:00
modified: 2024-10-21 22:11:47.051979618+00:00
metadata: {"tags":["node-js"]}
body:
  ## List installed packages

  ```bash
  npm list
  ```

  This will return a recursive tree that lists dependencies, dependences of
  dependencies, ... and so on. To limit the depth you can add the `--depth=` flag.
  For example to see only your installed packages and their versions use
  `npm list --depth=0`.

  ## View `package.json` data for an installed package

  We could go to the NPM registry and view details or we can quickly view the
  `package.json` for the dependency with the command `npm view [package_name]`

  We can pinpoint specific dependencies in the `package.json`, e.g.
  `npm view [package_name] dependencies `

  ## View outdated modules

  See whether your dependency version is out of date use `npm outdated`. This
  gives us a table, for example:

  ![Pasted image 20220411082627.png](static/Pasted_image_20220411082627.png)

  - _Latest_ tells us the latest release available from the developers
  - _Wanted_ tells us the version that our `package.json` rules target. To take
    the first dependency as an example. We must have set our SemVer syntax to
    `^0.4.x` since it is telling us that there is a minor release that is more
    recent than the one we have installed but is not advising that we update to
    the latest major release.
  - _Current_ tells us which version we currently have installed regardless of the
    version that our `package.json` is targeting or the most recent version
    available.

  ## Updating

  `npm update` only updates from _current_ to _wanted_. In other words it only
  updates in accordance with your caret and tilde rules applied to
  [semantic versioning](Semantic_versioning.md).

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

  # Package management

  ## List installed packages

  ```bash
  npm list
  ```

  This will return a recursive tree that lists dependencies, dependences of
  dependencies, ... and so on. To limit the depth you can add the `--depth=` flag.
  For example to see only your installed packages and their versions use
  `npm list --depth=0`.

  ## View `package.json` data for an installed package

  We could go to the NPM registry and view details or we can quickly view the
  `package.json` for the dependency with the command `npm view [package_name]`

  We can pinpoint specific dependencies in the `package.json`, e.g.
  `npm view [package_name] dependencies `

  ## View outdated modules

  See whether your dependency version is out of date use `npm outdated`. This
  gives us a table, for example:

  ![Pasted image 20220411082627.png](static/Pasted_image_20220411082627.png)

  - _Latest_ tells us the latest release available from the developers
  - _Wanted_ tells us the version that our `package.json` rules target. To take
    the first dependency as an example. We must have set our SemVer syntax to
    `^0.4.x` since it is telling us that there is a minor release that is more
    recent than the one we have installed but is not advising that we update to
    the latest major release.
  - _Current_ tells us which version we currently have installed regardless of the
    version that our `package.json` is targeting or the most recent version
    available.

  ## Updating

  `npm update` only updates from _current_ to _wanted_. In other words it only
  updates in accordance with your caret and tilde rules applied to
  [semantic versioning](Semantic_versioning.md).


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Practical_walkthrough_Lambda_creation_within_AWS.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Practical_walkthrough_Lambda_creation_within_AWS.md
title: Practical walkthrough of creating a Lambda function via the AWS Management Console
lead: ## Basic set-up
checksum: 9bb08d606286d3b6a09c1bc02bc6e895fae0034ebd72683c7bb39d87efd8a69f
created: 2024-10-22 05:38:04.698362354+00:00
modified: 2024-10-21 22:11:47.068646566+00:00
metadata: {"tags":["AWS","aws-lambda","node-js"]}
body:
  ## Basic set-up

  First we name the function and accept the defaults:

  ![](static/lambda_func_one.png)

  This presents us with the function dashboard - a graphical representation of the
  Lambda showing [triggers]() as an input and destination as an output:

  ![](static/lambda-func-two.png)

  Beneath this we have a code editor with the handler function with a basic
  boilerplate:

  ![](static/lambda-func-three.png)

  ## Adding a trigger

  Next we need to add a [trigger](Lambda_triggers.md) that
  execute the handler.

  We will do this using [AWS API Gateway](AWS_API_Gateway.md). We
  select "Add trigger" from the dashboard view and input basic settings:

  ![](static/api-gateway-trigger.png)

  Now we see this step displayed in the dashboard:

  ![](static/lambda-overview.png)

  With the endpoint and other settings displayed:

  ![](static/trigger-info.png)

  If we go to the endpoint URL
  (`https://4kjqwbvd7g.execute-api.us-east-1.amazonaws.com/default/myFirstFunction`),
  we will see the output: `Hello from Lambda`.

  ## Handling parameters

  We can make the example more realistic by expanding the handler to accept query
  parameters. We do this by accessing the value `queryStringParameters` on the
  `event` object:

  ```js
  exports.handler = async (event) => {
    const name = event.queryStringParameters && event.queryStringParameters.name;

    let message = "Hello Lambda";

    if (name !== null) {
      message = `Hello ${name}`;
    }

    const response {
      statusCode: 200,
      body: JSON.stringify(message)

    }
  };
  ```

  If we now access
  `https://4kjqwbvd7g.execute-api.us-east-1.amazonaws.com/default/myFirstFunction?name=Thomas`

  We get `Hello Thomas` as output.

  For a more advanced API with multiple endpoints and parameters, it's easiest to
  use Postman:

  ![](static/postman-lambda.png)

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

  # Practical walkthrough of creating a Lambda function via the AWS Management Console

  ## Basic set-up

  First we name the function and accept the defaults:

  ![](static/lambda_func_one.png)

  This presents us with the function dashboard - a graphical representation of the
  Lambda showing [triggers]() as an input and destination as an output:

  ![](static/lambda-func-two.png)

  Beneath this we have a code editor with the handler function with a basic
  boilerplate:

  ![](static/lambda-func-three.png)

  ## Adding a trigger

  Next we need to add a [trigger](Lambda_triggers.md) that
  execute the handler.

  We will do this using [AWS API Gateway](AWS_API_Gateway.md). We
  select "Add trigger" from the dashboard view and input basic settings:

  ![](static/api-gateway-trigger.png)

  Now we see this step displayed in the dashboard:

  ![](static/lambda-overview.png)

  With the endpoint and other settings displayed:

  ![](static/trigger-info.png)

  If we go to the endpoint URL
  (`https://4kjqwbvd7g.execute-api.us-east-1.amazonaws.com/default/myFirstFunction`),
  we will see the output: `Hello from Lambda`.

  ## Handling parameters

  We can make the example more realistic by expanding the handler to accept query
  parameters. We do this by accessing the value `queryStringParameters` on the
  `event` object:

  ```js
  exports.handler = async (event) => {
    const name = event.queryStringParameters && event.queryStringParameters.name;

    let message = "Hello Lambda";

    if (name !== null) {
      message = `Hello ${name}`;
    }

    const response {
      statusCode: 200,
      body: JSON.stringify(message)

    }
  };
  ```

  If we now access
  `https://4kjqwbvd7g.execute-api.us-east-1.amazonaws.com/default/myFirstFunction?name=Thomas`

  We get `Hello Thomas` as output.

  For a more advanced API with multiple endpoints and parameters, it's easiest to
  use Postman:

  ![](static/postman-lambda.png)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Prime_factorization.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Prime_factorization.md
title: Prime factorisation
checksum: 1446d4d510b43bd965266474746cb9d57c8b3cacb82fd23db58f9e0373e6c4d4
created: 2024-10-22 05:38:04.821315763+00:00
modified: 2024-10-21 22:11:47.068646566+00:00
metadata: {"tags":["prealgebra","number-theory"]}
lead:
  Prime factorisation is the activity of expressing a composite number as the
  unique product of [prime numbers](Primes%20and%20composites.md). There are two
  main approaches to this:

body:
  Prime factorisation is the activity of expressing a composite number as the
  unique product of [prime numbers](Primes%20and%20composites.md). There are two
  main approaches to this:

  - - factor\* trees
  - repeated division by two

  > **Factor trees:** we take a number $n$ and break it down into two factors of
  > $n$. We then repeat this process with the resulting factors working
  > recursively until the numbers we are left with are primes.

  ![Untitled Diagram-Page-1.drawio.png](static/Untitled%20Diagram-Page-1.drawio.png) _The
  prime factors of 27 are 2, 3, 3_

  it doesn't matter which products we choose as the interim factors, we should
  always reach the same outcome:
  ![Untitled Diagram-Page-3.drawio 1.png](static/Untitled%20Diagram-Page-3.drawio%201.png)

  ![Untitled Diagram-Page-2.drawio.png](static/Untitled%20Diagram-Page-2.drawio.png)

raw_content:
  ---
  tags:
    - prealgebra
    - number-theory
  ---

  # Prime factorisation

  Prime factorisation is the activity of expressing a composite number as the
  unique product of [prime numbers](Primes%20and%20composites.md). There are two
  main approaches to this:

  - - factor\* trees
  - repeated division by two

  > **Factor trees:** we take a number $n$ and break it down into two factors of
  > $n$. We then repeat this process with the resulting factors working
  > recursively until the numbers we are left with are primes.

  ![Untitled Diagram-Page-1.drawio.png](static/Untitled%20Diagram-Page-1.drawio.png) _The
  prime factors of 27 are 2, 3, 3_

  it doesn't matter which products we choose as the interim factors, we should
  always reach the same outcome:
  ![Untitled Diagram-Page-3.drawio 1.png](static/Untitled%20Diagram-Page-3.drawio%201.png)

  ![Untitled Diagram-Page-2.drawio.png](static/Untitled%20Diagram-Page-2.drawio.png)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Processes.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Processes.md
title: Processes
checksum: 85de93544e720b60790727fc91c5f7a09ab3cabd1f8ed0adbcbee2190e9957d0
created: 2024-10-22 05:38:05.200054303+00:00
modified: 2024-10-21 22:11:47.068646566+00:00
metadata: {"created":"Friday, June 21, 2024","id":"t70u","tags":["operating-systems"]}
lead:
  Programs are sequences of machine instructions stored in a file. However they do
  not work by themselves. Something needs to load the file's intructions into
  memory, direct the CPU to run the program and manage it during runtime. The OS
  does this via processes.

body:
  Programs are sequences of machine instructions stored in a file. However they do
  not work by themselves. Something needs to load the file's intructions into
  memory, direct the CPU to run the program and manage it during runtime. The OS
  does this via processes.

  A process **is a running instance of a given program**. It can be thought of as
  a container in which a program runs. This container includes:

  - a copy of the program code loaded into memory
  - a memory address
  - other information about the state of the process

  Other than the `init` process started by the kernel (PID1) (see
  ![systemd](static/systemd.md)), every process has a parent process that started it.
  This parent-child relationship creates a tree of processes.

  It is possible that a parent process will terminate before one of its child
  processes. In this instance the child becomes an orphan. When this occurs in
  #Linux, the orphan process is adopted by `init`.

  Below, I have used the `pstree` utility to list all the running processes on my
  machine hierarchically.

  ![diagram of `pstree` output](static/ps-tree.png)

  Children are represented vertically and horizontally.

  ```
  ├─terminator─┬─zsh───tmux: client
  │            ├─zsh───pstree
  │            └─6*[{terminator}]

  ```

  For instance here, `terminator` is a child of `init`, as are `zsh` and `tmux`
  but they are also children of `terminator`.`pstree` is a child of `zsh` and
  therefore also a child (grandchild) of `terminator`.

  Each process has a unique identifier called a _process identifier_, a
  _processID_ or just _PID_.

  ## Related notes

  ![systemd](static/systemd.md)

  ![ps](static/ps.md)

  ![Monitoring processes and resources](static/Monitoring_processes_and_resources.md)

raw_content:
  ---
  id: t70u
  tags: [operating-systems]
  created: Friday, June 21, 2024
  ---

  # Processes

  Programs are sequences of machine instructions stored in a file. However they do
  not work by themselves. Something needs to load the file's intructions into
  memory, direct the CPU to run the program and manage it during runtime. The OS
  does this via processes.

  A process **is a running instance of a given program**. It can be thought of as
  a container in which a program runs. This container includes:

  - a copy of the program code loaded into memory
  - a memory address
  - other information about the state of the process

  Other than the `init` process started by the kernel (PID1) (see
  ![systemd](static/systemd.md)), every process has a parent process that started it.
  This parent-child relationship creates a tree of processes.

  It is possible that a parent process will terminate before one of its child
  processes. In this instance the child becomes an orphan. When this occurs in
  #Linux, the orphan process is adopted by `init`.

  Below, I have used the `pstree` utility to list all the running processes on my
  machine hierarchically.

  ![diagram of `pstree` output](static/ps-tree.png)

  Children are represented vertically and horizontally.

  ```
  ├─terminator─┬─zsh───tmux: client
  │            ├─zsh───pstree
  │            └─6*[{terminator}]

  ```

  For instance here, `terminator` is a child of `init`, as are `zsh` and `tmux`
  but they are also children of `terminator`.`pstree` is a child of `zsh` and
  therefore also a child (grandchild) of `terminator`.

  Each process has a unique identifier called a _process identifier_, a
  _processID_ or just _PID_.

  ## Related notes

  ![systemd](static/systemd.md)

  ![ps](static/ps.md)

  ![Monitoring processes and resources](static/Monitoring_processes_and_resources.md)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Push_buttons.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Push_buttons.md
title: Push buttons
checksum: 51dbede23accc3c21eae47a638fd02c489d8860f3333aeb761b8af610d7e1ba5
created: 2024-10-22 05:38:05.315936314+00:00
modified: 2024-10-21 22:11:47.058646397+00:00
metadata: {"created":"Tuesday, June 25, 2024","id":"nsrv","tags":["electronics"]}
lead:
  Push buttons are push-to-make switches. This means that they are **on when they
  pressed and off when they are not pressed**. A classic example would be the
  switches on a keyboard.

body:
  Push buttons are push-to-make switches. This means that they are **on when they
  pressed and off when they are not pressed**. A classic example would be the
  switches on a keyboard.

  Push buttons have four legs that are connected in signal-in and signal-out
  pairs.

  To create a breakable circuit we would connect a signal-in cable to the top left
  at row 23 and a signal-out cable to the bottom-right at row 25.

  ![Push button legs diagram](static/push-button-legs.png)

  _The correct use of a push button where the button breaks the circuit_

  ![](static/correct_push_button.jpg)

  To override the switch functionality and just have the button work as a
  connector we would connect the signal-in to the row 23 input and the row 23
  output.

  _The push button being used as simple connector which does not break the
  circuit:_

  ![](static/incorrect_push_button.jpg)

  ## Related notes

  https://projects.raspberrypi.org/en/projects/button-switch-scratch-pi/1

raw_content:
  ---
  id: nsrv
  tags: [electronics]
  created: Tuesday, June 25, 2024
  ---

  # Push buttons

  Push buttons are push-to-make switches. This means that they are **on when they
  pressed and off when they are not pressed**. A classic example would be the
  switches on a keyboard.

  Push buttons have four legs that are connected in signal-in and signal-out
  pairs.

  To create a breakable circuit we would connect a signal-in cable to the top left
  at row 23 and a signal-out cable to the bottom-right at row 25.

  ![Push button legs diagram](static/push-button-legs.png)

  _The correct use of a push button where the button breaks the circuit_

  ![](static/correct_push_button.jpg)

  To override the switch functionality and just have the button work as a
  connector we would connect the signal-in to the row 23 input and the row 23
  output.

  _The push button being used as simple connector which does not break the
  circuit:_

  ![](static/incorrect_push_button.jpg)

  ## Related notes

  https://projects.raspberrypi.org/en/projects/button-switch-scratch-pi/1


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Queue.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Queue.md
title: Queues
lead: _Visualization of the queue data structure_
checksum: 83f89784b24fc99246421dca513519c9d27bb10b823bcf7ec0bd156d6ae1fc39
created: 2024-10-22 05:38:05.707370336+00:00
modified: 2024-10-21 22:11:47.051979618+00:00
metadata: {"tags":["data-structures"]}
body:
  _Visualization of the queue data structure_

  ![queue.svg](static/queue.svg)

  ## A queue is a sequential data structure and most similar to a stack

  A queue is basically a stack inverted and can be visualised as a line of people
  waiting to be served. The person that is first into the queue is served first.
  The people who join the queue behind this person will be served after them.

  As a result it can be summarised as **'first in, first out'** or FIFO.

  Just like a stack it is a sequential data structure without random access. You
  cannot access all the elements at one instant, you can only access the oldest
  element. If you wish to access the newest element, you have to move through all
  the others that are ahead of it first, at which point it becomes the oldest.

  It differs from a stack in that a stack only has one point of transaction: the
  front or 'top' of the stack. With a stack you add and remove from the top or
  front. With a queue, you have two points of transaction: the front of the queue
  for removing elements and the back of the queue for adding elements.

  We can however add a 'peek' method to see which is the next element in line to
  come out.

  As we are removing the first element added, we use an `array shift` method to
  remove items from the front of the array.

  Removing an element from the queue is called **dequeuing**. Adding an element to
  the queue is called **enqueuing**. In terms of the tail/head nomenclature, the
  end of the queue where elements are enqueued is the **tail** and front of the
  queue, where elements are removed is the **head**.

  ```js
  class Queue {
    items = []; // array to store the elements comprising the queue
    enqueue = (element) => this.items.push(element); // add element to back
    dequeue = () => this.items.shift(); // remove element from the front

    // Optional helper methods:
    isEmpty = () => this.items.length === 0; // return true if the queue is empty
    clear = () => (this.items.length = 0); // empty the queue
    size = () => this.items.length; // count elements in queue
    peek = () => (!this.isEmpty() ? this.items[0] : undefined); // check which element is next in line
  }
  ```

  ## Use cases

  - A queue sequences data in the order that it was first received. Thus it is
    most beneficial in scenarios where receipt time is a factor. For example,
    imagine a service whereby tickets go on sale at a certain time for a limited
    period. You may want to prioritise those who sent their payment earliest over
    those who arrived later.
  - Serving requests on a single shared resource like a printer or CPU task
    scheduling.

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

  # Queues

  _Visualization of the queue data structure_

  ![queue.svg](static/queue.svg)

  ## A queue is a sequential data structure and most similar to a stack

  A queue is basically a stack inverted and can be visualised as a line of people
  waiting to be served. The person that is first into the queue is served first.
  The people who join the queue behind this person will be served after them.

  As a result it can be summarised as **'first in, first out'** or FIFO.

  Just like a stack it is a sequential data structure without random access. You
  cannot access all the elements at one instant, you can only access the oldest
  element. If you wish to access the newest element, you have to move through all
  the others that are ahead of it first, at which point it becomes the oldest.

  It differs from a stack in that a stack only has one point of transaction: the
  front or 'top' of the stack. With a stack you add and remove from the top or
  front. With a queue, you have two points of transaction: the front of the queue
  for removing elements and the back of the queue for adding elements.

  We can however add a 'peek' method to see which is the next element in line to
  come out.

  As we are removing the first element added, we use an `array shift` method to
  remove items from the front of the array.

  Removing an element from the queue is called **dequeuing**. Adding an element to
  the queue is called **enqueuing**. In terms of the tail/head nomenclature, the
  end of the queue where elements are enqueued is the **tail** and front of the
  queue, where elements are removed is the **head**.

  ```js
  class Queue {
    items = []; // array to store the elements comprising the queue
    enqueue = (element) => this.items.push(element); // add element to back
    dequeue = () => this.items.shift(); // remove element from the front

    // Optional helper methods:
    isEmpty = () => this.items.length === 0; // return true if the queue is empty
    clear = () => (this.items.length = 0); // empty the queue
    size = () => this.items.length; // count elements in queue
    peek = () => (!this.isEmpty() ? this.items[0] : undefined); // check which element is next in line
  }
  ```

  ## Use cases

  - A queue sequences data in the order that it was first received. Thus it is
    most beneficial in scenarios where receipt time is a factor. For example,
    imagine a service whereby tickets go on sale at a certain time for a limited
    period. You may want to prioritise those who sent their payment earliest over
    those who arrived later.
  - Serving requests on a single shared resource like a printer or CPU task
    scheduling.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Recursion.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Recursion.md
title: Schema
checksum: 2c12e1fc7153e3a6a2e16c6df4351f670908f2c373fb5008bf738401579f2848
created: 2024-10-22 05:38:06.390567087+00:00
modified: 2024-10-21 22:11:47.065313176+00:00
metadata: {"tags":["algorithms","data-structures","recursion"]}
lead:
  The general structure of a recursive function is as follows:
  ![javascript-recursion.png](static/javascript-recursion.png)

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

  ## Why use recursive functions?

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

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

  ## Base condition

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

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

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

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

  recurse();
  ```

  ## Demonstrations

  ### Countdown

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

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

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

  countDown(4);
  ```

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

  Each stage in the process is noted below:

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

  ### Finding factorials

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

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

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

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

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

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

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

  let num = 3;

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

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

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

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

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

  ## Schema

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

  ## Why use recursive functions?

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

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

  ## Base condition

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

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

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

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

  recurse();
  ```

  ## Demonstrations

  ### Countdown

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

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

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

  countDown(4);
  ```

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

  Each stage in the process is noted below:

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

  ### Finding factorials

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

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

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

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

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

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

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

  let num = 3;

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

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


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Reducing_fractions.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Reducing_fractions.md
title: Reducing fractions to their lowest terms
checksum: a983af1af143aca0f038e3c8236e4a48c5ae1c76c7c5ad71103b19eae1c5c011
created: 2024-10-22 05:38:06.567157051+00:00
modified: 2024-10-21 22:11:47.071979955+00:00
metadata: {"tags":["prealgebra","fractions"]}
lead:
  > A fraction is said to be _reduced to its lowest terms_ if the
  > [greatest common divisor](Factors%20and%20divisors.md#greatest-common-divisor)
  > of the numerator and the denominator is $1$.

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

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

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

  ### 1. Reducing with repeated application of divisors

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

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

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

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

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

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

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

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

  ### 3. Reducing with factors and cancellation

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

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

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

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

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

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

  ### 4. Reducing with prime factorisation

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

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

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

  This gives us:

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

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

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

  This gives us:

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

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

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

  ## Reducing fractions that contain variables

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

  ### Demonstration

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

  The prime factors of the numerator and denominator:

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

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

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

  Canceled:

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

  Which gives us:

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

  ## Reducing fractions that contain negative values

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

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

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

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

  - Cancel the coefficients and variable parts

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

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

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

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

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

  - Cancel the coefficients and variable parts

    $$

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

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

  # Reducing fractions to their lowest terms

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

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

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

  ### 1. Reducing with repeated application of divisors

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

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

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

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

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

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

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

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

  ### 3. Reducing with factors and cancellation

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

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

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

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

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

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

  ### 4. Reducing with prime factorisation

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

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

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

  This gives us:

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

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

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

  This gives us:

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

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

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

  ## Reducing fractions that contain variables

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

  ### Demonstration

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

  The prime factors of the numerator and denominator:

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

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

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

  Canceled:

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

  Which gives us:

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

  ## Reducing fractions that contain negative values

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

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

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

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

  - Cancel the coefficients and variable parts

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

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

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

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

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

  - Cancel the coefficients and variable parts

    $$

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


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Reiteration.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Reiteration.md
title: Reiteration
checksum: e62f304f8d727b7b397e93b77c77368c632e04ac4b556b1b96ec4c1152e1c00e
created: 2024-10-22 05:38:07.24393366+00:00
modified: 2024-10-21 22:11:47.058646397+00:00
metadata: {"tags":["logic","propositional-logic"]}
lead:
  **Reiteration (R)** allows us to restate any proposition already in the proof
  within the main proof or a more deeply nested sub-proof. Reiteration allows us
  to reuse any assumptions, or propositions derived from assumptions, without
  having to introduce a new dependency with another assumption.

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

  ![](static/reiteration.png)

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

  # Reiteration

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

  ![](static/reiteration.png)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Relation_between_kernel_and_CPU.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Relation_between_kernel_and_CPU.md
title: Relation between the kernel and CPU
checksum: a91ce914eacc56cc8be96824568c74e5e089bdcc534bc759f083c760f043d6f0
created: 2024-10-22 05:38:07.270741129+00:00
modified: 2024-10-21 22:11:47.068646566+00:00
metadata: {"tags":["CPU","computer-architecture","operating-systems"]}
lead:
  It can be confusing to understand how the kernel and CPU interact with one
  another. Whilst it is true to say the kernel mediates between the hardware and
  user processes, this led me to think that the kernel was somehow 'over and
  above' the actions of the CPU. This is wrong and if you think about it, how
  could this be since any process, kernel included, requires the processor? The
  kernel is executed by and loaded into the CPU just like any other instruction in
  the
  [fetch, decode, execute cycle](Fetch_decode_execute.md)
  of the CPU.

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

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

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

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

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

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

  # Relation between the kernel and CPU

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

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

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

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

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


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Relational_database_architecture.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Relational_database_architecture.md
title: Relational database architecture
lead: Tables, fields and records are the basic building blocks of databases
checksum: 9881c3c4bfb0f8db45ec1cc624285f106ccd0ffc47f43e7f0c7282508ac9a99e
created: 2024-10-22 05:38:07.314514874+00:00
modified: 2024-10-21 22:11:47.071979955+00:00
metadata: {"tags":["databases"]}
body:
  Tables, fields and records are the basic building blocks of databases

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

  ## Table

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

  ## Record

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

  ## Field

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

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

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

  # Relational database architecture

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

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

  ## Table

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

  ## Record

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

  ## Field

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

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


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Relays.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Relays.md
title: Relays
lead: A relay is an electrically operated switch.
checksum: a2c69bb93c9d5e6c86a601244e7428f5637d1ff1c9f74389d350dc3e59743987
created: 2024-10-22 05:38:07.331687189+00:00
modified: 2024-10-21 22:11:47.068646566+00:00
metadata: {"created":"Monday, September 09, 2024","tags":["electronics"]}
body:
  A relay is an electrically operated switch.

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

  ## Core components

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

  ## Operation

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

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

  ## Usage

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

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

  # Relays

  A relay is an electrically operated switch.

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

  ## Core components

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

  ## Operation

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

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

  ## Usage

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


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Role_of_memory_in_computation.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Role_of_memory_in_computation.md
title: The role of memory in computation
checksum: 2676711a4cfe526eb17833e8f6163ec67501a34040fbb692a56c5aac673d57ce
created: 2024-10-22 05:38:07.679009127+00:00
modified: 2024-10-21 22:11:47.051979618+00:00
metadata: {"created":"Friday, July 12, 2024","tags":["memory"]}
lead:
  The following steps outline the way in which memory interacts with the processor
  during computational cycles, once the [bootstrapping](Boot_process.md) process
  has completed and the OS kernel is itself loaded into memory.

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

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

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

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

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

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

  # The role of memory in computation

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

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

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

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

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


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/SAM.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887SAM.md
title: AWS SAM
checksum: cbada2f6f90b251a9a700e3d574fd76ff659d7f74c78812ccf1de14121a2fdd1
created: 2024-10-22 05:38:07.933465361+00:00
modified: 2024-10-21 22:11:47.071979955+00:00
metadata: {"tags":["AWS"]}
lead:
  SAM stands for **serverless application model**. It is a framework developed by
  AWS to simplify the process of building, deploying and managing serverless
  applications. It provides a concise syntax for defining the components of a
  serverless application, such as
  [Lambda functions](zk/Lambda_programming_model.md),
  [API gateway](/zk/AWS_API_Gateway.md) and database tables.

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

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

  ## Key features of SAM

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

  ## Main technologies required

  ### Docker

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

  ### AWS CLI

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

  ### AWS SAM CLI

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

  ## Setting up credentials for the AWS CLI

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

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

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

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

  ### Switching between credentials

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

  You can do this with:

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

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

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

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

  ```

  ## Starting a SAM project

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

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

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

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

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

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

  ### `template.yaml`

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

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

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

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

    return response;
  };
  ```

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

  The full template is below:

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

  ## Adding our own code

  We will create our own function and API Gateway trigger.

  We will place our function after the existing `HelloWorldFunction`

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

  We can test the syntax with:

  ```sh
  sam validate
  ```

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

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

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

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

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

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

  The directory structure is as follows:

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

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

  ## Deploying the project

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

  The process is as follows:

  1. Build
  2. Package
  3. Deploy

  ### Build

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

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

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

  ### Package

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

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

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

  The command is as follows:

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

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

  ### Local development with Docker

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

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

  ### Deploy

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

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

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

  ## Call the endpoint

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

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

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

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

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

  # AWS SAM

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

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

  ## Key features of SAM

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

  ## Main technologies required

  ### Docker

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

  ### AWS CLI

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

  ### AWS SAM CLI

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

  ## Setting up credentials for the AWS CLI

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

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

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

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

  ### Switching between credentials

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

  You can do this with:

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

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

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

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

  ```

  ## Starting a SAM project

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

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

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

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

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

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

  ### `template.yaml`

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

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

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

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

    return response;
  };
  ```

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

  The full template is below:

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

  ## Adding our own code

  We will create our own function and API Gateway trigger.

  We will place our function after the existing `HelloWorldFunction`

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

  We can test the syntax with:

  ```sh
  sam validate
  ```

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

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

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

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

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

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

  The directory structure is as follows:

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

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

  ## Deploying the project

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

  The process is as follows:

  1. Build
  2. Package
  3. Deploy

  ### Build

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

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

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

  ### Package

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

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

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

  The command is as follows:

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

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

  ### Local development with Docker

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

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

  ### Deploy

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

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

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

  ## Call the endpoint

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

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

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

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


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Set_DNS_settings.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Set_DNS_settings.md
title: Set DNS settings
lead: ## Change the default DNS server
checksum: cfeee8b26f847e701acec99eb1819b53e2472f4e973b7f9ffc27777d731cc4bd
created: 2024-10-22 05:38:08.395609224+00:00
modified: 2024-10-21 22:11:47.055313007+00:00
metadata: {"created":"Wednesday, June 12, 2024","id":"2w0e","tags":["Linux","networks"]}
body:
  ## Change the default DNS server

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

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

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

  ```
  #wlan.network

  [Match]
  Name=wl*

  [Link]
  RequiredForOnline=routable

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

  [DHCPv4]
  UseDNS=no
  RouteMetric=600

  [IPv6AcceptRA]
  RouteMetric=600

  ```

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

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

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

  ```

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

  ```

  ## Related notes

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

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

  # Set DNS settings

  ## Change the default DNS server

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

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

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

  ```
  #wlan.network

  [Match]
  Name=wl*

  [Link]
  RequiredForOnline=routable

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

  [DHCPv4]
  UseDNS=no
  RouteMetric=600

  [IPv6AcceptRA]
  RouteMetric=600

  ```

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

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

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

  ```

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

  ```

  ## Related notes

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


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Single_file_Python_scripts.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Single_file_Python_scripts.md
title: Single file Python scripts
lead: ## Basic architecture
checksum: 549ab3f50d3ada31a2037a1768c3cde53585e0bea4d42e81099554cebb183e11
created: 2024-10-22 05:38:08.701872693+00:00
modified: 2024-10-21 22:11:47.051979618+00:00
metadata: {"created":"Friday, April 26, 2024","id":"atzw","tags":[]}
body:
  ## Basic architecture

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

  import sys

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

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

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

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

  ```py

  def main():
      # Do some stuff


  if __name__ == "__main__":
      main()

  ```

  ## Related notes

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

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

  # Single file Python scripts

  ## Basic architecture

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

  import sys

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

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

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

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

  ```py

  def main():
      # Do some stuff


  if __name__ == "__main__":
      main()

  ```

  ## Related notes

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


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Stacks.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Stacks.md
title: A stack is a linear data structure that observes LIFO
checksum: a59a78baf347d32d5a27262120f9ad2752da1ae819a49d812da79b7651090b5d
created: 2024-10-22 05:38:09.109959799+00:00
modified: 2024-10-21 22:11:47.061979786+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](static/stack2.svg)

  _A stack visualised horizontally_ ![stack1.svg](static/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: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Step_functions_and_state_machines.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Step_functions_and_state_machines.md
title: AWS Step functions and state machines
checksum: 8f8d10c12c0f83ca09fa25556fec683632776b831e2709c6248f208b2a2fa502
created: 2024-10-22 05:38:09.185903874+00:00
modified: 2024-10-21 22:11:47.058646397+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:

  ![](static/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:

  ![](static/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:

  ![](static/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:

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


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Strategies_for_constructing_proofs.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Strategies_for_constructing_proofs.md
title: Strategies for constructing proofs in propositional logic
lead: ## General strategy
checksum: 68a35c81c565ac2bcf880f80870d8ba553dc1ae04cc0bac41df0b75c610735fd
created: 2024-10-22 05:38:09.27674403+00:00
modified: 2024-10-21 22:11:47.065313176+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:

  ![](static/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:

  ![](static/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:

  ![](static/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.

  ![](static/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](static/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.

  ![](static/theoremproof.png)

  ## A complex theorem proof

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

  ![](static/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:

  ![](static/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:

  ![](static/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:

  ![](static/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.

  ![](static/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](static/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.

  ![](static/theoremproof.png)

  ## A complex theorem proof

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

  ![](static/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: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/The_History_of_Computing_Swade.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887The_History_of_Computing_Swade.md
title: History of Computing (Swade, 2022 )
checksum: 175100321d73fc26d754f01bdb4d012523dddab0b6e20c549cb5f2c21f1950eb
created: 2024-10-22 05:38:10.486839991+00:00
modified: 2024-10-21 22:11:47.048646228+00:00
metadata: {"created":"Friday, August 23, 2024","tags":["literature","computer-history"]}
lead:
  | Title                                               | Author      | Publication date | Resource type |
  | --------------------------------------------------- | ----------- | ---------------- | ------------- |
  | The History of Computing: A Very Short Introduction | Doron Swade | 2022             | Book          |

body:
  | Title                                               | Author      | Publication date | Resource type |
  | --------------------------------------------------- | ----------- | ---------------- | ------------- |
  | The History of Computing: A Very Short Introduction | Doron Swade | 2022             | Book          |

  ## Timeline

  A typical timeline approach rooted in major innovations.

  - Ancient aids to counting: knotted cords and notched sticks
  - Ancient aids to calculation: counting boards and abacii
  - Early mechanical calculator devices in the 17th century (number wheels,
    Pascal, Leibniz)
  - Modern aids to calculation: slide rules following the discovery of
    [logarithms](Logarithms.md)
  - Mechanised, automated calculating engines of Babbage in the 19th century
  - Punched-card machines leading to IBM in the early 20th century
  - Analogue and electro-mechanical computers of the early 20th century inclusive
    of wartime computers
  - Early valve-based (vacuum-tubed) digital computers (again wartime and early
    Cold War)
  - The invention of the transistor and first fully-digital computers
  - The invention of [integrated_circuits](Integrated_circuits.md)
  - Supercomputers
  - Minicomputers
  - Consumer personal computers
  - Internet and later, Web
  - Smart phones

  First three phases of digital electronic computers:

  - Wartime up to 1950s vacuum-tube era
  - Transistor era up to 1963
  - First microchip era ending in early 1970s

  ## Terms

  | Term            | Definition                                                                                                                                                                  |
  | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | automatic       | can execute programs, process data and perform functions on its own once it has been started. Can monitor its own processes and make adjustments without human intervention |
  | general-purpose | a computer designed to be able to solve a wide variety of problems and perform many different types of tasks as opposed to a computer built for a specific, limited purpose |
  | programmable    | the ability to change the operations a computer peforms without physically altering its hardware                                                                            |

  ## Mechanical calculating devices in the 17th century

  Focus was chiefly on creating a desktop calculator capable of four-function
  arithmetic.

  ![Photograph of Pascaline](static/pascaline.jpg)

  ![Photograph of replica of Leibniz stepped drum machine](static/leibniz-stepped-drum.jpg)

  The main contenders were the Pascaline of #Pascal (which only did cumulative
  addition) and the wheel or "stepped drum" calculator of #Leibniz that could do
  all operations (in theory).

  Subsequent designs were based on these artefacts. In practice, neither worked
  consistently well with the carriage of tens remaining a sticking point.

  ![Photograph of Arithmometer](static/arithmometer.jpg)

  ![Photograph of Comptometer](static/comptometer.png)

  The arithmometer (crank driven) and comptometer (key-driven) were descendents of
  the Leibniz design that became commercially viable by the 19th century along
  with other mechanical calculators. In the US, Burroughs dominated the market.

  ## Babbage: mechanized, automated calculation

  > I wish to God these calculations had been executed by Steam (#Babbage)

  With Babbage's machines we see an approach to computation that can only be
  understood against the backdrop of the Industrial Revolution in which they were
  conceived.

  The idea is that the machine is a factory and number is the product. In the same
  way as the mechanised looms created textiles. It is the extension of a model of
  industrial production from goods/commodities to information.

  Babbage conceived two machines: the Difference Engine (DE) and the Analytical
  Engine (AE). Neither were successfully built in his lifetime. The DE preceded
  the AE and was basically an advanced mechanical calculator whereas the AE
  approximated a general purpose computer.

  Despite this, with the Difference Engine, in contrast to preceding _aids to
  calculation_, the steps of the computational algorithm were no longer directed
  by human intelligence but by internal rules embodied in the mechanism and
  automatically generated.

  ### Difference Engine

  The DE's single purpose was to calculate and output mathematical tables such as
  the results of polynomial equations. The idea was that you would input the
  answer a bell would ring and the machine would _halt_. This influenced #Turing
  later. It was non-programmable and designed for a specific set of calculations.

  ### Analytical Engine

  Conceived as a general-purpose computing machine capable of perfoming a wide
  range of calculations, programmable using punched cards similar to those used
  with Jacquard looms.

  It more resembled modern computers in that Babbage used concepts that would
  later translate into the #vonNeumann architecture. There was a "mill" (CPU),
  "store" (memory) and input/output mechanisms. It also had a concept of looping
  and conditional branching.

  ### Lovelace's insight

  A central idea of Ada #Lovelace, expressed in her notes on the Analytical Engine
  is that **number can represent entities other than quantity**.

  If we assign meaning to number, results arrived at by operating on number
  according to rules can say things about the world when mapped back onto the
  world using the meanings assigned to them.

  Lovelace's insight was that the potential of computing lay in the power of
  machines to manipulate representations of the world contained in symbols.

  ## Analogue computers

  Both digital and analogue computers are automatic. They differ in _how they
  represent quantities_ and how their outputs are derived.

  With digital machines, quantity is represented as a string of discrete digits.

  With analogue machines, quantity is a physical property _in itself_ rather than
  a representation. This could be, for example: the lowering of a weight, the flow
  of a liquid, or an electrical charge. This physical behaviour is **analagous**
  to the system that is being modelled. Quantities are continuously variable
  values rather than discrete (discontinuous values).

  Digital machines produce results by _calculation_ whereas analogue machines
  produce results by _measurement_, e.g. the height of liquid in a tank or the
  time it taks for a tank to be emptied.

  ### Examples of analogue computers

  #### The Phillips Hydraulic Computer

  This used fluid to model the workings of the British economy. It consisted of a
  series of transparent plastic tanks and pipes which were fastened to a wooden
  board.

  ![Philips Hydraulic Computer](static/hydraulic_computer.jpg)

  Each tank represented some aspect of the UK national economy and the flow of
  money around the economy was illustrated by coloured water. At the top of the
  board was a large tank called the treasury. Water (representing money) flowed
  from the treasury to other tanks representing the various ways in which a
  country could spend its money. For example, there were tanks for health and
  education. To increase spending on health care a tap could be opened to drain
  water from the treasury to the tank which represented health spending.

  #### Bush's Differential Analyser

  This was designed to solve differential equations by integration. In contrast to
  the Philips Computer it was general enough to be used to solve problems from
  different contexts. Examples of these contexts: heat flow, ballistics,
  mechanics, population growth, chemical interactions, astronomy.

  ![Differential Analyser](static/differential_analyser_bush.jpg)

  It was about the size of a room and used shafts, motors, discs and wheels to
  work.

  ### Historiography

  There is a tendency in the history of computing to downplay or diminish the
  contribution of analogue computing devices and to present them as just an
  inferior precursor to the inevitable dominance of digital electronic computers.

  This is ahistorical and inaccurate.

  Analogue (and electromechanical devices) overlapped with and coexisted with
  digital devices for 40 years, spanning the first three generations of digital
  electronic devices. The term "analogue" itself only came about when the need
  arose to distinguish digital devices from other types of computer. They were not
  "rivals" before this.

  ### Electro-mechanical computers

  Electro-mechanical devices (also known as "electronic analogue computers") are a
  sort of midway between full digital devices and analogue computers, forming a
  bridge between the two eras.

  Their heyday was roughly 1935 - 1945.

  Their key components were:

  - [relays](Relays.md) for logic operations
  - rotating shafts and gears for performing calculations
  - punched cards or paper tape for input instructions and outputs

  They were slower than fully digital computers as they were limited by the speed
  of moving components rather than the flow of electric charge. In addition the
  various mechanical parts were prone to wear and needed frequent replacement.

  ### Examples of electro-mechanical computers

  #### Harvard Mark 1 (1937-1944) - Aikin, IBM

  Designed by Howard Aiken and built by IBM (1937-1944). Also known as the
  _Automatic Sequence Controlled Calculator_ (ASCC). A general-purpose
  electro-mechanical computer it was most famously used at Los Alamos by
  #vonNeumann to calculate the blast yield of the atomic bomb.

  ![Harvard Mark One](static/Harvard_Mark_1.jpg)

  It was more than 15m in length and weighed 5 tonnes comprising over 750,000
  parts. It used paper tape and punched cards for input/output.

  #### Complex Number Calculator (1940) - Bell Labs, Stibitz

  Not general purpose nor was it programmable. It was hardwired to perform a
  specific set of operations on numbers and nothing else. It used relays like the
  others. Its distinguishing feature was that it used a teletype for input rather
  than cards or paper tape.

  ![Calculating panel of the CMC](static/complex-number-calculator-bell-labs.jpg)

  ![Teletype input of CMC](static/stibitz-teletype.jpg)

  It comprised a panel (the calculating unit) and teletype (the input). One could
  remotely access the computer from the teletype in another location, providing it
  was connected to Bell Lab's telephone network.

  #### Z3 (1941) - Konrad Zuse

  ![Z3](static/Zuse-Z3.jpeg)

  First designed in 1938 and completed in 1941. Considered the first fully
  automatic, programmable digital computer although relay-based. Programs were
  stored on external punched tape and binary was used as the number system.
  Destroyed by Allied bombing in the War but a replica (pictured) was built
  in 1998.

  ## Vacuum-tube devices

  Vacuum-tube devices were the first fully electronic digital devices as they used
  vacuum-tubes for implementing binary logic rather than relay switches.

  Canonical examples from this era are the ABC computer, the Collossus and most
  famously, the ENIAC.

  ### The Antanasoff-Berry Computer (1939 - 1942)

  ![ABC Computer](static/abc-computer.jpg)

  Fully automatic, digital electronic computer but not programmable or really
  general-purpose, being created to do linear equations. It took decimal input and
  output but operated internally using binary arithmetic. The input was punched
  cards and the output was via a front-panel display.

  Its importance stems from the fact that it was the first to use vacuum-tubes for
  the arithmetic calculations of the processor.

  ### Colossus (1943-1945) - Tommy Flowers/Max Newman

  Designed and built at the Post Office Research Station at Dollis Hill. Used for
  cracking the Lorenz messages of the German high command.

  Mainly important (beyond its role in the War) for establishing the viability of
  vacuum-tubes over relays. It comprised 2.4 thousand tubes.

  Debatable the extent to which the Collosus can be called the _first digital
  computer_ since it was not general purpose and not fully programmable.

  Furthermore it was not even a complete solution in its context of application.
  The codebreakers would be given a certain advantage using the Collosus
  (narrowing the field of possible deciphering keys) but still need to use manual
  methods to break the code. It made decipher time shorter rather than executing
  the full diciphering. In contrast to, say, the Bombe computer that would
  identify the key for the days transmissions.

  It's actual influence is also hard to gauge because of the secrecy that remained
  in place after the War. Apart from those who worked on it, its technical
  advancements were not public knowledge until the late 1970s.

  ### ENIAC (1945) John Mauchly/J.P Eckert

  The _Electronic Numerical Calculator and Integrator_

  The main motivation for creating the ENIAC was military: the US Army needed
  speed and accuracy when calculating artiliary firing tables and munition
  trajectory so that shells could hit their intended targets. Accuracy depended on
  myriad factors: gun elevation, shell shape and weight, explosive charge,
  distance, wind, temperature etc. Previously this had been done by human
  computers under the aegis of the Ballistic Research Laboratory.

  ![The main control panel of the ENIAC](static/eniac.jpg)

  It was not completed until after VE day but was used at Los Alamos after the War
  and retired in 1955.

  The BRL commissioned the Moore School of Engineering at the University of
  Pennsylvania for the construction of an automatic electronic calculator.

  It used vacuum-tubes, along with switches and realys. It used decimal rather
  than binary and had punch card input and output. It was crudely programmable and
  could be set up to work on different problems by rearranging the different
  components using switches and plugboards. But this took roughly two days.

  It's design was very influential because it was made public and not classified.

  ### EDVAC (1944-49) John Mauchly/J.P Eckert/ John von Neumann

  Built at the Moore School for the BRL as a successor to ENIAC by Mauchly and
  Eckert whilst ENIAC was still operational. It's architecture arose out of
  discussions between #vonNeumann and Mauchly and Eckert and was necessitated by
  the need to calculate the behaviour of the fissile materials in the atomic bomb.

  It resulted in the famous _First Draft of a Report on the EDVAC_ by #vonNeumann
  which is seen as the definitive statement of the modern **digital programmable
  general-purpose computer**.

  As a result of the paper, #vonNeumann effectively got the credit for the
  invention even though it synthesised ideas from the ENIAC and the contributions
  of others to the EDVAC.

  There was lots of disputed provenance and claims of plagiarism generally.
  Antanasoff won a legal claim against Eckert and Mauchly for stealing ideas from
  the ABC to make ENIAC.

  // Add more on the hardware.

  ### Concept of the _internal stored program_

  - Key innovation of the EDVAC was the _internal stored program_ (ISP).

  - Solution to the problem of preceding computers requiring down-time when
    switching between applications and problem sets.

  - Circuitry would need to be reconfigured before the computer could run on the
    next problem

  - ISP removed the friction:

    - the instructions comprising the program would be prepared on tape or punched
      cards and read into electronic memory
    - the hardware configuration of the machine would remain the same accross
      different programs and require no reconfiguration, only the input program
      data would change

  - This was basically the invention of software. Both instructions and data
    shared the same memory space once they were read-in. Before, the data would be
    stored in memory but the instructions would be read one-by-one from the
    external storage media.

  - Key consequences:
    - flexibility (no need for re-wiring)
    - speed: fetching instructions from memory is quicker than fetching from
      external storage devicde
    - self-modification: since they were in memory, programs could modify
      themselves during their execution (e.g. use branching conditions) and
      treating one program as data for another. This enabled more complex
      algorithms and also opened the possibility of higher-level languages and
      compilers
    - simplified architecture: both programs and data in memory
    - generalisation: computers could switch between different tasks simply by
      loading different programs

  ### Post-EDVAC devices

  There were several notable devices which attempted to implement the architecture
  described in the _First Draft_ using vacuum tubes for logic operations and a
  variety of different electronic methods for memory:

  - the Manchester "Baby"
  - the Cambridge EDSAC
  - Turing's ACE computer

  #### Manchester Baby (1948)

  ![The Manchester Baby Computer](static/manchester_baby.png)

  - An experimental computer intended to create the
    [von Neumann architecture](CPU_architecture.md) using
    [Williams_Tube_RAM](Williams_Tube_RAM.md)

  - Developed at the Univesity of Manchester and completed in 1948.

  - Considered the first electronic stored-program computer and first to contain
    all the elements of a modern electronic digital computer.

  #### EDSAC (1949) J.Wilkes et al.

  ![EDSAC](static/EDSAC.jpg)

  - _Electronic Delay Storge Automatic Computer_

  - Constructed by Maurice Wilkes and others at the Mathematical Laboratory of the
    University of Cambridge.

  - The second digital stored-program computer after the Manchester Baby.

  - It used vacuum-tubes for the arithmetical operations in the ALU and mercury
    [delay line memory](Delay_line_memory.md) for the RAM.

  - Designed to be used by relatively non-specialist practitioners from other
    university departments who were expected to program it themselves. To this
    end, a formal programming paradigm was forged for the EDSAC which established
    the following:

    - subroutines as a library of common procedures available to programmers (e.g.
      printing a result, reading input tape, program checking, mathematical
      operations)
    - diagnostics: techniques for verifying program code and its correctness

  ![Programming Digital Electronic Computer book](static/programming_a_digital_electronic_computter.jpg)

  - The key players wrote the first textbook on programming in 1951: _The
    Preparation of Programs for an Electronic Digital Computer_ (Wilkes, Wheeler,
    and Gill)

  #### The Automatic Computing Engine (ACE) (1946) Alan Turing

  - Technical design provided by Alan #Turing working at the Mathematics Division
    of the National Physical Laboratory. The product of Turing's theoretical work
    in "On Computable Numbers" where he proposes the concept of a
    [Turing_machine](Turing_machines.md) and based on his experience with early
    single-purpose computing devices at Bletchley.

  - Due to secrecy over wartime work, it was hard for get Turing to build a fully
    electronic implementation since this would disclose advances made during the
    War. Tommy Flowers was intended to be brought in to build it but again this
    was scuppered over the classification of wartime technology. Had this not been
    the case, it is likely it would've been seen as on par with the EDVAC in its
    design, if not dwarfing it.

  - Turing proposed a stored program architecture with high-speed memory. It would
    be more perfomant than the EDVAC as a result.

  ![The Pilot ACE computer](static/pilot-ace.jpg)

  - Turing's actual design was not implemented as it was thought too ambitious
    given how powerful it would need to be. Instead a smaller prototype was made,
    the Pilot ACE. It used vacuum-tubes for logic, and mercury delay lines for
    memory. Data and memory was input via punched cards.

  ### Further applications of the EDSAC architecture

  ### LEO I

  - Built by Lyons Tea Company to manage business affairs (payroll, inventory,
    stock management)

  - Marked a shift from military and academic contexts for computers to business
    and data management.

  ### IAS machines

  ![The MANIAC computer](static/MANIAC_computer.jpg)

  Several machines were built at the Institute for Advanced Study utilising the
  "von Neumann" architecture and associated advancements such as vacuum-tubes and
  Williams' tubes. There was the original IAS machine (1952) as well as the
  JOHNNIAC (1954) and MANIAC (1956).

  Although their purpose was military (Los Alamos), their designs were public and
  widely studied making them influential outside of academia.

  ### UNIVAC (1951)

  ![UNIVAC_computer](static/univac-1.jpg)

  Mauchley and Eckert, who had designed the ENIAC left the Moore School and went
  into business: Eckert-Mauchley Computer Corporation. This was bought by
  Remington Rand and in subsequent years became the main competitor to IBM.

  In this capacity they built the UNIVAC: _Universal Automatic Computer_. It's
  name being an embodiment of its nature as a general-purpose, electronic digital
  computer.

  It used vacuum-tubes for logic and mercury delay lines for memory. It had
  multiple means of input/output including: directly via an operator console
  (basically a typewriter keyboard), magnetic tape for input and output, along
  with punched cards.

  ![UNIVAC operator console](static/univac-operator-console.jpeg)

  It was the first computer specifically designed to include business and
  administrative use. This was underscored by its first client: the US Census
  Bureau.

  A key event was its succesful prediction of the 1952 general election. It
  correctly predicted a landslide for Eisenhower (against expectations). (This was
  so unlikely, they actually fudged the data because they thought the machine was
  way off.) It was a novelty on the results night but it cemented a certain
  concept of the computer in the public imagination - large, room sized machines
  with blinking lights.

  Swade notes that Eckert and Mauchley effectively launched the US commercial
  computer industry with the UNIVAC.

  ## IBM

  - First created in 1911 as a merger of four companies all involved in some
    capacity in business data processing. The most significant of these was the
    Hollerith Tabulating Machine Company who's punched card machines had been used
    in the 1890 census, greatly reducing its collation time

  - Named IBM in 1924

  - UNIVAC represented the encroachment of computing devices into IBM's market of
    punched-card based data-processing and effectively put IBM on notice.

  - To attack the threat IBM brought out three crucial machines: the 701, 702
    and 650.

  - IBM's approach to marketing their computers was to emphasise the comfort of
    continuity over the shock of the new represented by the UNIVAC. IBM had
    already captured the business market. Their computers would be more of the
    same, just faster and more powerful than their punched card machines due to
    the electronics. To this end they initially did not call their devices
    'computers'.

  - IBM were ultimately totally successful in their pivot to the computing market.
    By 1960 it owned over 70% of the data processing and computer market.

  ### IBM 701 ("Defense Calculator") (1952)

  ![Control unit of the IBM 701](static/ibm-701-control-unit.jpeg)

  IBM's first electronic computer. It directly competed with the UNIVAC for
  government contracts. It followed the prevailing approach of vaccuum tubes for
  logic and had a variety of storage methods: Williams tubes,
  [magnetic tape](Magnetic_tape.md) , and magentic drums.

  701s were used at Los Alamos and avionics companies. Some were also used to
  manage payroll in business contexts.

  ### IBM 702 ("Tape Processing Machine") (1953)

  ![Advertisement for the IBM 702](static/ibm-add.jpeg)

  Focused primarily on business applications and targetted at businesses rather
  than government contracts. Less powerful than the 701.

  ### IBM 650 (1953)

  ![IBM 650](static/ibm-650.jpg)

  Low-cost general purpose machine using magnetic drum memory. Marketed as slower
  but more affordable than the 700 range. It was mass-produced unlike the others
  which were built for specific customers. It was in fact the first mass-produced
  computer in the world. It proved the breakout star in IBMs initial line up and
  sometimes called "IBM's Model T".

  ![IBM_650 console panel](static/ibm-650-console-panel.jpg)

  IBM offerred 650s to univesities at a 60% discount on the condition that the
  universities would establish courses in computing. This was shrewd as it meant
  that a whole generation of engineers and computer scientists learned on 650s and
  would prefer them in their research. It also created a pool of skilled users
  that would go on to work at IBM.

  Donald Knuth dedicated _The Art of Computer Programming_ to the 650.

  It used [magnetic drum memory](./Magnetic_drum_memory.md) along with
  vacuum-tubes.

  ### IBM 1401 (1959)

  ![IBM 1401](static/ibm-1401.jpg)

  Another IBM mainframe. Transistors replaced vacuum-tubes. Magnetic core storage
  replaced magnetic drum storage. It was housed in rectangular light-blue cabinets
  and the ubiquity of the 1401 in industry earned IBM the moniker 'Big Blue'.

  ### IBM System/360 (1964)

  ![IBM 360](static/ibm-360.jpg)

  Named to suggest all-round compatibility - a family of mainframes designed to
  cover commercial and scientific applications. Considered one of history's most
  successful computers.

  Up until this point all IBM computers had a programming language unique to the
  specific processor. At that point there were about seven IBM computer models in
  active use and they were all incompatible with each other.

  ![IBM 360 promo poster](static/ibm-360-in-use.jpeg)

  In contrast the 360 computers all used the same programming language. This meant
  they were interoperable with each others. Because the there were variants in the
  360 ranges, customers could purchase a smaller system knowing they could expand
  it if their needs grew, without reprogramming their application software.

  ## Magnetic core devices: Whirlwind and SAGE

  Two devices that leveraged the new technology of
  [magnetic_core_memory](Magnetic_core_memory.md) where the Whirlwind and SAGE
  computers. The ENIAC was also updated to use magnetic cores.

  ![Whirlwind magnetic core memory](static/whirlwind-magnetic-cores.JPG)

  The Whirlwind computer (1953) was a flight simulator and the first to use
  magnetic cores. Crucially was able to operate in realtime for output.

  The SAGE computer (_Semi-Automatic Ground Environment_) emerged as a response to
  the Soviet acquisition of the atomic bomb in 1949. The Americans realised they
  needed a much better early warning and air defence system that would allow them
  to identify bombers in their airspace and dispatch fighters.

  To achieve this, it was necessary to detect enemy bombers with radar and compute
  an interception course. Alterations in the bomber flight path required
  continuous updates and real-time computation of directions for the responding
  fighter.

  The resulting computer was SAGE (made by IBM) which was modelled on Whirlwind,
  using magnetic cores. SAGE computers were spread accross the continental US in
  sectors and managed by NORAD.

  ![SAGE command post](static/SAGE_command_post.jpg)

  The SAGE system was operation between 1958-1984, receiving continual
  improvements and updates. In addition to magnetic cores it introduced many
  technological innovations that influenced computers generally: interactive
  screens, printed circuit boards, mass-storage devices, digital communication
  over telephone lines, time-sharing, along with advances in software.

  Because so many private companies worked as contractors on SAGE (IBM, Burroughs,
  Bell Labs, RAND Corp), the military technology (both hardware and software) made
  its way from the military context to business and consumer computing.

  An example of a civilian application of SAGE technology was airline booking
  systems, where booking reservation data needed to be processed in realtime. IBM
  worked with American Airlines to introduce this.

  ## Transistor and integrated circuit revolution

  The arc of modern computing history (and its main eras) can be presented as

  - vacuum-tubes
  - [transistors](Transistors.md)
  - [integrated_circuits](Integrated_circuits.md)

  The transistor was invented at Bell Labs in 1947 by William Shockley and others.
  They were not created with computers in mind. Instead they were first used in
  hearing aids (1953) and transistor radios (1954).

  From the mid-1950s onwards they started being used in military computers.

  They had several advantages over vacuum-tubes: they were durable and reliable
  with no required startup time, they were power efficient and small.

  The actual period where transistors alone were supreme in the form of a
  transistor board was relatively short-lived and rapidly gave way to ICs.

  Computers of the transistor era had the following components separate from each
  other: the transistors, wired connections, resistors and capacitors. Integrated
  circuits put these all on the same piece of silicon.

  They were first manufactured by Fairchild Semiconductor which was an offshoot of
  Shockley's Shockley Semiconductor Laboratory.

  The initial marked for ICs was military and scientific: the Minuteman ICBM and
  computers of the Apollo spacecraft. This laid the groundworkd for later
  commercial use and the personal computer revolution.

  ## Mini-computers

  Mini computers did not threaten mainframes but they opened up a new class of
  user. They were vastly cheaper than a mainframe and only the size of a small
  fridge.

  The main player was DEC (Digital Equipment Corporation) starting in the
  mid-1960s. The introduced the PDP-8 in 1965 (Programmed Data Processor). The
  PDP-8 used transisotrs and magnetic core memory. It was affordable to smaller
  businesses if not yet, consumers.

  ![The PDP-8](static/540px-PDP-8_(1).jpg>)

  The internals were made public and DEC encouraged making the machine extensible
  by users being permitted to create their own programs and specialised
  applications. This was a very different culture to IBM where technicians were
  required and machines were typically leased.

  By 1988, DEC was the second largest computer company after IBM. This led to
  additional companies entering the mini-computer market: Data General, Honeywell,
  Hewlett-Packard.

  Mini-computers died as a category in the mid-1990s in the wake of the PC.

  ## The Personal Computer

  The PC emerged out of advances in IC chip design - this was the engine of the
  PC.

  Intel (newly founded by Robert Noyce and Gordon Moore) were charged with making
  custom chips for a Japanese calculator company. Instead of creating the custom
  chips, they advised the company to use a general purpose chip that could handle
  general computing as well as the basic calculator functions. This generic chip -
  the Intel 4004 - was the microprocessor combined with three other ICs
  (comprising 2.3k transistors) that comprised the fundamental building blocks of
  the #vonNeumann architecture.

  ![Intel 4004 chip](static/intel-4004.jpg)

  However it took until the Apple II in 1977 for the phrase "personal computer" to
  enter the lexicon and become an identifiable class.

  The broader context for this was the computer hobbyists in Silicon Valley/Menlo
  Park (particularly the Homebrew Club of amateur computer engineers) along with
  idealistic figures from the 60s counterculture (such as Stuart Brand) who began
  associating computing technology as a tool of personal liberty and fulfillment.

  ### Microsoft and Apple

  The PC "boom" began with the Altair 8800. This was a mail-order kit that
  required manual assembly. It was released by Micro Instrumentation Telemetry
  Systems (MITS) in 1975. It couldn't do much but had expansion capability
  (memory, teletype interface, casette player for data storage).

  ![Altair 8800](static/altair-8800.jpg)

  Bill Gates and Paul Allen made a proposal to MITS: they would write software
  that would allow users to program the Altair in BASIC. They agreed and were
  prepared to distribute the software. As a result Gates and Allen founded
  Microsoft in 1975.

  Around the same time, Steve Wozniak (member of the Homebrew club) built the
  Apple I as a single-board hobbyist project. He made this available to buy via
  mail order and formed Apple with Steve Jobs in 1976 to manage the enterprise.

  ![Apple I](static/apple1.jpg)

  By 1977 they had investment capital and brought out the Apple II. This was sold
  preassembled with casing and required no soldering. It had expansion slots for
  third-party vendors to create compatible devices and had colour graphics.

  The design of the Apple II was published in _Byte_ in order to invite
  crowd-sourced plugins and extensions. One of these was the Z-80 softcard which
  allowed it to run the CP/M OS (created by the company Digital Research) giving
  ready-made access to software such as a word-processor, spreadsheets and
  databases.

  ![Apple 2](static/apple2.jpg)

  Other competitors in the PC market at this time were Radioshack's TRS-80 and the
  Commodore PET.

  ### IBM

  All the while it was hobbyist and tinkerers driving the PC market, IBM had no
  real interest. When PCSs started being used by businesses and their capacity
  grew to include typical business applications (databases, spreadsheets) it
  sensed an incursion into its market.

  ![IBM PC](static/ibm-pc.jpg)

  Thus, in 1981, IBM launched the "IBM Personal Computer" which rapidly became the
  industry standard. This had the effect of legitimising the concept of a PC.
  There was overwhelming demand. In their marketing IBM melded home and office use
  to capture both markets at once.

  The internal specification of the IBM PC was freely available (dubbed 'open
  architecture'), in contrast to IBM's business computers, to encourage software
  expansion. Cards could be created to fit in its expansion slots adding graphics,
  sound, additional memory and networking capability.

  IBM originally contracted Digital Research to create software based on CP/M but
  this fell through. Microsoft got the deal to supply the OS which became MS-DOS
  (Microsoft Disk Operating System). Henceforth every PC and PC-clone came with
  DOS bundled.

raw_content:
  ---
  tags: [literature, computer-history]
  created: Friday, August 23, 2024
  ---

  # History of Computing (Swade, 2022 )

  | Title                                               | Author      | Publication date | Resource type |
  | --------------------------------------------------- | ----------- | ---------------- | ------------- |
  | The History of Computing: A Very Short Introduction | Doron Swade | 2022             | Book          |

  ## Timeline

  A typical timeline approach rooted in major innovations.

  - Ancient aids to counting: knotted cords and notched sticks
  - Ancient aids to calculation: counting boards and abacii
  - Early mechanical calculator devices in the 17th century (number wheels,
    Pascal, Leibniz)
  - Modern aids to calculation: slide rules following the discovery of
    [logarithms](Logarithms.md)
  - Mechanised, automated calculating engines of Babbage in the 19th century
  - Punched-card machines leading to IBM in the early 20th century
  - Analogue and electro-mechanical computers of the early 20th century inclusive
    of wartime computers
  - Early valve-based (vacuum-tubed) digital computers (again wartime and early
    Cold War)
  - The invention of the transistor and first fully-digital computers
  - The invention of [integrated_circuits](Integrated_circuits.md)
  - Supercomputers
  - Minicomputers
  - Consumer personal computers
  - Internet and later, Web
  - Smart phones

  First three phases of digital electronic computers:

  - Wartime up to 1950s vacuum-tube era
  - Transistor era up to 1963
  - First microchip era ending in early 1970s

  ## Terms

  | Term            | Definition                                                                                                                                                                  |
  | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | automatic       | can execute programs, process data and perform functions on its own once it has been started. Can monitor its own processes and make adjustments without human intervention |
  | general-purpose | a computer designed to be able to solve a wide variety of problems and perform many different types of tasks as opposed to a computer built for a specific, limited purpose |
  | programmable    | the ability to change the operations a computer peforms without physically altering its hardware                                                                            |

  ## Mechanical calculating devices in the 17th century

  Focus was chiefly on creating a desktop calculator capable of four-function
  arithmetic.

  ![Photograph of Pascaline](static/pascaline.jpg)

  ![Photograph of replica of Leibniz stepped drum machine](static/leibniz-stepped-drum.jpg)

  The main contenders were the Pascaline of #Pascal (which only did cumulative
  addition) and the wheel or "stepped drum" calculator of #Leibniz that could do
  all operations (in theory).

  Subsequent designs were based on these artefacts. In practice, neither worked
  consistently well with the carriage of tens remaining a sticking point.

  ![Photograph of Arithmometer](static/arithmometer.jpg)

  ![Photograph of Comptometer](static/comptometer.png)

  The arithmometer (crank driven) and comptometer (key-driven) were descendents of
  the Leibniz design that became commercially viable by the 19th century along
  with other mechanical calculators. In the US, Burroughs dominated the market.

  ## Babbage: mechanized, automated calculation

  > I wish to God these calculations had been executed by Steam (#Babbage)

  With Babbage's machines we see an approach to computation that can only be
  understood against the backdrop of the Industrial Revolution in which they were
  conceived.

  The idea is that the machine is a factory and number is the product. In the same
  way as the mechanised looms created textiles. It is the extension of a model of
  industrial production from goods/commodities to information.

  Babbage conceived two machines: the Difference Engine (DE) and the Analytical
  Engine (AE). Neither were successfully built in his lifetime. The DE preceded
  the AE and was basically an advanced mechanical calculator whereas the AE
  approximated a general purpose computer.

  Despite this, with the Difference Engine, in contrast to preceding _aids to
  calculation_, the steps of the computational algorithm were no longer directed
  by human intelligence but by internal rules embodied in the mechanism and
  automatically generated.

  ### Difference Engine

  The DE's single purpose was to calculate and output mathematical tables such as
  the results of polynomial equations. The idea was that you would input the
  answer a bell would ring and the machine would _halt_. This influenced #Turing
  later. It was non-programmable and designed for a specific set of calculations.

  ### Analytical Engine

  Conceived as a general-purpose computing machine capable of perfoming a wide
  range of calculations, programmable using punched cards similar to those used
  with Jacquard looms.

  It more resembled modern computers in that Babbage used concepts that would
  later translate into the #vonNeumann architecture. There was a "mill" (CPU),
  "store" (memory) and input/output mechanisms. It also had a concept of looping
  and conditional branching.

  ### Lovelace's insight

  A central idea of Ada #Lovelace, expressed in her notes on the Analytical Engine
  is that **number can represent entities other than quantity**.

  If we assign meaning to number, results arrived at by operating on number
  according to rules can say things about the world when mapped back onto the
  world using the meanings assigned to them.

  Lovelace's insight was that the potential of computing lay in the power of
  machines to manipulate representations of the world contained in symbols.

  ## Analogue computers

  Both digital and analogue computers are automatic. They differ in _how they
  represent quantities_ and how their outputs are derived.

  With digital machines, quantity is represented as a string of discrete digits.

  With analogue machines, quantity is a physical property _in itself_ rather than
  a representation. This could be, for example: the lowering of a weight, the flow
  of a liquid, or an electrical charge. This physical behaviour is **analagous**
  to the system that is being modelled. Quantities are continuously variable
  values rather than discrete (discontinuous values).

  Digital machines produce results by _calculation_ whereas analogue machines
  produce results by _measurement_, e.g. the height of liquid in a tank or the
  time it taks for a tank to be emptied.

  ### Examples of analogue computers

  #### The Phillips Hydraulic Computer

  This used fluid to model the workings of the British economy. It consisted of a
  series of transparent plastic tanks and pipes which were fastened to a wooden
  board.

  ![Philips Hydraulic Computer](static/hydraulic_computer.jpg)

  Each tank represented some aspect of the UK national economy and the flow of
  money around the economy was illustrated by coloured water. At the top of the
  board was a large tank called the treasury. Water (representing money) flowed
  from the treasury to other tanks representing the various ways in which a
  country could spend its money. For example, there were tanks for health and
  education. To increase spending on health care a tap could be opened to drain
  water from the treasury to the tank which represented health spending.

  #### Bush's Differential Analyser

  This was designed to solve differential equations by integration. In contrast to
  the Philips Computer it was general enough to be used to solve problems from
  different contexts. Examples of these contexts: heat flow, ballistics,
  mechanics, population growth, chemical interactions, astronomy.

  ![Differential Analyser](static/differential_analyser_bush.jpg)

  It was about the size of a room and used shafts, motors, discs and wheels to
  work.

  ### Historiography

  There is a tendency in the history of computing to downplay or diminish the
  contribution of analogue computing devices and to present them as just an
  inferior precursor to the inevitable dominance of digital electronic computers.

  This is ahistorical and inaccurate.

  Analogue (and electromechanical devices) overlapped with and coexisted with
  digital devices for 40 years, spanning the first three generations of digital
  electronic devices. The term "analogue" itself only came about when the need
  arose to distinguish digital devices from other types of computer. They were not
  "rivals" before this.

  ### Electro-mechanical computers

  Electro-mechanical devices (also known as "electronic analogue computers") are a
  sort of midway between full digital devices and analogue computers, forming a
  bridge between the two eras.

  Their heyday was roughly 1935 - 1945.

  Their key components were:

  - [relays](Relays.md) for logic operations
  - rotating shafts and gears for performing calculations
  - punched cards or paper tape for input instructions and outputs

  They were slower than fully digital computers as they were limited by the speed
  of moving components rather than the flow of electric charge. In addition the
  various mechanical parts were prone to wear and needed frequent replacement.

  ### Examples of electro-mechanical computers

  #### Harvard Mark 1 (1937-1944) - Aikin, IBM

  Designed by Howard Aiken and built by IBM (1937-1944). Also known as the
  _Automatic Sequence Controlled Calculator_ (ASCC). A general-purpose
  electro-mechanical computer it was most famously used at Los Alamos by
  #vonNeumann to calculate the blast yield of the atomic bomb.

  ![Harvard Mark One](static/Harvard_Mark_1.jpg)

  It was more than 15m in length and weighed 5 tonnes comprising over 750,000
  parts. It used paper tape and punched cards for input/output.

  #### Complex Number Calculator (1940) - Bell Labs, Stibitz

  Not general purpose nor was it programmable. It was hardwired to perform a
  specific set of operations on numbers and nothing else. It used relays like the
  others. Its distinguishing feature was that it used a teletype for input rather
  than cards or paper tape.

  ![Calculating panel of the CMC](static/complex-number-calculator-bell-labs.jpg)

  ![Teletype input of CMC](static/stibitz-teletype.jpg)

  It comprised a panel (the calculating unit) and teletype (the input). One could
  remotely access the computer from the teletype in another location, providing it
  was connected to Bell Lab's telephone network.

  #### Z3 (1941) - Konrad Zuse

  ![Z3](static/Zuse-Z3.jpeg)

  First designed in 1938 and completed in 1941. Considered the first fully
  automatic, programmable digital computer although relay-based. Programs were
  stored on external punched tape and binary was used as the number system.
  Destroyed by Allied bombing in the War but a replica (pictured) was built
  in 1998.

  ## Vacuum-tube devices

  Vacuum-tube devices were the first fully electronic digital devices as they used
  vacuum-tubes for implementing binary logic rather than relay switches.

  Canonical examples from this era are the ABC computer, the Collossus and most
  famously, the ENIAC.

  ### The Antanasoff-Berry Computer (1939 - 1942)

  ![ABC Computer](static/abc-computer.jpg)

  Fully automatic, digital electronic computer but not programmable or really
  general-purpose, being created to do linear equations. It took decimal input and
  output but operated internally using binary arithmetic. The input was punched
  cards and the output was via a front-panel display.

  Its importance stems from the fact that it was the first to use vacuum-tubes for
  the arithmetic calculations of the processor.

  ### Colossus (1943-1945) - Tommy Flowers/Max Newman

  Designed and built at the Post Office Research Station at Dollis Hill. Used for
  cracking the Lorenz messages of the German high command.

  Mainly important (beyond its role in the War) for establishing the viability of
  vacuum-tubes over relays. It comprised 2.4 thousand tubes.

  Debatable the extent to which the Collosus can be called the _first digital
  computer_ since it was not general purpose and not fully programmable.

  Furthermore it was not even a complete solution in its context of application.
  The codebreakers would be given a certain advantage using the Collosus
  (narrowing the field of possible deciphering keys) but still need to use manual
  methods to break the code. It made decipher time shorter rather than executing
  the full diciphering. In contrast to, say, the Bombe computer that would
  identify the key for the days transmissions.

  It's actual influence is also hard to gauge because of the secrecy that remained
  in place after the War. Apart from those who worked on it, its technical
  advancements were not public knowledge until the late 1970s.

  ### ENIAC (1945) John Mauchly/J.P Eckert

  The _Electronic Numerical Calculator and Integrator_

  The main motivation for creating the ENIAC was military: the US Army needed
  speed and accuracy when calculating artiliary firing tables and munition
  trajectory so that shells could hit their intended targets. Accuracy depended on
  myriad factors: gun elevation, shell shape and weight, explosive charge,
  distance, wind, temperature etc. Previously this had been done by human
  computers under the aegis of the Ballistic Research Laboratory.

  ![The main control panel of the ENIAC](static/eniac.jpg)

  It was not completed until after VE day but was used at Los Alamos after the War
  and retired in 1955.

  The BRL commissioned the Moore School of Engineering at the University of
  Pennsylvania for the construction of an automatic electronic calculator.

  It used vacuum-tubes, along with switches and realys. It used decimal rather
  than binary and had punch card input and output. It was crudely programmable and
  could be set up to work on different problems by rearranging the different
  components using switches and plugboards. But this took roughly two days.

  It's design was very influential because it was made public and not classified.

  ### EDVAC (1944-49) John Mauchly/J.P Eckert/ John von Neumann

  Built at the Moore School for the BRL as a successor to ENIAC by Mauchly and
  Eckert whilst ENIAC was still operational. It's architecture arose out of
  discussions between #vonNeumann and Mauchly and Eckert and was necessitated by
  the need to calculate the behaviour of the fissile materials in the atomic bomb.

  It resulted in the famous _First Draft of a Report on the EDVAC_ by #vonNeumann
  which is seen as the definitive statement of the modern **digital programmable
  general-purpose computer**.

  As a result of the paper, #vonNeumann effectively got the credit for the
  invention even though it synthesised ideas from the ENIAC and the contributions
  of others to the EDVAC.

  There was lots of disputed provenance and claims of plagiarism generally.
  Antanasoff won a legal claim against Eckert and Mauchly for stealing ideas from
  the ABC to make ENIAC.

  // Add more on the hardware.

  ### Concept of the _internal stored program_

  - Key innovation of the EDVAC was the _internal stored program_ (ISP).

  - Solution to the problem of preceding computers requiring down-time when
    switching between applications and problem sets.

  - Circuitry would need to be reconfigured before the computer could run on the
    next problem

  - ISP removed the friction:

    - the instructions comprising the program would be prepared on tape or punched
      cards and read into electronic memory
    - the hardware configuration of the machine would remain the same accross
      different programs and require no reconfiguration, only the input program
      data would change

  - This was basically the invention of software. Both instructions and data
    shared the same memory space once they were read-in. Before, the data would be
    stored in memory but the instructions would be read one-by-one from the
    external storage media.

  - Key consequences:
    - flexibility (no need for re-wiring)
    - speed: fetching instructions from memory is quicker than fetching from
      external storage devicde
    - self-modification: since they were in memory, programs could modify
      themselves during their execution (e.g. use branching conditions) and
      treating one program as data for another. This enabled more complex
      algorithms and also opened the possibility of higher-level languages and
      compilers
    - simplified architecture: both programs and data in memory
    - generalisation: computers could switch between different tasks simply by
      loading different programs

  ### Post-EDVAC devices

  There were several notable devices which attempted to implement the architecture
  described in the _First Draft_ using vacuum tubes for logic operations and a
  variety of different electronic methods for memory:

  - the Manchester "Baby"
  - the Cambridge EDSAC
  - Turing's ACE computer

  #### Manchester Baby (1948)

  ![The Manchester Baby Computer](static/manchester_baby.png)

  - An experimental computer intended to create the
    [von Neumann architecture](CPU_architecture.md) using
    [Williams_Tube_RAM](Williams_Tube_RAM.md)

  - Developed at the Univesity of Manchester and completed in 1948.

  - Considered the first electronic stored-program computer and first to contain
    all the elements of a modern electronic digital computer.

  #### EDSAC (1949) J.Wilkes et al.

  ![EDSAC](static/EDSAC.jpg)

  - _Electronic Delay Storge Automatic Computer_

  - Constructed by Maurice Wilkes and others at the Mathematical Laboratory of the
    University of Cambridge.

  - The second digital stored-program computer after the Manchester Baby.

  - It used vacuum-tubes for the arithmetical operations in the ALU and mercury
    [delay line memory](Delay_line_memory.md) for the RAM.

  - Designed to be used by relatively non-specialist practitioners from other
    university departments who were expected to program it themselves. To this
    end, a formal programming paradigm was forged for the EDSAC which established
    the following:

    - subroutines as a library of common procedures available to programmers (e.g.
      printing a result, reading input tape, program checking, mathematical
      operations)
    - diagnostics: techniques for verifying program code and its correctness

  ![Programming Digital Electronic Computer book](static/programming_a_digital_electronic_computter.jpg)

  - The key players wrote the first textbook on programming in 1951: _The
    Preparation of Programs for an Electronic Digital Computer_ (Wilkes, Wheeler,
    and Gill)

  #### The Automatic Computing Engine (ACE) (1946) Alan Turing

  - Technical design provided by Alan #Turing working at the Mathematics Division
    of the National Physical Laboratory. The product of Turing's theoretical work
    in "On Computable Numbers" where he proposes the concept of a
    [Turing_machine](Turing_machines.md) and based on his experience with early
    single-purpose computing devices at Bletchley.

  - Due to secrecy over wartime work, it was hard for get Turing to build a fully
    electronic implementation since this would disclose advances made during the
    War. Tommy Flowers was intended to be brought in to build it but again this
    was scuppered over the classification of wartime technology. Had this not been
    the case, it is likely it would've been seen as on par with the EDVAC in its
    design, if not dwarfing it.

  - Turing proposed a stored program architecture with high-speed memory. It would
    be more perfomant than the EDVAC as a result.

  ![The Pilot ACE computer](static/pilot-ace.jpg)

  - Turing's actual design was not implemented as it was thought too ambitious
    given how powerful it would need to be. Instead a smaller prototype was made,
    the Pilot ACE. It used vacuum-tubes for logic, and mercury delay lines for
    memory. Data and memory was input via punched cards.

  ### Further applications of the EDSAC architecture

  ### LEO I

  - Built by Lyons Tea Company to manage business affairs (payroll, inventory,
    stock management)

  - Marked a shift from military and academic contexts for computers to business
    and data management.

  ### IAS machines

  ![The MANIAC computer](static/MANIAC_computer.jpg)

  Several machines were built at the Institute for Advanced Study utilising the
  "von Neumann" architecture and associated advancements such as vacuum-tubes and
  Williams' tubes. There was the original IAS machine (1952) as well as the
  JOHNNIAC (1954) and MANIAC (1956).

  Although their purpose was military (Los Alamos), their designs were public and
  widely studied making them influential outside of academia.

  ### UNIVAC (1951)

  ![UNIVAC_computer](static/univac-1.jpg)

  Mauchley and Eckert, who had designed the ENIAC left the Moore School and went
  into business: Eckert-Mauchley Computer Corporation. This was bought by
  Remington Rand and in subsequent years became the main competitor to IBM.

  In this capacity they built the UNIVAC: _Universal Automatic Computer_. It's
  name being an embodiment of its nature as a general-purpose, electronic digital
  computer.

  It used vacuum-tubes for logic and mercury delay lines for memory. It had
  multiple means of input/output including: directly via an operator console
  (basically a typewriter keyboard), magnetic tape for input and output, along
  with punched cards.

  ![UNIVAC operator console](static/univac-operator-console.jpeg)

  It was the first computer specifically designed to include business and
  administrative use. This was underscored by its first client: the US Census
  Bureau.

  A key event was its succesful prediction of the 1952 general election. It
  correctly predicted a landslide for Eisenhower (against expectations). (This was
  so unlikely, they actually fudged the data because they thought the machine was
  way off.) It was a novelty on the results night but it cemented a certain
  concept of the computer in the public imagination - large, room sized machines
  with blinking lights.

  Swade notes that Eckert and Mauchley effectively launched the US commercial
  computer industry with the UNIVAC.

  ## IBM

  - First created in 1911 as a merger of four companies all involved in some
    capacity in business data processing. The most significant of these was the
    Hollerith Tabulating Machine Company who's punched card machines had been used
    in the 1890 census, greatly reducing its collation time

  - Named IBM in 1924

  - UNIVAC represented the encroachment of computing devices into IBM's market of
    punched-card based data-processing and effectively put IBM on notice.

  - To attack the threat IBM brought out three crucial machines: the 701, 702
    and 650.

  - IBM's approach to marketing their computers was to emphasise the comfort of
    continuity over the shock of the new represented by the UNIVAC. IBM had
    already captured the business market. Their computers would be more of the
    same, just faster and more powerful than their punched card machines due to
    the electronics. To this end they initially did not call their devices
    'computers'.

  - IBM were ultimately totally successful in their pivot to the computing market.
    By 1960 it owned over 70% of the data processing and computer market.

  ### IBM 701 ("Defense Calculator") (1952)

  ![Control unit of the IBM 701](static/ibm-701-control-unit.jpeg)

  IBM's first electronic computer. It directly competed with the UNIVAC for
  government contracts. It followed the prevailing approach of vaccuum tubes for
  logic and had a variety of storage methods: Williams tubes,
  [magnetic tape](Magnetic_tape.md) , and magentic drums.

  701s were used at Los Alamos and avionics companies. Some were also used to
  manage payroll in business contexts.

  ### IBM 702 ("Tape Processing Machine") (1953)

  ![Advertisement for the IBM 702](static/ibm-add.jpeg)

  Focused primarily on business applications and targetted at businesses rather
  than government contracts. Less powerful than the 701.

  ### IBM 650 (1953)

  ![IBM 650](static/ibm-650.jpg)

  Low-cost general purpose machine using magnetic drum memory. Marketed as slower
  but more affordable than the 700 range. It was mass-produced unlike the others
  which were built for specific customers. It was in fact the first mass-produced
  computer in the world. It proved the breakout star in IBMs initial line up and
  sometimes called "IBM's Model T".

  ![IBM_650 console panel](static/ibm-650-console-panel.jpg)

  IBM offerred 650s to univesities at a 60% discount on the condition that the
  universities would establish courses in computing. This was shrewd as it meant
  that a whole generation of engineers and computer scientists learned on 650s and
  would prefer them in their research. It also created a pool of skilled users
  that would go on to work at IBM.

  Donald Knuth dedicated _The Art of Computer Programming_ to the 650.

  It used [magnetic drum memory](./Magnetic_drum_memory.md) along with
  vacuum-tubes.

  ### IBM 1401 (1959)

  ![IBM 1401](static/ibm-1401.jpg)

  Another IBM mainframe. Transistors replaced vacuum-tubes. Magnetic core storage
  replaced magnetic drum storage. It was housed in rectangular light-blue cabinets
  and the ubiquity of the 1401 in industry earned IBM the moniker 'Big Blue'.

  ### IBM System/360 (1964)

  ![IBM 360](static/ibm-360.jpg)

  Named to suggest all-round compatibility - a family of mainframes designed to
  cover commercial and scientific applications. Considered one of history's most
  successful computers.

  Up until this point all IBM computers had a programming language unique to the
  specific processor. At that point there were about seven IBM computer models in
  active use and they were all incompatible with each other.

  ![IBM 360 promo poster](static/ibm-360-in-use.jpeg)

  In contrast the 360 computers all used the same programming language. This meant
  they were interoperable with each others. Because the there were variants in the
  360 ranges, customers could purchase a smaller system knowing they could expand
  it if their needs grew, without reprogramming their application software.

  ## Magnetic core devices: Whirlwind and SAGE

  Two devices that leveraged the new technology of
  [magnetic_core_memory](Magnetic_core_memory.md) where the Whirlwind and SAGE
  computers. The ENIAC was also updated to use magnetic cores.

  ![Whirlwind magnetic core memory](static/whirlwind-magnetic-cores.JPG)

  The Whirlwind computer (1953) was a flight simulator and the first to use
  magnetic cores. Crucially was able to operate in realtime for output.

  The SAGE computer (_Semi-Automatic Ground Environment_) emerged as a response to
  the Soviet acquisition of the atomic bomb in 1949. The Americans realised they
  needed a much better early warning and air defence system that would allow them
  to identify bombers in their airspace and dispatch fighters.

  To achieve this, it was necessary to detect enemy bombers with radar and compute
  an interception course. Alterations in the bomber flight path required
  continuous updates and real-time computation of directions for the responding
  fighter.

  The resulting computer was SAGE (made by IBM) which was modelled on Whirlwind,
  using magnetic cores. SAGE computers were spread accross the continental US in
  sectors and managed by NORAD.

  ![SAGE command post](static/SAGE_command_post.jpg)

  The SAGE system was operation between 1958-1984, receiving continual
  improvements and updates. In addition to magnetic cores it introduced many
  technological innovations that influenced computers generally: interactive
  screens, printed circuit boards, mass-storage devices, digital communication
  over telephone lines, time-sharing, along with advances in software.

  Because so many private companies worked as contractors on SAGE (IBM, Burroughs,
  Bell Labs, RAND Corp), the military technology (both hardware and software) made
  its way from the military context to business and consumer computing.

  An example of a civilian application of SAGE technology was airline booking
  systems, where booking reservation data needed to be processed in realtime. IBM
  worked with American Airlines to introduce this.

  ## Transistor and integrated circuit revolution

  The arc of modern computing history (and its main eras) can be presented as

  - vacuum-tubes
  - [transistors](Transistors.md)
  - [integrated_circuits](Integrated_circuits.md)

  The transistor was invented at Bell Labs in 1947 by William Shockley and others.
  They were not created with computers in mind. Instead they were first used in
  hearing aids (1953) and transistor radios (1954).

  From the mid-1950s onwards they started being used in military computers.

  They had several advantages over vacuum-tubes: they were durable and reliable
  with no required startup time, they were power efficient and small.

  The actual period where transistors alone were supreme in the form of a
  transistor board was relatively short-lived and rapidly gave way to ICs.

  Computers of the transistor era had the following components separate from each
  other: the transistors, wired connections, resistors and capacitors. Integrated
  circuits put these all on the same piece of silicon.

  They were first manufactured by Fairchild Semiconductor which was an offshoot of
  Shockley's Shockley Semiconductor Laboratory.

  The initial marked for ICs was military and scientific: the Minuteman ICBM and
  computers of the Apollo spacecraft. This laid the groundworkd for later
  commercial use and the personal computer revolution.

  ## Mini-computers

  Mini computers did not threaten mainframes but they opened up a new class of
  user. They were vastly cheaper than a mainframe and only the size of a small
  fridge.

  The main player was DEC (Digital Equipment Corporation) starting in the
  mid-1960s. The introduced the PDP-8 in 1965 (Programmed Data Processor). The
  PDP-8 used transisotrs and magnetic core memory. It was affordable to smaller
  businesses if not yet, consumers.

  ![The PDP-8](static/540px-PDP-8_(1).jpg>)

  The internals were made public and DEC encouraged making the machine extensible
  by users being permitted to create their own programs and specialised
  applications. This was a very different culture to IBM where technicians were
  required and machines were typically leased.

  By 1988, DEC was the second largest computer company after IBM. This led to
  additional companies entering the mini-computer market: Data General, Honeywell,
  Hewlett-Packard.

  Mini-computers died as a category in the mid-1990s in the wake of the PC.

  ## The Personal Computer

  The PC emerged out of advances in IC chip design - this was the engine of the
  PC.

  Intel (newly founded by Robert Noyce and Gordon Moore) were charged with making
  custom chips for a Japanese calculator company. Instead of creating the custom
  chips, they advised the company to use a general purpose chip that could handle
  general computing as well as the basic calculator functions. This generic chip -
  the Intel 4004 - was the microprocessor combined with three other ICs
  (comprising 2.3k transistors) that comprised the fundamental building blocks of
  the #vonNeumann architecture.

  ![Intel 4004 chip](static/intel-4004.jpg)

  However it took until the Apple II in 1977 for the phrase "personal computer" to
  enter the lexicon and become an identifiable class.

  The broader context for this was the computer hobbyists in Silicon Valley/Menlo
  Park (particularly the Homebrew Club of amateur computer engineers) along with
  idealistic figures from the 60s counterculture (such as Stuart Brand) who began
  associating computing technology as a tool of personal liberty and fulfillment.

  ### Microsoft and Apple

  The PC "boom" began with the Altair 8800. This was a mail-order kit that
  required manual assembly. It was released by Micro Instrumentation Telemetry
  Systems (MITS) in 1975. It couldn't do much but had expansion capability
  (memory, teletype interface, casette player for data storage).

  ![Altair 8800](static/altair-8800.jpg)

  Bill Gates and Paul Allen made a proposal to MITS: they would write software
  that would allow users to program the Altair in BASIC. They agreed and were
  prepared to distribute the software. As a result Gates and Allen founded
  Microsoft in 1975.

  Around the same time, Steve Wozniak (member of the Homebrew club) built the
  Apple I as a single-board hobbyist project. He made this available to buy via
  mail order and formed Apple with Steve Jobs in 1976 to manage the enterprise.

  ![Apple I](static/apple1.jpg)

  By 1977 they had investment capital and brought out the Apple II. This was sold
  preassembled with casing and required no soldering. It had expansion slots for
  third-party vendors to create compatible devices and had colour graphics.

  The design of the Apple II was published in _Byte_ in order to invite
  crowd-sourced plugins and extensions. One of these was the Z-80 softcard which
  allowed it to run the CP/M OS (created by the company Digital Research) giving
  ready-made access to software such as a word-processor, spreadsheets and
  databases.

  ![Apple 2](static/apple2.jpg)

  Other competitors in the PC market at this time were Radioshack's TRS-80 and the
  Commodore PET.

  ### IBM

  All the while it was hobbyist and tinkerers driving the PC market, IBM had no
  real interest. When PCSs started being used by businesses and their capacity
  grew to include typical business applications (databases, spreadsheets) it
  sensed an incursion into its market.

  ![IBM PC](static/ibm-pc.jpg)

  Thus, in 1981, IBM launched the "IBM Personal Computer" which rapidly became the
  industry standard. This had the effect of legitimising the concept of a PC.
  There was overwhelming demand. In their marketing IBM melded home and office use
  to capture both markets at once.

  The internal specification of the IBM PC was freely available (dubbed 'open
  architecture'), in contrast to IBM's business computers, to encourage software
  expansion. Cards could be created to fit in its expansion slots adding graphics,
  sound, additional memory and networking capability.

  IBM originally contracted Digital Research to create software based on CP/M but
  this fell through. Microsoft got the deal to supply the OS which became MS-DOS
  (Microsoft Disk Operating System). Henceforth every PC and PC-clone came with
  DOS bundled.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/The_Little_Man_computer.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887The_Little_Man_computer.md
title: The Little Man Computer
checksum: c4ced2c5a5ebf1d635db1654f14ab8b81de5168b422d7a9aa95ca37347a66fde
created: 2024-10-22 05:38:10.864919292+00:00
modified: 2024-10-21 22:11:47.055313007+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](static/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](static/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: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/The_memory_hierarchy.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887The_memory_hierarchy.md
title: The memory hierarchy
checksum: f13788d00c19460ae3e4fa76126b5645fa2eee3dce5701386fed0d358651f56b
created: 2024-10-22 05:38:11.17361451+00:00
modified: 2024-10-21 22:11:47.071979955+00:00
metadata: {"created":"Friday, July 12, 2024","tags":["memory"]}
lead:
  The diagram below compares the different forms of memory within a computing
  device in terms of speed, monetary cost and capacity:

body:
  The diagram below compares the different forms of memory within a computing
  device in terms of speed, monetary cost and capacity:

  ![Memory hierarchy diagram](static/Memory-Hierarchy.jpg)

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

  # The memory hierarchy

  The diagram below compares the different forms of memory within a computing
  device in terms of speed, monetary cost and capacity:

  ![Memory hierarchy diagram](static/Memory-Hierarchy.jpg)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Theorems_and_empty_sets.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Theorems_and_empty_sets.md
title: Theorems and empty sets
checksum: c2330287e67f11a176c0feef5c4f9cd2b7bcbbde471d1628ef9432481da7727b
created: 2024-10-22 05:38:11.200129821+00:00
modified: 2024-10-21 22:11:47.068646566+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_

  ![](static/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_

  ![](static/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: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Threads.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Threads.md
title: Threads
checksum: c4b3378278b013392f55d1722b7ca2609601a7e7ff6a736bec19ca4b047304b0
created: 2024-10-22 05:38:11.252780058+00:00
modified: 2024-10-21 22:11:47.055313007+00:00
metadata: {"created":"Wednesday, June 26, 2024","id":"zadl","tags":["operating-systems"]}
lead:
  A ![process](static/Processes.md) is a running instance of a given program. A program
  runs sequentially handling one task at a time, however we may need to run
  certain tasks in parallel.

body:
  A ![process](static/Processes.md) is a running instance of a given program. A program
  runs sequentially handling one task at a time, however we may need to run
  certain tasks in parallel.

  Think of a program that is downloading a resource from the internet and wants to
  update the UI at the same time to show the download process.

  If the program is strictly sequential, once the program starts the download, the
  UI is neglected since the CPU time dedicated to the process must be focused
  elsewhere.

  We need the UI update and the download to execute in parallel. This is achieved
  via operating system **threads of execution**.

  > A thread is a schedulable unit of execution within a process. It runs within a
  > process, shares its memory address and can execute any program code loaded
  > within it.

  Each thread has an identifier called a _thread ID_ (TID).

  Both procesess in [user space](User_Space.md) and the [kernel](The_kernel.md)
  utilise threads.

  ## Hardware considerations

  Each [processor core](./CPU_architecture.md) can only run one thread at a time
  so the number of cores that a device's CPU has deterimines how many threads can
  run at once. In practice, then, the CPU shuffles between multiple threads in
  [clock segments](./Clock_signals.md). This is known as scheduling and is managed
  by the process that the thread belongs to.

  ## Related notes

  ![Processes](static/Processes.md)

raw_content:
  ---
  id: zadl
  tags: [operating-systems]
  created: Wednesday, June 26, 2024
  ---

  # Threads

  A ![process](static/Processes.md) is a running instance of a given program. A program
  runs sequentially handling one task at a time, however we may need to run
  certain tasks in parallel.

  Think of a program that is downloading a resource from the internet and wants to
  update the UI at the same time to show the download process.

  If the program is strictly sequential, once the program starts the download, the
  UI is neglected since the CPU time dedicated to the process must be focused
  elsewhere.

  We need the UI update and the download to execute in parallel. This is achieved
  via operating system **threads of execution**.

  > A thread is a schedulable unit of execution within a process. It runs within a
  > process, shares its memory address and can execute any program code loaded
  > within it.

  Each thread has an identifier called a _thread ID_ (TID).

  Both procesess in [user space](User_Space.md) and the [kernel](The_kernel.md)
  utilise threads.

  ## Hardware considerations

  Each [processor core](./CPU_architecture.md) can only run one thread at a time
  so the number of cores that a device's CPU has deterimines how many threads can
  run at once. In practice, then, the CPU shuffles between multiple threads in
  [clock segments](./Clock_signals.md). This is known as scheduling and is managed
  by the process that the thread belongs to.

  ## Related notes

  ![Processes](static/Processes.md)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Three_bit_counter.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Three_bit_counter.md
title: 3-bit Counter
checksum: 0c552eb6b98c7b3b4dabaa167d46bafe36dad749075a2cab74fd2b0c6d9eb676
created: 2024-10-22 05:38:11.397165348+00:00
modified: 2024-10-21 22:11:47.051979618+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:
  ![](static/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:
  ![](static/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: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Transistors.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Transistors.md
title: Transistors
checksum: e6ecd3cda992d94153a6044165f07b8dffb6b2985b4d3da78a2f9922e22384ac
created: 2024-10-22 05:38:11.600636832+00:00
modified: 2024-10-21 22:11:47.068646566+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](static/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.

  ![](static/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](static/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.

  ![](static/and-transistor.png)

  // Add example of OR gate created with transistors


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Transport_Layer_of_Internet_Protocol.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Transport_Layer_of_Internet_Protocol.md
title: Transport Layer of the Internet Protocol
checksum: 000fd29809a96336c6824692d9140f58eae52dcb1e43715b7348cf35c4ac6dd3
created: 2024-10-22 05:38:11.695370491+00:00
modified: 2024-10-21 22:11:47.068646566+00:00
metadata: {"created":"Tuesday, September 03, 2024","tags":["internet","networks","ports"]}
lead:
  Transport Layer protocols provide a communications channel for data to be sent
  between hosts. The Transport Layer is dependent upon the
  [Internet Layer](Internet_Layer_of_Internet_Protocol.md) for addressing and
  routing the data and the
  [Link_Layer_of_Internet_Protocol](Link_Layer_of_Internet_Protocol.md) for
  transferring the data to a specific host once it has reached the local network.

body:
  Transport Layer protocols provide a communications channel for data to be sent
  between hosts. The Transport Layer is dependent upon the
  [Internet Layer](Internet_Layer_of_Internet_Protocol.md) for addressing and
  routing the data and the
  [Link_Layer_of_Internet_Protocol](Link_Layer_of_Internet_Protocol.md) for
  transferring the data to a specific host once it has reached the local network.

  The protocol governing the Transport Layer is the **Transmission Control
  Protocol** (TCP).

  A TCP **fragment** fits within an IP packet's data section, giving us the
  following iteration of the network stack:

  ![Transport layer fragment](static/transport-layer-segment.png)

  The segment header contains a destination network port number. The port number
  identifies the specific service or process on the host device which will receive
  the data.

  A useful analogy:

  _The IP address is like the street address of an office building. The port
  number is like the office number of a specific worker within the building. Once
  a letter arrives at the building, the mail processing office (like the OS) is
  not the intended recipient. Instead the mail worker passes on the letter to the
  intended recipient. Likewise, the OS examines the port number and delivers the
  inbound data to the process listening on the specified port._

  ## Ports

  A host with a single IP address can have multiple active ports, each used for
  performing a different type of activity on the network.

  Both servers and clients possess ports. A server will have a dedicated port
  which it listens for requests on (typically port 80 or port 443). A client will
  have an "ephemeral" port where it listens for responses from a server. They are
  ephemeral because they are open only temporarily to receive a response to a
  certain request and are assigned dynamically by the networking components of the
  OS.

  An IP address plus a port (e.g: 192.168.2.1:3000) constitutes an **endpoint**.
  An instance of an endpoint is a **socket**.

  If multiple clients connect to the same endpoint each has its own socket (for
  instance on a host that is a server). A socket can listen for new connections or
  it can represent an established, ongoing connection.

raw_content:
  ---
  tags: [internet, networks, ports]
  created: Tuesday, September 03, 2024
  ---

  # Transport Layer of the Internet Protocol

  Transport Layer protocols provide a communications channel for data to be sent
  between hosts. The Transport Layer is dependent upon the
  [Internet Layer](Internet_Layer_of_Internet_Protocol.md) for addressing and
  routing the data and the
  [Link_Layer_of_Internet_Protocol](Link_Layer_of_Internet_Protocol.md) for
  transferring the data to a specific host once it has reached the local network.

  The protocol governing the Transport Layer is the **Transmission Control
  Protocol** (TCP).

  A TCP **fragment** fits within an IP packet's data section, giving us the
  following iteration of the network stack:

  ![Transport layer fragment](static/transport-layer-segment.png)

  The segment header contains a destination network port number. The port number
  identifies the specific service or process on the host device which will receive
  the data.

  A useful analogy:

  _The IP address is like the street address of an office building. The port
  number is like the office number of a specific worker within the building. Once
  a letter arrives at the building, the mail processing office (like the OS) is
  not the intended recipient. Instead the mail worker passes on the letter to the
  intended recipient. Likewise, the OS examines the port number and delivers the
  inbound data to the process listening on the specified port._

  ## Ports

  A host with a single IP address can have multiple active ports, each used for
  performing a different type of activity on the network.

  Both servers and clients possess ports. A server will have a dedicated port
  which it listens for requests on (typically port 80 or port 443). A client will
  have an "ephemeral" port where it listens for responses from a server. They are
  ephemeral because they are open only temporarily to receive a response to a
  certain request and are assigned dynamically by the networking components of the
  OS.

  An IP address plus a port (e.g: 192.168.2.1:3000) constitutes an **endpoint**.
  An instance of an endpoint is a **socket**.

  If multiple clients connect to the same endpoint each has its own socket (for
  instance on a host that is a server). A socket can listen for new connections or
  it can represent an established, ongoing connection.


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Truth_trees.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Truth_trees.md
title: Truth trees
lead: ## Rationale
checksum: ab3394458b2d2d10967e643411493752aca319d502de00e5d4e71ed51baf6bac
created: 2024-10-22 05:38:11.955313778+00:00
modified: 2024-10-21 22:11:47.061979786+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](static/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](static/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](static/negated-negation-decomposition-rule%202.svg)

  Truth passes only if $P$ is true

  ### Conjunction decomposition: `&D`

  ![conjunction-decomposition-rule.svg](static/conjunction-decomposition-rule.svg)

  Truth passes only $P$ and $Q$ are both true.

  ### Negated Conjunction decomposition: `~&D`

  ![negated-conjunction-decomposition-rule.svg](static/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](static/disjunction-decomposition-rule.svg)

  Truth passes if either $P$or $Q$ are true.

  ### Negated Disjunction decomposition: `~vD`

  ![negated-disjunction-decomposition-rule.svg](static/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](static/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](static/negated-conditional-decomposition-rule.svg)

  ### Biconditional decomposition: `≡D`

  ![biconditional-decomposition-rule.drawio(1).svg](static/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](static/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](static/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](static/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](static/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](static/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](static/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](static/negated-negation-decomposition-rule%202.svg)

  Truth passes only if $P$ is true

  ### Conjunction decomposition: `&D`

  ![conjunction-decomposition-rule.svg](static/conjunction-decomposition-rule.svg)

  Truth passes only $P$ and $Q$ are both true.

  ### Negated Conjunction decomposition: `~&D`

  ![negated-conjunction-decomposition-rule.svg](static/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](static/disjunction-decomposition-rule.svg)

  Truth passes if either $P$or $Q$ are true.

  ### Negated Disjunction decomposition: `~vD`

  ![negated-disjunction-decomposition-rule.svg](static/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](static/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](static/negated-conditional-decomposition-rule.svg)

  ### Biconditional decomposition: `≡D`

  ![biconditional-decomposition-rule.drawio(1).svg](static/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](static/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](static/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](static/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](static/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: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Turing_machines.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Turing_machines.md
title: Turing Machines
lead: ## What is a Turing Machine?
checksum: 81bb07721178fb1c135756cea4af36e8a0b4c39a1826610e57aa70799a070858
created: 2024-10-22 05:38:12.565576214+00:00
modified: 2024-10-21 22:11:47.068646566+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](static/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](static/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: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Twos_complement.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Twos_complement.md
title: Two's complement
lead: ## Summary
checksum: 9227b8ebfef51236d3a31a740c35b256f633b994084c39aa398b082af3802c91
created: 2024-10-22 05:38:12.59298892+00:00
modified: 2024-10-21 22:11:47.065313176+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

  ![](static/unsigned-to-signed.png)

  To derive the unsigned equivalent of a signed number you invert the process but
  still make the smallest digit `1`:

  ![](static/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

  ![](static/unsigned-to-signed.png)

  To derive the unsigned equivalent of a signed number you invert the process but
  still make the smallest digit `1`:

  ![](static/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: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/VirtualMemory.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887VirtualMemory.md
title: Virtual memory
checksum: bc9b527b56ae6f2f2c5d008f3ea0673b04477ce9ec385f33d6a3e55fa733199b
created: 2024-10-22 05:38:13.562402026+00:00
modified: 2024-10-21 22:11:47.061979786+00:00
metadata: {"tags":["memory","Linux","kernel"]}
lead:
  Virtual memory is an abstracted and idealised representation of the physical
  memory capacity of a machine that is presented to user space for its memory
  operations.

body:
  Virtual memory is an abstracted and idealised representation of the physical
  memory capacity of a machine that is presented to user space for its memory
  operations.

  When an OS implements virtual memory, [processes](./Processes.md) in
  [user space](./User_Space.md) cannot directly read or write to the physical
  memory. Instead they execute memory operations against virtual memory and the
  [kernel](./The_kernel.md) translates these into the actual operations against
  the memory hardware.

  The main benefits:

  - User mode processes do not have to be concerned with the physical memory
    management
  - There is a buffer between user mode processes and physical memory, meaning
    that memory cannot be accidentally corrupted by other processes in user space.

  Because the physical memory is abstracted, it can be the case that the physical
  [memory addresses](./Memory_addresses.md) are non-contiguous or even distributed
  accross different hardware components (such as the
  [cache](./Register_and_cache_memory.md) and [swap](./Swap_space.md)). Despite
  this, the memory addresses will appear contiguous in virtual memory. Each user
  space process is presented with the same range of available memory addresses and
  the same total capacity.

  It is also possible for the kernel to present user space with an available
  virtual memory capcacity that exceeds the current physical capacity of the
  machine:

  > _It's possible for the kernel and all running processes to request more
  > byte<!--  -->s of virtual memory than the total size of RAM. In that
  > situation, the OS can move move bytes of memory to secondary storage to make
  > room in RAM for newly requested memory._

  _How Computers Really Work_ (2021) p.206

  ## Virtual memory and the kernel

  The kernel itself utilises virtual memory.

  During the [boot_process](Boot_process.md), it runs in physical memory mode
  however it rapidly creates a 1:1 mapping of physical to virtual memory for
  itself. This direct mapping allows the kernel to easily access any physical
  memory location as needed, whilst retaining the benefits of virtual memory.

  With its own virtual memory established, it can then build additional virtual
  memory structures for itself and user processes.

  While the kernel uses virtual memory it can also access physical memory at any
  time wherease user space processes can only ever access virtual memory.

  The kernel virtual memory has a different range of virtual addresses to work
  with than user space virtual memory.

  Unlike user space virtual memory, the kernel has access to everything running in
  kernel address space whereas processes in user address space are partitioned
  from each other with separate address spaces that cannot interact.

  In addition to being able to access its own virtual memory (and physical memory,
  as required) the kernel can also access any user processes' virtual address.

  This said, the virtual memory space of the kernel and the virtual memory space
  of the user processes are distinct. It is not the case that the kernel is
  superset of all available virtual memory. It can access user space virtual
  memory because it sets up the tables and locations, not because it is a subset
  of its own virtual memory.

  ![Virtual memory diagram](static/virtual-memory-diagram.jpg)

raw_content:
  ---
  tags:
    - memory
    - Linux
    - kernel
  ---

  # Virtual memory

  Virtual memory is an abstracted and idealised representation of the physical
  memory capacity of a machine that is presented to user space for its memory
  operations.

  When an OS implements virtual memory, [processes](./Processes.md) in
  [user space](./User_Space.md) cannot directly read or write to the physical
  memory. Instead they execute memory operations against virtual memory and the
  [kernel](./The_kernel.md) translates these into the actual operations against
  the memory hardware.

  The main benefits:

  - User mode processes do not have to be concerned with the physical memory
    management
  - There is a buffer between user mode processes and physical memory, meaning
    that memory cannot be accidentally corrupted by other processes in user space.

  Because the physical memory is abstracted, it can be the case that the physical
  [memory addresses](./Memory_addresses.md) are non-contiguous or even distributed
  accross different hardware components (such as the
  [cache](./Register_and_cache_memory.md) and [swap](./Swap_space.md)). Despite
  this, the memory addresses will appear contiguous in virtual memory. Each user
  space process is presented with the same range of available memory addresses and
  the same total capacity.

  It is also possible for the kernel to present user space with an available
  virtual memory capcacity that exceeds the current physical capacity of the
  machine:

  > _It's possible for the kernel and all running processes to request more
  > byte<!--  -->s of virtual memory than the total size of RAM. In that
  > situation, the OS can move move bytes of memory to secondary storage to make
  > room in RAM for newly requested memory._

  _How Computers Really Work_ (2021) p.206

  ## Virtual memory and the kernel

  The kernel itself utilises virtual memory.

  During the [boot_process](Boot_process.md), it runs in physical memory mode
  however it rapidly creates a 1:1 mapping of physical to virtual memory for
  itself. This direct mapping allows the kernel to easily access any physical
  memory location as needed, whilst retaining the benefits of virtual memory.

  With its own virtual memory established, it can then build additional virtual
  memory structures for itself and user processes.

  While the kernel uses virtual memory it can also access physical memory at any
  time wherease user space processes can only ever access virtual memory.

  The kernel virtual memory has a different range of virtual addresses to work
  with than user space virtual memory.

  Unlike user space virtual memory, the kernel has access to everything running in
  kernel address space whereas processes in user address space are partitioned
  from each other with separate address spaces that cannot interact.

  In addition to being able to access its own virtual memory (and physical memory,
  as required) the kernel can also access any user processes' virtual address.

  This said, the virtual memory space of the kernel and the virtual memory space
  of the user processes are distinct. It is not the case that the kernel is
  superset of all available virtual memory. It can access user space virtual
  memory because it sets up the tables and locations, not because it is a subset
  of its own virtual memory.

  ![Virtual memory diagram](static/virtual-memory-diagram.jpg)


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Virtual_private_cloud.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Virtual_private_cloud.md
title: Virtual Private Cloud
checksum: 42b006540ddb4dc3f980688579959a7afa6b2055d3ac1d9ccd12b5f3da17f1d7
created: 2024-10-22 05:38:13.763085316+00:00
modified: 2024-10-21 22:11:47.061979786+00:00
metadata: {"created":"Tuesday, February 20, 2024 | 08:31","id":"mdw5fe5a","tags":["AWS","networks"]}
lead:
  AWS VPC is used to create a virtual network. This is typically used in concert
  with [[zk/Elastic_Compute_Cloud]] to manage connections to a virtual server both
  privately and accross the internet.

body:
  AWS VPC is used to create a virtual network. This is typically used in concert
  with [[zk/Elastic_Compute_Cloud]] to manage connections to a virtual server both
  privately and accross the internet.

  You define a network address range and then create subnets for managing
  different connections and functionality. You use a public subnet for resources
  that must be connected to the internet and a private subnet for resources that
  are to remain isolated from the internet.

  The diagram below details a basic VPC configuration:

  ![](static/aws-vpc-example.png)

  - Within a given AWS region we have created a VPC network.
  - This comprises public and private subnets
  - Both subnets host an EC2 instance
  - The public subnet has a bridge to the internet through the Internet Gateway
  - Both subnets have a routing table to manage requests and access

raw_content:
  ---
  id: mdw5fe5a
  tags: [AWS, networks]
  created: Tuesday, February 20, 2024 | 08:31
  ---

  # Virtual Private Cloud

  AWS VPC is used to create a virtual network. This is typically used in concert
  with [[zk/Elastic_Compute_Cloud]] to manage connections to a virtual server both
  privately and accross the internet.

  You define a network address range and then create subnets for managing
  different connections and functionality. You use a public subnet for resources
  that must be connected to the internet and a private subnet for resources that
  are to remain isolated from the internet.

  The diagram below details a basic VPC configuration:

  ![](static/aws-vpc-example.png)

  - Within a given AWS region we have created a VPC network.
  - This comprises public and private subnets
  - Both subnets host an EC2 instance
  - The public subnet has a bridge to the internet through the Internet Gateway
  - Both subnets have a routing table to manage requests and access


path: neuron/4e98d18e-1a79-4e78-a4ac-f7e3a2de9887/Voltage.md
sortable_path: neuron4e98d18e-1a79-4e78-a4ac-f7e3a2de9887Voltage.md
title: Voltage
lead: ## Difference of potential and the transfer of energy
checksum: 8fd6c549d55b44d439e8add2a30cf3c46f9bf977b69de8572c254451f7663f8d
created: 2024-10-22 05:38:13.800378246+00:00
modified: 2024-10-21 22:11:47.065313176+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:

  ![](static/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:

  ![](static/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.


