# notes

path: The_kernel.md
sortable_path: The_kernel.md
title: The kernel
checksum: 96c9ce4e7d5964c10b9af81b81e350210f24146bc8a16245169ff057cdb3dd4f
created: 2024-10-22 16:52:59.402623302+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["systems-programming","memory","operating-systems","computer-architecture"]}
lead:
  Within the [hierarchy of the OS](./Basic_model_of_the_operating_system.md), the
  kernel acts as the primary mediator between the hardware (CPU, memory) and
  [user](./User_Space.md) [processes](Processes.md). Let's look at each of its
  responsibilities in greater depth:

body:
  Within the [hierarchy of the OS](./Basic_model_of_the_operating_system.md), the
  kernel acts as the primary mediator between the hardware (CPU, memory) and
  [user](./User_Space.md) [processes](Processes.md). Let's look at each of its
  responsibilities in greater depth:

  - process management
  - memory management
  - device drivers
  - system calls

  ## Process management

  > A process is just another name for a running program. Process management is
  > the starting, pausing, resuming, scheduling and terminating of processes.

  On modern computers it appears that multiple processes can run simultaneously at
  once. This is only because the processor is so fast that we do not detect
  changes. In fact access to the CPU is always sequential. The sequence in which
  multiple programs are allowed to access the CPU is managed by the kernel.

  > Consider a system with a one-core CPU. Many processes may be _able_ to use the
  > CPU, but only one process can actually use the CPU at any given time...Each
  > process uses the CPU for a fraction of a second, then pauses, then another
  > process uses it for a fraction of a second and so on... (_How Linux Works:
  > Third Edition_, Brian Ward 2021)

  This process of the CPU shuffling between multiple processes is called _context
  switching_.

  The role of the kernel in facilitating this, is as follows:

  1. CPU runs process for a time slice based on its internal time. Then hands
     control back to the kernel (kernel mode)
  2. Kernel records current state of CPU and memory. This is necessary in order to
     resume the progress that was just interupted.
  3. The kernel executes any tasks that arose in the last timeslice executed by
     the CPU (e.g. collecting data from I/0)
  4. Kernel then analyses the list of processes that are ready to run next and
     chooses one.
  5. Kernel prepares memory for this new process and prepares the CPU.
  6. Kernel tells CPU how long the time slice for the new process will last.
  7. Kernel switches the CPU into user mode and hands control of CPU to the
     process.

  ## Memory management

  During the context switch from CPU to user space, the kernel allocates memory.
  It has the following jobs to manage:

  - Keeping its own private area in memory for itself that user processes cannot
    access
  - Assigning each user process its own section of memory
  - Managing shared memory between processes and ensuring the private memory of
    processes is not accessed by others
  - Managing read-only memory
  - Allowing for the use of disk space as auxiliary memory

  > Modern CPUs include a
  > [memory management unit](Virtual_memory_and_the_MMU_in_Linux.md#the-memory-management-unit-mmu)
  > which provides the kernel with **virtual** memory. In this scenario, memory
  > isn't directly accessed by the process instead it works on the assumption that
  > is has access to the entire memory of the machine and this is then translated
  > into a map that is applied to the real memory and managed by the kernel.

  ## Device drivers

  Devices are managed by the kernal and are not accessible directly via user
  space, since improper usage could crash the whole machine. There is little
  uniformity between devices and as a result drivers are needed. Thes are kernl
  code that enable different OS kernels to access and control the devices.

  ## System calls

  Syscalls are what enable programs to start and are required for the acts of
  opening, reading and writing files. System calls in Linux are typically managed
  via C.

  In Linux there are two particularly important system calls:

  - `fork()`
    - When a process calls fork, the kernel creates a nearly identical copy of
      this running process
  - `exec()`
    - When a process calls exec it passes a program name as a parameter. Then the
      kernel loads and starts this program, replacing the current process.

  Example with a terminal program like `ls`:

  > When you enter `ls` into the terminal window, the shell that's running inside
  > the terminal window calls `fork()` to create a copy of the shell, and then the
  > new copy of the shell calls `exec(ls)` to run `ls`. (_Ibid._)

  ## Controlling processes

  In Linux we can view, kill, pause and resume processes using [ps](Processes.md).

raw_content:
  ---
  tags:
    - systems-programming
    - memory
    - operating-systems
    - computer-architecture
  ---

  # The kernel

  Within the [hierarchy of the OS](./Basic_model_of_the_operating_system.md), the
  kernel acts as the primary mediator between the hardware (CPU, memory) and
  [user](./User_Space.md) [processes](Processes.md). Let's look at each of its
  responsibilities in greater depth:

  - process management
  - memory management
  - device drivers
  - system calls

  ## Process management

  > A process is just another name for a running program. Process management is
  > the starting, pausing, resuming, scheduling and terminating of processes.

  On modern computers it appears that multiple processes can run simultaneously at
  once. This is only because the processor is so fast that we do not detect
  changes. In fact access to the CPU is always sequential. The sequence in which
  multiple programs are allowed to access the CPU is managed by the kernel.

  > Consider a system with a one-core CPU. Many processes may be _able_ to use the
  > CPU, but only one process can actually use the CPU at any given time...Each
  > process uses the CPU for a fraction of a second, then pauses, then another
  > process uses it for a fraction of a second and so on... (_How Linux Works:
  > Third Edition_, Brian Ward 2021)

  This process of the CPU shuffling between multiple processes is called _context
  switching_.

  The role of the kernel in facilitating this, is as follows:

  1. CPU runs process for a time slice based on its internal time. Then hands
     control back to the kernel (kernel mode)
  2. Kernel records current state of CPU and memory. This is necessary in order to
     resume the progress that was just interupted.
  3. The kernel executes any tasks that arose in the last timeslice executed by
     the CPU (e.g. collecting data from I/0)
  4. Kernel then analyses the list of processes that are ready to run next and
     chooses one.
  5. Kernel prepares memory for this new process and prepares the CPU.
  6. Kernel tells CPU how long the time slice for the new process will last.
  7. Kernel switches the CPU into user mode and hands control of CPU to the
     process.

  ## Memory management

  During the context switch from CPU to user space, the kernel allocates memory.
  It has the following jobs to manage:

  - Keeping its own private area in memory for itself that user processes cannot
    access
  - Assigning each user process its own section of memory
  - Managing shared memory between processes and ensuring the private memory of
    processes is not accessed by others
  - Managing read-only memory
  - Allowing for the use of disk space as auxiliary memory

  > Modern CPUs include a
  > [memory management unit](Virtual_memory_and_the_MMU_in_Linux.md#the-memory-management-unit-mmu)
  > which provides the kernel with **virtual** memory. In this scenario, memory
  > isn't directly accessed by the process instead it works on the assumption that
  > is has access to the entire memory of the machine and this is then translated
  > into a map that is applied to the real memory and managed by the kernel.

  ## Device drivers

  Devices are managed by the kernal and are not accessible directly via user
  space, since improper usage could crash the whole machine. There is little
  uniformity between devices and as a result drivers are needed. Thes are kernl
  code that enable different OS kernels to access and control the devices.

  ## System calls

  Syscalls are what enable programs to start and are required for the acts of
  opening, reading and writing files. System calls in Linux are typically managed
  via C.

  In Linux there are two particularly important system calls:

  - `fork()`
    - When a process calls fork, the kernel creates a nearly identical copy of
      this running process
  - `exec()`
    - When a process calls exec it passes a program name as a parameter. Then the
      kernel loads and starts this program, replacing the current process.

  Example with a terminal program like `ls`:

  > When you enter `ls` into the terminal window, the shell that's running inside
  > the terminal window calls `fork()` to create a copy of the shell, and then the
  > new copy of the shell calls `exec(ls)` to run `ls`. (_Ibid._)

  ## Controlling processes

  In Linux we can view, kill, pause and resume processes using [ps](Processes.md).


path: The_memory_hierarchy.md
sortable_path: The_memory_hierarchy.md
title: The memory hierarchy
checksum: 520f038e59b4ff5a9e388265ab28df99ee8da22437dbdbe7be84333c974b1aac
created: 2024-10-22 16:52:59.441593963+00:00
modified: 2024-10-18 17:55:56.302394226+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](/img/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](/img/Memory-Hierarchy.jpg)


path: Theorems_and_empty_sets.md
sortable_path: Theorems_and_empty_sets.md
title: Theorems and empty sets
checksum: 52cac37f8c279c1d947878b3bd503eac5dc8f7c50468f4bdaec0cf7e7e2f04fe
created: 2024-10-22 16:52:59.447709431+00:00
modified: 2024-10-19 09:00:27.867625356+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  We know that when we construct a
  [derivation](Formal_proofs_in_propositional_logic.md#derivation-rules) we start
  from a set of assumptions and then attempt to reach a proposition that is a
  consequence of the starting assumptions. However it does not always have to be
  the case that the starting set contains members. The set can in fact be empty.

body:
  We know that when we construct a
  [derivation](Formal_proofs_in_propositional_logic.md#derivation-rules) we start
  from a set of assumptions and then attempt to reach a proposition that is a
  consequence of the starting assumptions. However it does not always have to be
  the case that the starting set contains members. The set can in fact be empty.

  _Demonstration_

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

  We see in this example that there is no starting set and thus no primary
  assumptions. Instead we start with nothing other than the proposition we wish to
  derive. The proposition is effectively derived from itself. In these scenarios
  we say that we are constructing a derivation from an **empty set**.

  Propositions which possess this property are called theorems:

  > A proposition $P$ or a system of propositions in propositional logic is a
  > theorem in a system of derivation for that logic if $P$ is derivable from the
  > empty set.

  We represent a theorem as:

  $$
  \vdash P
  $$

  (There is no preceding $\Gamma$ as the set is empty. )

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

  # Theorems and empty sets

  We know that when we construct a
  [derivation](Formal_proofs_in_propositional_logic.md#derivation-rules) we start
  from a set of assumptions and then attempt to reach a proposition that is a
  consequence of the starting assumptions. However it does not always have to be
  the case that the starting set contains members. The set can in fact be empty.

  _Demonstration_

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

  We see in this example that there is no starting set and thus no primary
  assumptions. Instead we start with nothing other than the proposition we wish to
  derive. The proposition is effectively derived from itself. In these scenarios
  we say that we are constructing a derivation from an **empty set**.

  Propositions which possess this property are called theorems:

  > A proposition $P$ or a system of propositions in propositional logic is a
  > theorem in a system of derivation for that logic if $P$ is derivable from the
  > empty set.

  We represent a theorem as:

  $$
  \vdash P
  $$

  (There is no preceding $\Gamma$ as the set is empty. )


path: Threads.md
sortable_path: Threads.md
title: Threads
checksum: 59e40ca378bfe6cdffdc24908333c2184b4fc172e8428052b85a6b322df2d351
created: 2024-10-22 16:52:59.459295006+00:00
modified: 2024-10-24 16:25:42.630238745+00:00
metadata: {"created":"Wednesday, June 26, 2024","id":"zadl","tags":["operating-systems"]}
lead:
  A ![process](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](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](Processes.md)

raw_content:
  ---
  id: zadl
  tags: [operating-systems]
  created: Wednesday, June 26, 2024
  ---

  # Threads

  A ![process](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](Processes.md)


path: Three_bit_counter.md
sortable_path: Three_bit_counter.md
title: 3-bit Counter
checksum: 3ce02ec5bc1d8a10a459d711a57cd60465a70e680b6b4f7b2b66a443232fa403
created: 2024-10-22 16:52:59.485929174+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["logic-gates","binary","memory"]}
lead:
  To demonstrate the use of
  [Flip-Flops](Flip_flops.md) we will
  create the circuit for a 3-bit counter. This simply counts up from 0 to 7
  because 7 is the maximum decimal number we can create with three bits ($2^3$):

body:
  To demonstrate the use of
  [Flip-Flops](Flip_flops.md) we will
  create the circuit for a 3-bit counter. This simply counts up from 0 to 7
  because 7 is the maximum decimal number we can create with three bits ($2^3$):

  | Binary | Decimal |
  | ------ | ------- |
  | 000    | 0       |
  | 001    | 1       |
  | 010    | 2       |
  | 011    | 3       |
  | 100    | 4       |
  | 101    | 5       |
  | 110    | 6       |
  | 111    | 7       |

  The circuit will have three memory components, each representing one bit of the
  3-bit number. When the
  [clock pulses](Clock_signals.md), the
  3-bit number increments by one. We need to synchronise the operation with a
  clock because each bit by itself is meaningless, it only gains meaning by the
  relation it sustains to the other two bits hence it must be kept in sync with
  them.

  We can derive the circuit arrangement by analysing the outputs of a flipflop at
  each pulse:

  | Combined bits | Q2  | Q1  | Q0  | Decimal |
  | ------------- | --- | --- | --- | ------- |
  | 000           | 0   | 0   | 0   | 0       |
  | 001           | 0   | 0   | 1   | 1       |
  | 010           | 0   | 1   | 0   | 2       |
  | 011           | 0   | 1   | 1   | 3       |
  | 100           | 1   | 0   | 0   | 4       |
  | 101           | 1   | 0   | 1   | 5       |
  | 110           | 1   | 1   | 0   | 6       |
  | 111           | 1   | 1   | 1   | 7       |

  If we look at the pattern of each flip-flops' output we notice the following:

  - _Q0_ toggles on every pulse
  - _Q1_ toggles every time _Q0_ was previously 1
  - _Q2_ toggles when both _Q1_ and _Q0_ were previously 1

  This means that to construct a circuit that displays this behaviour we just have
  to use a
  [T flip-flop](Flip_flops.md#t-flip-flops)
  since the only state change we need is a single bit toggle three times that
  retains its value.

  Using these pulse patterns we can construct a circuit as follows:
  ![](/img/3-bit-adder-diagram.png)

  <iframe src="https://circuitverse.org/simulator/embed/3-bit-counter-d33846e3-7538-427d-b4cc-dc64fdaf0af3?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="500" width="600" allowFullScreen></iframe>

  <br />

  > Remember from the table: Q2 is the most significant bit and Q0 is the least
  > significant bit so we should read the output in the interactive circuit
  > backwards, reversing the order. Then we will see that the cicuit is cyling
  > through 0-7

  This is how the circuit works:

  - All three flip-flops are connected to the same clock signal so that they are
    synchnonised
  - Because _T0_ is connected to the voltage source _Q0_ toggles every time the
    clock pulses
  - _T1_ is connected to _Q0_ so a clock pulse causes _Q1_ to toggle only when
    _Q0_ is high in line with the table
  - _T2_ is connected to _Q0 AND Q1_ so _Q2_ only toggles on a clock pulse when
    _Q0_ and _Q1_ are both high

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

  # 3-bit Counter

  To demonstrate the use of
  [Flip-Flops](Flip_flops.md) we will
  create the circuit for a 3-bit counter. This simply counts up from 0 to 7
  because 7 is the maximum decimal number we can create with three bits ($2^3$):

  | Binary | Decimal |
  | ------ | ------- |
  | 000    | 0       |
  | 001    | 1       |
  | 010    | 2       |
  | 011    | 3       |
  | 100    | 4       |
  | 101    | 5       |
  | 110    | 6       |
  | 111    | 7       |

  The circuit will have three memory components, each representing one bit of the
  3-bit number. When the
  [clock pulses](Clock_signals.md), the
  3-bit number increments by one. We need to synchronise the operation with a
  clock because each bit by itself is meaningless, it only gains meaning by the
  relation it sustains to the other two bits hence it must be kept in sync with
  them.

  We can derive the circuit arrangement by analysing the outputs of a flipflop at
  each pulse:

  | Combined bits | Q2  | Q1  | Q0  | Decimal |
  | ------------- | --- | --- | --- | ------- |
  | 000           | 0   | 0   | 0   | 0       |
  | 001           | 0   | 0   | 1   | 1       |
  | 010           | 0   | 1   | 0   | 2       |
  | 011           | 0   | 1   | 1   | 3       |
  | 100           | 1   | 0   | 0   | 4       |
  | 101           | 1   | 0   | 1   | 5       |
  | 110           | 1   | 1   | 0   | 6       |
  | 111           | 1   | 1   | 1   | 7       |

  If we look at the pattern of each flip-flops' output we notice the following:

  - _Q0_ toggles on every pulse
  - _Q1_ toggles every time _Q0_ was previously 1
  - _Q2_ toggles when both _Q1_ and _Q0_ were previously 1

  This means that to construct a circuit that displays this behaviour we just have
  to use a
  [T flip-flop](Flip_flops.md#t-flip-flops)
  since the only state change we need is a single bit toggle three times that
  retains its value.

  Using these pulse patterns we can construct a circuit as follows:
  ![](/img/3-bit-adder-diagram.png)

  <iframe src="https://circuitverse.org/simulator/embed/3-bit-counter-d33846e3-7538-427d-b4cc-dc64fdaf0af3?theme=default&display_title=false&clock_time=true&fullscreen=true&zoom_in_out=true" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="500" width="600" allowFullScreen></iframe>

  <br />

  > Remember from the table: Q2 is the most significant bit and Q0 is the least
  > significant bit so we should read the output in the interactive circuit
  > backwards, reversing the order. Then we will see that the cicuit is cyling
  > through 0-7

  This is how the circuit works:

  - All three flip-flops are connected to the same clock signal so that they are
    synchnonised
  - Because _T0_ is connected to the voltage source _Q0_ toggles every time the
    clock pulses
  - _T1_ is connected to _Q0_ so a clock pulse causes _Q1_ to toggle only when
    _Q0_ is high in line with the table
  - _T2_ is connected to _Q0 AND Q1_ so _Q2_ only toggles on a clock pulse when
    _Q0_ and _Q1_ are both high


path: Time_and_computers.md
sortable_path: Time_and_computers.md
title: Time and computers
lead: ## Epochs and Unix Time
checksum: 27ee0c19f35eb4bd4eaff528bf3d7f8c2bdaa3723eaddf607a8ef49ef4d21a69
created: 2024-10-22 16:52:59.505051003+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"created":"Sunday, April 28, 2024","id":"l29u","tags":["unix","time"]}
body:
  ## Epochs and Unix Time

  An epoch is a period of time identified by a starting point.

  The standard epoch for measuring against is known colloqially as "Unix Time":
  midnight on 1st January 1970. Unix time is the number of seconds that have
  elapsed since this point. This is the standard for Windows and most unix
  systems.

  For example midnight on 2nd January 1970 would be 86400 seconds since the epoch.
  The deduction is as follows:

  ```
  60 * 60 * 24 = 86400
  ```

  We are multiplpying the number of seconds in a minute by the number of minutes
  in an hour by the number of hours in a day.

  To represent times and dates before 1970, we use negative integers.

  ## UTC and time zones

  "Coordinated Universal Time" (UTC) is the time standard against which the
  world's timekeeping is synchronized. It is not itself a timezone, it is a
  transcendent standard that defines what time zones are.

  Time zones are defined relative to UTC, by their offset to this value.

  For example North America is behind UTC by five or six hours hence uses the
  notation UTC-5:00 or UTC-6:00.

  UTC is the successor to GMT of which there were variances necessitating a
  universal value. Despite this GMT is equal to UTC+00:00 so apart from when
  daylight saving is in effect in the UK, GMT = UTC.

  Depending on your locale, relative to UTC, you will get different outputs when
  you attempt to log Unix Time.

  Also daylight savings can impact on the outputs. For instance for half of the
  year, when DST applies, the UK is UTC+01:00.

  ## Examples in Python

  The following gives us the unix seconds in my current time zone (GMT DST):

  ```py
  import time
  print(time.time())
  # 1714322393.2929392
  ```

  If I want UTC, I can do:

  ```py
  import datetime
  print(datetime.datetime.utcnow().strftime('%s'))
  # '1714318952'
  ```

  We see clearly that they are not identical.

raw_content:
  ---
  id: l29u
  tags:
    - unix
    - time
  created: Sunday, April 28, 2024
  ---

  # Time and computers

  ## Epochs and Unix Time

  An epoch is a period of time identified by a starting point.

  The standard epoch for measuring against is known colloqially as "Unix Time":
  midnight on 1st January 1970. Unix time is the number of seconds that have
  elapsed since this point. This is the standard for Windows and most unix
  systems.

  For example midnight on 2nd January 1970 would be 86400 seconds since the epoch.
  The deduction is as follows:

  ```
  60 * 60 * 24 = 86400
  ```

  We are multiplpying the number of seconds in a minute by the number of minutes
  in an hour by the number of hours in a day.

  To represent times and dates before 1970, we use negative integers.

  ## UTC and time zones

  "Coordinated Universal Time" (UTC) is the time standard against which the
  world's timekeeping is synchronized. It is not itself a timezone, it is a
  transcendent standard that defines what time zones are.

  Time zones are defined relative to UTC, by their offset to this value.

  For example North America is behind UTC by five or six hours hence uses the
  notation UTC-5:00 or UTC-6:00.

  UTC is the successor to GMT of which there were variances necessitating a
  universal value. Despite this GMT is equal to UTC+00:00 so apart from when
  daylight saving is in effect in the UK, GMT = UTC.

  Depending on your locale, relative to UTC, you will get different outputs when
  you attempt to log Unix Time.

  Also daylight savings can impact on the outputs. For instance for half of the
  year, when DST applies, the UK is UTC+01:00.

  ## Examples in Python

  The following gives us the unix seconds in my current time zone (GMT DST):

  ```py
  import time
  print(time.time())
  # 1714322393.2929392
  ```

  If I want UTC, I can do:

  ```py
  import datetime
  print(datetime.datetime.utcnow().strftime('%s'))
  # '1714318952'
  ```

  We see clearly that they are not identical.


path: Torrenting.md
sortable_path: Torrenting.md
title: Torrenting
checksum: 5fbc5690a97e5fb717e54d69288e597c1f5003ed492b254c9f864e4b38bd948b
created: 2024-10-22 16:52:59.511348373+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"created":"Saturday, August 03, 2024","tags":["question","networks"]}
lead:
  **What is torrenting? Is this the same thing as a peer-to-peer network? How do
  these technologies fit within the client-server paradigm?**

body:
  **What is torrenting? Is this the same thing as a peer-to-peer network? How do
  these technologies fit within the client-server paradigm?**

  Torrenting is a specific application of
  [peer_to_peer_networking](Peer_to_peer_network.md) applied to file-sharing.

  Each peer on the network both consumes resources and makes resources available
  to others.

  ## Procedure

  Large files are divided into small equal sized pieces and each piece is assigned
  a unique hash identifier. The `.torrent` file details the number of pieces,
  their sizes and hash values.

  Clients request different pieces from different peers simultaneously. As pieces
  are received, they are verified against their hash. Corrupted or incorrect
  pieces are discarded and re-downloaded.

  The client software reassembles the pieces in the correct order and the final
  file will be identical to the original which can be verified by comparing
  hashes.

  ## Key terms

  - _tracker_
    - a server that coordinates communication between peers (so there is a server
      but its role is more lightweight than client-server, coordinating the peers
      rather than hosting or distributing file content)
  - _swarm_
    - a group of peers sharing the same file
  - _seeding_
    - uploading file pieces to other peers
  - _leeching_
    - downloading file pieces from other peers

  > An alternative to a tracker server is to use trackerless methods such as
  > distributed hash tables

  ## Terminological clarification

  - _BitTorrent_ is the communications [protocols](Network_protocols.md) for P2P
    file sharing. It was also a client software developed by the company
    BitTorrent Inc.

  - _torrenting_ is the general terms for that act of sharing files using the
    BitTorrent protocol

  ## Napster

  Napster pre-dated the emergence of the BitTorrent protocol. It did not segment
  files and files were downloaded directly from peers. It was a centralised P2P
  network because there was a central server that indexed all available files
  which kept a database of all connected users and their files.

  When you searched Napster you would be querying this DB on the server. The
  server would facilitate a direct connection between the consumer and provider
  peer for the data exchange.

  When users share with BitTorrent the `.torrent` file is the source of truth and
  a swarm is created around the users who have possess the file.

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

  # Torrenting

  **What is torrenting? Is this the same thing as a peer-to-peer network? How do
  these technologies fit within the client-server paradigm?**

  Torrenting is a specific application of
  [peer_to_peer_networking](Peer_to_peer_network.md) applied to file-sharing.

  Each peer on the network both consumes resources and makes resources available
  to others.

  ## Procedure

  Large files are divided into small equal sized pieces and each piece is assigned
  a unique hash identifier. The `.torrent` file details the number of pieces,
  their sizes and hash values.

  Clients request different pieces from different peers simultaneously. As pieces
  are received, they are verified against their hash. Corrupted or incorrect
  pieces are discarded and re-downloaded.

  The client software reassembles the pieces in the correct order and the final
  file will be identical to the original which can be verified by comparing
  hashes.

  ## Key terms

  - _tracker_
    - a server that coordinates communication between peers (so there is a server
      but its role is more lightweight than client-server, coordinating the peers
      rather than hosting or distributing file content)
  - _swarm_
    - a group of peers sharing the same file
  - _seeding_
    - uploading file pieces to other peers
  - _leeching_
    - downloading file pieces from other peers

  > An alternative to a tracker server is to use trackerless methods such as
  > distributed hash tables

  ## Terminological clarification

  - _BitTorrent_ is the communications [protocols](Network_protocols.md) for P2P
    file sharing. It was also a client software developed by the company
    BitTorrent Inc.

  - _torrenting_ is the general terms for that act of sharing files using the
    BitTorrent protocol

  ## Napster

  Napster pre-dated the emergence of the BitTorrent protocol. It did not segment
  files and files were downloaded directly from peers. It was a centralised P2P
  network because there was a central server that indexed all available files
  which kept a database of all connected users and their files.

  When you searched Napster you would be querying this DB on the server. The
  server would facilitate a direct connection between the consumer and provider
  peer for the data exchange.

  When users share with BitTorrent the `.torrent` file is the source of truth and
  a swarm is created around the users who have possess the file.


path: Transistors.md
sortable_path: Transistors.md
title: Transistors
checksum: 0614b5cebf4f7eb2dc413e31ea7e64a83a356d6362d4970ffc14917642857bb7
created: 2024-10-22 16:52:59.527889153+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["logic-gates","binary","electronics"]}
lead:
  A digital circuit requires that electrical phenomena be treated as discrete
  rather than continuous values.

body:
  A digital circuit requires that electrical phenomena be treated as discrete
  rather than continuous values.

  Although a given voltage at a point in the circuit can vary widely, in order to
  represent the binary states of 'on' and 'off' we need it to remain fixed within
  certain narrow parameters. This is achieved with transistors.

  Transistors are an electrical component that is capable of controlling the flow
  of current in the manner of a switch where the 'off' and 'on' states are
  represented by [voltage](Voltage.md) values within set parameters.

  There are different types of transistors but the simplest for the purposes of
  explanation are **bipolar junction transistors**.

  ![BJT transistor terminals](../img/bjt-terminals.jpg)

  The pins:

  - C: collector
  - B: base
  - E: emitter

  Applying a small amount of current at the base allows a larger current to flow
  from the collector to the emitter. Applying current to the base is like turning
  the switch on. Removing this current is like turning the switch off.

  This happens because of how current and voltage interact in a transistor. The
  small base current controls the larger collector-emitter current through a
  process called current amplification.

  When a small voltage is applied to the base, it allows a small current to flow
  from the base to the emitter. This base current triggers a much larger current
  to flow from the collector to the emitter. Think of it like a water valve: a
  small turn of the handle (base current) can control a large flow of water
  (collector-emitter current). The transistor amplifies this effect, allowing a
  tiny input signal to control a much larger output.

  ## Transistors and logic gates

  We can combine transistors to create logic gates. A logic gate is a combination
  of transistors arranged such that the logical function is embodied by the
  characteristic input and output voltages to the transistor.

  For example to create an [AND](Logic_gates.md#and-gate) gate we would have two
  voltage inputs going into two transistors that are connected in sequence. The
  two transistors create a continuous line going from the collector of one to the
  emitter of the other. If either voltage input is low then the voltage of the
  combined line is low (equivalent to the circuit being broken) and there is no
  current flowing.

  ![](/img/and-transistor.png)

  // Add example of OR gate created with transistors

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

  # Transistors

  A digital circuit requires that electrical phenomena be treated as discrete
  rather than continuous values.

  Although a given voltage at a point in the circuit can vary widely, in order to
  represent the binary states of 'on' and 'off' we need it to remain fixed within
  certain narrow parameters. This is achieved with transistors.

  Transistors are an electrical component that is capable of controlling the flow
  of current in the manner of a switch where the 'off' and 'on' states are
  represented by [voltage](Voltage.md) values within set parameters.

  There are different types of transistors but the simplest for the purposes of
  explanation are **bipolar junction transistors**.

  ![BJT transistor terminals](../img/bjt-terminals.jpg)

  The pins:

  - C: collector
  - B: base
  - E: emitter

  Applying a small amount of current at the base allows a larger current to flow
  from the collector to the emitter. Applying current to the base is like turning
  the switch on. Removing this current is like turning the switch off.

  This happens because of how current and voltage interact in a transistor. The
  small base current controls the larger collector-emitter current through a
  process called current amplification.

  When a small voltage is applied to the base, it allows a small current to flow
  from the base to the emitter. This base current triggers a much larger current
  to flow from the collector to the emitter. Think of it like a water valve: a
  small turn of the handle (base current) can control a large flow of water
  (collector-emitter current). The transistor amplifies this effect, allowing a
  tiny input signal to control a much larger output.

  ## Transistors and logic gates

  We can combine transistors to create logic gates. A logic gate is a combination
  of transistors arranged such that the logical function is embodied by the
  characteristic input and output voltages to the transistor.

  For example to create an [AND](Logic_gates.md#and-gate) gate we would have two
  voltage inputs going into two transistors that are connected in sequence. The
  two transistors create a continuous line going from the collector of one to the
  emitter of the other. If either voltage input is low then the voltage of the
  combined line is low (equivalent to the circuit being broken) and there is no
  current flowing.

  ![](/img/and-transistor.png)

  // Add example of OR gate created with transistors


path: Transport_Layer_of_Internet_Protocol.md
sortable_path: Transport_Layer_of_Internet_Protocol.md
title: Transport Layer of the Internet Protocol
checksum: 0e37694c48b92f88286f2e70bec9ccf9f0085572171cd1f2f1a2d44c0db5d344
created: 2024-10-22 16:52:59.545897642+00:00
modified: 2024-10-18 17:55:56.302394226+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](../img/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](../img/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: Troubleshooting_MongoDB.md
sortable_path: Troubleshooting_MongoDB.md
title: Troubleshooting MongoDB connection issues
lead: ## Arch Linux troubleshooting
checksum: 764b19f1d36a6c0d05f28bf1f30d52c829fe3030cef31f8e3c8efabf28f1f4c3
created: 2024-10-22 16:52:59.562957188+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["mongo-db"]}
body:
  ## Arch Linux troubleshooting

  Most times any problems will be a result of a Mongo process that is already
  running. Resolve with:

  ```bash
  # Kill running Mongo process...
  # Identify PID
  sudo lsof -iTCP -sTCP:LISTEN -n -P
  sudo kill [pid]
  ```

  Otherwise try the below.

  ```bash
  # Check that the systemd service is runnign for Mongo
  sudo systemctl status mongodb

  # If not, enable and start service (only need to do this once for machine)
  sudo systemctl enable --now mongodb
  sudo systemctl start --now mongodb

  # If issues with Mongo daemon...
  # Remove the current socket
  rm /tmp/mongodb-27017.sock
  ```

  For additional command line utilities you will also need to install
  `mongodb-tools` from the AUR.

raw_content:
  ---
  tags: [mongo-db]
  ---

  # Troubleshooting MongoDB connection issues

  ## Arch Linux troubleshooting

  Most times any problems will be a result of a Mongo process that is already
  running. Resolve with:

  ```bash
  # Kill running Mongo process...
  # Identify PID
  sudo lsof -iTCP -sTCP:LISTEN -n -P
  sudo kill [pid]
  ```

  Otherwise try the below.

  ```bash
  # Check that the systemd service is runnign for Mongo
  sudo systemctl status mongodb

  # If not, enable and start service (only need to do this once for machine)
  sudo systemctl enable --now mongodb
  sudo systemctl start --now mongodb

  # If issues with Mongo daemon...
  # Remove the current socket
  rm /tmp/mongodb-27017.sock
  ```

  For additional command line utilities you will also need to install
  `mongodb-tools` from the AUR.


path: Truth_functional_connectives.md
sortable_path: Truth_functional_connectives.md
title: Truth-functional connectives
checksum: 3c886282a2241af43af36a2ea37b6afc0518d29737ccc87107855f072226efeb
created: 2024-10-22 16:52:59.568881877+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  Propositions generated from other (simple) propositions by means of
  propositional connectives are
  [compound propositions](Atomic_and_molecular_propositions.md).

body:
  Propositions generated from other (simple) propositions by means of
  propositional connectives are
  [compound propositions](Atomic_and_molecular_propositions.md).

  We know that
  [logically determinant](Logical_indeterminacy.md)
  propositions express a truth value. When simple propositions are joined with a
  connective to make a compound proposition they also have a truth value. This is
  determined by the nature of the connective and the truth value of the
  constituent propositions. We therefore call connectives of this nature truth
  _functional_ connectives since the **truth value of the compound is a function
  of the truth values of its components**.

  > A propositional connective is used truth-functionally if and only if it is
  > used to generate a compound proposition from one or more propositions in such
  > a way that the truth value of the generated compound is wholly determined by
  > the truth-values of those one or more propositions from which the compound is
  > generated, no matter what the truth values may be.

  Each truth-functional connective has a characteristic **truth table**. This
  discloses the conditions under which the constituent propositions have a given
  truth value when combined with one or more connectives.

  We shall now review each of the truth-functional connectives in detail.

  ### Conjunction

  Conjunction is equivalent to the word AND in natural language. We use $\land$ as
  the symbol for this connective.

  A molecular proposition joining two conjuncts P and Q is true iff both conjuncts
  are true and false otherwise:

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  ### Disjunction

  Conjunction is equivalent to the word OR in natural language. We use `v` as the
  symbol of this connective.

  A molecular proposition joining two disjuncts P and Q is true if either disjunct
  is true or if both disjuncts are true and false otherwise. This corresponds to
  the inclusive sense of OR in natural language.

  ```
  P	Q				P	∨	Q
  T	T					T
  T	F					T
  F	T					T
  F	F					F
  ```

  ### Negation

  In contrast to the two previous connectives, negation is a unary connective not
  a binary connective. We use `~` to symbolise negation. It does not join two or
  more propositions, it applies to one proposition as a whole. This can be a
  simple proposition or a complex proposition. It simply negates the truth-value
  of whichever proposition it is applied to. Hence applied to P, it is true if P
  is false. And if P is false, it is true when P is true. !

  ```
  P			~	P
  T			F
  F			T
  ```

  ### Material conditional (a.k.a implication)

  The material conditional approximates the meaning expressed in natural language
  when we say _if_ such-and-such is the case _then_ such-and-such will the case.
  Another way of expressing the sense of the material conditional is to say that
  **P** implies **Q.**

  ```
  If it rains today the pavement will be wet.
  ```

  We call the proposition that expresses the 'if' proposition the **antecedent**
  and the proposition that expresses the 'then' statement the **consequent**. The
  symbol we use to represent the material conditional is `⊃` although you may see
  `→` used as well.

  The truth table is as follows:

  ```
  P	Q				P	⊃	Q
  T	T					T
  T	F					F
  F	T					T
  F	F					T
  ```

  The material conditional is perhaps the least intuitive of the logical
  connectives. The first case (TT) closely matches what we expect the connective
  to mean: it has rained so the pavement is wet. The antecedent is true and
  therefore the consequent is true. This chimes with what we tend to mean by 'if'
  in natural language. In the second case (TF) it also makes sense: the complex
  proposition is false because it rained and the pavement wasn't wet: this negates
  the truth of the expression. The final case (FF) is also straight forward. It
  didn't rain therefore the pavement wasn't wet, thus the overall assertion that
  rain implies wet pavements is retained.

  FT is less intuitive:

  ```
  It did not rain today. The pavement was wet.
  ```

  To some degree one just has to take these statements as axioms, whether or not
  they have intuitive sense is a secondary, more philosophical question. The
  semantic issues arise because we tacitly assume the material conditional to be a
  causal connective: there is something about the nature of **P** that _engenders_
  or _brings about_ **Q** but causality is not a logical concern.

  If we instead just focus on the simple propositions that comprise the truth
  value it is more plausible. In the case of FT we can say it didn't rain yet the
  pavement was wet does not stop the pavement being wet when it rains. The fact
  that I can pour a beer on the pavement thereby making it wet doesn't stop or
  render false the idea that the rain can also make the pavement wet. The same
  explanation covers the FF case: it hasn't rained and so the pavement is not wet
  does not contradict the assertion that when it rains the pavement will be wet.

  Things are elucidated when we look at an equivalent expression of P ⊃ Q, ~P v Q:

  ```
  P	Q				~	P	∨	Q
  T	T						T
  T	F						F
  F	T						T
  F	F						T
  ```

  A disjunction is true whenever either disjunct is true so when both are false
  the overall expression is false, the same as with FT and FF with the material
  conditional.

  ### Material biconditional (a.k.a equivalence)

  The material biconditional equates to the English expression 'if and only if',
  as a conditional connective it therefore avoids some of the perplexity aroused
  by its material cousin. In this scenario both antecedent and consequent have to
  be true for the overall expression to be true. If either is false the complex
  proposition is false. Other ways of expressing the semantics of this connective
  is to say that one proposition implies the other or that **P** and **Q** are
  equivalent.

  ```
  If and only if James studies every day he will pass the exam.
  ```

  There is no possibility in which James passes the exam and has not studied every
  day. If he studies for three out of the seven days leading up to the exam he
  will not pass. Alternatively, there is no possibility that James studied every
  day yet failed the exam. The antecedent and consequent are locked, as indicated
  by the truth-table:

  ```
  P	Q				P	≡	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					T
  ```

  The last condition (FF) maybe requires some explanation: if he has not studied
  every day then he cannot have passed the exam. Therefore, to say that he will
  pass iff he studies every day is rendered true.

  ## Combinations of truth-functional connectives

  ---

  So far we have applied connectives to simple propositions. In so doing we
  generate complex propositions. However propositions and connectives are
  inherently generative: we can build more complex expressions from less complex
  parts, using more than one type of connective or several different connectives
  to make larger complex propositions and express more detailed logical conditions
  ans statements about the world.

  For example the proposition:

  ```
  Socrates was either a philosopher or a drinker but he wasn't a politician.
  ```

  Can be expressed with greater logical clarity as:

  ```
  Socrates was a philosopher or Socrates was a drinker and Socrates was not a politician.
  ```

  Using P for 'Socrates was a philosopher', Q for 'Socrates was a drinker' and R
  for 'Socrates was a politician' we can express this symbolically as:

  ```
  (P v Q) & ~R
  ```

  Which has the truth table:

  ```
  P	Q	R				(	P	∨	Q	)	&	~	R
  T	T	T									F
  T	T	F									T
  T	F	T									F
  T	F	F									T
  F	T	T									F
  F	T	F									T
  F	F	T									F
  F	F	F									F
  ```

  Let's walk through each case where S stands for the overall proposition.

  1. S is false if Socrates was a philosopher, a drinker and a politician.
  1. **S is true if Socrates was a philosopher, a drinker but not a politician.**
  1. S is false if Socrates was a philosopher, a politician but not a drinker.
  1. **S is true if Socrates was a philosopher but not a drinker or politician.**
  1. S is false if Socrates was not a philosopher but was a drinker and politician
  1. **S is true if Socrates was not a philosopher or politician but was a
     drinker.**
  1. S is false if Socrates was neither a philosopher or drinker but was a
     politician.
  1. S is false if Socrates was neither a philosopher, drinker, or politician.

  If we look just at the true cases for simplicity, it becomes obvious that the
  truth value of the whole is a function of the truth-values of the parts.

  At the highest level of generality the proposition is a conjunction with two
  disjuncts: `P v Q` and `~R` . Therefore, for the proposition to be true both
  conjuncts must be true. The first conjunct is true just if one of the
  subordinate disjuncts is true (Socrates is either a philosopher, a drinker, or
  both). The second conjunct is true just if Socrates is not a politician. Thus
  there is only one variation for the second conjunct (not being a politician) and
  two variations for the first conjunct (being a drinker/being a philosopher)
  hence there are three cases where the overall proposition is true.

  ### Logical equivalence

  Once we start working with complex propositions with more than one
  truth-functional connective it becomes clear that the same proposition expressed
  in natural language can be expressed formally more than one way and thus that in
  logical terms, both formal expressions are equivalent. We can prove this
  equivalence by comparing truth tables.

  For example the proposition:

  ```
  I am going to the shops and the gym.
  ```

  Can obviously be expressed formally as:

  ```
  P & Q
  ```

  But also as:

  ```
  ~ (~P v ~Q)
  ```

  And we know this because the truth-tables are identical:consistency

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  ```
  P	Q			~	(	~	P	∨	~	Q	)
  T	T			T
  T	F			F
  F	T			F
  F	F			F
  ```

  Another example of equivalent expressions:

  ```
  Neither Watson or Sherlock Holmes is fond of criminals
  ```

  The first formalisation:

  ```
  ~P & ~Q
  ```

  Equivalent to:

  ```
  ~(P v Q)
  ```

  Again the truth-tables for verification:

  ```
  P	Q				~	P	&	~	Q
  T	T						F
  T	F						F
  F	T						F
  F	F						T
  ```

  `~P & ~Q`

  ```
  P	Q			~	(	P	∨	Q	)
  T	T			F
  T	F			F
  F	T			F
  F	F			T
  ```

  ### Important equivalences

  The example above is a key equivalence that you will encounter a lot especially
  when deriving formal proofs. It goes together with another one. We have noted
  them both below for future reference:

  ```
  ~P & ~Q = ~P v ~Q
  ```

  ```
  ~P v ~Q = ~(P & Q)
  ```

  ## Enforcing binary connectives through bracketing

  If we had a proposition of the form

  ```
  Socrates is man, is mortal and a philosopher.
  ```

  We could not write this as:

  ```
  P & Q & R
  ```

  This would not be a well-formed proposition because at most truth functional
  connectives can only connect two simple propositions. It would not be possible
  to generate truth conditions for this proposition in its current form. Instead
  we introduce brackets to enforce a binary grouping of simple propositions. In
  this instance, the placement of the brackets does not affect the accurate
  interpretation of the truth conditions of the compound, so the following two
  formalisations are equivalent:

  ```
  (P & Q) & R

  P & (Q & R)
  ```

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

  # Truth-functional connectives

  Propositions generated from other (simple) propositions by means of
  propositional connectives are
  [compound propositions](Atomic_and_molecular_propositions.md).

  We know that
  [logically determinant](Logical_indeterminacy.md)
  propositions express a truth value. When simple propositions are joined with a
  connective to make a compound proposition they also have a truth value. This is
  determined by the nature of the connective and the truth value of the
  constituent propositions. We therefore call connectives of this nature truth
  _functional_ connectives since the **truth value of the compound is a function
  of the truth values of its components**.

  > A propositional connective is used truth-functionally if and only if it is
  > used to generate a compound proposition from one or more propositions in such
  > a way that the truth value of the generated compound is wholly determined by
  > the truth-values of those one or more propositions from which the compound is
  > generated, no matter what the truth values may be.

  Each truth-functional connective has a characteristic **truth table**. This
  discloses the conditions under which the constituent propositions have a given
  truth value when combined with one or more connectives.

  We shall now review each of the truth-functional connectives in detail.

  ### Conjunction

  Conjunction is equivalent to the word AND in natural language. We use $\land$ as
  the symbol for this connective.

  A molecular proposition joining two conjuncts P and Q is true iff both conjuncts
  are true and false otherwise:

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  ### Disjunction

  Conjunction is equivalent to the word OR in natural language. We use `v` as the
  symbol of this connective.

  A molecular proposition joining two disjuncts P and Q is true if either disjunct
  is true or if both disjuncts are true and false otherwise. This corresponds to
  the inclusive sense of OR in natural language.

  ```
  P	Q				P	∨	Q
  T	T					T
  T	F					T
  F	T					T
  F	F					F
  ```

  ### Negation

  In contrast to the two previous connectives, negation is a unary connective not
  a binary connective. We use `~` to symbolise negation. It does not join two or
  more propositions, it applies to one proposition as a whole. This can be a
  simple proposition or a complex proposition. It simply negates the truth-value
  of whichever proposition it is applied to. Hence applied to P, it is true if P
  is false. And if P is false, it is true when P is true. !

  ```
  P			~	P
  T			F
  F			T
  ```

  ### Material conditional (a.k.a implication)

  The material conditional approximates the meaning expressed in natural language
  when we say _if_ such-and-such is the case _then_ such-and-such will the case.
  Another way of expressing the sense of the material conditional is to say that
  **P** implies **Q.**

  ```
  If it rains today the pavement will be wet.
  ```

  We call the proposition that expresses the 'if' proposition the **antecedent**
  and the proposition that expresses the 'then' statement the **consequent**. The
  symbol we use to represent the material conditional is `⊃` although you may see
  `→` used as well.

  The truth table is as follows:

  ```
  P	Q				P	⊃	Q
  T	T					T
  T	F					F
  F	T					T
  F	F					T
  ```

  The material conditional is perhaps the least intuitive of the logical
  connectives. The first case (TT) closely matches what we expect the connective
  to mean: it has rained so the pavement is wet. The antecedent is true and
  therefore the consequent is true. This chimes with what we tend to mean by 'if'
  in natural language. In the second case (TF) it also makes sense: the complex
  proposition is false because it rained and the pavement wasn't wet: this negates
  the truth of the expression. The final case (FF) is also straight forward. It
  didn't rain therefore the pavement wasn't wet, thus the overall assertion that
  rain implies wet pavements is retained.

  FT is less intuitive:

  ```
  It did not rain today. The pavement was wet.
  ```

  To some degree one just has to take these statements as axioms, whether or not
  they have intuitive sense is a secondary, more philosophical question. The
  semantic issues arise because we tacitly assume the material conditional to be a
  causal connective: there is something about the nature of **P** that _engenders_
  or _brings about_ **Q** but causality is not a logical concern.

  If we instead just focus on the simple propositions that comprise the truth
  value it is more plausible. In the case of FT we can say it didn't rain yet the
  pavement was wet does not stop the pavement being wet when it rains. The fact
  that I can pour a beer on the pavement thereby making it wet doesn't stop or
  render false the idea that the rain can also make the pavement wet. The same
  explanation covers the FF case: it hasn't rained and so the pavement is not wet
  does not contradict the assertion that when it rains the pavement will be wet.

  Things are elucidated when we look at an equivalent expression of P ⊃ Q, ~P v Q:

  ```
  P	Q				~	P	∨	Q
  T	T						T
  T	F						F
  F	T						T
  F	F						T
  ```

  A disjunction is true whenever either disjunct is true so when both are false
  the overall expression is false, the same as with FT and FF with the material
  conditional.

  ### Material biconditional (a.k.a equivalence)

  The material biconditional equates to the English expression 'if and only if',
  as a conditional connective it therefore avoids some of the perplexity aroused
  by its material cousin. In this scenario both antecedent and consequent have to
  be true for the overall expression to be true. If either is false the complex
  proposition is false. Other ways of expressing the semantics of this connective
  is to say that one proposition implies the other or that **P** and **Q** are
  equivalent.

  ```
  If and only if James studies every day he will pass the exam.
  ```

  There is no possibility in which James passes the exam and has not studied every
  day. If he studies for three out of the seven days leading up to the exam he
  will not pass. Alternatively, there is no possibility that James studied every
  day yet failed the exam. The antecedent and consequent are locked, as indicated
  by the truth-table:

  ```
  P	Q				P	≡	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					T
  ```

  The last condition (FF) maybe requires some explanation: if he has not studied
  every day then he cannot have passed the exam. Therefore, to say that he will
  pass iff he studies every day is rendered true.

  ## Combinations of truth-functional connectives

  ---

  So far we have applied connectives to simple propositions. In so doing we
  generate complex propositions. However propositions and connectives are
  inherently generative: we can build more complex expressions from less complex
  parts, using more than one type of connective or several different connectives
  to make larger complex propositions and express more detailed logical conditions
  ans statements about the world.

  For example the proposition:

  ```
  Socrates was either a philosopher or a drinker but he wasn't a politician.
  ```

  Can be expressed with greater logical clarity as:

  ```
  Socrates was a philosopher or Socrates was a drinker and Socrates was not a politician.
  ```

  Using P for 'Socrates was a philosopher', Q for 'Socrates was a drinker' and R
  for 'Socrates was a politician' we can express this symbolically as:

  ```
  (P v Q) & ~R
  ```

  Which has the truth table:

  ```
  P	Q	R				(	P	∨	Q	)	&	~	R
  T	T	T									F
  T	T	F									T
  T	F	T									F
  T	F	F									T
  F	T	T									F
  F	T	F									T
  F	F	T									F
  F	F	F									F
  ```

  Let's walk through each case where S stands for the overall proposition.

  1. S is false if Socrates was a philosopher, a drinker and a politician.
  1. **S is true if Socrates was a philosopher, a drinker but not a politician.**
  1. S is false if Socrates was a philosopher, a politician but not a drinker.
  1. **S is true if Socrates was a philosopher but not a drinker or politician.**
  1. S is false if Socrates was not a philosopher but was a drinker and politician
  1. **S is true if Socrates was not a philosopher or politician but was a
     drinker.**
  1. S is false if Socrates was neither a philosopher or drinker but was a
     politician.
  1. S is false if Socrates was neither a philosopher, drinker, or politician.

  If we look just at the true cases for simplicity, it becomes obvious that the
  truth value of the whole is a function of the truth-values of the parts.

  At the highest level of generality the proposition is a conjunction with two
  disjuncts: `P v Q` and `~R` . Therefore, for the proposition to be true both
  conjuncts must be true. The first conjunct is true just if one of the
  subordinate disjuncts is true (Socrates is either a philosopher, a drinker, or
  both). The second conjunct is true just if Socrates is not a politician. Thus
  there is only one variation for the second conjunct (not being a politician) and
  two variations for the first conjunct (being a drinker/being a philosopher)
  hence there are three cases where the overall proposition is true.

  ### Logical equivalence

  Once we start working with complex propositions with more than one
  truth-functional connective it becomes clear that the same proposition expressed
  in natural language can be expressed formally more than one way and thus that in
  logical terms, both formal expressions are equivalent. We can prove this
  equivalence by comparing truth tables.

  For example the proposition:

  ```
  I am going to the shops and the gym.
  ```

  Can obviously be expressed formally as:

  ```
  P & Q
  ```

  But also as:

  ```
  ~ (~P v ~Q)
  ```

  And we know this because the truth-tables are identical:consistency

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  ```
  P	Q			~	(	~	P	∨	~	Q	)
  T	T			T
  T	F			F
  F	T			F
  F	F			F
  ```

  Another example of equivalent expressions:

  ```
  Neither Watson or Sherlock Holmes is fond of criminals
  ```

  The first formalisation:

  ```
  ~P & ~Q
  ```

  Equivalent to:

  ```
  ~(P v Q)
  ```

  Again the truth-tables for verification:

  ```
  P	Q				~	P	&	~	Q
  T	T						F
  T	F						F
  F	T						F
  F	F						T
  ```

  `~P & ~Q`

  ```
  P	Q			~	(	P	∨	Q	)
  T	T			F
  T	F			F
  F	T			F
  F	F			T
  ```

  ### Important equivalences

  The example above is a key equivalence that you will encounter a lot especially
  when deriving formal proofs. It goes together with another one. We have noted
  them both below for future reference:

  ```
  ~P & ~Q = ~P v ~Q
  ```

  ```
  ~P v ~Q = ~(P & Q)
  ```

  ## Enforcing binary connectives through bracketing

  If we had a proposition of the form

  ```
  Socrates is man, is mortal and a philosopher.
  ```

  We could not write this as:

  ```
  P & Q & R
  ```

  This would not be a well-formed proposition because at most truth functional
  connectives can only connect two simple propositions. It would not be possible
  to generate truth conditions for this proposition in its current form. Instead
  we introduce brackets to enforce a binary grouping of simple propositions. In
  this instance, the placement of the brackets does not affect the accurate
  interpretation of the truth conditions of the compound, so the following two
  formalisations are equivalent:

  ```
  (P & Q) & R

  P & (Q & R)
  ```


path: Truth_tables.md
sortable_path: Truth_tables.md
title: Truth-tables
checksum: b6e6d1948359ad35aa044e4981d8103136f4294c4447567608a2b2a15e38db4d
created: 2024-10-22 16:52:59.584724567+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["logic","propositional-logic"]}
lead:
  We are already familiar with truth-tables from the previous entry on the
  _truth-functional connectives_ and the relationship between sentences,
  connectives and the overall truth-value of a sentence. Here we will look in
  further depth at how to build truth-tables and on their mathematical relation to
  binary truth-values. We will also look at examples of complex truth-tables for
  large compound expressions and the systematic steps we follow to derive the
  truth conditions of compound sentences from their simple constituents.

body:
  We are already familiar with truth-tables from the previous entry on the
  _truth-functional connectives_ and the relationship between sentences,
  connectives and the overall truth-value of a sentence. Here we will look in
  further depth at how to build truth-tables and on their mathematical relation to
  binary truth-values. We will also look at examples of complex truth-tables for
  large compound expressions and the systematic steps we follow to derive the
  truth conditions of compound sentences from their simple constituents.

  ## Formulae for constructing truth-tables

  For any truth-table, the number of rows it will contain is equal to $2n$ where:

  - $n$ stands for the number of sentences
  - $2$ is the total number of possible truth values that the sentence may have:
    true or false.

  When we count the number of sentences, we mean atomic sentences. And we only
  count each sentence once. Hence for a compound sentence of the form
  $(\sim B \supset C) & (A \equiv B)$, $B$ occurs twice but there are only three
  sentences: $A$, $B$, and $C$.

  Thus for the sentence $P & Q$ ,we have two sentences so $n$ is 2 which equals 4
  rows (2 x 2):

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  For the sentence $(P \lor Q) & R$ we have three sentences so $n$ is 3 which
  equals 8 rows (2 x 2 x 2):

  ```
  P	Q	R				(	P	∨	Q	)	&	R
  T	T	T									T
  T	T	F									F
  T	F	T									T
  T	F	F									F
  F	T	T									T
  F	T	F									F
  F	F	T									F
  F	F	F									F
  ```

  For the single sentence $P$ we have one sentence so $n$ is 1 which equals 2 rows
  (2 x 1):

  ```
  P			P
  T			T
  F			F
  ```

  This tells us how many rows the truth-table should have but it doesn't tell us
  what each row should consist in. In other words: how many Ts and Fs it should
  contain. This is fine with simple truth-tables since we can just alternate each
  value but for truth-tables with three sentences and more it is easy to make
  mistakes.

  To simplify this and ensure that we are including the right number of possible
  truth-values we can extend the formula to $2n^-i$. This formula tells us how
  many groups of T and F we should have in each column.

  We can already see that there is a pattern at work by looking at the columns of
  the truth tables above. If we take the sentence $(P \lor Q) & R$ we can see that
  for each sentence:

  - $P$ consists in two sets of ${\textsf{T,T,T,T}}$ and ${\textsf{F,F,F,F}}$ with
    **four** elements per set
  - $Q$ consists in four sets of ${\textsf{T,T}}$ , ${\textsf{F,F}}$,
    ${\textsf{T,T}}$ , ${\textsf{F,F}}$ with **two** elements per set
  - $R$ consists in eight sets of ${\textsf{T}}$, ${\textsf{F}}$, ${\textsf{T}}$,
    ${\textsf{F}}$, ${\textsf{T}}$, ${\textsf{F}}$, ${\textsf{T}}$, ${\textsf{F}}$
    with **one** element per set.

  If we work through the formula we see that it returns 4, 2, 1:

  $$\begin{equation} \begin{split} 2n^-1 = 3 -1 \\ = 2 \\ = 2 \cdot 2 \\ = 4 \end{split} \end{equation}$$

  $$
  \\begin{equation} \begin{split} 2n^-2 = 3 - 2 \\ = 1 \\ = 2 \cdot 1 \\ = 2 \end{split} \end{equation}
  $$

  $$
  \\begin{equation} \begin{split} 2n^-3 = 3 - 3 \\ = 0 \\ = 2 \cdot 0 \\ = 1 \end{split} \end{equation}
  $$

  ## Truth-table concepts

  ### Recursion

  When we move to complex truth-tables with more than one connective we realise
  that truth-tables are recursive. The truth-tables for the truth-functional
  connectives provide all that we need to determine the truth-values of complex
  sentences:

  > The core truth-tables tell us how to determine the truth-value of a molecular
  > sentence given the truth-values of its
  > [immediate sentential components](Syntax%20of%20sentential%20logic.md). And if
  > the immediate sentential components of a molecular sentence are also
  > molecular, we can use the information in the characteristic truth-tables to
  > determine how the truth-value of each immediate component depends n the
  > truth-values of _its_ components and so on.

  ### Truth-value assignment

  > A truth-value assignment is an assignment of truth-values (either T or F) to
  > the atomic sentences of SL.

  When working on complex truth tables, we use the truth-assignment of atomic
  sentences to count as the values that we feed into the larger expressions at a
  higher level of the sentential abstraction.

  ### Partial assignment

  We talk about partial assignments of truth-values when we look at one specific
  row of the truth-table, independently of the others. The total set of partial
  assignments comprise all possible truth assignments for the given sentence.

  ## Working through complex truth-tables

  The truth-table below shows all truth-value assignments for the sentence
  $(\sim B \supset C) & (A \equiv B)$ :

  ```
  A	B	C				(	~	B	⊃	C	)	&	(	A	≡	B	)
  T	T	T					F	T	T	T		T		T	T	T
  T	T	F					F	T	T	F		T		T	T	T
  T	F	T					T	F	T	T		F		T	F	F
  T	F	F					T	F	F	F		F		T	F	F
  F	T	T					F	T	T	T		F		F	F	T
  F	T	F					F	T	T	F		F		F	F	T
  F	F	T					T	F	T	T		T		F	T	F
  F	F	F					T	F	F	F		F		F	T	F
  ```

  As with algebra we work outwards from each set of brackets. The sequence for
  manually arriving at the above table would be roughly as follows:

  1. For each sentence letter, copy the truth value for it in each row.
  1. Identify the connectives in the atomic sentences and the main overall
     sentence.
  1. Work out the truth-values for the smallest connectives and sub-compound
     sentences. The first should always be negation and then the other atomic
     connectives.
  1. Feed-in the truth-values of the atomic sentences as values into the main
     connective, through a process of elimination you then reach the core
     truth-assignments:

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

  # Truth-tables

  We are already familiar with truth-tables from the previous entry on the
  _truth-functional connectives_ and the relationship between sentences,
  connectives and the overall truth-value of a sentence. Here we will look in
  further depth at how to build truth-tables and on their mathematical relation to
  binary truth-values. We will also look at examples of complex truth-tables for
  large compound expressions and the systematic steps we follow to derive the
  truth conditions of compound sentences from their simple constituents.

  ## Formulae for constructing truth-tables

  For any truth-table, the number of rows it will contain is equal to $2n$ where:

  - $n$ stands for the number of sentences
  - $2$ is the total number of possible truth values that the sentence may have:
    true or false.

  When we count the number of sentences, we mean atomic sentences. And we only
  count each sentence once. Hence for a compound sentence of the form
  $(\sim B \supset C) & (A \equiv B)$, $B$ occurs twice but there are only three
  sentences: $A$, $B$, and $C$.

  Thus for the sentence $P & Q$ ,we have two sentences so $n$ is 2 which equals 4
  rows (2 x 2):

  ```
  P	Q				P	&	Q
  T	T					T
  T	F					F
  F	T					F
  F	F					F
  ```

  For the sentence $(P \lor Q) & R$ we have three sentences so $n$ is 3 which
  equals 8 rows (2 x 2 x 2):

  ```
  P	Q	R				(	P	∨	Q	)	&	R
  T	T	T									T
  T	T	F									F
  T	F	T									T
  T	F	F									F
  F	T	T									T
  F	T	F									F
  F	F	T									F
  F	F	F									F
  ```

  For the single sentence $P$ we have one sentence so $n$ is 1 which equals 2 rows
  (2 x 1):

  ```
  P			P
  T			T
  F			F
  ```

  This tells us how many rows the truth-table should have but it doesn't tell us
  what each row should consist in. In other words: how many Ts and Fs it should
  contain. This is fine with simple truth-tables since we can just alternate each
  value but for truth-tables with three sentences and more it is easy to make
  mistakes.

  To simplify this and ensure that we are including the right number of possible
  truth-values we can extend the formula to $2n^-i$. This formula tells us how
  many groups of T and F we should have in each column.

  We can already see that there is a pattern at work by looking at the columns of
  the truth tables above. If we take the sentence $(P \lor Q) & R$ we can see that
  for each sentence:

  - $P$ consists in two sets of ${\textsf{T,T,T,T}}$ and ${\textsf{F,F,F,F}}$ with
    **four** elements per set
  - $Q$ consists in four sets of ${\textsf{T,T}}$ , ${\textsf{F,F}}$,
    ${\textsf{T,T}}$ , ${\textsf{F,F}}$ with **two** elements per set
  - $R$ consists in eight sets of ${\textsf{T}}$, ${\textsf{F}}$, ${\textsf{T}}$,
    ${\textsf{F}}$, ${\textsf{T}}$, ${\textsf{F}}$, ${\textsf{T}}$, ${\textsf{F}}$
    with **one** element per set.

  If we work through the formula we see that it returns 4, 2, 1:

  $$\begin{equation} \begin{split} 2n^-1 = 3 -1 \\ = 2 \\ = 2 \cdot 2 \\ = 4 \end{split} \end{equation}$$

  $$
  \\begin{equation} \begin{split} 2n^-2 = 3 - 2 \\ = 1 \\ = 2 \cdot 1 \\ = 2 \end{split} \end{equation}
  $$

  $$
  \\begin{equation} \begin{split} 2n^-3 = 3 - 3 \\ = 0 \\ = 2 \cdot 0 \\ = 1 \end{split} \end{equation}
  $$

  ## Truth-table concepts

  ### Recursion

  When we move to complex truth-tables with more than one connective we realise
  that truth-tables are recursive. The truth-tables for the truth-functional
  connectives provide all that we need to determine the truth-values of complex
  sentences:

  > The core truth-tables tell us how to determine the truth-value of a molecular
  > sentence given the truth-values of its
  > [immediate sentential components](Syntax%20of%20sentential%20logic.md). And if
  > the immediate sentential components of a molecular sentence are also
  > molecular, we can use the information in the characteristic truth-tables to
  > determine how the truth-value of each immediate component depends n the
  > truth-values of _its_ components and so on.

  ### Truth-value assignment

  > A truth-value assignment is an assignment of truth-values (either T or F) to
  > the atomic sentences of SL.

  When working on complex truth tables, we use the truth-assignment of atomic
  sentences to count as the values that we feed into the larger expressions at a
  higher level of the sentential abstraction.

  ### Partial assignment

  We talk about partial assignments of truth-values when we look at one specific
  row of the truth-table, independently of the others. The total set of partial
  assignments comprise all possible truth assignments for the given sentence.

  ## Working through complex truth-tables

  The truth-table below shows all truth-value assignments for the sentence
  $(\sim B \supset C) & (A \equiv B)$ :

  ```
  A	B	C				(	~	B	⊃	C	)	&	(	A	≡	B	)
  T	T	T					F	T	T	T		T		T	T	T
  T	T	F					F	T	T	F		T		T	T	T
  T	F	T					T	F	T	T		F		T	F	F
  T	F	F					T	F	F	F		F		T	F	F
  F	T	T					F	T	T	T		F		F	F	T
  F	T	F					F	T	T	F		F		F	F	T
  F	F	T					T	F	T	T		T		F	T	F
  F	F	F					T	F	F	F		F		F	T	F
  ```

  As with algebra we work outwards from each set of brackets. The sequence for
  manually arriving at the above table would be roughly as follows:

  1. For each sentence letter, copy the truth value for it in each row.
  1. Identify the connectives in the atomic sentences and the main overall
     sentence.
  1. Work out the truth-values for the smallest connectives and sub-compound
     sentences. The first should always be negation and then the other atomic
     connectives.
  1. Feed-in the truth-values of the atomic sentences as values into the main
     connective, through a process of elimination you then reach the core
     truth-assignments:


path: Truth_trees.md
sortable_path: Truth_trees.md
title: Truth trees
lead: ## Rationale
checksum: 345d5759680369e156c87dc4229f912c1cd9ae3319d8d3e509a792ac372c1366
created: 2024-10-22 16:52:59.60304631+00:00
modified: 2024-10-19 09:00:39.867853525+00:00
metadata: {"tags":["logic","propositional-logic"]}
body:
  ## Rationale

  Like [truth-tables](Truth-tables.md), truth-trees are a means of graphically
  representing the logical relationships that may obtain between propositions.
  Truth-trees and truth-tables complement each other and which method you choose
  depends on which logical property you are seeking to derive.

  Whilst truth-tables have the benefit of being exhaustive - every possible truth
  assignment is factored into the representation - their complexity grows
  exponentially with each additional proposition they contain. This can make
  manually constructing truth tables long-winded and prone to mistakes.

  Truth-trees are less onerous but they lack the exhaustive scope of a
  truth-table. They are more targeted and are best used for demonstrating _that
  something is the case_ rather than _all the possible states that could be the
  case_. For example, a truth tree will tell us that a set _S is logically
  consistent_ whereas a truth-table will tell us that _S is consistent on the
  following three assignments._

  ## Logical consistency

  Recall that a set of propositions is logically or truth-functionally
  [consistent](Consistency.md) just if there is at least one assignment of truth
  conditions which results in all members of the set being true. To identify
  consistency for a set of three propositions via the truth table approach we
  would need to construct a truth table with $2^3$ (8) rows. Assume that this set
  is consistent on one partial assignment only. This means that 87.5% of our rows
  are redundant, they are not required to prove the consistency of the set.
  However we can only know this and we can only be sure of consistency once we
  have gone through the process of generating an assignment for each row.

  Truth trees allow us to reduce the amount of work required and go straight to
  the assignment that proves consistency, disregarding the rest which are
  irrelevant.

  ## Truth tree structure and key terms

  **When using a truth tree to derive logical consistency, the goal is to
  determine whether there is a truth-value assignment on which all of the
  sentences of a set are true. If the set is consistent we should be able to
  derive a partial assignment from the tree that demonstrates consistency.**

  Each truth tree begins with a series of sentences one on top of the other in a
  column. We call the sentences that comprise the initial column **set members**.
  In constructing the tree, we work downwards from the initial column decomposing
  set members into their atomic constituents. We a call an atomic sentence that
  has been decomposed a **literal.** A literal will either be an atomic sentence
  or the negation of an atomic sentence. If one of the set members is already a
  literal, there is no need to decompose it; it can remain as it is.

  Once every set member has been decomposed the truth tree is complete. It can
  then be interpreted in order to derive logical consistency or inconsistency. If
  the set is consistent, we are able to derive the partial assignment(s) that
  demonstrate consistency.

  The rules for decomposing compound sentences match the truth conditions of the
  logical connectives. There are rules for every possible connective and the
  negation of every possible connective however in terms of their tree shape they
  all correspond to either a conjunction or a disjunction. Disjunctive
  decomposition results in new branches being formed off the main column (or
  trunk). Conjunctive decomposition is non-branching which means the decomposed
  constituents are placed within the trunk of whichever tree or branch they are
  decomposed within.

  As we construct the tree we list each line in the left-hand margin and the
  decomposition rule in the right-hand margin. When we apply a decomposition rule
  we must cite the lines to which it applies.

  ### Closed and open branches

  Any branch on which an atomic sentence ($P$) and the negation of that sentence
  ($\sim P$) both occur is a **closed branch**. A branch that is not closed is an
  **open branch**. No partial assignment is recoverable from a closed branch. An
  open branch allows truth to ‘flow up’ to the original set members whereas a
  closed branch blocks this passage.

  ### Completed open branch

  A completed open branch occurs when we have an open branch that has been fully
  decomposed: the branch is open and all molecular sentences have been ticked off
  such that it contains only literals.

  ### Completed tree

  A tree where all its branches are either completed open branches or closed
  branches.

  ### Closed tree

  A tree where all the branches are closed

  ### Open tree

  A tree with at least one completed open branch

  ## Deriving consistency

  Using the definitions above, we can now define truth-functional consistency and
  inconsistency in terms of truth trees:

  > A finite set ($\Gamma$ ) of sentences is truth-functionally inconsistent if
  > $\Gamma$ is a closed tree

  > A finite set ($\Gamma$ ) of sentences is truth-functionally consistent if
  > $\Gamma$ is an open tree

  ## Examples

  ### First example

  The following is a truth tree for the set ${P \lor Q, \sim P }$:

  ![basic-open-tree 1.svg](basic-open-tree%201.svg)

  ### Interpretation

  - We decompose the disjunction at line 1 on line 3. We tick off the compound
    sentence to indicate that it is now decomposed and no longer under
    consideration.
  - Both P and its negation exist on a single branch (at line 2 and line 3). This
    makes it a closed branch. We indicate this by the X beneath the branch that is
    closed, citing the source of the closure by line number.
  - The rightward branch is a completed open branch given the decomposition at 3
    and the lack of negation of Q. Overall this makes the tree an open tree.

  As the set gives us an open tree, it must be truth-functionally consistent. If
  this is the case we should be able to determine the partial assignment in which
  each set member is true. Given that Q is not negated the assignment of
  consistency will contain Q but we have both P and ~P. This means there are two
  possible assignments where the set is consistent: $P, Q$ and $\sim P, Q$. This
  is confirmed by the truth-table:

  ```
  P	Q				P	∨	~	P				Q
  T	T					T						T     *
  T	F					T						F
  F	T					T						T     *
  F	F					T						F
  ```

  **Any time there is an open tree with a closed branch it will be the case that
  the negated sentences of the closed branch will appear both as** $S$ and
  $\sim S$ i**n the resultant assignment.**

  Invoking the truth-table highlights the differences between the two techniques.
  The values that are derived when we interpret a truth tree are not the
  truth-functions of the set members but the truth-values for when they are
  simultaneously true. With truth-tables in contrast, we are deriving the truth
  functions for every possible truth-value assignment. In other words the values
  derived from a truth tree correspond to the left hand side of the truth table
  not the right hand side.

  ### Second example

  The following is a truth tree for the set
  ${A & \sim B, C, \sim A \lor \sim B }$.

  ![basic-closed-tree 1.svg](basic-closed-tree%201.svg)

  ### Interpretation

  - The two molecular set members are decomposed. The disjunction (line 3) results
    in a branching tree. The conjunction (line 1) results in the continuation of
    the trunk.
  - Both branches are completed making it a completed tree. As each branch is
    closed this is a closed tree.

  As this is a closed tree, the set is not truth-functionally consistent. This is
  confirmed by the truth table where there is no partial assignment where all set
  members are true.

  ```
  A	B	C				A	&	~	B				C				~	A	∨	~	C
  T	T	T					F						T						F
  T	T	F					F						F						T
  T	F	T					T						T						F
  T	F	F					T						F						T
  F	T	T					F						T						T
  F	T	F					F						F						T
  F	F	T					F						T						T
  F	F	F					F						F						T
  ```

  ## Truth tree decomposition rules

  ---

  So far we have encountered the decomposition rules for conjunction (`&D`) and
  disjunction (`vD`). We will now list all the rules. We will see that for each
  rule, the decomposition either branches or does not branch which is to say that
  each rule either has the shape of a conjunction or a disjunction (however the
  permitted values of the specific disjuncts/conjuncts obviously differ in each
  case). Moreover there is a parallel rule for the decomposition of the negation
  of each of the main connectives and these rules rely on logical equivalences

  ### Negated negation decomposition: `~~D`

  ![negated-negation-decomposition-rule 2.svg](negated-negation-decomposition-rule%202.svg)

  Truth passes only if $P$ is true

  ### Conjunction decomposition: `&D`

  ![conjunction-decomposition-rule.svg](conjunction-decomposition-rule.svg)

  Truth passes only $P$ and $Q$ are both true.

  ### Negated Conjunction decomposition: `~&D`

  ![negated-conjunction-decomposition-rule.svg](negated-conjunction-decomposition-rule.svg)

  Truth passes if either $\sim P$ or $\sim Q$ is true. This rule is a consequence
  of the equivalence between $\sim (P & Q)$ and $\sim P \lor \sim Q$ , the first
  of DeMorgan’s Laws.

  ### Disjunction decomposition: `vD`

  ![disjunction-decomposition-rule.svg](disjunction-decomposition-rule.svg)

  Truth passes if either $P$or $Q$ are true.

  ### Negated Disjunction decomposition: `~vD`

  ![negated-disjunction-decomposition-rule.svg](negated-disjunction-decomposition-rule.svg)

  Truth passes if both $P$ and $Q$ are false. This rule is a consequence of the
  equivalence between $\sim (P \lor Q)$ and $\sim P & \sim Q$, the second of
  DeMorgan’s Laws.

  ### Conditional decomposition: `⊃D`

  ![conditional-decomposition-rule.svg](conditional-decomposition-rule.svg)

  Truth passes if either $\sim P$ or $Q$ are true. This rule is a consequence of
  the equivalence between $P \supset Q$ and $\sim P \lor Q$ therefore this branch
  has the shape of a disjunction with $\sim P$ , $Q$ as its disjuncts.

  ### Negated Conditional decomposition: `~⊃D`

  Truth passes if both $P$ and $\sim Q$ are true. This is a consequence of the
  equivalence between $\sim (P \supset Q)$ and $P & \sim Q$.

  ![negated-conditional-decomposition-rule.svg](negated-conditional-decomposition-rule.svg)

  ### Biconditional decomposition: `≡D`

  ![biconditional-decomposition-rule.drawio(1).svg](biconditional-decomposition-rule.drawio%281%29.svg)

  Truth passes if either $P$ and $Q$ are true or $\sim P & \sim Q$ are true. This
  is an interesting rule because it combines the disjunction and conjunction tree
  shapes.

  ### Negated biconditional decomposition: `~≡D`

  ![negated-biconditional-decomposition-rule.drawio.svg](negated-biconditional-decomposition-rule.drawio.svg)

  Truth passes if either $P$ and $\sim Q$ is true or if $\sim P$ and $Q$ is true.

  ## Further examples and heuristics for complex truth trees

  With truth-trees regardless of which order you decompose the set members, the
  conclusion should always be the same. This said, there more are more efficient
  ways than others to construct the trees. You want to find the route that will
  demonstrate consistency or non-consistency with the shortest amount of work. The
  following heuristic techniques followed in order, facilitate this:

  1. Decompose those molecular sentences the decomposition of which does not
     produce new branches. In other words that are decompositions of double
     negations or pure conjunctions.
  1. Perform those decompositions that will rapidly generate closed branches.
  1. If neither (1) or (2) is applicable, decompose **the most complex** sentence
     first.

  Here are some examples of these rules applied:

  ![complex-tree.svg](complex-tree.svg)

  Observe that here we don’t bother to decompose the sentence on line 1. This is
  because, having decomposed the sentences on lines 2 and 3 we have arrived at a
  closed tree. It is therefore unnecessary to go any further for if two sentences
  in the set are inconsistent with each other, adding another sentence is not
  going to change the overall assignment of inconsistency.

  ## Deriving properties other than logical consistency from truth trees

  So far truth trees have been discussed purely in terms of logical consistency
  however they can be used to derive all the other key truth-functional properties
  of propositional logic. Given the foundational role of consistency to logic,
  these properties are expressible in terms of consistency which is what makes
  them amenable to formulation in terms of truth trees.

  ### Logical falsity

  For a given finite set $\Gamma$, $\Gamma$ is logically consistent just if all of
  its members can be true at once. Expressed in terms of truth trees, this is
  equivalent to an open tree. Contrariwise, $\Gamma$ is inconsistent if it is not
  possible for every member of the set to be true at once. This is the same as a
  tree where all of the branches are closed (i.e. a closed tree).

  When we wish to assess
  [logical falsity](Logical%20truth%20and%20falsity.md#logical-falsity) we are not
  focused on sets however, we are interested in a property of a sentence. However
  we can easily construe single sentences as unit sets: sets with a single member.
  With this in mind and the above accounts of consistency and logical falsity we
  are equipped to express logical falsity in terms of truth-trees with the
  following rule:

  > A sentence $P$ is logically false if and only if the unit set ${ P }$ has a
  > closed tree

  A logically false sentence cannot be true on any assignment. This is the same
  thing as an inconsistent set. Thus it will be represented in a truth tree as
  inconsistency which is disclosed via a closed tree.

  ![logical-falsity-tree.svg](logical-falsity-tree.svg)

  ### Logical truth

  For a sentence $P$ to be
  [logically true](Logical%20truth%20and%20falsity.md#logical-truth), there must
  be no possible assignment in which $P$ is false. We express this informally by
  saying _it is not possible to consistently deny $P$._ We know that in terms of
  truth trees an inconsistent set is a closed tree therefore a unit set of ${ P }$
  is logically true if ${ \sim P }$ is a closed tree. This is to say: if the
  negation of $P$ is inconsistent.

  > A sentence $P$ is logically true if and only if the set ${ \sim P }$ has a
  > closed tree

  ### Logical indeterminacy

  [Indeterminacy](Indeterminacy.md) follows from the two definitions above; we do
  not require any additional apparatus. We recall that a sentence $P$ is logically
  indeterminate just if it is neither logically true or logically false. Thus the
  truth tree for an indeterminate sentence is straightforward:

  > A sentence $P$ is logically indeterminate if and only if neither the set
  >  ${ P }$ nor the set ${ \sim P }$ has a closed tree

  This follows because a closed tree for  ${ P }$ means it is not logically false
  and an open tree for ${ \sim P }$ means it is not logically true. So if it is
  neither of these things, $P$ must be indeterminate.

  ### Logical equivalence

  Recall that $P$ and $Q$ are [logically equivalent](Logical%20equivalence.md)
  just if there is no truth assignment on which one is true and the other is
  false. We know from the
  [material biconditional shorthand](Corresponding%20material%20and%20biconditional.md#corresponding-material-biconditional)
  that this state of affairs can be expressed as $P \equiv Q$ and that if this
  compound sentence is true on every assignment then both simple sentences are
  equivalent. But ‘true on every assignment’ is another way of saying _logically
  true_ since there is no possibility of a false assignment. We already know what
  logical truth looks like as a truth tree: it is a closed tree for the negation
  of the sentence being tested. Therefore, to test the logical equivalence of two
  sentences it is necessary to construct a truth tree for the negation of the
  sentences conjoined by the biconditional (i.e. $\sim (P \equiv Q)$ )and see if
  this results in a closed tree. If it does, the two sentences are logically
  equivalent.

  > Sentences $P$ and $Q$ are truth-functionally equivalent if and only if the set
  > $\sim (P \equiv Q)$ has a closed tree

  ![logical-equivalence-tree.svg](logical-equivalence-tree.svg)

  ### Logical entailment and validity

  Let’s remind ourselves of the meaning of truth-functional
  [entailment](Validity%20and%20entailment.md#entailment) and
  [validity](Validity%20and%20entailment.md#validity) and the relation between the
  two. $\Gamma$ $\vdash$ $P$ is true if and only if there is no truth-assignment
  in which every member of $\Gamma$ is true and $P$ is false. Entailment is
  closely related to validity; it is really just a matter of emphasis: we say that
  $\Gamma$ are the premises and $P$ is the conclusion and that this is a valid
  argument if there is no assignment in which every member of $\Gamma$ is true and
  $P$ is false.

  As with the previous properties, to express validity and entailment in terms of
  truth trees we need to express these concepts in the language of logical
  consistency. $\Gamma$ entails $P$ just if one cannot consistently assert
  $\Gamma$ whilst denying $P$. This is to say that the set $\Gamma \cup {\sim P}$
  is inconsistent. So we just need a closed truth tree for $\Gamma \cup {\sim P}$
  to demonstrate the validity of this set.

  > A finite set of sentences $\Gamma$ truth-functionally entails a sentence $P$
  > if and only if the set $\Gamma \cup {\sim P}$ has a closed truth tree.

  > An argument is truth functionally valid if and only if the set consisting of
  > the premises and the negation of the conclusion has a closed truth tree.

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

  # Truth trees

  ## Rationale

  Like [truth-tables](Truth-tables.md), truth-trees are a means of graphically
  representing the logical relationships that may obtain between propositions.
  Truth-trees and truth-tables complement each other and which method you choose
  depends on which logical property you are seeking to derive.

  Whilst truth-tables have the benefit of being exhaustive - every possible truth
  assignment is factored into the representation - their complexity grows
  exponentially with each additional proposition they contain. This can make
  manually constructing truth tables long-winded and prone to mistakes.

  Truth-trees are less onerous but they lack the exhaustive scope of a
  truth-table. They are more targeted and are best used for demonstrating _that
  something is the case_ rather than _all the possible states that could be the
  case_. For example, a truth tree will tell us that a set _S is logically
  consistent_ whereas a truth-table will tell us that _S is consistent on the
  following three assignments._

  ## Logical consistency

  Recall that a set of propositions is logically or truth-functionally
  [consistent](Consistency.md) just if there is at least one assignment of truth
  conditions which results in all members of the set being true. To identify
  consistency for a set of three propositions via the truth table approach we
  would need to construct a truth table with $2^3$ (8) rows. Assume that this set
  is consistent on one partial assignment only. This means that 87.5% of our rows
  are redundant, they are not required to prove the consistency of the set.
  However we can only know this and we can only be sure of consistency once we
  have gone through the process of generating an assignment for each row.

  Truth trees allow us to reduce the amount of work required and go straight to
  the assignment that proves consistency, disregarding the rest which are
  irrelevant.

  ## Truth tree structure and key terms

  **When using a truth tree to derive logical consistency, the goal is to
  determine whether there is a truth-value assignment on which all of the
  sentences of a set are true. If the set is consistent we should be able to
  derive a partial assignment from the tree that demonstrates consistency.**

  Each truth tree begins with a series of sentences one on top of the other in a
  column. We call the sentences that comprise the initial column **set members**.
  In constructing the tree, we work downwards from the initial column decomposing
  set members into their atomic constituents. We a call an atomic sentence that
  has been decomposed a **literal.** A literal will either be an atomic sentence
  or the negation of an atomic sentence. If one of the set members is already a
  literal, there is no need to decompose it; it can remain as it is.

  Once every set member has been decomposed the truth tree is complete. It can
  then be interpreted in order to derive logical consistency or inconsistency. If
  the set is consistent, we are able to derive the partial assignment(s) that
  demonstrate consistency.

  The rules for decomposing compound sentences match the truth conditions of the
  logical connectives. There are rules for every possible connective and the
  negation of every possible connective however in terms of their tree shape they
  all correspond to either a conjunction or a disjunction. Disjunctive
  decomposition results in new branches being formed off the main column (or
  trunk). Conjunctive decomposition is non-branching which means the decomposed
  constituents are placed within the trunk of whichever tree or branch they are
  decomposed within.

  As we construct the tree we list each line in the left-hand margin and the
  decomposition rule in the right-hand margin. When we apply a decomposition rule
  we must cite the lines to which it applies.

  ### Closed and open branches

  Any branch on which an atomic sentence ($P$) and the negation of that sentence
  ($\sim P$) both occur is a **closed branch**. A branch that is not closed is an
  **open branch**. No partial assignment is recoverable from a closed branch. An
  open branch allows truth to ‘flow up’ to the original set members whereas a
  closed branch blocks this passage.

  ### Completed open branch

  A completed open branch occurs when we have an open branch that has been fully
  decomposed: the branch is open and all molecular sentences have been ticked off
  such that it contains only literals.

  ### Completed tree

  A tree where all its branches are either completed open branches or closed
  branches.

  ### Closed tree

  A tree where all the branches are closed

  ### Open tree

  A tree with at least one completed open branch

  ## Deriving consistency

  Using the definitions above, we can now define truth-functional consistency and
  inconsistency in terms of truth trees:

  > A finite set ($\Gamma$ ) of sentences is truth-functionally inconsistent if
  > $\Gamma$ is a closed tree

  > A finite set ($\Gamma$ ) of sentences is truth-functionally consistent if
  > $\Gamma$ is an open tree

  ## Examples

  ### First example

  The following is a truth tree for the set ${P \lor Q, \sim P }$:

  ![basic-open-tree 1.svg](basic-open-tree%201.svg)

  ### Interpretation

  - We decompose the disjunction at line 1 on line 3. We tick off the compound
    sentence to indicate that it is now decomposed and no longer under
    consideration.
  - Both P and its negation exist on a single branch (at line 2 and line 3). This
    makes it a closed branch. We indicate this by the X beneath the branch that is
    closed, citing the source of the closure by line number.
  - The rightward branch is a completed open branch given the decomposition at 3
    and the lack of negation of Q. Overall this makes the tree an open tree.

  As the set gives us an open tree, it must be truth-functionally consistent. If
  this is the case we should be able to determine the partial assignment in which
  each set member is true. Given that Q is not negated the assignment of
  consistency will contain Q but we have both P and ~P. This means there are two
  possible assignments where the set is consistent: $P, Q$ and $\sim P, Q$. This
  is confirmed by the truth-table:

  ```
  P	Q				P	∨	~	P				Q
  T	T					T						T     *
  T	F					T						F
  F	T					T						T     *
  F	F					T						F
  ```

  **Any time there is an open tree with a closed branch it will be the case that
  the negated sentences of the closed branch will appear both as** $S$ and
  $\sim S$ i**n the resultant assignment.**

  Invoking the truth-table highlights the differences between the two techniques.
  The values that are derived when we interpret a truth tree are not the
  truth-functions of the set members but the truth-values for when they are
  simultaneously true. With truth-tables in contrast, we are deriving the truth
  functions for every possible truth-value assignment. In other words the values
  derived from a truth tree correspond to the left hand side of the truth table
  not the right hand side.

  ### Second example

  The following is a truth tree for the set
  ${A & \sim B, C, \sim A \lor \sim B }$.

  ![basic-closed-tree 1.svg](basic-closed-tree%201.svg)

  ### Interpretation

  - The two molecular set members are decomposed. The disjunction (line 3) results
    in a branching tree. The conjunction (line 1) results in the continuation of
    the trunk.
  - Both branches are completed making it a completed tree. As each branch is
    closed this is a closed tree.

  As this is a closed tree, the set is not truth-functionally consistent. This is
  confirmed by the truth table where there is no partial assignment where all set
  members are true.

  ```
  A	B	C				A	&	~	B				C				~	A	∨	~	C
  T	T	T					F						T						F
  T	T	F					F						F						T
  T	F	T					T						T						F
  T	F	F					T						F						T
  F	T	T					F						T						T
  F	T	F					F						F						T
  F	F	T					F						T						T
  F	F	F					F						F						T
  ```

  ## Truth tree decomposition rules

  ---

  So far we have encountered the decomposition rules for conjunction (`&D`) and
  disjunction (`vD`). We will now list all the rules. We will see that for each
  rule, the decomposition either branches or does not branch which is to say that
  each rule either has the shape of a conjunction or a disjunction (however the
  permitted values of the specific disjuncts/conjuncts obviously differ in each
  case). Moreover there is a parallel rule for the decomposition of the negation
  of each of the main connectives and these rules rely on logical equivalences

  ### Negated negation decomposition: `~~D`

  ![negated-negation-decomposition-rule 2.svg](negated-negation-decomposition-rule%202.svg)

  Truth passes only if $P$ is true

  ### Conjunction decomposition: `&D`

  ![conjunction-decomposition-rule.svg](conjunction-decomposition-rule.svg)

  Truth passes only $P$ and $Q$ are both true.

  ### Negated Conjunction decomposition: `~&D`

  ![negated-conjunction-decomposition-rule.svg](negated-conjunction-decomposition-rule.svg)

  Truth passes if either $\sim P$ or $\sim Q$ is true. This rule is a consequence
  of the equivalence between $\sim (P & Q)$ and $\sim P \lor \sim Q$ , the first
  of DeMorgan’s Laws.

  ### Disjunction decomposition: `vD`

  ![disjunction-decomposition-rule.svg](disjunction-decomposition-rule.svg)

  Truth passes if either $P$or $Q$ are true.

  ### Negated Disjunction decomposition: `~vD`

  ![negated-disjunction-decomposition-rule.svg](negated-disjunction-decomposition-rule.svg)

  Truth passes if both $P$ and $Q$ are false. This rule is a consequence of the
  equivalence between $\sim (P \lor Q)$ and $\sim P & \sim Q$, the second of
  DeMorgan’s Laws.

  ### Conditional decomposition: `⊃D`

  ![conditional-decomposition-rule.svg](conditional-decomposition-rule.svg)

  Truth passes if either $\sim P$ or $Q$ are true. This rule is a consequence of
  the equivalence between $P \supset Q$ and $\sim P \lor Q$ therefore this branch
  has the shape of a disjunction with $\sim P$ , $Q$ as its disjuncts.

  ### Negated Conditional decomposition: `~⊃D`

  Truth passes if both $P$ and $\sim Q$ are true. This is a consequence of the
  equivalence between $\sim (P \supset Q)$ and $P & \sim Q$.

  ![negated-conditional-decomposition-rule.svg](negated-conditional-decomposition-rule.svg)

  ### Biconditional decomposition: `≡D`

  ![biconditional-decomposition-rule.drawio(1).svg](biconditional-decomposition-rule.drawio%281%29.svg)

  Truth passes if either $P$ and $Q$ are true or $\sim P & \sim Q$ are true. This
  is an interesting rule because it combines the disjunction and conjunction tree
  shapes.

  ### Negated biconditional decomposition: `~≡D`

  ![negated-biconditional-decomposition-rule.drawio.svg](negated-biconditional-decomposition-rule.drawio.svg)

  Truth passes if either $P$ and $\sim Q$ is true or if $\sim P$ and $Q$ is true.

  ## Further examples and heuristics for complex truth trees

  With truth-trees regardless of which order you decompose the set members, the
  conclusion should always be the same. This said, there more are more efficient
  ways than others to construct the trees. You want to find the route that will
  demonstrate consistency or non-consistency with the shortest amount of work. The
  following heuristic techniques followed in order, facilitate this:

  1. Decompose those molecular sentences the decomposition of which does not
     produce new branches. In other words that are decompositions of double
     negations or pure conjunctions.
  1. Perform those decompositions that will rapidly generate closed branches.
  1. If neither (1) or (2) is applicable, decompose **the most complex** sentence
     first.

  Here are some examples of these rules applied:

  ![complex-tree.svg](complex-tree.svg)

  Observe that here we don’t bother to decompose the sentence on line 1. This is
  because, having decomposed the sentences on lines 2 and 3 we have arrived at a
  closed tree. It is therefore unnecessary to go any further for if two sentences
  in the set are inconsistent with each other, adding another sentence is not
  going to change the overall assignment of inconsistency.

  ## Deriving properties other than logical consistency from truth trees

  So far truth trees have been discussed purely in terms of logical consistency
  however they can be used to derive all the other key truth-functional properties
  of propositional logic. Given the foundational role of consistency to logic,
  these properties are expressible in terms of consistency which is what makes
  them amenable to formulation in terms of truth trees.

  ### Logical falsity

  For a given finite set $\Gamma$, $\Gamma$ is logically consistent just if all of
  its members can be true at once. Expressed in terms of truth trees, this is
  equivalent to an open tree. Contrariwise, $\Gamma$ is inconsistent if it is not
  possible for every member of the set to be true at once. This is the same as a
  tree where all of the branches are closed (i.e. a closed tree).

  When we wish to assess
  [logical falsity](Logical%20truth%20and%20falsity.md#logical-falsity) we are not
  focused on sets however, we are interested in a property of a sentence. However
  we can easily construe single sentences as unit sets: sets with a single member.
  With this in mind and the above accounts of consistency and logical falsity we
  are equipped to express logical falsity in terms of truth-trees with the
  following rule:

  > A sentence $P$ is logically false if and only if the unit set ${ P }$ has a
  > closed tree

  A logically false sentence cannot be true on any assignment. This is the same
  thing as an inconsistent set. Thus it will be represented in a truth tree as
  inconsistency which is disclosed via a closed tree.

  ![logical-falsity-tree.svg](logical-falsity-tree.svg)

  ### Logical truth

  For a sentence $P$ to be
  [logically true](Logical%20truth%20and%20falsity.md#logical-truth), there must
  be no possible assignment in which $P$ is false. We express this informally by
  saying _it is not possible to consistently deny $P$._ We know that in terms of
  truth trees an inconsistent set is a closed tree therefore a unit set of ${ P }$
  is logically true if ${ \sim P }$ is a closed tree. This is to say: if the
  negation of $P$ is inconsistent.

  > A sentence $P$ is logically true if and only if the set ${ \sim P }$ has a
  > closed tree

  ### Logical indeterminacy

  [Indeterminacy](Indeterminacy.md) follows from the two definitions above; we do
  not require any additional apparatus. We recall that a sentence $P$ is logically
  indeterminate just if it is neither logically true or logically false. Thus the
  truth tree for an indeterminate sentence is straightforward:

  > A sentence $P$ is logically indeterminate if and only if neither the set
  >  ${ P }$ nor the set ${ \sim P }$ has a closed tree

  This follows because a closed tree for  ${ P }$ means it is not logically false
  and an open tree for ${ \sim P }$ means it is not logically true. So if it is
  neither of these things, $P$ must be indeterminate.

  ### Logical equivalence

  Recall that $P$ and $Q$ are [logically equivalent](Logical%20equivalence.md)
  just if there is no truth assignment on which one is true and the other is
  false. We know from the
  [material biconditional shorthand](Corresponding%20material%20and%20biconditional.md#corresponding-material-biconditional)
  that this state of affairs can be expressed as $P \equiv Q$ and that if this
  compound sentence is true on every assignment then both simple sentences are
  equivalent. But ‘true on every assignment’ is another way of saying _logically
  true_ since there is no possibility of a false assignment. We already know what
  logical truth looks like as a truth tree: it is a closed tree for the negation
  of the sentence being tested. Therefore, to test the logical equivalence of two
  sentences it is necessary to construct a truth tree for the negation of the
  sentences conjoined by the biconditional (i.e. $\sim (P \equiv Q)$ )and see if
  this results in a closed tree. If it does, the two sentences are logically
  equivalent.

  > Sentences $P$ and $Q$ are truth-functionally equivalent if and only if the set
  > $\sim (P \equiv Q)$ has a closed tree

  ![logical-equivalence-tree.svg](logical-equivalence-tree.svg)

  ### Logical entailment and validity

  Let’s remind ourselves of the meaning of truth-functional
  [entailment](Validity%20and%20entailment.md#entailment) and
  [validity](Validity%20and%20entailment.md#validity) and the relation between the
  two. $\Gamma$ $\vdash$ $P$ is true if and only if there is no truth-assignment
  in which every member of $\Gamma$ is true and $P$ is false. Entailment is
  closely related to validity; it is really just a matter of emphasis: we say that
  $\Gamma$ are the premises and $P$ is the conclusion and that this is a valid
  argument if there is no assignment in which every member of $\Gamma$ is true and
  $P$ is false.

  As with the previous properties, to express validity and entailment in terms of
  truth trees we need to express these concepts in the language of logical
  consistency. $\Gamma$ entails $P$ just if one cannot consistently assert
  $\Gamma$ whilst denying $P$. This is to say that the set $\Gamma \cup {\sim P}$
  is inconsistent. So we just need a closed truth tree for $\Gamma \cup {\sim P}$
  to demonstrate the validity of this set.

  > A finite set of sentences $\Gamma$ truth-functionally entails a sentence $P$
  > if and only if the set $\Gamma \cup {\sim P}$ has a closed truth tree.

  > An argument is truth functionally valid if and only if the set consisting of
  > the premises and the negation of the conclusion has a closed truth tree.


path: Tuples_in_Python.md
sortable_path: Tuples_in_Python.md
title: Tuples in Python
lead: TODO: Exapand tuple notes - give more use cases
checksum: 030111bc7de9da55f9dfd32fbbf6212bc9095e33ae0e9729bc59f02b2410c4b6
created: 2024-10-22 16:52:59.69650903+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["python","data-structures"]}
body:
  TODO: Exapand tuple notes - give more use cases

  Tuples are one of the main data-structures or containers in Python. Tuples are
  useful in cases where you want to group related data and ensure that it will not
  change.

  Tuples have the following properties:

  - They are **ordered**
  - They have a **fixed size**
  - They are **immutable** and cannot be modified
  - **Allow duplicate** members
  - They are **indexed**

  > In essence a tuple is a list that is immutable.

  As with all containers in Python they permit any data type.

  > Tuples are denoted with `(...)`

  ## Basic usage

  ```python
  tup1 = (1, 3, 5, 7)
  print(tup1[2])

  # 5
  """
  ```

  ## Slicing

  ```python
  tup1 = (1, 3, 5, 7)

  print(tup1[1:3])
  print(tup1[:3])
  print(tup1[1:])
  print(tup1[::-1])

  """
  (3, 5)
  (1, 3, 5)
  (3, 5, 7)
  (7, 5, 3, 1)
  """
  ```

  ## Looping

  ```python
  tup3 = ('apple', 'pear', 'orange', 'plum', 'apple')
  for x in tup3:
      print(x)

  """
  apple
  pear
  orange
  plum
  apple
  """
  ```

  ## Useful methods and predicates

  ```python
  tup3 = ('apple', 'pear', 'orange', 'plum', 'apple')

  # Count instances of a member
  print(tup3.count('apple'))
  # 2

  # Get index of a member
  print(tup3.index('pear'))
  # 1

  # Check for membership
  if 'orange' in tup3:
      print('orange is in the Tuple')

  # orange is in the Tuple
  ```

  ## Nest tuples

  ```python

  tuple2 = ('John', 'Denise', 'Phoebe', 'Adam')
  tuple3 = (42, tuple1, tuple2, 5.5)
  print(tuple3)

  # (42, (1, 3, 5, 7), ('John', 'Denise', 'Phoebe', 'Adam'), 5.5)

  ```

  // TODO: How to flatten a tuple?

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

  # Tuples in Python

  TODO: Exapand tuple notes - give more use cases

  Tuples are one of the main data-structures or containers in Python. Tuples are
  useful in cases where you want to group related data and ensure that it will not
  change.

  Tuples have the following properties:

  - They are **ordered**
  - They have a **fixed size**
  - They are **immutable** and cannot be modified
  - **Allow duplicate** members
  - They are **indexed**

  > In essence a tuple is a list that is immutable.

  As with all containers in Python they permit any data type.

  > Tuples are denoted with `(...)`

  ## Basic usage

  ```python
  tup1 = (1, 3, 5, 7)
  print(tup1[2])

  # 5
  """
  ```

  ## Slicing

  ```python
  tup1 = (1, 3, 5, 7)

  print(tup1[1:3])
  print(tup1[:3])
  print(tup1[1:])
  print(tup1[::-1])

  """
  (3, 5)
  (1, 3, 5)
  (3, 5, 7)
  (7, 5, 3, 1)
  """
  ```

  ## Looping

  ```python
  tup3 = ('apple', 'pear', 'orange', 'plum', 'apple')
  for x in tup3:
      print(x)

  """
  apple
  pear
  orange
  plum
  apple
  """
  ```

  ## Useful methods and predicates

  ```python
  tup3 = ('apple', 'pear', 'orange', 'plum', 'apple')

  # Count instances of a member
  print(tup3.count('apple'))
  # 2

  # Get index of a member
  print(tup3.index('pear'))
  # 1

  # Check for membership
  if 'orange' in tup3:
      print('orange is in the Tuple')

  # orange is in the Tuple
  ```

  ## Nest tuples

  ```python

  tuple2 = ('John', 'Denise', 'Phoebe', 'Adam')
  tuple3 = (42, tuple1, tuple2, 5.5)
  print(tuple3)

  # (42, (1, 3, 5, 7), ('John', 'Denise', 'Phoebe', 'Adam'), 5.5)

  ```

  // TODO: How to flatten a tuple?


path: Turing_completeness.md
sortable_path: Turing_completeness.md
title: Turing Completeness
lead:
  We know that a [Turing machine](Turing_machines.md) is a theoretical construct
  of a computer that:

checksum: 6955170d1e0c4ae74beaea68d24b8d72bbe977f01608376028bdbd8b888f430e
created: 2024-10-22 16:52:59.705019576+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"created":"Friday, September 13, 2024","tags":["theory-of-computation","Turing"]}
body:
  We know that a [Turing machine](Turing_machines.md) is a theoretical construct
  of a computer 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.)

  A Turing Complete (TC) system is a system that abides by, or can be reduced to,
  the above description.

  TC also serves as a _definition of computability_ and provides a formal basis
  for conceiving of computation at a theoretical level.

  All Turing Complete systems are functionally equivalent. This means they can
  simulate each other given enough time and memory. Similarly a TC system can in
  principle perform any computation that any other programmable computer can
  perform. This is true for _other_ TC systems and also those that are not TC
  however the inverse doesn't hold: a non-TC system cannot emulate a TS system.
  For instance a calculator cannot do what a TC smart phone can do. But a smart
  phone can act as a calculator.

  This concept of completeness is also expressed in terms of a Universal Turing
  Machine - a TM capable of simulating any other Turing machine. Given that the
  ability of a TM to simulate another TM is the condition for completeness, a TC
  system is also a UTM.

  Completeness applies to the hardware of computers as well as their software.

  Turing Completeness is the theoretical basis of the practical concept of a
  "general-purpose computer": a general-purpose computer is such because it is
  TC - it can in theory compute anything that is computable.

  Most modern programming languages are Turing Complete in that they can, in
  theory, be used to compute anything that is computable.

raw_content:
  ---
  tags: [theory-of-computation, Turing]
  created: Friday, September 13, 2024
  ---

  # Turing Completeness

  We know that a [Turing machine](Turing_machines.md) is a theoretical construct
  of a computer 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.)

  A Turing Complete (TC) system is a system that abides by, or can be reduced to,
  the above description.

  TC also serves as a _definition of computability_ and provides a formal basis
  for conceiving of computation at a theoretical level.

  All Turing Complete systems are functionally equivalent. This means they can
  simulate each other given enough time and memory. Similarly a TC system can in
  principle perform any computation that any other programmable computer can
  perform. This is true for _other_ TC systems and also those that are not TC
  however the inverse doesn't hold: a non-TC system cannot emulate a TS system.
  For instance a calculator cannot do what a TC smart phone can do. But a smart
  phone can act as a calculator.

  This concept of completeness is also expressed in terms of a Universal Turing
  Machine - a TM capable of simulating any other Turing machine. Given that the
  ability of a TM to simulate another TM is the condition for completeness, a TC
  system is also a UTM.

  Completeness applies to the hardware of computers as well as their software.

  Turing Completeness is the theoretical basis of the practical concept of a
  "general-purpose computer": a general-purpose computer is such because it is
  TC - it can in theory compute anything that is computable.

  Most modern programming languages are Turing Complete in that they can, in
  theory, be used to compute anything that is computable.


path: Turing_machines.md
sortable_path: Turing_machines.md
title: Turing Machines
lead: ## What is a Turing Machine?
checksum: 77c4c42bdf565593e4df5a982bb47a1527efcfc69dd5cf0904e13b214dea73d8
created: 2024-10-22 16:52:59.720685906+00:00
modified: 2024-10-19 09:00:57.414852952+00:00
metadata: {"tags":["theory-of-computation","Turing"]}
body:
  ## What is a Turing Machine?

  Turing Machine is a machine that contains mutable state, executes sequences of
  simple instructions that read and write that state, and can pick different
  execution paths depending on the state (via conditional branch instructions.)

  This is a general expression, it is typically expanded as follows.

  The machine consists of an infinitely long tape, that has been divided up into
  cells. Each cell can contain either a 1, a 0 or an empty space. Above one cell
  of the tape is a head, which can either move left or right, and can read the
  symbols written in the cells. The head is also capable of erasing symbols and
  writing new symbols into the cells.

  ![Turing_machines_01.gif](Turing_machines_01.gif)

  The direction that the head moves, which values it erases, and which values it
  writes in, are dependent on a set of instructions provided to the machine.

  Different sets of instructions can be divided into **states.** States are like
  sub-routines and can themselves feature as part of instructions.

  For example:

  ### State 2

  - If 0 then erase
  - Write 1 then move right
  - Go to state 5

  ### State 5

  - If 1, then erase
  - Write 0 then move left
  - Go to state _n_

  Alan Turing proved that **any problem that is computable** can be computed by a
  Turing Machine using this simple system.

raw_content:
  ---
  tags:
    - theory-of-computation
    - Turing
  ---

  # Turing Machines

  ## What is a Turing Machine?

  Turing Machine is a machine that contains mutable state, executes sequences of
  simple instructions that read and write that state, and can pick different
  execution paths depending on the state (via conditional branch instructions.)

  This is a general expression, it is typically expanded as follows.

  The machine consists of an infinitely long tape, that has been divided up into
  cells. Each cell can contain either a 1, a 0 or an empty space. Above one cell
  of the tape is a head, which can either move left or right, and can read the
  symbols written in the cells. The head is also capable of erasing symbols and
  writing new symbols into the cells.

  ![Turing_machines_01.gif](Turing_machines_01.gif)

  The direction that the head moves, which values it erases, and which values it
  writes in, are dependent on a set of instructions provided to the machine.

  Different sets of instructions can be divided into **states.** States are like
  sub-routines and can themselves feature as part of instructions.

  For example:

  ### State 2

  - If 0 then erase
  - Write 1 then move right
  - Go to state 5

  ### State 5

  - If 1, then erase
  - Write 0 then move left
  - Go to state _n_

  Alan Turing proved that **any problem that is computable** can be computed by a
  Turing Machine using this simple system.


path: Twos_complement.md
sortable_path: Twos_complement.md
title: Two's complement
lead: ## Summary
checksum: 6fb7bb890f9352267a3cd807838ba5cc81c5affae5d5d6fc0528d34dde324d4d
created: 2024-10-22 16:52:59.729059059+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"created":"Tuesday, March 19, 2024","id":"gktb","tags":["binary"]}
body:
  ## Summary

  - _Two's complement_ is a method for representing signed numbers (negative
    integers) in binary.

  - It is derived by inverting the values of an unsigned binary integer to create
    its signed equivalent.

  - A benefit is that hardware implementation of the binary arithmetic of signed
    and unsigned numbers can be handled in the same manner as unsigned numbers,
    requiring no additional handling. A drawback is that it halves the
    informational capacity of the given word length for the binary system.

  ## Detail

  ### Procedural steps

  Two's complement divides the available word length (see
  [[Binary_encoding|binary encoding]]) into two subsets: one for negative integrs
  and one for positive integers.

  Take the binary encoding of decimal five (`0101`). Its complement is `1011`.

  The procedure for deriving the complement is as follows.

  To derive the complement of an unsigned number:

  1. Take the unsigned number and invert its digits: `0` becomes `1`, `1` becomes
     `0`
  2. Add one

  ![](/img/unsigned-to-signed.png)

  To derive the unsigned equivalent of a signed number you invert the process but
  still make the smallest digit `1`:

  ![](/img/signed-to-unsigned.png)

  ### Formal expression

  $$
      2^n - x
  $$

  - where $x$ is the negative integer in binary that we wish to derive
  - where $n$ is the word length of the binary system in bits.

  Applied to the earlier example we have $2^4 -5$ which is:

  $$
      16 - 5 = 11
  $$

  When we convert the decimal `11` to binary we get `1011` which is identical to
  the signed version of the unsigned integer.

  We can confirm the correctness of the derviation by summing the signed and
  unsigned binary values. If this results in zeros (ignoring the overflow bit),
  the derivation is correct as the two values effectively cancel each other out:

  $$

    1011 + 0101 = 0000
  $$

  ### Advantages

  - The circuit implementation of arithmetic involving positive and negative
    integers is the same as the implementation of positive integers. There is no
    need for additional harware or special handling of the values.
  - This can be contrasted with the alternative approaches to signing numbers such
    as **signed magnitude representation** which uses certain bits as designators
    of negative/positive status.

  ### Limitations

  - Two's complement reduces the overall informational capacity of the given
    binary word length, effectively halving the total number of unique values.

  - In a 4-bit system instead of 16 total unique encodings of integers you have 8
    encodings for positive integers and 8 encodings for the their signed
    equivalent. For integers larger than denary 8 you would need to increase the
    bit length of the system

  - Consequently two's complement can necessitate larger overall word lengths.

  ## Related notes

  [[Signed_and_unsigned_numbers|signed_and_unsigned_numbers]],
  [[Binary_addition|binary addition]], [[Signed_magnitude_representation]]

raw_content:
  ---
  id: gktb
  tags:
    - binary
  created: Tuesday, March 19, 2024
  ---

  # Two's complement

  ## Summary

  - _Two's complement_ is a method for representing signed numbers (negative
    integers) in binary.

  - It is derived by inverting the values of an unsigned binary integer to create
    its signed equivalent.

  - A benefit is that hardware implementation of the binary arithmetic of signed
    and unsigned numbers can be handled in the same manner as unsigned numbers,
    requiring no additional handling. A drawback is that it halves the
    informational capacity of the given word length for the binary system.

  ## Detail

  ### Procedural steps

  Two's complement divides the available word length (see
  [[Binary_encoding|binary encoding]]) into two subsets: one for negative integrs
  and one for positive integers.

  Take the binary encoding of decimal five (`0101`). Its complement is `1011`.

  The procedure for deriving the complement is as follows.

  To derive the complement of an unsigned number:

  1. Take the unsigned number and invert its digits: `0` becomes `1`, `1` becomes
     `0`
  2. Add one

  ![](/img/unsigned-to-signed.png)

  To derive the unsigned equivalent of a signed number you invert the process but
  still make the smallest digit `1`:

  ![](/img/signed-to-unsigned.png)

  ### Formal expression

  $$
      2^n - x
  $$

  - where $x$ is the negative integer in binary that we wish to derive
  - where $n$ is the word length of the binary system in bits.

  Applied to the earlier example we have $2^4 -5$ which is:

  $$
      16 - 5 = 11
  $$

  When we convert the decimal `11` to binary we get `1011` which is identical to
  the signed version of the unsigned integer.

  We can confirm the correctness of the derviation by summing the signed and
  unsigned binary values. If this results in zeros (ignoring the overflow bit),
  the derivation is correct as the two values effectively cancel each other out:

  $$

    1011 + 0101 = 0000
  $$

  ### Advantages

  - The circuit implementation of arithmetic involving positive and negative
    integers is the same as the implementation of positive integers. There is no
    need for additional harware or special handling of the values.
  - This can be contrasted with the alternative approaches to signing numbers such
    as **signed magnitude representation** which uses certain bits as designators
    of negative/positive status.

  ### Limitations

  - Two's complement reduces the overall informational capacity of the given
    binary word length, effectively halving the total number of unique values.

  - In a 4-bit system instead of 16 total unique encodings of integers you have 8
    encodings for positive integers and 8 encodings for the their signed
    equivalent. For integers larger than denary 8 you would need to increase the
    bit length of the system

  - Consequently two's complement can necessitate larger overall word lengths.

  ## Related notes

  [[Signed_and_unsigned_numbers|signed_and_unsigned_numbers]],
  [[Binary_addition|binary addition]], [[Signed_magnitude_representation]]


path: Type_guarding_and_narrowing_in_TS.md
sortable_path: Type_guarding_and_narrowing_in_TS.md
title: Type narrowing and guarding
checksum: 490017e14d24edb1d0dbb214c845e7c0f56679e29856822fd555a7f4d46e88b5
created: 2024-10-22 16:52:59.755872043+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["typescript"]}
lead:
  Type narrowing is the process of working out from a supertype like
  [any](Any.md) or [unknown](Unknown_type_in_TS.md) whic type the value should be in the
  x course of your code. This is necessary since a process will not necessarily
  always return or involve homogenous types.

body:
  Type narrowing is the process of working out from a supertype like
  [any](Any.md) or [unknown](Unknown_type_in_TS.md) whic type the value should be in the
  x course of your code. This is necessary since a process will not necessarily
  always return or involve homogenous types.

  Related to this is type guarding: ensuring that a value is of the suitable type
  as a factor of control flow. For instance using typeof to ensure that an input
  is numerical before proceeding with a function's logic.

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

  # Type narrowing and guarding

  Type narrowing is the process of working out from a supertype like
  [any](Any.md) or [unknown](Unknown_type_in_TS.md) whic type the value should be in the
  x course of your code. This is necessary since a process will not necessarily
  always return or involve homogenous types.

  Related to this is type guarding: ensuring that a value is of the suitable type
  as a factor of control flow. For instance using typeof to ensure that an input
  is numerical before proceeding with a function's logic.


path: Type_hinting.md
sortable_path: Type_hinting.md
title: Type hinting in Python
checksum: 2edd704a1bbc701030556aa35823683193b46331daf39f1bed8c62a50c08a78b
created: 2024-10-22 16:52:59.772431378+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["python","data-types"]}
lead:
  With type hinting we can add type information to variables, functions, and
  classes. This is not enforced by the Python interpreter but can be used by
  external tools like `mypy` to check the code.

body:
  With type hinting we can add type information to variables, functions, and
  classes. This is not enforced by the Python interpreter but can be used by
  external tools like `mypy` to check the code.

  Prior to 3.10, to use types you would have to import the types you wanted `from`
  the `typing` module. With later versions you can use them directly without
  importing from `typing`.

  ## Basic syntax

  ### Variables

  ```py
  name: str = "John"
  age: int = 30
  ```

  ### Functions

  ```py
  def greet(name: str) -> str:
      return "Hello, " + name
  ```

  ### Classes

  // TODO: Add examples

  ## Complex data types

  We can type complex data types such as dictionaries, lists, tuples etc.

  The following function takes a tuple comprising two integers as a parameter and
  returns a list of ints:

  ```py
  def populate_pair_values(pair: tuple[int, int]) -> list[int]:
      return [i for i in range(pair[0], pair[1] + 1)]
  ```

  ## Custom dictionary types

  We can create custom dictionary types in the manner of TypeScript types and
  interfaces as follows:

  ```py
  class ArticleInfo(TypedDict):
      timestamp: str
      article_    link: str
  ```

  We could then use it thus:

  ```py
  def parse_articles() -> list[ArticleInfo]:
  ```

  ## Optional types

  `Optional` can be used to indicate that a variable can be `None` or the
  specified type.

  ```py
  from typing import Optional

  def find_index(numbers: list[int], target: int) -> Optional[int]:
      try:
          return numbers.index(target)
      except ValueError:
          return None
  ```

  The function above returns an `int` or `None`.

  Post 3.10, we don't need to use `Optional`, we can use a union to cover the
  `None` case. Refactoring the previous example:

  ```py
  def find_index(numbers: list[int], target: int) -> int | None:
      try:
          return numbers.index(target)
      except ValueError:
          return None
  ```

  ## Union types

  `Union` can be used to indicate that a variable can be one of several types.

  ```py
  from typing import Union

  def add(a: Union[int, float], b: Union[int, float]) -> Union[int, float]:
      return a + b
  ```

  The function above returns an `int` or `float`.

  > Note that in the case of `Optional` and `Union` the type is specified using
  > square brackets. This is a syntactic covnention and does not denote that the
  > return type is a list. Below is an example of how we would use type hinting to
  > indicate that the return type actually is a list:

  ```py
  from typing import List, Union
  def my_function() -> List[int]:
      return [1, 2, 3]

  # And to show a return type of a list of integers or floats:

  def my_function() -> List[Union[int, float]]:
      return [1, 2, 3]
  ```

  Post 3.10 we can use `|` syntax instead of importing `Union`. E.g:

  ```py

  def add(a: int | float, b: int | float) -> int | float:
      return a + b
  ```

raw_content:
  ---
  tags:
    - python
    - data-types
  ---

  # Type hinting in Python

  With type hinting we can add type information to variables, functions, and
  classes. This is not enforced by the Python interpreter but can be used by
  external tools like `mypy` to check the code.

  Prior to 3.10, to use types you would have to import the types you wanted `from`
  the `typing` module. With later versions you can use them directly without
  importing from `typing`.

  ## Basic syntax

  ### Variables

  ```py
  name: str = "John"
  age: int = 30
  ```

  ### Functions

  ```py
  def greet(name: str) -> str:
      return "Hello, " + name
  ```

  ### Classes

  // TODO: Add examples

  ## Complex data types

  We can type complex data types such as dictionaries, lists, tuples etc.

  The following function takes a tuple comprising two integers as a parameter and
  returns a list of ints:

  ```py
  def populate_pair_values(pair: tuple[int, int]) -> list[int]:
      return [i for i in range(pair[0], pair[1] + 1)]
  ```

  ## Custom dictionary types

  We can create custom dictionary types in the manner of TypeScript types and
  interfaces as follows:

  ```py
  class ArticleInfo(TypedDict):
      timestamp: str
      article_    link: str
  ```

  We could then use it thus:

  ```py
  def parse_articles() -> list[ArticleInfo]:
  ```

  ## Optional types

  `Optional` can be used to indicate that a variable can be `None` or the
  specified type.

  ```py
  from typing import Optional

  def find_index(numbers: list[int], target: int) -> Optional[int]:
      try:
          return numbers.index(target)
      except ValueError:
          return None
  ```

  The function above returns an `int` or `None`.

  Post 3.10, we don't need to use `Optional`, we can use a union to cover the
  `None` case. Refactoring the previous example:

  ```py
  def find_index(numbers: list[int], target: int) -> int | None:
      try:
          return numbers.index(target)
      except ValueError:
          return None
  ```

  ## Union types

  `Union` can be used to indicate that a variable can be one of several types.

  ```py
  from typing import Union

  def add(a: Union[int, float], b: Union[int, float]) -> Union[int, float]:
      return a + b
  ```

  The function above returns an `int` or `float`.

  > Note that in the case of `Optional` and `Union` the type is specified using
  > square brackets. This is a syntactic covnention and does not denote that the
  > return type is a list. Below is an example of how we would use type hinting to
  > indicate that the return type actually is a list:

  ```py
  from typing import List, Union
  def my_function() -> List[int]:
      return [1, 2, 3]

  # And to show a return type of a list of integers or floats:

  def my_function() -> List[Union[int, float]]:
      return [1, 2, 3]
  ```

  Post 3.10 we can use `|` syntax instead of importing `Union`. E.g:

  ```py

  def add(a: int | float, b: int | float) -> int | float:
      return a + b
  ```


path: Union_types_in_TS.md
sortable_path: Union_types_in_TS.md
title: Union types
checksum: c821ae262bc36f541ebebc61d22c3d5bd95bb51fa3dd7b44522c80fe2956ee3f
created: 2024-10-22 16:52:59.780584098+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["typescript"]}
lead:
  Like intersection types, union types allow us to combine the properties of
  existing primitive and custom types to make composites.

body:
  Like intersection types, union types allow us to combine the properties of
  existing primitive and custom types to make composites.

  Intersection types work well when you have complete knowledge of what form your
  data will take. This approach only works if you know in advance that your data
  will be and that it will share properties from more than one type. But what if
  you don't have this knowledge? You only know that it will be one of the three
  possible types? This is where union types become helpful. Instead of saying that
  event X will be of type A we say that event X may be one of types A or B or C.

  > A union type is a type formed from two or more other types, representing
  > values that may be any one of those types. We refer to each of these types as
  > the union’s members.

  ## Demonstration

  ```ts
  type Petrol = {
    maxSpeed: number;
    tankVolume: number; // unique property
  };

  type Electric = {
    maxSpeed: number;
    maxCharge: number; // unique property
  };

  type Car = Petrol | Electric;

  const tesla: Car = {
    maxSpeed: 120,
    maxCharge: 22,
  };
  ```

  - We have defined two custom types: `Petrol` and `Electric`
  - We have unified them to make a new custom type of `Car`
  - `tesla` is an instance of the union type

  ## Usage with functions

  Union type syntax is not limited to the declaration phase. You can also use them
  as parameters to functions or function return types:

  ```ts
  function generateCar(car: Petrol | Electric): void {
    console.log(`The car has a max speed of ${car.maxSpeed}`);
  }
  ```

  ## Unifying primitive and custom types

  We can also use unions with primitive types as well as custom types:

  ```ts
  function printId(id: number | string): void {
    console.log(`Your id is ${id}`);
  }
  ```

  We can also combine primitive and custom types in the same union:

  ```ts
  function printStringOrNumber(): string | Petrol {}
  ```

  ## Set theory: understanding errors

  In JavaScript we have the primitive types of string , number , boolean etc. In
  TS, these types are distinct and exclusive: if you declare that x : string you
  cannot redefine it as boolean or expect Boolean properties and methods to belong
  to a string type. For instance a string type is incapable of having as its value
  false.

  This is not true in the case of unions.

  With a union of `string | boolean` a value `x` could be `'banana'` or `true`. We
  are allowing for all values that are either from the set string or the set
  boolean. Logically this is equivalent to the OR operator: a value of type
  `string | boolean` could be one of infinite possible strings or true/false. With
  union types then, we radically widen the scope of possible values for the type.
  a

  These constraints don't just apply to the values themselves but the methods and
  properties you can use with the values. For instance a boolean doesn't have the
  property of `length`. Therefore if you have a union type of `string|boolean` you
  are going to get an error if you apply `length` to it since **the property must
  exist on both types that comprise the union**. The way round this is to use type
  narrowing and execute control flow based on the parameter that is passed to a
  function that is declared to be of this mixed type.

  Here is an example of this:

  ```ts
  function printId(id: number | string) {
    if (typeof id === "string") {
      // In this branch, id is of type 'string'
      console.log(id.toUpperCase());
    } else {
      // Here, id is of type 'number'
      console.log(id);
    }
  }
  ```

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

  # Union types

  Like intersection types, union types allow us to combine the properties of
  existing primitive and custom types to make composites.

  Intersection types work well when you have complete knowledge of what form your
  data will take. This approach only works if you know in advance that your data
  will be and that it will share properties from more than one type. But what if
  you don't have this knowledge? You only know that it will be one of the three
  possible types? This is where union types become helpful. Instead of saying that
  event X will be of type A we say that event X may be one of types A or B or C.

  > A union type is a type formed from two or more other types, representing
  > values that may be any one of those types. We refer to each of these types as
  > the union’s members.

  ## Demonstration

  ```ts
  type Petrol = {
    maxSpeed: number;
    tankVolume: number; // unique property
  };

  type Electric = {
    maxSpeed: number;
    maxCharge: number; // unique property
  };

  type Car = Petrol | Electric;

  const tesla: Car = {
    maxSpeed: 120,
    maxCharge: 22,
  };
  ```

  - We have defined two custom types: `Petrol` and `Electric`
  - We have unified them to make a new custom type of `Car`
  - `tesla` is an instance of the union type

  ## Usage with functions

  Union type syntax is not limited to the declaration phase. You can also use them
  as parameters to functions or function return types:

  ```ts
  function generateCar(car: Petrol | Electric): void {
    console.log(`The car has a max speed of ${car.maxSpeed}`);
  }
  ```

  ## Unifying primitive and custom types

  We can also use unions with primitive types as well as custom types:

  ```ts
  function printId(id: number | string): void {
    console.log(`Your id is ${id}`);
  }
  ```

  We can also combine primitive and custom types in the same union:

  ```ts
  function printStringOrNumber(): string | Petrol {}
  ```

  ## Set theory: understanding errors

  In JavaScript we have the primitive types of string , number , boolean etc. In
  TS, these types are distinct and exclusive: if you declare that x : string you
  cannot redefine it as boolean or expect Boolean properties and methods to belong
  to a string type. For instance a string type is incapable of having as its value
  false.

  This is not true in the case of unions.

  With a union of `string | boolean` a value `x` could be `'banana'` or `true`. We
  are allowing for all values that are either from the set string or the set
  boolean. Logically this is equivalent to the OR operator: a value of type
  `string | boolean` could be one of infinite possible strings or true/false. With
  union types then, we radically widen the scope of possible values for the type.
  a

  These constraints don't just apply to the values themselves but the methods and
  properties you can use with the values. For instance a boolean doesn't have the
  property of `length`. Therefore if you have a union type of `string|boolean` you
  are going to get an error if you apply `length` to it since **the property must
  exist on both types that comprise the union**. The way round this is to use type
  narrowing and execute control flow based on the parameter that is passed to a
  function that is declared to be of this mixed type.

  Here is an example of this:

  ```ts
  function printId(id: number | string) {
    if (typeof id === "string") {
      // In this branch, id is of type 'string'
      console.log(id.toUpperCase());
    } else {
      // Here, id is of type 'number'
      console.log(id);
    }
  }
  ```


path: Unknown_type_in_TS.md
sortable_path: Unknown_type_in_TS.md
title: Unknown
checksum: 8c0f68fad261ac22cf13cf968e917e2308e19247798725d27b71e2be1a1a3da2
created: 2024-10-22 16:52:59.786885495+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["typescript"]}
lead:
  You might think that a good use case for `any` is a scenario where you don't
  know in advance what the data type is going to be.

body:
  You might think that a good use case for `any` is a scenario where you don't
  know in advance what the data type is going to be.

  In order to mark that this is the case, you type the value as `any`. In fact,
  TypeScript provides a type for this specific scenario: `unknown`.

  Like `any`, `unknown` is equivalent to every type in TS (it is also a supertype)
  but it is deliberately inhibiting, in contrast to `any`. When you use `unknown`
  you have to use type narrowing to avoid it being returned. So if your code
  starts with `unknown`, you should filter out scenarios where it evaluates to
  each of the possible types otherwise if `unknown` is returned it will cause an
  error.

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

  # Unknown

  You might think that a good use case for `any` is a scenario where you don't
  know in advance what the data type is going to be.

  In order to mark that this is the case, you type the value as `any`. In fact,
  TypeScript provides a type for this specific scenario: `unknown`.

  Like `any`, `unknown` is equivalent to every type in TS (it is also a supertype)
  but it is deliberately inhibiting, in contrast to `any`. When you use `unknown`
  you have to use type narrowing to avoid it being returned. So if your code
  starts with `unknown`, you should filter out scenarios where it evaluates to
  each of the possible types otherwise if `unknown` is returned it will cause an
  error.


path: Update_a_Mongo_document.md
sortable_path: Update_a_Mongo_document.md
title: Update a MongoDB document
lead: There are two methods for updating a document
checksum: 45e294432528520811c33744708746297fb71bfdc96726da610ef0146c820ba4
created: 2024-10-22 16:52:59.793568869+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["mongo-db","node-js","mongoose","databases"]}
body:
  There are two methods for updating a document

  - query first
  - update first

  ## Query first document update

  With this approach we first execute a
  [query](Querying_a_Mongo_collection.md) to retrieve the document we
  want to edit and then make the change. We use the `findById` method to identify
  the document by its UUID and then `set` to update specified properties on the
  document. The `set` method is one of many operators that can be used to update
  values. For example there is also built-in operators for increment, renaming,
  multiplying values.

  Query first is best used when you want to secure the update with some prior
  logic or to validate. For example you may not want to update a course if it is
  listed as published. You could use a query to determine the publish status and
  then only update the entry if it returns `isPublished: false`.

  ```js
  async function updateCourseFromQuery(id) {
    const course = await Course.findById(id);
    if (!course) return;
    course.set({
      isPublished: true,
      author: "A new author",
    });

    // Instead of an object, we could also set the updated properties individually
    course.isPublished = true;
    course.author = "Biggs Volunksire";

    const result = course.save();
    console.log(result);
  }
  ```

  ## Update first document update

  With this approach we don't bother with a prior query. We are confident that the
  update is legitimate and do not need to first determine that certain conditions
  are met.

  To do this we directly use the `update` method, not `find`:

  ```js
  async function updateCourseFromQuery(id) {
    const result = await Course.update({ _id: id });
    $set: {
      // Invoke the set operator
      author: "Terry Nutile";
      isPublished: true;
    }
    console.log(result);
  }
  ```

  This function will just return some metadata about the update. It won't by
  default return the updated value. To do this use the `findByIdAndUpdate()`
  method instead of `update`:

  ```js
  async function updateCourseFromQuery(id) {
      const course = await Course.findByIdAndUpdate(id, {
      $set: { // Invoke the set operator
          author: 'Terry Nutile'
          isPublished: true
      }, {new: true}});
      console.log(result)
  }
  ```

  If we don't add `{new: true}`, it will return the document before the update.

  ### Updating multiple documents at once

  // Add: notes on this

raw_content:
  ---
  tags:
    - mongo-db
    - node-js
    - mongoose
    - databases
  ---

  # Update a MongoDB document

  There are two methods for updating a document

  - query first
  - update first

  ## Query first document update

  With this approach we first execute a
  [query](Querying_a_Mongo_collection.md) to retrieve the document we
  want to edit and then make the change. We use the `findById` method to identify
  the document by its UUID and then `set` to update specified properties on the
  document. The `set` method is one of many operators that can be used to update
  values. For example there is also built-in operators for increment, renaming,
  multiplying values.

  Query first is best used when you want to secure the update with some prior
  logic or to validate. For example you may not want to update a course if it is
  listed as published. You could use a query to determine the publish status and
  then only update the entry if it returns `isPublished: false`.

  ```js
  async function updateCourseFromQuery(id) {
    const course = await Course.findById(id);
    if (!course) return;
    course.set({
      isPublished: true,
      author: "A new author",
    });

    // Instead of an object, we could also set the updated properties individually
    course.isPublished = true;
    course.author = "Biggs Volunksire";

    const result = course.save();
    console.log(result);
  }
  ```

  ## Update first document update

  With this approach we don't bother with a prior query. We are confident that the
  update is legitimate and do not need to first determine that certain conditions
  are met.

  To do this we directly use the `update` method, not `find`:

  ```js
  async function updateCourseFromQuery(id) {
    const result = await Course.update({ _id: id });
    $set: {
      // Invoke the set operator
      author: "Terry Nutile";
      isPublished: true;
    }
    console.log(result);
  }
  ```

  This function will just return some metadata about the update. It won't by
  default return the updated value. To do this use the `findByIdAndUpdate()`
  method instead of `update`:

  ```js
  async function updateCourseFromQuery(id) {
      const course = await Course.findByIdAndUpdate(id, {
      $set: { // Invoke the set operator
          author: 'Terry Nutile'
          isPublished: true
      }, {new: true}});
      console.log(result)
  }
  ```

  If we don't add `{new: true}`, it will return the document before the update.

  ### Updating multiple documents at once

  // Add: notes on this


path: Update_an_SQL_table.md
sortable_path: Update_an_SQL_table.md
title: Updating an SQL table
checksum: 9645a802e36a0c8433bba7eea42e93ce70bebe748378d2896fd9a1f511bf77c5
created: 2024-10-22 16:52:59.805658961+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["SQL","databases"]}
lead:
  We use the `ALTER` query to add, remove and otherwise change the structural
  properties of a table in the database.

body:
  We use the `ALTER` query to add, remove and otherwise change the structural
  properties of a table in the database.

  ## Add an additional field to existing table

  This adds a `price` field to the `sales` table. The `price` field accepts data
  of the type `real`.

  ```sql
  ALTER TABLE sales ADD price real;
  ```

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

  # Updating an SQL table

  We use the `ALTER` query to add, remove and otherwise change the structural
  properties of a table in the database.

  ## Add an additional field to existing table

  This adds a `price` field to the `sales` table. The `price` field accepts data
  of the type `real`.

  ```sql
  ALTER TABLE sales ADD price real;
  ```


path: Update_existing_data_in_SQL_table.md
sortable_path: Update_existing_data_in_SQL_table.md
title: Update existing data with the SQL UPDATE command
lead: We use `UPDATE` to modify existing records in our table.
checksum: 8d899c4df6b07aa260bf8286610be856b821f2fdc69aaebbd7557a866cf15cf5
created: 2024-10-22 16:52:59.812067856+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["SQL","databases"]}
body:
  We use `UPDATE` to modify existing records in our table.

  ## Schematic syntax

  ```sql
  UPDATE [table_name]
  SET [field]
  WHERE [conditional expression/filter]
  ```

  ## Example

  ```sql
  UPDATE manufacturer
  SET url = '<http://www.hp.co.uk>'
  WHERE manufacturer_id = 4; --typically this will be the primary key as you are updating and existing record and need to identify it uniquely
  ```

  ## Update multiple fields

  ```sql
  UPDATE manufacturer
  SET url = '<http://www.apple.co.uk>',
      year_founded = 1977
  WHERE manufacturer_id = 2;
  ```

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

  # Update existing data with the SQL `UPDATE` command

  We use `UPDATE` to modify existing records in our table.

  ## Schematic syntax

  ```sql
  UPDATE [table_name]
  SET [field]
  WHERE [conditional expression/filter]
  ```

  ## Example

  ```sql
  UPDATE manufacturer
  SET url = '<http://www.hp.co.uk>'
  WHERE manufacturer_id = 4; --typically this will be the primary key as you are updating and existing record and need to identify it uniquely
  ```

  ## Update multiple fields

  ```sql
  UPDATE manufacturer
  SET url = '<http://www.apple.co.uk>',
      year_founded = 1977
  WHERE manufacturer_id = 2;
  ```


path: Useful_operators_in_SQL.md
sortable_path: Useful_operators_in_SQL.md
title: Useful operators in SQL
lead: ## True and false
checksum: 378ed8a4886205275eb46560a2a2acab4e6a008ca30aa641e5a9529d3500620c
created: 2024-10-22 16:52:59.817530145+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["SQL","databases"]}
body:
  ## True and false

  > In SQL false results are represented by `0` and true results are represented
  > by `1`. Any zero value is false and any non-zero value is true.

  > When comparing operators we can use `SELECT` as a general "execute" function
  > for SQL to interpret

  ```sql
  SELECT 0 = 0 -- 1
  SELECT 0.0 = 0 -- 1
  SELECT 9 >= 12 -- 0
  ```

  ## Boolean operators

  SQL supports all Boolean operators:

  ```sql
  SELECT (9 > 7) AND (3 < 5) -- 1
  SELECT (3 > 1) OR (2 > 5) -- 1
  SELECT (3 > 1) XOR (5 > 1) -- 0
  ```

  We also have `IS` and `IS NOT` for testing Boolean values:

  ```sql
  SELECT (9 > 7) IS TRUE -- 1
  SELECT (9 > 7) IS NOT TRUE -- 0
  ```

  ## `NULL`

  - `NULL` is neither true (1) or false (0). It has no value.
  - An empty string (`""`) is **not** `NULL`

  ```sql
  SELECT 1 IS NULL -- 0
  SELECT 1 IS NOT NULL -- 1
  SELECT NULL IS NULL -- 1
  SELECT "" IS NULL -- 0
  ```

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

  # Useful operators in SQL

  ## True and false

  > In SQL false results are represented by `0` and true results are represented
  > by `1`. Any zero value is false and any non-zero value is true.

  > When comparing operators we can use `SELECT` as a general "execute" function
  > for SQL to interpret

  ```sql
  SELECT 0 = 0 -- 1
  SELECT 0.0 = 0 -- 1
  SELECT 9 >= 12 -- 0
  ```

  ## Boolean operators

  SQL supports all Boolean operators:

  ```sql
  SELECT (9 > 7) AND (3 < 5) -- 1
  SELECT (3 > 1) OR (2 > 5) -- 1
  SELECT (3 > 1) XOR (5 > 1) -- 0
  ```

  We also have `IS` and `IS NOT` for testing Boolean values:

  ```sql
  SELECT (9 > 7) IS TRUE -- 1
  SELECT (9 > 7) IS NOT TRUE -- 0
  ```

  ## `NULL`

  - `NULL` is neither true (1) or false (0). It has no value.
  - An empty string (`""`) is **not** `NULL`

  ```sql
  SELECT 1 IS NULL -- 0
  SELECT 1 IS NOT NULL -- 1
  SELECT NULL IS NULL -- 1
  SELECT "" IS NULL -- 0
  ```


path: User_Space.md
sortable_path: User_Space.md
title: User space
lead:
  User space is the portion of the main memory that the kernel allocates for user
  processes.

checksum: 9fda30f69fca66abe1243711ac15679517d775e629de2c0d9175dff66ed21cc5
created: 2024-10-22 16:52:59.824457658+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["operating-systems"]}
body:
  User space is the portion of the main memory that the kernel allocates for user
  processes.

  There is a hierarchy within the user space with several different groups of user
  processes:

  <dl>

  <dt>Basic services</dt>
  <dd>Run at the bottom level and therefore are closest to ther kernel. Comprises small components that perform single, uncomplicated tasks. Examples include network configuration, the communication bus, and diagnostic logging</dd> 
  <dt>Utiility services</dt>
  <dd>The middle level: larger components such as mail, print, and database services</dd>
  <dt>Services controllable by the user</dt>
  <dd>The top level: complicated tasks that the user controls directly such as using a web-browser, editing a text file</dd>
  </dl>

  ## What is a user?

  > A user is an entity that can run processes and own files.

  At the level of users, a user is associated with a user name however the kernel
  does not manage usernames, it identifies users via numeric identifiers called
  **user IDs**.

  Users exist in order to support permissions and access boundaries. A user may
  terminate or modify the behaviour of its own processes (within limits) but
  cannot interfere with another user's proceses. In addition, users may own files
  and choose whether to share them with other users.

  ### Root user (superuser)

  A Linux system normally has a number of users in addition to the ones that
  correspond to the real human users. The most important is **root**. The root
  user _can_ terminate and alter another user's processes and access any file on
  the local system.

  A person who can operate as root is an administrator on a typical user system.

raw_content:
  ---
  tags:
    - operating-systems
  ---

  # User space

  User space is the portion of the main memory that the kernel allocates for user
  processes.

  There is a hierarchy within the user space with several different groups of user
  processes:

  <dl>

  <dt>Basic services</dt>
  <dd>Run at the bottom level and therefore are closest to ther kernel. Comprises small components that perform single, uncomplicated tasks. Examples include network configuration, the communication bus, and diagnostic logging</dd> 
  <dt>Utiility services</dt>
  <dd>The middle level: larger components such as mail, print, and database services</dd>
  <dt>Services controllable by the user</dt>
  <dd>The top level: complicated tasks that the user controls directly such as using a web-browser, editing a text file</dd>
  </dl>

  ## What is a user?

  > A user is an entity that can run processes and own files.

  At the level of users, a user is associated with a user name however the kernel
  does not manage usernames, it identifies users via numeric identifiers called
  **user IDs**.

  Users exist in order to support permissions and access boundaries. A user may
  terminate or modify the behaviour of its own processes (within limits) but
  cannot interfere with another user's proceses. In addition, users may own files
  and choose whether to share them with other users.

  ### Root user (superuser)

  A Linux system normally has a number of users in addition to the ones that
  correspond to the real human users. The most important is **root**. The root
  user _can_ terminate and alter another user's processes and access any file on
  the local system.

  A person who can operate as root is an administrator on a typical user system.


path: User_management_in_Linux.md
sortable_path: User_management_in_Linux.md
title: User management
lead: ## Switch user
checksum: e5c96a6157409e2d25e0f6278475b4bb97323774450952af78f3ad3ea1369622
created: 2024-10-22 16:52:59.83238617+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["Linux","procedural"]}
body:
  ## Switch user

  If already logged in as a user you can switch users with the command
  `su - [username]`.

  ## Login as root

  If you are logged in as a standard user, if you use `su -` without specifying a
  username, then it will assume you wish to log in as root.

  If you wish to login as root in the tty at startup, then use `root` as the
  username.

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

  # User management

  ## Switch user

  If already logged in as a user you can switch users with the command
  `su - [username]`.

  ## Login as root

  If you are logged in as a standard user, if you use `su -` without specifying a
  username, then it will assume you wish to log in as root.

  If you wish to login as root in the tty at startup, then use `root` as the
  username.


path: User_management_on_AWS.md
sortable_path: User_management_on_AWS.md
title: User management and roles
lead: ## IAM
checksum: 173c1b3adbc04dfc09924b34e135e231e739c359cede8053c343a03bc5c2f4a8
created: 2024-10-22 16:52:59.838129084+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["AWS"]}
body:
  ## IAM

  > Identity and Access Management

  An IAM user is an identity with long-term credentials that is used to interact
  with AWS in an account. An IAM login is different from a root login. The root
  user is the one who creates the AWS account and who has the power to delete it.
  The root user can create IAM logins so that other users can log in through
  dedicated IAM portals. The root user should also create an IAM login for himself
  and use that, set to administrator policies, for most activities, reserving root
  for the most consequential stuff like adding users etc.

  ## Users and user groups

  - Use to create users and access rights accross the AWS services
  - Users get permissions through policies (use template or create your own)
  - You can also create user groups and add users to them to simplify role
    attribution

  ## Roles

  Roles are adopted by _services_ rather than users. They run either periodically
  (from a `cronjob` say) or when they are hit from an API.

  This allows you to assign policies to a role that a service can adopt when
  running. A good example is a lambda. Lambdas often need access to other services
  such as an S3 bucket or a DynamoDB table. Say you have a service that combines a
  lambda with a DynamoDB database. You could assign a role to the lambda and it
  would have access to the database.

  ## Distinction between _principal_ and _identity_

  Both “principal” and “identity” refer to entities that can perform actions and
  interact with AWS resources. However, there is a subtle difference in their
  usage:

  > a principal is a specific type of entity that can take actions in AWS, while
  > an identity is the unique identifier associated with that principal.

  1. Principal: In the context of IAM policies, a principal represents the entity
     that is allowed or denied access to AWS rThe principal is specified in the
     policy statement as the entity to which the permissions are granted or
     deniedesources. It can be an IAM user, an IAM role, an AWS service. The
     principal is specified in the policy statement as the entity to which the
     permissions are granted or denied.
  2. Identity: An identity, on the other hand, is a broader term that encompasses
     both the principal and the authentication credentials associated with that
     principal. It refers to the entity’s unique identifier, such as an IAM user’s
     username or an IAM role’s ARN (Amazon Resource Name). An identity is used for
     authentication purposes to verify the entity’s identity and determine its
     permissions.

  ## Cognito

  > Amazon Cognito provides authentication, authorization, and user management for
  > your web and mobile apps.

  There are **user pools** and **identity pools**.

  User pools provide sign-up and sign-in options for your app users. Identity
  pools provide AWS credentials to grant your users access to other AWS services.

  You would use identity pools to manage access to API endpoints or S3 buckets.
  They can be authenticated or unauthenticated. For example if you want to use an
  image from S3 in a website you would authenticate for that, but you would
  explicitly have to specify that it is general access by applying the policy
  `AmazonS3ReadOnlyAccess`.

raw_content:
  ---
  tags:
    - AWS
  ---

  # User management and roles

  ## IAM

  > Identity and Access Management

  An IAM user is an identity with long-term credentials that is used to interact
  with AWS in an account. An IAM login is different from a root login. The root
  user is the one who creates the AWS account and who has the power to delete it.
  The root user can create IAM logins so that other users can log in through
  dedicated IAM portals. The root user should also create an IAM login for himself
  and use that, set to administrator policies, for most activities, reserving root
  for the most consequential stuff like adding users etc.

  ## Users and user groups

  - Use to create users and access rights accross the AWS services
  - Users get permissions through policies (use template or create your own)
  - You can also create user groups and add users to them to simplify role
    attribution

  ## Roles

  Roles are adopted by _services_ rather than users. They run either periodically
  (from a `cronjob` say) or when they are hit from an API.

  This allows you to assign policies to a role that a service can adopt when
  running. A good example is a lambda. Lambdas often need access to other services
  such as an S3 bucket or a DynamoDB table. Say you have a service that combines a
  lambda with a DynamoDB database. You could assign a role to the lambda and it
  would have access to the database.

  ## Distinction between _principal_ and _identity_

  Both “principal” and “identity” refer to entities that can perform actions and
  interact with AWS resources. However, there is a subtle difference in their
  usage:

  > a principal is a specific type of entity that can take actions in AWS, while
  > an identity is the unique identifier associated with that principal.

  1. Principal: In the context of IAM policies, a principal represents the entity
     that is allowed or denied access to AWS rThe principal is specified in the
     policy statement as the entity to which the permissions are granted or
     deniedesources. It can be an IAM user, an IAM role, an AWS service. The
     principal is specified in the policy statement as the entity to which the
     permissions are granted or denied.
  2. Identity: An identity, on the other hand, is a broader term that encompasses
     both the principal and the authentication credentials associated with that
     principal. It refers to the entity’s unique identifier, such as an IAM user’s
     username or an IAM role’s ARN (Amazon Resource Name). An identity is used for
     authentication purposes to verify the entity’s identity and determine its
     permissions.

  ## Cognito

  > Amazon Cognito provides authentication, authorization, and user management for
  > your web and mobile apps.

  There are **user pools** and **identity pools**.

  User pools provide sign-up and sign-in options for your app users. Identity
  pools provide AWS credentials to grant your users access to other AWS services.

  You would use identity pools to manage access to API endpoints or S3 buckets.
  They can be authenticated or unauthenticated. For example if you want to use an
  image from S3 in a website you would authenticate for that, but you would
  explicitly have to specify that it is general access by applying the policy
  `AmazonS3ReadOnlyAccess`.


path: Using_GraphQL_with_Node.md
sortable_path: Using_GraphQL_with_Node.md
title: Using GraphQL with Node.js
lead: ## Create a basic Express server
checksum: 685307a804f6e41606f8a1dacebf338a95dd7476b1496eced0506b8f73f5f9df
created: 2024-10-22 16:52:59.844321359+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["graphql","node-js"]}
body:
  ## Create a basic Express server

  First we create a basic server in Node using Express:

  ```js
  import express from "express";

  app.get("/", (req, res) => {
    res.send("Graph QL test project");
  });

  app.listen(8080, () =>
    console.log("Running server on port localhost:8080/graphql")
  );
  ```

  ## Add GraphQL as middlewear

  Next we introduce GraphQL as a piece of Node.js
  [middlewear](Middleware_in_NodeJS.md), with the
  `app.use()` method.

  ```js
  import { graphqlHTTP } from "express-graphql";

  app.use(
    "/graphql",
    graphqlHTTP({
      schema: schema,
      rootValue: resolvers,
      graphiql: true,
    })
  );
  ```

  - `schema` is a reference to our GraphQL schema - the structure of the fields
    that define our server.
  - `rootValue` is a reference to our resolvers.
  - `graphiql` is the GUI tool that will be served from the GraphQL endpoint at
    `localhost:8080/graphql`. This tool enables us to interrogate our data using
    the defined schema and see what data we would get back from frontend queries.

raw_content:
  ---
  tags: [graphql, node-js]
  ---

  # Using GraphQL with Node.js

  ## Create a basic Express server

  First we create a basic server in Node using Express:

  ```js
  import express from "express";

  app.get("/", (req, res) => {
    res.send("Graph QL test project");
  });

  app.listen(8080, () =>
    console.log("Running server on port localhost:8080/graphql")
  );
  ```

  ## Add GraphQL as middlewear

  Next we introduce GraphQL as a piece of Node.js
  [middlewear](Middleware_in_NodeJS.md), with the
  `app.use()` method.

  ```js
  import { graphqlHTTP } from "express-graphql";

  app.use(
    "/graphql",
    graphqlHTTP({
      schema: schema,
      rootValue: resolvers,
      graphiql: true,
    })
  );
  ```

  - `schema` is a reference to our GraphQL schema - the structure of the fields
    that define our server.
  - `rootValue` is a reference to our resolvers.
  - `graphiql` is the GUI tool that will be served from the GraphQL endpoint at
    `localhost:8080/graphql`. This tool enables us to interrogate our data using
    the defined schema and see what data we would get back from frontend queries.


path: Using_arguments_with_Apollo_Client.md
sortable_path: Using_arguments_with_Apollo_Client.md
title: Using arguments with Apollo Client
lead: ## What are arguments?
checksum: 3d64509966807a581a0b74dcb400372cbc2441e505fe91173b05d7fed2473ed1
created: 2024-10-22 16:52:59.855549997+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["graphql"]}
body:
  ## What are arguments?

  - An argument is a value you provide for a particular field in a query.
  - The schema must define the arguments that a field accepts (if any)
  - The resolver invokes a field's provided arguments to determine how to return
    specific data
  - Some example use cases of arguments:
    - retrieve specific objects
    - filter through a set of objects
    - transform a field's returned value

  ## Updated schema

  In order to demonstrate arguments we need to expand the
  [original schema](Apollo_Server.md#example-schema).

  Remember a Track is a group of modules that teaches about a specific topic. We
  are going to add:

  - `description` and `numberOfViews` fields to the original `Track` type
  - A new `Module` type
  - A field `modules` to the `Track` type that will be an array of type `Module`

  ```js
  type Query {
    tracksForHome: [Track!]!
  }

  type Track {
      id: ID!
          authorId: String! // They added this on the sly
      thumbnail: String
      length: Int
      modulesCount: Int
      description: String
      numberOfViews: Int
      modules: [Module!]!
  }

  type Module {
      id: ID!
          length: Int
  }

  type Author {
      id: ID!
      name: String!
      photo: String
  }
  ```

  ## Adding additional query

  > Currently we only have one query in the schema (`tracksForHome`) and this
  > returns an array of all the tracks. To demonstrate arguments we want to return
  > a specific track by its `id`. We are going to add a query that enables this.

  ```js
  type Query {
      track(id: ID!):  Track
  }
  ```

  This query will take an `id` as an argument and the resolver will match the `id`
  to a given track and return that `Track`.

  To define an argument for a field in the schema we add parentheses after the
  field name. In the parentheses we write the name of the argument and its type.
  If we have more than one argument we can separate them with commas.

  ## Create resolver for new query

  Now we have to create a resolver for our new `track` query. We will quickly run
  through the [server-side process](Apollo_Server.md).

  ### Update our `RESTDataSource`

  ```js
  class TrackAPI extends RESTDataSource {
    constructor() {...}
    getTracksForHome() {...}
    getAuthor(authorId) {...}

    getTrack(trackId){
      return this.get(`track/${trackId}`);
    }
  }

  ```

  ### Add resolver to `resolvers.js`

  ```js
  const resolvers = {
    Query: {
      tracksForHome: (_, __, { dataSources }) => {
        return dataSources.trackApi.getTracksForHome();
      },
      track: (_, { id }, { dataSources }) => {
        return dataSources.trackAPI.getTrack(id);
      },
    },
    Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
    },
  };
  ```

  ### Making a query

  With the server changes complete, we can now issue a query with an argument from
  the client:

  ```js
  query track(id: 'xyz'){
    title
  }
  ```

  This will return the `title` field from the track with the specific id. This
  query complies with the schema since the `Track` type has the field `title`

  ### Resolver chains

  What about the following query:

  ```js
  query track(id: 'xyz'){
    title
    author {
      name
    }
  }
  ```

  It's not obvious how the resolver should respond to the nested query here since
  author name is not a field on `Track`, it is a field on `Author`:

  ```js
  type Track {
    id: ID!
      authorId: String!
    thumbnail: String
    length: Int
    modulesCount: Int
    description: String
    numberOfViews: Int
    modules: [Module!]!
  }

  type Author {
    id: ID!
    name: String!
    photo: String
  }
  ```

  This is where we use a **resolver chain**. Because `authorId` exists on `Track`
  we can use this to get the `name` part of the query. We already have a resolver
  for `author` under the `Track` resolver:

  ```js
  Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
    },
  ```

  Notice that `authorId` is used in the place of the `parent` parameter. It
  already exists on the `Track` type that wraps the resolver. So this can be
  invoked to fulfill `author` and thereby access the author name from the graph.

  This process is also required for our extended schema. The `Track` type now has
  a `modules` field that comprises an array of the `Module` type.

  Here's a reminder of the `Module` type:

  ```js
  type Module {
      id: ID!
          length: Int
  }
  ```

  The modules for a `Track` can be sourced from the track `id`. So we can again
  add a resolver chain function to the resolvers file:

  ```js
  const resolvers = {
    Query: {
      tracksForHome: (_, __, { dataSources }) => {
        return dataSources.trackApi.getTracksForHome();
      },
      track: (_, { id }, { dataSources }) => {
        return dataSources.trackAPI.getTrack(id);
      },
    },
    Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
      modules: ({ id }, _, { dataSources }) => {
        return dataSources.trackAPI.getTrackModules(id);
      },
    },
  };
  ```

  Once again we are using a property on the parent `Track` type to use as a
  parameter in the resolver function.

  This setup would enables like the following to be run:

  ```js
  query track(id: 'xyz'){
    title
    modules
  }
  ```

  The `id` parameter would be used by the `modules` resolver to return the array
  of the `Modules` type.

  ## Variables in arguments

  Instead of writing the following within our query constants on the client-side:

  ```js
  query GetTrack {
    track(id: 'xyz'){
      title
    }
  }
  ```

  We can make the code more reusable by using variables instead of the hardcoded
  `id` argument:

  ```js
  query GetTrack($trackId: ID!) {
    track(id: $trackId){
      title
    }
  }
  ```

  This way we do not need to write a new query constant every time we want to
  return a specific track.

  ## Send query with arguments using Apollo `useQuery`

  We can now write a proper query using the
  [useQuery hook](Apollo_Client.md#usequery-hook) from
  Apollo Client, with variables.

  First define our query constant:

  ```js
  export const GET_TRACK = gql`
    query GetTrack($trackId: ID!) {
      track(id: $trackId) {
        id
        title
        author {
          name
        }
        description
        modules {
          id
          title
          length
        }
      }
    }
  `;
  ```

  Then to employ in React:

  ```jsx
  const trackId = "xyz";

  const { loading, error, data } = useQuery(GET_TRACK, {
    variables: {
      id: trackId,
    },
  });
  ```

  Note that in contrast to the
  [simple example](Apollo_Client.md#query-constants)
  because we are using variables, we have to pass-in an additional options object
  with the query constant that specifies our variables.

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

  # Using arguments with Apollo Client

  ## What are arguments?

  - An argument is a value you provide for a particular field in a query.
  - The schema must define the arguments that a field accepts (if any)
  - The resolver invokes a field's provided arguments to determine how to return
    specific data
  - Some example use cases of arguments:
    - retrieve specific objects
    - filter through a set of objects
    - transform a field's returned value

  ## Updated schema

  In order to demonstrate arguments we need to expand the
  [original schema](Apollo_Server.md#example-schema).

  Remember a Track is a group of modules that teaches about a specific topic. We
  are going to add:

  - `description` and `numberOfViews` fields to the original `Track` type
  - A new `Module` type
  - A field `modules` to the `Track` type that will be an array of type `Module`

  ```js
  type Query {
    tracksForHome: [Track!]!
  }

  type Track {
      id: ID!
          authorId: String! // They added this on the sly
      thumbnail: String
      length: Int
      modulesCount: Int
      description: String
      numberOfViews: Int
      modules: [Module!]!
  }

  type Module {
      id: ID!
          length: Int
  }

  type Author {
      id: ID!
      name: String!
      photo: String
  }
  ```

  ## Adding additional query

  > Currently we only have one query in the schema (`tracksForHome`) and this
  > returns an array of all the tracks. To demonstrate arguments we want to return
  > a specific track by its `id`. We are going to add a query that enables this.

  ```js
  type Query {
      track(id: ID!):  Track
  }
  ```

  This query will take an `id` as an argument and the resolver will match the `id`
  to a given track and return that `Track`.

  To define an argument for a field in the schema we add parentheses after the
  field name. In the parentheses we write the name of the argument and its type.
  If we have more than one argument we can separate them with commas.

  ## Create resolver for new query

  Now we have to create a resolver for our new `track` query. We will quickly run
  through the [server-side process](Apollo_Server.md).

  ### Update our `RESTDataSource`

  ```js
  class TrackAPI extends RESTDataSource {
    constructor() {...}
    getTracksForHome() {...}
    getAuthor(authorId) {...}

    getTrack(trackId){
      return this.get(`track/${trackId}`);
    }
  }

  ```

  ### Add resolver to `resolvers.js`

  ```js
  const resolvers = {
    Query: {
      tracksForHome: (_, __, { dataSources }) => {
        return dataSources.trackApi.getTracksForHome();
      },
      track: (_, { id }, { dataSources }) => {
        return dataSources.trackAPI.getTrack(id);
      },
    },
    Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
    },
  };
  ```

  ### Making a query

  With the server changes complete, we can now issue a query with an argument from
  the client:

  ```js
  query track(id: 'xyz'){
    title
  }
  ```

  This will return the `title` field from the track with the specific id. This
  query complies with the schema since the `Track` type has the field `title`

  ### Resolver chains

  What about the following query:

  ```js
  query track(id: 'xyz'){
    title
    author {
      name
    }
  }
  ```

  It's not obvious how the resolver should respond to the nested query here since
  author name is not a field on `Track`, it is a field on `Author`:

  ```js
  type Track {
    id: ID!
      authorId: String!
    thumbnail: String
    length: Int
    modulesCount: Int
    description: String
    numberOfViews: Int
    modules: [Module!]!
  }

  type Author {
    id: ID!
    name: String!
    photo: String
  }
  ```

  This is where we use a **resolver chain**. Because `authorId` exists on `Track`
  we can use this to get the `name` part of the query. We already have a resolver
  for `author` under the `Track` resolver:

  ```js
  Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
    },
  ```

  Notice that `authorId` is used in the place of the `parent` parameter. It
  already exists on the `Track` type that wraps the resolver. So this can be
  invoked to fulfill `author` and thereby access the author name from the graph.

  This process is also required for our extended schema. The `Track` type now has
  a `modules` field that comprises an array of the `Module` type.

  Here's a reminder of the `Module` type:

  ```js
  type Module {
      id: ID!
          length: Int
  }
  ```

  The modules for a `Track` can be sourced from the track `id`. So we can again
  add a resolver chain function to the resolvers file:

  ```js
  const resolvers = {
    Query: {
      tracksForHome: (_, __, { dataSources }) => {
        return dataSources.trackApi.getTracksForHome();
      },
      track: (_, { id }, { dataSources }) => {
        return dataSources.trackAPI.getTrack(id);
      },
    },
    Track: {
      author: ({ authorId }, _, { dataSources }) => {
        return dataSources.trackApi.getAuthor(authorId);
      },
      modules: ({ id }, _, { dataSources }) => {
        return dataSources.trackAPI.getTrackModules(id);
      },
    },
  };
  ```

  Once again we are using a property on the parent `Track` type to use as a
  parameter in the resolver function.

  This setup would enables like the following to be run:

  ```js
  query track(id: 'xyz'){
    title
    modules
  }
  ```

  The `id` parameter would be used by the `modules` resolver to return the array
  of the `Modules` type.

  ## Variables in arguments

  Instead of writing the following within our query constants on the client-side:

  ```js
  query GetTrack {
    track(id: 'xyz'){
      title
    }
  }
  ```

  We can make the code more reusable by using variables instead of the hardcoded
  `id` argument:

  ```js
  query GetTrack($trackId: ID!) {
    track(id: $trackId){
      title
    }
  }
  ```

  This way we do not need to write a new query constant every time we want to
  return a specific track.

  ## Send query with arguments using Apollo `useQuery`

  We can now write a proper query using the
  [useQuery hook](Apollo_Client.md#usequery-hook) from
  Apollo Client, with variables.

  First define our query constant:

  ```js
  export const GET_TRACK = gql`
    query GetTrack($trackId: ID!) {
      track(id: $trackId) {
        id
        title
        author {
          name
        }
        description
        modules {
          id
          title
          length
        }
      }
    }
  `;
  ```

  Then to employ in React:

  ```jsx
  const trackId = "xyz";

  const { loading, error, data } = useQuery(GET_TRACK, {
    variables: {
      id: trackId,
    },
  });
  ```

  Note that in contrast to the
  [simple example](Apollo_Client.md#query-constants)
  because we are using variables, we have to pass-in an additional options object
  with the query constant that specifies our variables.


path: Utilities_operators_flags.md
sortable_path: Utilities_operators_flags.md
title: Utilities, operators, flags in Bash
checksum: 05c70aee147aa773a0a5398a3250697f6d1ff38a8998eeb9f614efd16dbb7e6c
created: 2024-10-22 16:52:59.876011197+00:00
modified: 2024-10-19 09:01:23.725350032+00:00
metadata: {"tags":["shell"]}
lead:
  The following are useful built-in utility methods that you can use for checking
  and validation in the course of your bash scripts.

body:
  The following are useful built-in utility methods that you can use for checking
  and validation in the course of your bash scripts.

  ## Flags

  ### Prevent newline

  Prevent bash from adding a new line after an echo:

  ```bash
  echo 'Your name is Thomas'
  echo 'and you are 33 years old'

  # Your name is Thomas
  # and you are 33 years old
  ```

  ```bash
  echo -n 'Your name is Thomas '
  echo 'and you are 33 years old'

  # Your name is Thomas and you are 33 years old
  ```

  ## Operators

  ### Mathematical

  ```bash
  -lt , -gt,
  ```

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

  # Utilities, operators, flags in Bash

  The following are useful built-in utility methods that you can use for checking
  and validation in the course of your bash scripts.

  ## Flags

  ### Prevent newline

  Prevent bash from adding a new line after an echo:

  ```bash
  echo 'Your name is Thomas'
  echo 'and you are 33 years old'

  # Your name is Thomas
  # and you are 33 years old
  ```

  ```bash
  echo -n 'Your name is Thomas '
  echo 'and you are 33 years old'

  # Your name is Thomas and you are 33 years old
  ```

  ## Operators

  ### Mathematical

  ```bash
  -lt , -gt,
  ```


path: Validating_Mongoose_schemas.md
sortable_path: Validating_Mongoose_schemas.md
title: Validating Mongoose schemas
lead: Let's say we have the following schema:
checksum: 847b00e5d4138313509f38e42afa01586c9e0fa1e3cd708baf6a709aafe2702a
created: 2024-10-22 16:52:59.881632011+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["mongo-db","mongoose","node-js","databases"]}
body:
  Let's say we have the following schema:

  ```js
  const courseSchema = new mongoose.Schema({
    name: { type: String, required: true, minlength: 5, maxlength: 255 },
    author: String,
    tags: [String],
    data: { type: Date, default: Date.now }, // if unspecified, entry will default to current date
    isPublished: Boolean,
  });
  ```

  We set our validation criteria as the second property for each schema value. If
  it does not obtain, an exception will be thrown when we attempt to modify the
  database. This will most likely take the form of a Promise rejection.

  Note that this validation only occurs at the level of Mongoose. There is no
  mechanism for running the validation within the database itself. This is one of
  the ways that Mongo differs from an SQL database where you would be able to set
  the property of `required` for a cell in the table. If we didn't set any
  validation via Mongoose, Mongo would accept whatever we sent to it.

  What is the relationship between this Mongoose validation and the
  [Joi](Validation_in_NodeJS.md) validation that we
  use when validating API requests in Node/Express? They complement each other. We
  use Joi to validate the client request to the API. If this is valid, the process
  would then move onto the next stage which would be transforming the data from a
  REST request to a database request implemented through Mongoose, where a second
  layer of validation would appy. We would also have validation on the
  client-side.

  ## Validator functions

  Instead of simply applying a built-in validator like `required`, we can instead
  pass a function that validates agains the validation status of another property
  in the schema. For instance: make the `price` property required only if the
  entry has `true` for `isPublished`:

  ```js
  const courseSchema = new mongoose.Schema({
    ...
    isPublished: Boolean,
    price: {
      type: Number,
      required: function() {return this.isPublished}
    }
  });
  ```

  ## Enum validators

  An enum validator enables us to ensure that whatever value is passed when
  instantiating a document that is of a given schema type, it must be one of a
  finite set of values:

  ```js
  const anotherSchema = new mongoose.Schema({
    sex: {
      type: String,
      enum: ["male", "female"],
    },
  });
  ```

raw_content:
  ---
  tags:
    - mongo-db
    - mongoose
    - node-js
    - databases
  ---

  # Validating Mongoose schemas

  Let's say we have the following schema:

  ```js
  const courseSchema = new mongoose.Schema({
    name: { type: String, required: true, minlength: 5, maxlength: 255 },
    author: String,
    tags: [String],
    data: { type: Date, default: Date.now }, // if unspecified, entry will default to current date
    isPublished: Boolean,
  });
  ```

  We set our validation criteria as the second property for each schema value. If
  it does not obtain, an exception will be thrown when we attempt to modify the
  database. This will most likely take the form of a Promise rejection.

  Note that this validation only occurs at the level of Mongoose. There is no
  mechanism for running the validation within the database itself. This is one of
  the ways that Mongo differs from an SQL database where you would be able to set
  the property of `required` for a cell in the table. If we didn't set any
  validation via Mongoose, Mongo would accept whatever we sent to it.

  What is the relationship between this Mongoose validation and the
  [Joi](Validation_in_NodeJS.md) validation that we
  use when validating API requests in Node/Express? They complement each other. We
  use Joi to validate the client request to the API. If this is valid, the process
  would then move onto the next stage which would be transforming the data from a
  REST request to a database request implemented through Mongoose, where a second
  layer of validation would appy. We would also have validation on the
  client-side.

  ## Validator functions

  Instead of simply applying a built-in validator like `required`, we can instead
  pass a function that validates agains the validation status of another property
  in the schema. For instance: make the `price` property required only if the
  entry has `true` for `isPublished`:

  ```js
  const courseSchema = new mongoose.Schema({
    ...
    isPublished: Boolean,
    price: {
      type: Number,
      required: function() {return this.isPublished}
    }
  });
  ```

  ## Enum validators

  An enum validator enables us to ensure that whatever value is passed when
  instantiating a document that is of a given schema type, it must be one of a
  finite set of values:

  ```js
  const anotherSchema = new mongoose.Schema({
    sex: {
      type: String,
      enum: ["male", "female"],
    },
  });
  ```


path: Validation_in_NodeJS.md
sortable_path: Validation_in_NodeJS.md
title: Creating a RESTful API: Validation
checksum: 6f0c4e7f0af95a7522c442d4991bd06ab0a817dac6eabb19ed81afb036089759
created: 2024-10-22 16:52:59.896458849+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["node-js","REST","APIs"]}
lead:
  We can provide server-side validation for our projects by using a **schema
  validator**. This is a program that declaratively parses the JSON values
  received as requests from the client. This makes it easy to systematically
  validate the data that we receive from any HTTP requests where the client sends
  a body to the endpoint.

body:
  We can provide server-side validation for our projects by using a **schema
  validator**. This is a program that declaratively parses the JSON values
  received as requests from the client. This makes it easy to systematically
  validate the data that we receive from any HTTP requests where the client sends
  a body to the endpoint.

  One of the most popular schema validators for NodeJS is
  [joi](joi).

  ## Demonstration

  Let's say we have a POST request that expects a single field as the body that
  must be a string and greater than two characters long. First we define our
  schema:

  ```js
  const schema = Joi.object({
    name: Joi.string().min(3).required(),
  });

  const { error } = schema.validate(req.body);
  ```

  The `schema` variable is an object whose keys should match those of the intended
  request body. Instead of actual values we provide Joi's in-built validators,
  concatenated as necessary. We then store the results of the validation in a
  variable.

  Next we add handling in the case of errors:

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

  We loop through the error array and return 400s as the response if they are
  found. If there are no errors, the Joi object will return `undefined`.

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

  # Creating a RESTful API: Validation

  We can provide server-side validation for our projects by using a **schema
  validator**. This is a program that declaratively parses the JSON values
  received as requests from the client. This makes it easy to systematically
  validate the data that we receive from any HTTP requests where the client sends
  a body to the endpoint.

  One of the most popular schema validators for NodeJS is
  [joi](joi).

  ## Demonstration

  Let's say we have a POST request that expects a single field as the body that
  must be a string and greater than two characters long. First we define our
  schema:

  ```js
  const schema = Joi.object({
    name: Joi.string().min(3).required(),
  });

  const { error } = schema.validate(req.body);
  ```

  The `schema` variable is an object whose keys should match those of the intended
  request body. Instead of actual values we provide Joi's in-built validators,
  concatenated as necessary. We then store the results of the validation in a
  variable.

  Next we add handling in the case of errors:

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

  We loop through the error array and return 400s as the response if they are
  found. If there are no errors, the Joi object will return `undefined`.


path: Validity_and_entailment.md
sortable_path: Validity_and_entailment.md
title: Validity and entailment
lead: ### Informal definition
checksum: c1ea50298640625de41a08e73e8852203b6a59a2f392299a43dc5ef7465fdb2b
created: 2024-10-22 16:52:59.909355046+00:00
modified: 2024-10-19 09:01:40.438998017+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  ### Informal definition

  In order to say whether an argument is 'good' or 'bad' we must have criteria of
  evaluation. in logic there are different criteria of evaluation:

  - **Deductive validity**

    An **argument is deductively valid if and only if it is not possible for the
    premises to be true and the conclusion false**. Linking to consistency: it is
    not possible to consistently assert all of the premises but deny the
    conclusion.

  - **Inductive strength**

    We do not say that inductive arguments have 'validity' because despite
    inductive premises being true, the conclusion may be falsifiable. Therefore we
    say inductive 'strength' rather than 'validity'. An argument is inductively
    strong if and only if the conclusion is probably true given the premises.

  #### Demonstration

  The Socrates demonstration above is an example of deductive validity.

  The following is an example of an argument that is inductively strong:

  ```
  99% of deaf persons have no musical talent.
  Beethoven was deaf.
  ___________________________________________
  Beethoven had no musical talent.
  ```

  The test for a strong inductive argument is not whether the conclusion is true,
  rather it concerns the evidence the premises provide in support of the
  conclusion.

  > In propositional logic we are concerned solely with deductive validity or
  > invalidity.

  ### Formal definition

  > An argument is truth-functionally valid if and only if there is no
  > truth-assignment on which all the premises are true and the conclusion is
  > false.

  Linking this to derivation, we say:

  > In a system of derivation in propositional logic, an argument is valid if the
  > conclusion of the argument is derivable within the system of derivation from
  > the set consisting of the premises, and invalid otherwise.

  ### Demonstration

  The inference from the set ${P, P \rightarrow Q}$ to $Q$ is valid

  ### Truth-table

  | $P$ | $Q$ | $P \rightarrow Q$ | $P$ | $Q$ | Assessment |
  | --- | --- | ----------------- | --- | --- | ---------- |
  | T   | T   | T                 | T   | T   | Valid      |
  | T   | F   | F                 | T   | F   |            |
  | F   | T   | T                 | F   | T   |            |

  ## Entailment

  ### Informal definition

  Entailment as a concept is almost identical to validity. We say that a
  proposition is entailed by a set of propositions if it is not possible for every
  member of this set to be true and the proposition to be false.

  The difference with validity resides in the fact that the propositions are
  distinguished in terms of whether they are premises or a conclusion. So,
  technically, validity is a subclass of entailment. A case of entailment where we
  distinguish propositions in terms of whether they are premises or conclusions. A
  proposition may be entailed by a given set without that proposition being the
  _conclusion_ of the set and where the set is a syllogism.

  ### Formal definition

  > A finite set of sentences $\Gamma$ $\vdash$ $P$ if and only if there is no
  > truth-assignment in which every member of $\Gamma$ is true and $P$ is false.

  #### Informal demonstration

  ```
  It is raining.
  If it is raining then the pavement will be wet.
  The pavement is wet.
  ```

  #### Formal demonstration

  $$
    \{ P, P\rightarrow Q   \} \vdash Q
  $$

  #### Truth-table

  | $P$ | $Q$ | $P \rightarrow Q$ | $P$ | $Q$ | Assessment |
  | --- | --- | ----------------- | --- | --- | ---------- |
  | T   | T   | T                 | T   | T   | Valid      |
  | T   | F   | F                 | T   | F   |            |
  | F   | T   | T                 | F   | T   |            |

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

  # Validity and entailment

  ### Informal definition

  In order to say whether an argument is 'good' or 'bad' we must have criteria of
  evaluation. in logic there are different criteria of evaluation:

  - **Deductive validity**

    An **argument is deductively valid if and only if it is not possible for the
    premises to be true and the conclusion false**. Linking to consistency: it is
    not possible to consistently assert all of the premises but deny the
    conclusion.

  - **Inductive strength**

    We do not say that inductive arguments have 'validity' because despite
    inductive premises being true, the conclusion may be falsifiable. Therefore we
    say inductive 'strength' rather than 'validity'. An argument is inductively
    strong if and only if the conclusion is probably true given the premises.

  #### Demonstration

  The Socrates demonstration above is an example of deductive validity.

  The following is an example of an argument that is inductively strong:

  ```
  99% of deaf persons have no musical talent.
  Beethoven was deaf.
  ___________________________________________
  Beethoven had no musical talent.
  ```

  The test for a strong inductive argument is not whether the conclusion is true,
  rather it concerns the evidence the premises provide in support of the
  conclusion.

  > In propositional logic we are concerned solely with deductive validity or
  > invalidity.

  ### Formal definition

  > An argument is truth-functionally valid if and only if there is no
  > truth-assignment on which all the premises are true and the conclusion is
  > false.

  Linking this to derivation, we say:

  > In a system of derivation in propositional logic, an argument is valid if the
  > conclusion of the argument is derivable within the system of derivation from
  > the set consisting of the premises, and invalid otherwise.

  ### Demonstration

  The inference from the set ${P, P \rightarrow Q}$ to $Q$ is valid

  ### Truth-table

  | $P$ | $Q$ | $P \rightarrow Q$ | $P$ | $Q$ | Assessment |
  | --- | --- | ----------------- | --- | --- | ---------- |
  | T   | T   | T                 | T   | T   | Valid      |
  | T   | F   | F                 | T   | F   |            |
  | F   | T   | T                 | F   | T   |            |

  ## Entailment

  ### Informal definition

  Entailment as a concept is almost identical to validity. We say that a
  proposition is entailed by a set of propositions if it is not possible for every
  member of this set to be true and the proposition to be false.

  The difference with validity resides in the fact that the propositions are
  distinguished in terms of whether they are premises or a conclusion. So,
  technically, validity is a subclass of entailment. A case of entailment where we
  distinguish propositions in terms of whether they are premises or conclusions. A
  proposition may be entailed by a given set without that proposition being the
  _conclusion_ of the set and where the set is a syllogism.

  ### Formal definition

  > A finite set of sentences $\Gamma$ $\vdash$ $P$ if and only if there is no
  > truth-assignment in which every member of $\Gamma$ is true and $P$ is false.

  #### Informal demonstration

  ```
  It is raining.
  If it is raining then the pavement will be wet.
  The pavement is wet.
  ```

  #### Formal demonstration

  $$
    \{ P, P\rightarrow Q   \} \vdash Q
  $$

  #### Truth-table

  | $P$ | $Q$ | $P \rightarrow Q$ | $P$ | $Q$ | Assessment |
  | --- | --- | ----------------- | --- | --- | ---------- |
  | T   | T   | T                 | T   | T   | Valid      |
  | T   | F   | F                 | T   | F   |            |
  | F   | T   | T                 | F   | T   |            |


path: Variable_indirection_in_Bash.md
sortable_path: Variable_indirection_in_Bash.md
title: Variable indirection
lead: > Write proper notes on this
checksum: 6682743efcc1a97b2ba0760899b1690e9af193e72e3df5a957904ab8e01726a5
created: 2024-10-22 16:52:59.916217809+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["shell"]}
body:
  > Write proper notes on this

  ```sh
  function array_empty() {
      declare -n arr=$1
      # Proceed if array not empty:
      if [ ${#arr[@]} -gt 0 ]; then
          return 1 # array is not empty
      else
          return 0 # array is empty
      fi
  }

  my_array=(1 2 3)

  function push() {
      # $1 = stack name
      local stack_name="$1"
      if [ "${stack_name}" ]; then # stack exists
          if array_empty "${!stack_name}"; then
              echo "stack is empty"
          else
              echo "stack is not empty"
          fi
      else
          echo "Error: ${stack_name} does not exist."
      fi
  }

  push "my_array"

  ```

  In Bash scripting, ${!stack_name} is an example of indirect variable referencing
  or variable indirection. It's a way to use a variable whose name is stored in
  another variable.

  Let's break it down:

  `${stack_name}`: This syntax is used to reference the value of a variable. So if
  stack_name="my_array", then ${stack_name} would return "my_array".

  `${!stack_name}`: Adding the ! introduces indirection. Now, instead of getting
  the value of stack_name, you get the value of the variable whose name is stored
  in stack_name. So if stack_name="my_array" and my_array=(1 2 3), then
  ${!stack_name} would return (1 2 3).

  So, in the context of your script, `${!stack_name} allows you to pass the name
  of an array (as a string) to your push function, and then use that string to
  indirectly reference the actual array within the function.

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

  # Variable indirection

  > Write proper notes on this

  ```sh
  function array_empty() {
      declare -n arr=$1
      # Proceed if array not empty:
      if [ ${#arr[@]} -gt 0 ]; then
          return 1 # array is not empty
      else
          return 0 # array is empty
      fi
  }

  my_array=(1 2 3)

  function push() {
      # $1 = stack name
      local stack_name="$1"
      if [ "${stack_name}" ]; then # stack exists
          if array_empty "${!stack_name}"; then
              echo "stack is empty"
          else
              echo "stack is not empty"
          fi
      else
          echo "Error: ${stack_name} does not exist."
      fi
  }

  push "my_array"

  ```

  In Bash scripting, ${!stack_name} is an example of indirect variable referencing
  or variable indirection. It's a way to use a variable whose name is stored in
  another variable.

  Let's break it down:

  `${stack_name}`: This syntax is used to reference the value of a variable. So if
  stack_name="my_array", then ${stack_name} would return "my_array".

  `${!stack_name}`: Adding the ! introduces indirection. Now, instead of getting
  the value of stack_name, you get the value of the variable whose name is stored
  in stack_name. So if stack_name="my_array" and my_array=(1 2 3), then
  ${!stack_name} would return (1 2 3).

  So, in the context of your script, `${!stack_name} allows you to pass the name
  of an array (as a string) to your push function, and then use that string to
  indirectly reference the actual array within the function.


path: Variables_and_data_types_in_Bash.md
sortable_path: Variables_and_data_types_in_Bash.md
title: Variables and datatypes in Bash
lead: ## Types
checksum: fc6431165e27bec3e2955dbf547f4094db1ea5c48191596be901043599a966a2
created: 2024-10-22 16:52:59.921197632+00:00
modified: 2024-10-19 09:01:58.29266654+00:00
metadata: {"tags":["shell"]}
body:
  ## Types

  ## Variables

  We use the equality symbol to create a variable:

  ```bash
  stringVar="My first variable"
  floatVar="50.3"
  boolVar="true"
  ```

  As there is no typing in bash, the names of these variables are purely notional.

  To invoke a variable we use special brackets:

  ```bash
  echo ${stringVar} # My first variable
  echo ${floatVar} # 50.3
  echo ${boolVar} # true
  ```

  - Quotation marks at declaration are also not strictly necessary however they
    can help avoid bugs. Also serves as a reminder that every type is basically a
    string at the end of the day

  ## Common operations

  ### Check if a variable exists

  ```sh
  if [ -v variable_name ]; then
      echo "The variable exists."
  else
      echo "The variable does not exist."
  fi
  ```

  ### Check if a variable is set (defined)

  ```sh
  if [ -z "${variable_name+x}" ]; then
      echo "The variable does not exist."
  else
      echo "The variable exists."
  fi
  ```

  ## Parameter expansion

  Still not very clear on this.

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

  # Variables and datatypes in Bash

  ## Types

  ## Variables

  We use the equality symbol to create a variable:

  ```bash
  stringVar="My first variable"
  floatVar="50.3"
  boolVar="true"
  ```

  As there is no typing in bash, the names of these variables are purely notional.

  To invoke a variable we use special brackets:

  ```bash
  echo ${stringVar} # My first variable
  echo ${floatVar} # 50.3
  echo ${boolVar} # true
  ```

  - Quotation marks at declaration are also not strictly necessary however they
    can help avoid bugs. Also serves as a reminder that every type is basically a
    string at the end of the day

  ## Common operations

  ### Check if a variable exists

  ```sh
  if [ -v variable_name ]; then
      echo "The variable exists."
  else
      echo "The variable does not exist."
  fi
  ```

  ### Check if a variable is set (defined)

  ```sh
  if [ -z "${variable_name+x}" ]; then
      echo "The variable does not exist."
  else
      echo "The variable exists."
  fi
  ```

  ## Parameter expansion

  Still not very clear on this.


path: Variables_in_C.md
sortable_path: Variables_in_C.md
title: Variables in C
lead:
  ```c
  int age = 25
  ```

body:
  ```c
  int age = 25
  ```

checksum: 5a795d624f330f27608912116a4e66a003e6534c585d38e5adfe4b8d7df0ee99
created: 2024-10-22 16:52:59.925822299+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"created":"Thursday, February 29, 2024 | 17:20","id":"r4i16p1x","tags":["C"]}
raw_content:
  ---
  id: r4i16p1x
  tags: [C]
  created: Thursday, February 29, 2024 | 17:20
  ---

  # Variables in C

  ```c
  int age = 25
  ```


path: Viewing_remote_changes_without_merging.md
sortable_path: Viewing_remote_changes_without_merging.md
title: Viewing remote changes without merging
lead:
  ```
  git fetch origin/master
  git log origin/main
  git show origin/master
  ```

body:
  ```
  git fetch origin/master
  git log origin/main
  git show origin/master
  ```

checksum: 1a5f587f96e84a733ebdf522999e5efdca8e55052d1f846baa71e7e768081e7c
created: 2024-10-22 16:52:59.932682422+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["git"]}
raw_content:
  ---
  tags: [git]
  ---

  # Viewing remote changes without merging

  ```
  git fetch origin/master
  git log origin/main
  git show origin/master
  ```


path: Views_in_relational_databases.md
sortable_path: Views_in_relational_databases.md
title: Views in relational databases
checksum: f48de8e2b132aa72d28eefba0fb3508f1baac92593009b7f0bd7be27e3a539ed
created: 2024-10-22 16:52:59.936945833+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["databases"]}
lead:
  A view is a virtual table that is based on the result-set of a query. Although
  it doesn't actually exist in the same manner as an actual table it has exactly
  the same properties and functionality. Rather than adding the raw data manually,
  you derive it from logic applied to existing actual tables.

body:
  A view is a virtual table that is based on the result-set of a query. Although
  it doesn't actually exist in the same manner as an actual table it has exactly
  the same properties and functionality. Rather than adding the raw data manually,
  you derive it from logic applied to existing actual tables.

  This is a good way of dynamically generating data and is often employed as a way
  of creating mutations without changing the underlying data structure. In SQL,
  views can be created by combining `PRIMARY` and `FOREIGN` keys.

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

  # Views in relational databases

  A view is a virtual table that is based on the result-set of a query. Although
  it doesn't actually exist in the same manner as an actual table it has exactly
  the same properties and functionality. Rather than adding the raw data manually,
  you derive it from logic applied to existing actual tables.

  This is a good way of dynamically generating data and is often employed as a way
  of creating mutations without changing the underlying data structure. In SQL,
  views can be created by combining `PRIMARY` and `FOREIGN` keys.


path: VirtualMemory.md
sortable_path: VirtualMemory.md
title: Virtual memory
checksum: d53518d5476f53f2549469757a80180fa1c29f287964788ec6185e7fa28c3f86
created: 2024-10-22 16:52:59.94229726+00:00
modified: 2024-10-19 09:18:48.224092511+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](../img/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](../img/virtual-memory-diagram.jpg)


path: Virtual_private_cloud.md
sortable_path: Virtual_private_cloud.md
title: Virtual Private Cloud
checksum: 34438b2d0b9aad65bd3698fdee839c0a140c5900550d8ed3f306cb75f0cd3306
created: 2024-10-22 16:52:59.981185733+00:00
modified: 2024-10-18 17:55:56.299060832+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:

  ![](../img/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:

  ![](../img/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: Voltage.md
sortable_path: Voltage.md
title: Voltage
lead: ## Difference of potential and the transfer of energy
checksum: aad60b4e37e2791658c5676cb4e0c66e309094763d8e8260822ed70057858666
created: 2024-10-22 16:53:00.003407945+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["physics","electricity"]}
body:
  ## Difference of potential and the transfer of energy

  We noted in the discussion of [current](Current.md) that current flows when
  there is a difference of potential between two points with negatively charged
  atoms at one point and positively charged atoms at the other.

  _Difference of potential_ is the same thing as voltage. Voltage is essential to
  current because it is the force that enables the current to flow.

  Without voltage there can be no current because in their natural state, the
  electrons in an atom are **in random motion with no direction. To produce a
  current, energy must be imparted to the electrons so that they all flow in the
  same direction.**

  Voltage is the application of this energy. Any
  [form of energy](Voltage_sources.md) that dislodges electrons from atoms can be
  used to produce current. Thus:

  > Voltage is the work required per coulomb to move a charge from one point to
  > another.

  ### Voltage exists even without current

  Given that voltage is the force that generates current, it would be natural to
  think that voltage only exists when a voltage source (such as a
  [battery](Cells_and_batteries.md)) is connected to a circuit. This however is
  not the case. Even if a 9V battery isn't connected to anything it still has a
  difference of potential of 9-volts accross its terminals. Remember voltage is
  _potential energy_ not just the actualisation of that energy.

  ## Voltage rise and voltage drops

  In circuits there are actually two types of voltage:

  1. Voltage rise
  2. Voltage drop

  ### Voltage rise

  When we introduce potential energy into a ciruit in the form of voltage, this is
  a voltage rise. The current flows from the negative terminal of the voltage
  source and returns to the positive terminal of the voltage source.

  A 12V battery connected to a circuit gives it a voltage rise of 12 volts.

  ### Voltage drop

  Voltage drop is the corrolary to voltage rise. It is the loss of energy that the
  electrons of the circuit current experience as a result of encountering
  resistance.

  As they move through the circuit the electrons encounter a **load** which is
  what we call resistance to the flow of current. As they run into this, they give
  up their energy. The relinquishing of energy happens in the form of a conversion
  of electrical energy to heat (hence why computers heat up). The amount lost is
  equal to the amount of energy imparted by the voltage rise.

  > The voltage drop in a circuit equals the the voltage rise of the circuit
  > because energy cannot be created or destroyed, only changed to another form.
  > When a voltage rise is converted to a voltage drop we say that **the energy
  > has been _consumed_ by the circuit**.

  #### Examples

  - If a 12V source is connected to a 12V lamp, the source supplies a 12V voltage
    rise and the lamp produces a 12V voltage drop.
  - If two identical 6V lamps are connected in series to the same 12V source, each
    lamp produces a 6V drop for a total of 12 volts
  - If two different lamps are connected in series to to a 12V source: a 3V and a
    9V lamp, the 9V lamp produces a drop of 9V and the 3V lamp produces a drop of
    3V. The sum of the voltage drops equals the voltage rise of 12 volts.

  These examples demonstrate that the voltage rise: voltage drop ratio always
  evens out.

  ### Kirchoff's Voltage Law

  The relationship between voltage rise and voltage drop is expressed in
  Kirchoff's Voltage Law:

  > The sum of the voltages around any closed loop in a circuit must be zero.

  The application of the Law is illustrated in the following diagram:

  ![](/img/voltage-drop.png)

  The explanation for the voltage drop at the positions $V^{A}$ and $V^{D}$ are
  obvious enough: they are at the beginning and end of the loop so are equal to
  the maximal voltage rise and minimal voltage drop, respectively.

  We can work out the voltage of the remaining voltage points by inverting
  [Ohm's Law](Ohms_Law.md): $V = I \times R$:

  For the voltage at $V^{B}$:

  $$
    0.5\textsf{mA} \times \textsf{4}k\Omega = 2 \textsf{V}
  $$

  For the voltage at $V^{C}$:

  $$
    0.5\textsf{mA} \times \textsf{6}k\Omega = 3 \textsf{V}
  $$

  The total drops (2V, 3V, 5V) are equal to the initial volatage rise (10V)

  ## Distinguishing _voltage_ from _electric field_

  It can be confusing that two different symbols often seemed to be used
  interchangeably when talking about voltage: $V$ and $E$. However, while they
  broadly point to the same phenomenon there is a difference in emphasis.

  - $V$ stands for volts or voltage conceived purely in terms of the difference in
    potential between two points: the positive and negative terminals
  - $E$ stands for electric field. This is the field that surround each electric
    charge and exerts force on all other charges in the field, attracting or
    repelling them. So it is more the physical process that the volatage
    represents rather than the numerical representation of the potential between
    the terminals.

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

  # Voltage

  ## Difference of potential and the transfer of energy

  We noted in the discussion of [current](Current.md) that current flows when
  there is a difference of potential between two points with negatively charged
  atoms at one point and positively charged atoms at the other.

  _Difference of potential_ is the same thing as voltage. Voltage is essential to
  current because it is the force that enables the current to flow.

  Without voltage there can be no current because in their natural state, the
  electrons in an atom are **in random motion with no direction. To produce a
  current, energy must be imparted to the electrons so that they all flow in the
  same direction.**

  Voltage is the application of this energy. Any
  [form of energy](Voltage_sources.md) that dislodges electrons from atoms can be
  used to produce current. Thus:

  > Voltage is the work required per coulomb to move a charge from one point to
  > another.

  ### Voltage exists even without current

  Given that voltage is the force that generates current, it would be natural to
  think that voltage only exists when a voltage source (such as a
  [battery](Cells_and_batteries.md)) is connected to a circuit. This however is
  not the case. Even if a 9V battery isn't connected to anything it still has a
  difference of potential of 9-volts accross its terminals. Remember voltage is
  _potential energy_ not just the actualisation of that energy.

  ## Voltage rise and voltage drops

  In circuits there are actually two types of voltage:

  1. Voltage rise
  2. Voltage drop

  ### Voltage rise

  When we introduce potential energy into a ciruit in the form of voltage, this is
  a voltage rise. The current flows from the negative terminal of the voltage
  source and returns to the positive terminal of the voltage source.

  A 12V battery connected to a circuit gives it a voltage rise of 12 volts.

  ### Voltage drop

  Voltage drop is the corrolary to voltage rise. It is the loss of energy that the
  electrons of the circuit current experience as a result of encountering
  resistance.

  As they move through the circuit the electrons encounter a **load** which is
  what we call resistance to the flow of current. As they run into this, they give
  up their energy. The relinquishing of energy happens in the form of a conversion
  of electrical energy to heat (hence why computers heat up). The amount lost is
  equal to the amount of energy imparted by the voltage rise.

  > The voltage drop in a circuit equals the the voltage rise of the circuit
  > because energy cannot be created or destroyed, only changed to another form.
  > When a voltage rise is converted to a voltage drop we say that **the energy
  > has been _consumed_ by the circuit**.

  #### Examples

  - If a 12V source is connected to a 12V lamp, the source supplies a 12V voltage
    rise and the lamp produces a 12V voltage drop.
  - If two identical 6V lamps are connected in series to the same 12V source, each
    lamp produces a 6V drop for a total of 12 volts
  - If two different lamps are connected in series to to a 12V source: a 3V and a
    9V lamp, the 9V lamp produces a drop of 9V and the 3V lamp produces a drop of
    3V. The sum of the voltage drops equals the voltage rise of 12 volts.

  These examples demonstrate that the voltage rise: voltage drop ratio always
  evens out.

  ### Kirchoff's Voltage Law

  The relationship between voltage rise and voltage drop is expressed in
  Kirchoff's Voltage Law:

  > The sum of the voltages around any closed loop in a circuit must be zero.

  The application of the Law is illustrated in the following diagram:

  ![](/img/voltage-drop.png)

  The explanation for the voltage drop at the positions $V^{A}$ and $V^{D}$ are
  obvious enough: they are at the beginning and end of the loop so are equal to
  the maximal voltage rise and minimal voltage drop, respectively.

  We can work out the voltage of the remaining voltage points by inverting
  [Ohm's Law](Ohms_Law.md): $V = I \times R$:

  For the voltage at $V^{B}$:

  $$
    0.5\textsf{mA} \times \textsf{4}k\Omega = 2 \textsf{V}
  $$

  For the voltage at $V^{C}$:

  $$
    0.5\textsf{mA} \times \textsf{6}k\Omega = 3 \textsf{V}
  $$

  The total drops (2V, 3V, 5V) are equal to the initial volatage rise (10V)

  ## Distinguishing _voltage_ from _electric field_

  It can be confusing that two different symbols often seemed to be used
  interchangeably when talking about voltage: $V$ and $E$. However, while they
  broadly point to the same phenomenon there is a difference in emphasis.

  - $V$ stands for volts or voltage conceived purely in terms of the difference in
    potential between two points: the positive and negative terminals
  - $E$ stands for electric field. This is the field that surround each electric
    charge and exerts force on all other charges in the field, attracting or
    repelling them. So it is more the physical process that the volatage
    represents rather than the numerical representation of the potential between
    the terminals.


path: Voltage_sources.md
sortable_path: Voltage_sources.md
title: Voltage sources
lead: The following phenomena are the main sources that we use to generate voltage:
checksum: 88d7509073e3453a58af7a34f89f36e3de1f0212048f3255ad57489a3e2bf0a2
created: 2024-10-22 16:53:00.033718655+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["physics","electricity"]}
body:
  The following phenomena are the main sources that we use to generate voltage:

  - friction
  - magnetism
  - chemicals
  - light
  - heat
  - pressure

  > In each case, energy in one state is transferred into energy as current. No
  > energy is lost or gained, it simply changes its form.

  ### Magentism (electrical generators)

  Magnetism is used as the voltage source in electrical generators and is by far
  the most common method of producing powerful and large currents at scale.

  If a conductive wire is passed through a magnetic field voltage will be produced
  so long as there is motion between the magnetic field and the conductor. A
  **generator** is a device that generates current in this manner. Generators
  themselves need to be powered. They can be powered by steam from a nuclear power
  plant, water, wind, coal or other fossil fuels.

  #### AC/DC

  Depending on how it is wired, a generator can produce **directed current** (DC)
  or **alternating current** (AC):

  - **Directed current**

    - The electrons flow in only one direction

  - **Alternating current**
    - The electrons flow in one direction and then the other

  ### Chemicals (cells and batteries)

  The chemical creation of current is the physics behind
  [batteries](Cells_and_batteries.md). Chemical current production produces
  currents on a smaller and less industrial scale than generators.

  A chemical cell consists in two dissimilar metals such as copper and zinc. We
  call these the **electrodes**. They are immersed in a salt, acid or alkaline
  solution. We call these the **electrolytes**. The electrolyte pulls the free
  electrons from the copper electrode which leaves it imbalanced with a positive
  charge. The zinc electrode attracts the free electrons from the electrolyte
  giving it a negative charge, thus a difference of potential is achieved.

  ### Light (photovoltaic cells)

  Solar energy can be converted to electrical energy through solar panels which
  are large collections of **photovoltaic cells**.

  When the surfaces of these cells are exposed to light, it dislodges electrons
  from their orbits around the surface atoms of the cell material. For each cell
  this only produces a very small amount of energy, therefore large quantities
  must be used.

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

  # Voltage sources

  The following phenomena are the main sources that we use to generate voltage:

  - friction
  - magnetism
  - chemicals
  - light
  - heat
  - pressure

  > In each case, energy in one state is transferred into energy as current. No
  > energy is lost or gained, it simply changes its form.

  ### Magentism (electrical generators)

  Magnetism is used as the voltage source in electrical generators and is by far
  the most common method of producing powerful and large currents at scale.

  If a conductive wire is passed through a magnetic field voltage will be produced
  so long as there is motion between the magnetic field and the conductor. A
  **generator** is a device that generates current in this manner. Generators
  themselves need to be powered. They can be powered by steam from a nuclear power
  plant, water, wind, coal or other fossil fuels.

  #### AC/DC

  Depending on how it is wired, a generator can produce **directed current** (DC)
  or **alternating current** (AC):

  - **Directed current**

    - The electrons flow in only one direction

  - **Alternating current**
    - The electrons flow in one direction and then the other

  ### Chemicals (cells and batteries)

  The chemical creation of current is the physics behind
  [batteries](Cells_and_batteries.md). Chemical current production produces
  currents on a smaller and less industrial scale than generators.

  A chemical cell consists in two dissimilar metals such as copper and zinc. We
  call these the **electrodes**. They are immersed in a salt, acid or alkaline
  solution. We call these the **electrolytes**. The electrolyte pulls the free
  electrons from the copper electrode which leaves it imbalanced with a positive
  charge. The zinc electrode attracts the free electrons from the electrolyte
  giving it a negative charge, thus a difference of potential is achieved.

  ### Light (photovoltaic cells)

  Solar energy can be converted to electrical energy through solar panels which
  are large collections of **photovoltaic cells**.

  When the surfaces of these cells are exposed to light, it dislodges electrons
  from their orbits around the surface atoms of the cell material. For each cell
  this only produces a very small amount of energy, therefore large quantities
  must be used.


path: What_are_disks.md
sortable_path: What_are_disks.md
title: What are disks?
lead:
  A disk is a mass storage [block_device](Devices.md) which we
  can write to and read from.

checksum: e2afba648e34963a7b5c9f4a8b9bb857ce1540de0997c2f7291479de9d905c5b
created: 2024-10-22 16:53:00.046393471+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["disks","computer-architecture"]}
body:
  A disk is a mass storage [block_device](Devices.md) which we
  can write to and read from.

  ## SCSI

  - Small Computer System Interface, responsible for handling disk access on most
    Linux systems.
  - It is a protocol that allows communicaton between printers, scanners and other
    peripherals in addition to harddisks.

  ## Disk schematic

  The following diagram represents the basic anatomy of a disk device.

  ![](/img/harddisk.png)

  - A disk is divided up into [partitions](Partitions.md)
    which are subsections of the overall disk. The kernel presents each partition
    as a [block device](Devices.md) as it would with an entire
    disk.
  - The disk dedicates a small part of its contents to a **partition table**: this
    defines the different partitions that comprise the total disk space.
  - The **filesystem** is a database of files and directories: this comprises the
    bulk of the partition and is what you interact with in
    [user space](User_Space.md) when reading and writing data.

raw_content:
  ---
  tags:
    - disks
    - computer-architecture
  ---

  # What are disks?

  A disk is a mass storage [block_device](Devices.md) which we
  can write to and read from.

  ## SCSI

  - Small Computer System Interface, responsible for handling disk access on most
    Linux systems.
  - It is a protocol that allows communicaton between printers, scanners and other
    peripherals in addition to harddisks.

  ## Disk schematic

  The following diagram represents the basic anatomy of a disk device.

  ![](/img/harddisk.png)

  - A disk is divided up into [partitions](Partitions.md)
    which are subsections of the overall disk. The kernel presents each partition
    as a [block device](Devices.md) as it would with an entire
    disk.
  - The disk dedicates a small part of its contents to a **partition table**: this
    defines the different partitions that comprise the total disk space.
  - The **filesystem** is a database of files and directories: this comprises the
    bulk of the partition and is what you interact with in
    [user space](User_Space.md) when reading and writing data.


path: What_is_memory.md
sortable_path: What_is_memory.md
title: What is memory ?
checksum: a8bdfe8e8fba49ccf433d7a0d90744c2b434915a5f1a4206998c53908a9d521a
created: 2024-10-22 16:53:00.074705255+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"created":"Friday, July 12, 2024","tags":["memory"]}
lead:
  > A CPU is just an operator on memory. It reads its instructions and data from
  > the memory and writes back out to the memory. (Ward 2021)

body:
  > A CPU is just an operator on memory. It reads its instructions and data from
  > the memory and writes back out to the memory. (Ward 2021)

  When a [CPU](CPU_architecture.md) executes a program, it needs a place to store
  the program's **instructions** and **related data**. This is the role of memory.

  The data that comprises a program is a series of bits. The basic unit of memory
  storage is a **memory cell**: a circuit that can store a single bit.

  [RAM](./DRAM_and_SRAM_memory.md) memory is _volatile_ : the memory is only
  retained whilst the computer has a power supply and is wiped when the computer
  is rebooted. This contrasts with the memory of the harddisk which is
  non-volatile and is retained after a reboot.

  Programs that are executing are loaded into memory because the chips that
  comprise memory can read and store data much faster than the harddisk. It would
  be possible to run a program from the harddisk but it would be 500 - 1000 times
  slower than memory.

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

  # What is memory ?

  > A CPU is just an operator on memory. It reads its instructions and data from
  > the memory and writes back out to the memory. (Ward 2021)

  When a [CPU](CPU_architecture.md) executes a program, it needs a place to store
  the program's **instructions** and **related data**. This is the role of memory.

  The data that comprises a program is a series of bits. The basic unit of memory
  storage is a **memory cell**: a circuit that can store a single bit.

  [RAM](./DRAM_and_SRAM_memory.md) memory is _volatile_ : the memory is only
  retained whilst the computer has a power supply and is wiped when the computer
  is rebooted. This contrasts with the memory of the harddisk which is
  non-volatile and is retained after a reboot.

  Programs that are executing are loaded into memory because the chips that
  comprise memory can read and store data much faster than the harddisk. It would
  be possible to run a program from the harddisk but it would be 500 - 1000 times
  slower than memory.


path: Whole_numbers.md
sortable_path: Whole_numbers.md
title: The set of whole numbers
lead: We recall the set of whole numbers:
checksum: 55a213e7c8d3fd57417fb5291e2452abb12fdc19266c9a6c00130544e5b85789
created: 2024-10-22 16:53:00.091628853+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["prealgebra"]}
body:
  We recall the set of whole numbers:

  $$ \mathbb{W} = {0, 1, 2, 3, ...} $$

  # The properties of $\mathbb{W}$

  > In mathematics, a **property** is any characteristic that applies to a given
  > set.

  ## The commutative property

  ### Addition

  When **adding** whole numbers, the placement of the addends does not affect the
  sum.

  Let **a**, **b** represent whole numbers, then:

  $$ a + b = b + a $$

  ### Multiplication

  When **multiplying** whole numbers the placement of the
  [multiplicands](Symbols-and-formal-conventions-80aeaf1872f94a0d97a2e8d07e3855bd)
  does not affect the
  [product](Symbols-and-formal-conventions-80aeaf1872f94a0d97a2e8d07e3855bd).

  Let **a, b** represent whole numbers, then:

  $$ a \cdot b = b \cdot a $$

  ### Subtraction

  **Subtraction** is not commutative, viz:

  $$ a - b \neq b - a $$

  ### Division

  Division is not commutative, viz:

  $$ a \div b \neq b \div a $$

  ## The associative property

  ### Addition

  When grouping symbols (parentheses, brackets, braces) are used with addition,
  the particular placement of the grouping symbols relative to each of the addends
  does not change the sum.

  Let **a**, **b, c** represent whole numbers, then:

  $$ (a + b) + c = a + (b + c) $$

  ### Multiplication

  Let **a, b, c** represent whole numbers, then:

  $$ a \cdot (b \cdot c) = (a \cdot b) \cdot c $$

  ### Subtraction

  Subtraction is not associative, viz:

  $$ (a - b) - c \neq a - (b - c) $$

  ### Division

  Division is not associative

  $$ (a \div b) \div c \neq a \div (b \div c) $$

  ## The property of additive identity

  If **a** is any whole number, then:

  $$ a + 0 = a $$

  We therefore call zero the additive identity: whenever we add zero to a whole
  number, the sum is equal to the whole number itself.

  ## The property of multiplicative identity

  If **a** is any whole number, then:

  $$ (a \cdot 1 = a) = (1 \cdot a = a) $$

  ## Multiplication by zero

  If **a** is any whole number, then:

  $$ (a \cdot 0 = 0) = (0 \cdot a = 0) $$

  ## Division by zero

  Division by zero is **undefined** but zero divided is zero.

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

  # The set of whole numbers

  We recall the set of whole numbers:

  $$ \mathbb{W} = {0, 1, 2, 3, ...} $$

  # The properties of $\mathbb{W}$

  > In mathematics, a **property** is any characteristic that applies to a given
  > set.

  ## The commutative property

  ### Addition

  When **adding** whole numbers, the placement of the addends does not affect the
  sum.

  Let **a**, **b** represent whole numbers, then:

  $$ a + b = b + a $$

  ### Multiplication

  When **multiplying** whole numbers the placement of the
  [multiplicands](Symbols-and-formal-conventions-80aeaf1872f94a0d97a2e8d07e3855bd)
  does not affect the
  [product](Symbols-and-formal-conventions-80aeaf1872f94a0d97a2e8d07e3855bd).

  Let **a, b** represent whole numbers, then:

  $$ a \cdot b = b \cdot a $$

  ### Subtraction

  **Subtraction** is not commutative, viz:

  $$ a - b \neq b - a $$

  ### Division

  Division is not commutative, viz:

  $$ a \div b \neq b \div a $$

  ## The associative property

  ### Addition

  When grouping symbols (parentheses, brackets, braces) are used with addition,
  the particular placement of the grouping symbols relative to each of the addends
  does not change the sum.

  Let **a**, **b, c** represent whole numbers, then:

  $$ (a + b) + c = a + (b + c) $$

  ### Multiplication

  Let **a, b, c** represent whole numbers, then:

  $$ a \cdot (b \cdot c) = (a \cdot b) \cdot c $$

  ### Subtraction

  Subtraction is not associative, viz:

  $$ (a - b) - c \neq a - (b - c) $$

  ### Division

  Division is not associative

  $$ (a \div b) \div c \neq a \div (b \div c) $$

  ## The property of additive identity

  If **a** is any whole number, then:

  $$ a + 0 = a $$

  We therefore call zero the additive identity: whenever we add zero to a whole
  number, the sum is equal to the whole number itself.

  ## The property of multiplicative identity

  If **a** is any whole number, then:

  $$ (a \cdot 1 = a) = (1 \cdot a = a) $$

  ## Multiplication by zero

  If **a** is any whole number, then:

  $$ (a \cdot 0 = 0) = (0 \cdot a = 0) $$

  ## Division by zero

  Division by zero is **undefined** but zero divided is zero.


path: Why_computers_use_binary.md
sortable_path: Why_computers_use_binary.md
title: Why computers use binary
lead: ## Now we know how binary works, how does it relate to computing?
checksum: f1e48d431648ff5f081d982bd3cde7673eb114fa43c6c62f39f4d8e9f23817f3
created: 2024-10-22 16:53:00.134623153+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["binary","computer-architecture"]}
body:
  ## Now we know how binary works, how does it relate to computing?

  The reason is straight forward: it is the simplest way on the level of pure
  engineering of representing numerical and logical values; both of which are the
  basic foundations of programming. An electronic circuit or transistor only needs
  to represent two states: on (1) and off (0) which corresponds to the switch on
  an electrical circuit. A single circuit representing the binary values of 1 and
  0:

  ![multi_on_off 1.gif](multi_on_off.gif)

  It would be much more complicated to have to represent ten different states
  under the decimal number system, although denary computers do exist.

  If we want more digits, we just need to add in more circuits, and we can
  represent as large a binary number as we need. We just need one switch for every
  digit we want to represent. The switches used in modern computers are so cheap
  and so small that billions can be fitted on a single circuit board.

  ![multiple_circuits.gif](multiple_circuits.gif)

  When we use the term 'switch' we actually mean the transistor components of a
  circuit. We don't need to know the physical details at this level but we can say
  that a transistor turns a current on and off. They can also be used to amplify
  the current.

  On the level of electrical engineering and in the subsequent examples using a
  light bulb and a simple circuit, the switch being OFF corresponds to the light
  being ON, this is because the switch breaks the current. So when it is unbroken,
  the current passes to the bulb. This also means that 1 corresponds to the switch
  being OFF and 0 corresponds to the switch being ON which can be confusing at
  first.

  ## From circuits to programs

  The following breaks down how we get from the binary number system to electrical
  circuits to computer programs:

  1. ”Data”= a piece or pieces of **information**

  1. In computing all data is represented in **binary form:**

     2.1. ”Binary” means that the value of a piece of data is exclusively one
     thing or another

     2.2. The values in binary code are exclusively 1 and 0 (either a piece of
     data is a 1 or it is a 0, it can never be both). Binary can also be expressed
     in logical terms where 1 is equal to `true` and 0 is equal to `false`

  1. The smallest piece of data is a **bit**

  1. We apply the binary number system to bits

  1. Thus by 1-4, **a bit is either 1 or 0**

  1. Binary form bears an isomorphic relation to switches on electrical circuits
     (the hardware of computers). Thus binary code can be _mapped onto circuits_.

     - Circuits are controlled by switches
     - A switch is a binary function: it is either on or off ”On/off”
       corresponding to 1/0.
     - In this way, switches control the flow of electric current

  1. Thus by the above, hardware (which is a physical structure made up of
     electric circuits) is **readily programmable in binary terms**

  1. Restatement of 6: in order for hardware to be programmed the program must
     provide the hardware with something that it can actually perform. The program
     must be formulated in binary code.

raw_content:
  ---
  tags:
    - binary
    - computer-architecture
  ---

  # Why computers use binary

  ## Now we know how binary works, how does it relate to computing?

  The reason is straight forward: it is the simplest way on the level of pure
  engineering of representing numerical and logical values; both of which are the
  basic foundations of programming. An electronic circuit or transistor only needs
  to represent two states: on (1) and off (0) which corresponds to the switch on
  an electrical circuit. A single circuit representing the binary values of 1 and
  0:

  ![multi_on_off 1.gif](multi_on_off.gif)

  It would be much more complicated to have to represent ten different states
  under the decimal number system, although denary computers do exist.

  If we want more digits, we just need to add in more circuits, and we can
  represent as large a binary number as we need. We just need one switch for every
  digit we want to represent. The switches used in modern computers are so cheap
  and so small that billions can be fitted on a single circuit board.

  ![multiple_circuits.gif](multiple_circuits.gif)

  When we use the term 'switch' we actually mean the transistor components of a
  circuit. We don't need to know the physical details at this level but we can say
  that a transistor turns a current on and off. They can also be used to amplify
  the current.

  On the level of electrical engineering and in the subsequent examples using a
  light bulb and a simple circuit, the switch being OFF corresponds to the light
  being ON, this is because the switch breaks the current. So when it is unbroken,
  the current passes to the bulb. This also means that 1 corresponds to the switch
  being OFF and 0 corresponds to the switch being ON which can be confusing at
  first.

  ## From circuits to programs

  The following breaks down how we get from the binary number system to electrical
  circuits to computer programs:

  1. ”Data”= a piece or pieces of **information**

  1. In computing all data is represented in **binary form:**

     2.1. ”Binary” means that the value of a piece of data is exclusively one
     thing or another

     2.2. The values in binary code are exclusively 1 and 0 (either a piece of
     data is a 1 or it is a 0, it can never be both). Binary can also be expressed
     in logical terms where 1 is equal to `true` and 0 is equal to `false`

  1. The smallest piece of data is a **bit**

  1. We apply the binary number system to bits

  1. Thus by 1-4, **a bit is either 1 or 0**

  1. Binary form bears an isomorphic relation to switches on electrical circuits
     (the hardware of computers). Thus binary code can be _mapped onto circuits_.

     - Circuits are controlled by switches
     - A switch is a binary function: it is either on or off ”On/off”
       corresponding to 1/0.
     - In this way, switches control the flow of electric current

  1. Thus by the above, hardware (which is a physical structure made up of
     electric circuits) is **readily programmable in binary terms**

  1. Restatement of 6: in order for hardware to be programmed the program must
     provide the hardware with something that it can actually perform. The program
     must be formulated in binary code.


path: Wildcards_in_SQL.md
sortable_path: Wildcards_in_SQL.md
title: Wildcards in SQL
lead:
  SQL does not use Regex. Instead it has a simpler glob-like syntax for carrying
  out string matching.

checksum: b761196c7c6f74fc89c73b5473e5d14148d6db91415904bc5456d190063aa12f
created: 2024-10-22 16:53:00.141891594+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["SQL","databases"]}
body:
  SQL does not use Regex. Instead it has a simpler glob-like syntax for carrying
  out string matching.

  In order to signal that you wish to compare by a wildcard and not a value, you
  have to use the `LIKE` keyword. The actual wildcard operator is `%` .

  In an SQL statement, the `%` wild card will match any number of occurrences of
  any character. Any characters can appear before or after ‘MacBook’ and the
  record will still be returned:

  ```sql
  SELECT name, cores, release_date
  FROM model
  WHERE name LIKE '%MacBook%';
  ```

  This wildcard only filters characters that come after `Raspberry` :

  ```sql
  SELECT name, cores, release_date
  FROM model
  WHERE name LIKE 'Raspberry%';
  ```

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

  # Wildcards in SQL

  SQL does not use Regex. Instead it has a simpler glob-like syntax for carrying
  out string matching.

  In order to signal that you wish to compare by a wildcard and not a value, you
  have to use the `LIKE` keyword. The actual wildcard operator is `%` .

  In an SQL statement, the `%` wild card will match any number of occurrences of
  any character. Any characters can appear before or after ‘MacBook’ and the
  record will still be returned:

  ```sql
  SELECT name, cores, release_date
  FROM model
  WHERE name LIKE '%MacBook%';
  ```

  This wildcard only filters characters that come after `Raspberry` :

  ```sql
  SELECT name, cores, release_date
  FROM model
  WHERE name LIKE 'Raspberry%';
  ```


path: Williams_Tube_memory.md
sortable_path: Williams_Tube_memory.md
title: Williams Tube memory
checksum: cafd2be17ab55aa9487b07d6eb6cc4b0f994a323b35a2654af5858c733326885
created: 2024-10-22 16:53:00.149592038+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"created":"Tuesday, September 17, 2024","tags":["computer-history","memory"]}
lead:
  - One of the early attempts to create RAM in computers based on the EDVAC
    stored-program architecture.

body:
  - One of the early attempts to create RAM in computers based on the EDVAC
    stored-program architecture.

  - Worked by displaying a grid of dots on a cathode-ray tube ("screen")

  - Due to the way CRTs work, this creates a small charge of static electricity
    over each dot. The charge at the location of each of the dots is read by a
    thin metal sheet just in front of the display.

  - Each dot position could be written to and read from and the pattern was
    constantly refreshed as the dots would fade over time.

  ![A Williams CRT tube](../img/williams-tube.jpg)

  ![Memory dot pattern from a Williams Tube](../img/williams-tube-dots.jpg)

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

  # Williams Tube memory

  - One of the early attempts to create RAM in computers based on the EDVAC
    stored-program architecture.

  - Worked by displaying a grid of dots on a cathode-ray tube ("screen")

  - Due to the way CRTs work, this creates a small charge of static electricity
    over each dot. The charge at the location of each of the dots is read by a
    thin metal sheet just in front of the display.

  - Each dot position could be written to and read from and the pattern was
    constantly refreshed as the dots would fade over time.

  ![A Williams CRT tube](../img/williams-tube.jpg)

  ![Memory dot pattern from a Williams Tube](../img/williams-tube-dots.jpg)


path: Working_with_CSVs_in_Python.md
sortable_path: Working_with_CSVs_in_Python.md
title: Working with CSVs in Python
lead: ## Core package
checksum: c766eccad8a6cb1d8f8a7115546fc6aa87c9e45ed09e22a331be092bbb2e2d56
created: 2024-10-22 16:53:00.155694883+00:00
modified: 2024-10-19 09:02:12.466265317+00:00
metadata: {"created":"Sunday, April 28, 2024","id":"sgtn","tags":["python","csv"]}
body:
  ## Core package

  ```py
  import csv
  ```

  ## Read and write to CSV

  ### Read

  Use standard Pythonic "read" syntax:

  ```py
  with open('./path.csv', mode="r") as csv_file:
      reader = csv.reader(csv_file)
  ```

  ### Parse values

  Having created the `reader` object, you can then loop through this as an
  iterable:

  ```py
  for row in reader:
      print(row)
  ```

  Will return something like:

  ```csv
  column something, column something else, ...
  ```

  Which we can individuate with:

  ```py
  print(row[0])
  # column something
  ```

  We can also parse the rows as a dictionary for easier individuation. We do this
  by creating a `DictReader` rather than the default `reader`:

  ```py
  ...
  dict_reader = csv.DictReader(csv_file)
  ```

  Now we can use the header row values to individuate particular columns.

  Say we have a CSV with the following headers:

  ```csv
  name, profession
  ```

  We can individuate thus:

  ```py
  for row in dict_reader
      name = row["name"]
  ```

  ### Write

  Use standard Pythonic "read" syntax:

  ```py
  with open('./new_csv_file.csv', mode="w") as csv_file:
      writer = csv.writer(csv_file)
  ```

  The above will create the file as well, it doesn't need to be pre-existing.

  This creates the writer object. To actually write a row:

  ```py
  some_list = ['thomas', 'engineer']
  for element in some_list:
      writer.writerow(element)
  ```

raw_content:
  ---
  id: sgtn
  tags: [python, csv]
  created: Sunday, April 28, 2024
  ---

  # Working with CSVs in Python

  ## Core package

  ```py
  import csv
  ```

  ## Read and write to CSV

  ### Read

  Use standard Pythonic "read" syntax:

  ```py
  with open('./path.csv', mode="r") as csv_file:
      reader = csv.reader(csv_file)
  ```

  ### Parse values

  Having created the `reader` object, you can then loop through this as an
  iterable:

  ```py
  for row in reader:
      print(row)
  ```

  Will return something like:

  ```csv
  column something, column something else, ...
  ```

  Which we can individuate with:

  ```py
  print(row[0])
  # column something
  ```

  We can also parse the rows as a dictionary for easier individuation. We do this
  by creating a `DictReader` rather than the default `reader`:

  ```py
  ...
  dict_reader = csv.DictReader(csv_file)
  ```

  Now we can use the header row values to individuate particular columns.

  Say we have a CSV with the following headers:

  ```csv
  name, profession
  ```

  We can individuate thus:

  ```py
  for row in dict_reader
      name = row["name"]
  ```

  ### Write

  Use standard Pythonic "read" syntax:

  ```py
  with open('./new_csv_file.csv', mode="w") as csv_file:
      writer = csv.writer(csv_file)
  ```

  The above will create the file as well, it doesn't need to be pre-existing.

  This creates the writer object. To actually write a row:

  ```py
  some_list = ['thomas', 'engineer']
  for element in some_list:
      writer.writerow(element)
  ```


path: Working_with_JSON_in_Python.md
sortable_path: Working_with_JSON_in_Python.md
title: Working with JSON in Python
lead: ## `json.loads()`
checksum: 7ceffd0f3280c65bdd423a236eadedbc95416fcea3b0c4331d12be2b47fefdb2
created: 2024-10-22 16:53:00.160475528+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"created":"Tuesday, June 25, 2024","id":"mjia","tags":["python","JSON"]}
body:
  ## `json.loads()`

  Convert a string containing JSON data into the native Python dictionary object.
  Equivalent to `JSON.parse()` in JavaScript.

  Will error if the input string is not properly formatted JSON. This will be
  `json.JSONDecodeError`, a subclass of the `ValueError`
  [exception](./Error_handling_in_Python.md)

  ## `json.dumps()`

  Take a JSON-formatted dictionary and convert it into a string. The reverse of
  `json.loads()` and equivalent to `JSON.stringify()` in JavaScript.

  ## JSON errors

  The `.loads()` and `.parse()` methods will throw an error if the input string is

  ## Related notes

raw_content:
  ---
  id: mjia
  tags: [python, JSON]
  created: Tuesday, June 25, 2024
  ---

  # Working with JSON in Python

  ## `json.loads()`

  Convert a string containing JSON data into the native Python dictionary object.
  Equivalent to `JSON.parse()` in JavaScript.

  Will error if the input string is not properly formatted JSON. This will be
  `json.JSONDecodeError`, a subclass of the `ValueError`
  [exception](./Error_handling_in_Python.md)

  ## `json.dumps()`

  Take a JSON-formatted dictionary and convert it into a string. The reverse of
  `json.loads()` and equivalent to `JSON.stringify()` in JavaScript.

  ## JSON errors

  The `.loads()` and `.parse()` methods will throw an error if the input string is

  ## Related notes


path: Working_with_numbers_in_Bash.md
sortable_path: Working_with_numbers_in_Bash.md
title: Working with numbers in Bash
lead: We distinguish:
checksum: d793b757573d5c77ece45d9b81efbc9a285dfe699f8221faf4f0ab9f095c96c5
created: 2024-10-22 16:53:00.172263405+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["shell"]}
body:
  We distinguish:

  - **arithmetic expansion** `$(( ))`
    - returns the result of literal numbers that can then be stored in a variable
  - **artihmetic evaluation** `(( ))`
    - perform calculations on _existing_ variables

  An example of expansion:

  ```bash
  a=3
  ((a += 3))
  echo $a
  # 6
  ((a++))
  # 7
  ```

  > Note: we do not use a dollar-sign when referring to variables within
  > arithmetic evaluation, there is no need. If we do, we get an error. This is
  > because we are using an
  > [expansion](Expansions_and_substitutions.md),
  > therefore the variables are already being interpreted as variables.

  ## Declaring variables as integers

  It is good practice to safeguard against Bash treating numbers as strings to
  declare them as integers in addition to using arithmetic evaluation, e.g:

  ```bash
  declare -i b=3
  ```

  Whilst this isn't a strict type, it means we can do this:

  ```bash
  b=$b+4
  echo b
  # 7
  ```

  Without getting `3+4` in return

  ## No decimals in bash

  Bash does not support decimal calculations natively. This is what you'd get for
  example:

  ```bash
  echo $(( 1/3 ))
  # 0 (not 0.33)
  ```

  So work with decimals you should use `awk` or `bc` ("basic calculations").

  Example of using `bc`:

  ```bash
  declare -i c=1
  declare -i d=3
  e=$(echo "scale=3; $c/$d" | bc)
  # 0.333
  ```

  ## Random numbers

  Generate a pseudo-random number between 1 and 20:

  ```bash
  echo $(( 1 + RANDOM % 10))
  # 18
  ```

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

  # Working with numbers in Bash

  We distinguish:

  - **arithmetic expansion** `$(( ))`
    - returns the result of literal numbers that can then be stored in a variable
  - **artihmetic evaluation** `(( ))`
    - perform calculations on _existing_ variables

  An example of expansion:

  ```bash
  a=3
  ((a += 3))
  echo $a
  # 6
  ((a++))
  # 7
  ```

  > Note: we do not use a dollar-sign when referring to variables within
  > arithmetic evaluation, there is no need. If we do, we get an error. This is
  > because we are using an
  > [expansion](Expansions_and_substitutions.md),
  > therefore the variables are already being interpreted as variables.

  ## Declaring variables as integers

  It is good practice to safeguard against Bash treating numbers as strings to
  declare them as integers in addition to using arithmetic evaluation, e.g:

  ```bash
  declare -i b=3
  ```

  Whilst this isn't a strict type, it means we can do this:

  ```bash
  b=$b+4
  echo b
  # 7
  ```

  Without getting `3+4` in return

  ## No decimals in bash

  Bash does not support decimal calculations natively. This is what you'd get for
  example:

  ```bash
  echo $(( 1/3 ))
  # 0 (not 0.33)
  ```

  So work with decimals you should use `awk` or `bc` ("basic calculations").

  Example of using `bc`:

  ```bash
  declare -i c=1
  declare -i d=3
  e=$(echo "scale=3; $c/$d" | bc)
  # 0.333
  ```

  ## Random numbers

  Generate a pseudo-random number between 1 and 20:

  ```bash
  echo $(( 1 + RANDOM % 10))
  # 18
  ```


path: Zero_property_of_multiplication.md
sortable_path: Zero_property_of_multiplication.md
title: Zero property of multiplication
lead: **Let $a$ represent any member of $\mathbb{W}$ or $\mathbb{Z}$ then:**
body:
  **Let $a$ represent any member of $\mathbb{W}$ or $\mathbb{Z}$ then:**

  $$ a \cdot 0 = 0 $$

checksum: c547a0d6c08c9a206ef1caaadd2811d792cf21109b64772a860ee712d21a01f0
created: 2024-10-22 16:53:00.183552474+00:00
modified: 2024-10-19 09:02:28.143224923+00:00
metadata: {"tags":["prealgebra","theorems"]}
raw_content:
  ---
  tags:
    - prealgebra
    - theorems
  ---

  # Zero property of multiplication

  **Let $a$ represent any member of $\mathbb{W}$ or $\mathbb{Z}$ then:**

  $$ a \cdot 0 = 0 $$


path: Zip_function_in_Python.md
sortable_path: Zip_function_in_Python.md
title: zip
checksum: 0e7191e6c8fbb369de0479b6d8f0ebcd60161e6cf2f4f978eb878aa8436e54a1
created: 2024-10-22 16:53:00.188263745+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["python","data-structures"]}
lead:
  The `zip` function returns a zip object when you pass in two iterator data types
  (typically two lists). This is an iterator of tuples where the first item in
  each passed iterator is paired together, and then the second item in each passed
  iterator are paired together, and so on.

body:
  The `zip` function returns a zip object when you pass in two iterator data types
  (typically two lists). This is an iterator of tuples where the first item in
  each passed iterator is paired together, and then the second item in each passed
  iterator are paired together, and so on.

  ## Example

  ```py
  a = ("John", "Charles", "Mike")
  b = ("Jenny", "Christy", "Monica", "Vicky")

  x = zip(a,b)

  print(x)
  # <zip object at 0x1514af34c340>

  # To display the resulting tuple in a readable format:

  print(tuple(x))

  (('John', 'Jenny'), ('Charles', 'Christy'), ('Mike', 'Monica'))
  ```

  As indicated above, if the input iterators are of equal lengths, Python will
  ignore the value in iterator B that doesn't have an equivalent index in iterator
  A.

  ## Real-life example

  ```py
  prompts = [
      "Enter title",
      "Enter author",
      "Enter publication date",
      "Enter year you read the book"
  ]

  keys = ["Title", "Author", "Publication_date", "Date_read"]

  book = {}

  for key, prompt in zip(keys,prompts):
      book[key] = input(prompt + ": ")

  ```

  Here I take the `prompts` and `keys` lists, collate them via `zip` and then loop
  through them to populate the `book` dictionary, which contains properties
  sourced from the `keys` list and values from the user's answers to the
  `prompts`.

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

  # `zip`

  The `zip` function returns a zip object when you pass in two iterator data types
  (typically two lists). This is an iterator of tuples where the first item in
  each passed iterator is paired together, and then the second item in each passed
  iterator are paired together, and so on.

  ## Example

  ```py
  a = ("John", "Charles", "Mike")
  b = ("Jenny", "Christy", "Monica", "Vicky")

  x = zip(a,b)

  print(x)
  # <zip object at 0x1514af34c340>

  # To display the resulting tuple in a readable format:

  print(tuple(x))

  (('John', 'Jenny'), ('Charles', 'Christy'), ('Mike', 'Monica'))
  ```

  As indicated above, if the input iterators are of equal lengths, Python will
  ignore the value in iterator B that doesn't have an equivalent index in iterator
  A.

  ## Real-life example

  ```py
  prompts = [
      "Enter title",
      "Enter author",
      "Enter publication date",
      "Enter year you read the book"
  ]

  keys = ["Title", "Author", "Publication_date", "Date_read"]

  book = {}

  for key, prompt in zip(keys,prompts):
      book[key] = input(prompt + ": ")

  ```

  Here I take the `prompts` and `keys` lists, collate them via `zip` and then loop
  through them to populate the `book` dictionary, which contains properties
  sourced from the `keys` list and values from the user's answers to the
  `prompts`.


path: fs.md
sortable_path: fs.md
title: fs module
checksum: acd2f1a9993165caa5ecf4a56b9e9dee29e00696a1f03582e784d576712099d4
created: 2024-10-22 16:53:00.203542262+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["node-js"]}
lead:
  File System is an essential built-in module of Node that contains utility
  methods for working with files and directories.

body:
  File System is an essential built-in module of Node that contains utility
  methods for working with files and directories.

  Every method associated with `fs` has a _blocking_ and _asynchronous_
  implementation. The former obviously blocks the
  [event queue](Event_loop.md), the
  latter does not.

  The synchronous methods are useful to have in some contexts but in general and
  with real-world applications, you should be using the async implementation so as
  to accord with the single-threaded event-driven architecture of Node.

  ## Common I/0 methods

  ### Read directory

  Return a string array of all files in the current directory.

  ```js
  fs.readdir("./", function (err, files) {
    if (err) {
      console.error(err);
    } else {
      console.log(files);
    }
  });
  ```

  ### Read from file

  ```js
  fs.readFile("./lorem.md", "UTF-8", function (err, fileContents) {
    console.log(fileContents);
  });
  ```

  ### Write to file

  ```js
  let md = `A new file`;

  fs.writeFile("testFile.md", md.trim(), function () {
    console.log("File was created");
  });
  ```

  ### Appending to file

  ```js
  fs.appendFile("testFile.md", "new content");
  ```

  ### Create directory

  ```js
  if (!fs.existsSync("directory_name")) {
    fs.mkdir("directory_name", function (err) {
      if (err) {
        console.err(err);
      }
    });
  } else {
    console.warn("Directory already exists");
  }
  ```

  ### Rename and remove files

  ```js
  fs.rename("./filename.js", "./newname.js", function () {
    console.log("file renamed");
  });
  ```

  ```js
  // Remove file
  fs.unlink("./file-to-delete", function (err) {
    if (err) {
      console.error(err);
    }
  });
  ```

  ### Rename and remove directories

  ```js
  // Best to use synchronous method here
  fs.rmSync("/dir", { recursive: true, force: true });
  ```

  ## Streams

  See [Handling streams with fs](Streams_in_NodeJS.md)

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

  # fs module

  File System is an essential built-in module of Node that contains utility
  methods for working with files and directories.

  Every method associated with `fs` has a _blocking_ and _asynchronous_
  implementation. The former obviously blocks the
  [event queue](Event_loop.md), the
  latter does not.

  The synchronous methods are useful to have in some contexts but in general and
  with real-world applications, you should be using the async implementation so as
  to accord with the single-threaded event-driven architecture of Node.

  ## Common I/0 methods

  ### Read directory

  Return a string array of all files in the current directory.

  ```js
  fs.readdir("./", function (err, files) {
    if (err) {
      console.error(err);
    } else {
      console.log(files);
    }
  });
  ```

  ### Read from file

  ```js
  fs.readFile("./lorem.md", "UTF-8", function (err, fileContents) {
    console.log(fileContents);
  });
  ```

  ### Write to file

  ```js
  let md = `A new file`;

  fs.writeFile("testFile.md", md.trim(), function () {
    console.log("File was created");
  });
  ```

  ### Appending to file

  ```js
  fs.appendFile("testFile.md", "new content");
  ```

  ### Create directory

  ```js
  if (!fs.existsSync("directory_name")) {
    fs.mkdir("directory_name", function (err) {
      if (err) {
        console.err(err);
      }
    });
  } else {
    console.warn("Directory already exists");
  }
  ```

  ### Rename and remove files

  ```js
  fs.rename("./filename.js", "./newname.js", function () {
    console.log("file renamed");
  });
  ```

  ```js
  // Remove file
  fs.unlink("./file-to-delete", function (err) {
    if (err) {
      console.error(err);
    }
  });
  ```

  ### Rename and remove directories

  ```js
  // Best to use synchronous method here
  fs.rmSync("/dir", { recursive: true, force: true });
  ```

  ## Streams

  See [Handling streams with fs](Streams_in_NodeJS.md)


path: http_in_Node.md
sortable_path: http_in_Node.md
title: http module
checksum: d74610fc301d67759fe76291d7e99eda1b918720fe38a694e5d28775c0b7ee25
created: 2024-10-22 16:53:00.222848316+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["node-js"]}
lead:
  The HTTP Module allows us to create a web server that listens for HTTP requests
  on a given port. It is therefore perfect for creating backends for client-side
  JavaScript.

body:
  The HTTP Module allows us to create a web server that listens for HTTP requests
  on a given port. It is therefore perfect for creating backends for client-side
  JavaScript.

  ## Creating a server

  An HTTP server is another instance of an
  [event emitter](events.md)). It
  therefore has all the same methods as the `EventEmitter` class: `on`, `emit`,
  `addListener` etc. This demonstrates again how much of Node's core functionality
  is based on event emitters.

  _Creating a server_

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

  const server = http.createServer(); // Create server as emitter

  // Register functions to run when listener is triggered
  server.on("connection", (socket) => {
    console.log("new connection...");
  });

  server.listen(3000);
  console.log("Listening on port 3000");
  ```

  This server is functionally equivalent to a generic event emitter:

  ```js
  // Raise an event
  const emitter = new EventEmitter("messageLogged");

  // Register a listener
  emitter.on("messagedLogged", function () {
    console.log("The listener was called.");
  });
  ```

  Whenever a request is made to this server, it raises an event. We can therefore
  target it with the `on` method and make it execute a function when requests are
  made.

  If we were to start the server by running the file and we then used a browser to
  navigate to the port, we would see `new connection` logged every time we refresh
  the page.

  ### Sockets and `req, res`

  A socket is a generic protocol for client-server communication. Crucially it
  **allows simultaneous communication both ways**. The client can contact the
  server but the server can also contact the client. Our listener function above
  uses a socket as the callback function but in most cases this is quite
  low-level, not distinguishing responses from requests. It is more likely that
  you would initiate a `request, resource` architecture in place of a socket:

  ```js
  const server = http.createServer((req, res) => {
    if (req.url === "/") {
      res.write("hello");
      res.end();
    }
  });
  ```

  #### Return JSON

  Below is an example of using this architecture to return JSON to the client:

  ```js
  const server = http.createServer((req, res) => {
    if (req.url === "/products") {
      res.write(JSON.stringify(["shoes", "lipstick", "cups"]));
      res.end();
    }
  });
  ```

  ### Express

  In reality you would rarely use the `http` module directly to create a server.
  This is because it is quite low level and each response must be written in a
  linear fashion as with the two URLs in the previous example. Instead we use
  Express which is a framework for creating servers and routing that is an
  abstraction on top of the core HTTP module.

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

  # `http` module

  The HTTP Module allows us to create a web server that listens for HTTP requests
  on a given port. It is therefore perfect for creating backends for client-side
  JavaScript.

  ## Creating a server

  An HTTP server is another instance of an
  [event emitter](events.md)). It
  therefore has all the same methods as the `EventEmitter` class: `on`, `emit`,
  `addListener` etc. This demonstrates again how much of Node's core functionality
  is based on event emitters.

  _Creating a server_

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

  const server = http.createServer(); // Create server as emitter

  // Register functions to run when listener is triggered
  server.on("connection", (socket) => {
    console.log("new connection...");
  });

  server.listen(3000);
  console.log("Listening on port 3000");
  ```

  This server is functionally equivalent to a generic event emitter:

  ```js
  // Raise an event
  const emitter = new EventEmitter("messageLogged");

  // Register a listener
  emitter.on("messagedLogged", function () {
    console.log("The listener was called.");
  });
  ```

  Whenever a request is made to this server, it raises an event. We can therefore
  target it with the `on` method and make it execute a function when requests are
  made.

  If we were to start the server by running the file and we then used a browser to
  navigate to the port, we would see `new connection` logged every time we refresh
  the page.

  ### Sockets and `req, res`

  A socket is a generic protocol for client-server communication. Crucially it
  **allows simultaneous communication both ways**. The client can contact the
  server but the server can also contact the client. Our listener function above
  uses a socket as the callback function but in most cases this is quite
  low-level, not distinguishing responses from requests. It is more likely that
  you would initiate a `request, resource` architecture in place of a socket:

  ```js
  const server = http.createServer((req, res) => {
    if (req.url === "/") {
      res.write("hello");
      res.end();
    }
  });
  ```

  #### Return JSON

  Below is an example of using this architecture to return JSON to the client:

  ```js
  const server = http.createServer((req, res) => {
    if (req.url === "/products") {
      res.write(JSON.stringify(["shoes", "lipstick", "cups"]));
      res.end();
    }
  });
  ```

  ### Express

  In reality you would rarely use the `http` module directly to create a server.
  This is because it is quite low level and each response must be written in a
  linear fashion as with the two URLs in the previous example. Instead we use
  Express which is a framework for creating servers and routing that is an
  abstraction on top of the core HTTP module.


path: journald.md
sortable_path: journald.md
title: journald
checksum: 44545669b98d5fb966e0e5acedac5d552eeae71380c97350c99984f74f7fcb04
created: 2024-10-22 16:53:00.239386451+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["systems-programming","Linux","procedural"]}
lead:
  `journald` is a program that comes as default with [systemd](systemd.md). It is
  a service for collecting and storing system-level log data. I keeps a track of
  all [kernel](The_kernel.md) processes. It is invaluable when tracing the source
  of problems and errors that may arise on the system level. It keeps a track of
  all kernal processes.

body:
  `journald` is a program that comes as default with [systemd](systemd.md). It is
  a service for collecting and storing system-level log data. I keeps a track of
  all [kernel](The_kernel.md) processes. It is invaluable when tracing the source
  of problems and errors that may arise on the system level. It keeps a track of
  all kernal processes.

  ![](/img/journald.png)

  ## `journalctl`

  We use `journalctl` to access the logs. The command by itself outputs the entire
  log which will be huge and hard to scroll through. We can refine the results
  with modifiers.

  ### View logs for a specific process with pid

  ```bash
  journalctl _PID=1234
  ```

  ### View logs for a specific time period

  This can be really helpful since you can bracket the most recent events which
  will be more memorable.

  ```bash
  journalctl -S -1h
  ```

  ### View logs for a specfic systemd unit

  ```bash
  journalctl -u [unit_name] -e
  ```

  ### View boot logs

  ```bash
  journalctl -b
  ```

  #### Identify specific boot

  ```bash
  journalctl --list-boots

  ```

  ### List only kernel entries to the journal

  ```bash
  journalctl -k

  ```

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

  # `journald`

  `journald` is a program that comes as default with [systemd](systemd.md). It is
  a service for collecting and storing system-level log data. I keeps a track of
  all [kernel](The_kernel.md) processes. It is invaluable when tracing the source
  of problems and errors that may arise on the system level. It keeps a track of
  all kernal processes.

  ![](/img/journald.png)

  ## `journalctl`

  We use `journalctl` to access the logs. The command by itself outputs the entire
  log which will be huge and hard to scroll through. We can refine the results
  with modifiers.

  ### View logs for a specific process with pid

  ```bash
  journalctl _PID=1234
  ```

  ### View logs for a specific time period

  This can be really helpful since you can bracket the most recent events which
  will be more memorable.

  ```bash
  journalctl -S -1h
  ```

  ### View logs for a specfic systemd unit

  ```bash
  journalctl -u [unit_name] -e
  ```

  ### View boot logs

  ```bash
  journalctl -b
  ```

  #### Identify specific boot

  ```bash
  journalctl --list-boots

  ```

  ### List only kernel entries to the journal

  ```bash
  journalctl -k

  ```


path: jq.md
sortable_path: jq.md
title: jq
lead: ## Remove property
checksum: 676854d84ae30e105cfd98da7b65640c36c42493225fcdd94a44f13d3be84d41
created: 2024-10-22 16:53:00.261975203+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["json","shell"]}
body:
  ## Remove property

  Remove property from a JSON array of objects:

  ```json
  {
    "member_inputs": [
      {
        "name": "Thomas",
        "input_type": "select"
      },
      {
        "name": "Martha",
        "input_type": "text"
      }
    ]
  }
  ```

  ```sh
  jq '.member_details |= map(del(.input_type))' memberDetails.json
  ```

  > Add '-i' to modify the source file directly (in-place)

  > Multiple properties can be deleted simply by chaining,
  > e.g.`(del(.input_type, .another_property))`

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

  # jq

  ## Remove property

  Remove property from a JSON array of objects:

  ```json
  {
    "member_inputs": [
      {
        "name": "Thomas",
        "input_type": "select"
      },
      {
        "name": "Martha",
        "input_type": "text"
      }
    ]
  }
  ```

  ```sh
  jq '.member_details |= map(del(.input_type))' memberDetails.json
  ```

  > Add '-i' to modify the source file directly (in-place)

  > Multiple properties can be deleted simply by chaining,
  > e.g.`(del(.input_type, .another_property))`


path: ps.md
sortable_path: ps.md
title: Processes (ps)
lead:
  `ps` allows us to control [user processes](The_kernel.md)
  from the shell.

checksum: db2045cff446875dd957c96c1b0b2dcaf4c16fed9ceb7c1f0f91146ea1f07297
created: 2024-10-22 16:53:00.270682411+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["shell","Linux"]}
body:
  `ps` allows us to control [user processes](The_kernel.md)
  from the shell.

  The command in its most minimal application returns the following

  ```
    PID TTY       TIME     CMD
    2437 pts/2    00:00:01 zsh
    7112 pts/2    00:00:00 ps
  ```

  With the `-e` modifier we can list more processes:

  ```
   PID TTY          TIME CMD
        1 ?        00:00:05 systemd
        2 ?        00:00:00 kthreadd
        3 ?        00:00:00 rcu_gp
        4 ?        00:00:00 rcu_par_gp
        5 ?        00:00:00 netns
        7 ?        00:00:00 kworker/0:0H-events_highpri
        9 ?        00:00:00 mm_percpu_wq
       11 ?        00:00:00 rcu_tasks_kthread
       12 ?        00:00:00 rcu_tasks_rude_kthread
       13 ?        00:00:00 rcu_tasks_trace_kthread
       14 ?        00:00:08 ksoftirqd/0
       15 ?        00:03:20 rcu_preempt
       16 ?        00:00:00 rcub/0
       17 ?        00:00:00 migration/0
       18 ?        00:00:00 idle_inject/0
       20 ?        00:00:00 cpuhp/0
       21 ?        00:00:00 cpuhp/1
       22 ?        00:00:00 idle_inject/1
       23 ?        00:00:00 migration/1
  ```

  <dl>
  <dt>pid</dt>
  <dd>Process ID: every currently running process has a unique ID<dd>
  <dt>tty</dt>
  <dd>The terminal device where the process is running<dd>
  <dt>Time</dt>
  <dd>The amount of CPU time in minutes and seconds that the process has used so far. The total amount of time that the process has spent running instructions on the processor.<dd>
  <dt>cmd</dt>
  <dd>The command used to run the program. Note this can change during the running of the program.<dd>
  </dl>

  ## Modifiers

  <dl>
  <dt><code>ps x</code></dt>
  <dd>Show all running processes<dd>
  <dt><code>ps ax</code></dt>
  <dd>Show all processes not just the ones you, the current user, own<dd>
  <dt><code>ps u</code></dt>
  <dd>Show detailed info on processes<dd>
  <dt><code>ps w</code></dt>
  <dd>Show full command names<dd>
  </dl>

  ## Process termination

  The general schema is: `kill [pid]`. This allows for process clean-up. If this
  doesn't succeed you can force with `KILL [pid]` which will terminate the process
  immediately but is obviously more risky.

  We can also start/stop processes with modifiers on `kill`:

  - `kill -STOP pid`
  - `kill -CONT pid`

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

  # Processes (`ps`)

  `ps` allows us to control [user processes](The_kernel.md)
  from the shell.

  The command in its most minimal application returns the following

  ```
    PID TTY       TIME     CMD
    2437 pts/2    00:00:01 zsh
    7112 pts/2    00:00:00 ps
  ```

  With the `-e` modifier we can list more processes:

  ```
   PID TTY          TIME CMD
        1 ?        00:00:05 systemd
        2 ?        00:00:00 kthreadd
        3 ?        00:00:00 rcu_gp
        4 ?        00:00:00 rcu_par_gp
        5 ?        00:00:00 netns
        7 ?        00:00:00 kworker/0:0H-events_highpri
        9 ?        00:00:00 mm_percpu_wq
       11 ?        00:00:00 rcu_tasks_kthread
       12 ?        00:00:00 rcu_tasks_rude_kthread
       13 ?        00:00:00 rcu_tasks_trace_kthread
       14 ?        00:00:08 ksoftirqd/0
       15 ?        00:03:20 rcu_preempt
       16 ?        00:00:00 rcub/0
       17 ?        00:00:00 migration/0
       18 ?        00:00:00 idle_inject/0
       20 ?        00:00:00 cpuhp/0
       21 ?        00:00:00 cpuhp/1
       22 ?        00:00:00 idle_inject/1
       23 ?        00:00:00 migration/1
  ```

  <dl>
  <dt>pid</dt>
  <dd>Process ID: every currently running process has a unique ID<dd>
  <dt>tty</dt>
  <dd>The terminal device where the process is running<dd>
  <dt>Time</dt>
  <dd>The amount of CPU time in minutes and seconds that the process has used so far. The total amount of time that the process has spent running instructions on the processor.<dd>
  <dt>cmd</dt>
  <dd>The command used to run the program. Note this can change during the running of the program.<dd>
  </dl>

  ## Modifiers

  <dl>
  <dt><code>ps x</code></dt>
  <dd>Show all running processes<dd>
  <dt><code>ps ax</code></dt>
  <dd>Show all processes not just the ones you, the current user, own<dd>
  <dt><code>ps u</code></dt>
  <dd>Show detailed info on processes<dd>
  <dt><code>ps w</code></dt>
  <dd>Show full command names<dd>
  </dl>

  ## Process termination

  The general schema is: `kill [pid]`. This allows for process clean-up. If this
  doesn't succeed you can force with `KILL [pid]` which will terminate the process
  immediately but is obviously more risky.

  We can also start/stop processes with modifiers on `kill`:

  - `kill -STOP pid`
  - `kill -CONT pid`


path: python_advent_learnings.md
sortable_path: python_advent_learnings.md
title: Python advent learnings
lead: ### Construct paths without hard-coding absolute path
checksum: 41e623fc64d7bdeedbc4cb74740ba94f0d812a86c637bec43edfdfb62f90f6b5
created: 2024-10-22 16:53:00.284859011+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {}
body:
  ### Construct paths without hard-coding absolute path

  ```py
  puzzle_input_path = os.path.join(os.path.dirname(__file__), "data/test_input.txt")

  ```

  ### Read file by line and store in variable

  ```py
  puzzle_input_path = os.path.join(os.path.dirname(__file__), "data/test_input.txt")

  def getPuzzleInput(file_path):
      with open(file_path, "r") as file:
          return [line.strip() for line in file]


  puzzle_input = getPuzzleInput(puzzle_input_path)

  ```

  ### Conditional tests for members of arrays

  Check that all elements are greater than zero:

  ```py
  if all(element >= 0 for element in array):
      print('All elements greater than 0')
  ```

  Check if any element is less than 0:

  ```py
  if any(element < 0 for element in array):
      print('There is an element that is less than 0')
  ```

  ### Check for substring:

  ```py
  text = "latest test"
  if "test" in text:
      return True
  ```

  ### Control flow in loop

  ```py
  for counter_values in games:
      colour = counter_values[1]
      count = int(counter_values[0])
      if colour in initial_values.keys() and count > initial_values[colour]:
          break
  else:
      valid_games_count += game_number
  ```

  The use of the `else` clause here is a special feature of Python. The `else`
  clause after a `for` loop will normally only execute when the loop has finished
  iterating over the list, but not when the loop is terminated by a `break`
  statement.

  The `else` clause is executed if the `for` loop completes normally. If the
  `break` statement is executed (i.e., if any count in `counter_values` is greater
  than the corresponding count in `initial_values`), the `for` loop is terminated
  and the `else` clause is skipped.

  Here the combination of a `for` loop and the `break` statement creates
  `if, else` logic. If the break condition is not reached, then the `else` block
  runs for every iteration of the loop.

  ### Multiple loop in list comprehension

  ```py
  games = [item.strip().split() for game in games for item in game.split(",")]
  ```

raw_content:
  # Python advent learnings

  ### Construct paths without hard-coding absolute path

  ```py
  puzzle_input_path = os.path.join(os.path.dirname(__file__), "data/test_input.txt")

  ```

  ### Read file by line and store in variable

  ```py
  puzzle_input_path = os.path.join(os.path.dirname(__file__), "data/test_input.txt")

  def getPuzzleInput(file_path):
      with open(file_path, "r") as file:
          return [line.strip() for line in file]


  puzzle_input = getPuzzleInput(puzzle_input_path)

  ```

  ### Conditional tests for members of arrays

  Check that all elements are greater than zero:

  ```py
  if all(element >= 0 for element in array):
      print('All elements greater than 0')
  ```

  Check if any element is less than 0:

  ```py
  if any(element < 0 for element in array):
      print('There is an element that is less than 0')
  ```

  ### Check for substring:

  ```py
  text = "latest test"
  if "test" in text:
      return True
  ```

  ### Control flow in loop

  ```py
  for counter_values in games:
      colour = counter_values[1]
      count = int(counter_values[0])
      if colour in initial_values.keys() and count > initial_values[colour]:
          break
  else:
      valid_games_count += game_number
  ```

  The use of the `else` clause here is a special feature of Python. The `else`
  clause after a `for` loop will normally only execute when the loop has finished
  iterating over the list, but not when the loop is terminated by a `break`
  statement.

  The `else` clause is executed if the `for` loop completes normally. If the
  `break` statement is executed (i.e., if any count in `counter_values` is greater
  than the corresponding count in `initial_values`), the `for` loop is terminated
  and the `else` clause is skipped.

  Here the combination of a `for` loop and the `break` statement creates
  `if, else` logic. If the break condition is not reached, then the `else` block
  runs for every iteration of the loop.

  ### Multiple loop in list comprehension

  ```py
  games = [item.strip().split() for game in games for item in game.split(",")]
  ```


path: systemd.md
sortable_path: systemd.md
title: systemd
checksum: 97593feb8a0b01ebe73a31ed767fe3fb51f286eb35ec06d79b9c6dfc1e3e9a59
created: 2024-10-22 16:53:00.291155057+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["systems-programming","Linux","procedural","systemd","operating-systems"]}
lead:
  Once the [boot process](Boot_process.md) has completed and the bootloader has
  located the kernel and injected it into memory the first user space program
  runs: `init` (for _initialisation_). `init` is a [daemon](Daemons.md)
  [process](./Processes.md) that continues running until shutdown and is
  responsible for starting all the processes that are prerequisites for user
  space. For example: network connections, disk access, user logins etc. `init` is
  the parent of all processes: PID1. Whilst it does a lot of its work in quick
  succession at boot time it is not limited to the this stage of the lifescycle
  but runs continuously in reponse to new user events.

body:
  Once the [boot process](Boot_process.md) has completed and the bootloader has
  located the kernel and injected it into memory the first user space program
  runs: `init` (for _initialisation_). `init` is a [daemon](Daemons.md)
  [process](./Processes.md) that continues running until shutdown and is
  responsible for starting all the processes that are prerequisites for user
  space. For example: network connections, disk access, user logins etc. `init` is
  the parent of all processes: PID1. Whilst it does a lot of its work in quick
  succession at boot time it is not limited to the this stage of the lifescycle
  but runs continuously in reponse to new user events.

  On Linux systems `systemd` is used to implement `init` and therefore `systemd`
  will always be listed as PID1.

  `systemd` is directly accessible from user space and provides a straightforward
  way to enable/disable, start/stop system level processes

  > `systemd` can track individual service daemons after they start, and group
  > together multiple processes associated with a service, giving you more power
  > and insight into exactly what is running on the system _How Linux Works: Third
  > Edition_, Brian Ward 2021

  ## How systemd works

  ### Goal-directed units

  `systemd` works on the basis of _goals_. Each goal is system task defined as a
  **unit**. A unit contains instructions and a specification of dependencies to
  other units.

  When activating a unit, `systemd` first activates the dependencies and then
  moves onto the details of the unit itself. `init` as implemented by `systemd`
  does not follow a rigid sequence every time, initialising units in the same
  sequence and waiting for one to complete before starting another. Instead it
  activates units whenever they are ready. This, its parallelized nature, is one
  of the main advantages over previous programs that managed the `init` sequence
  on Linux (such as for example System V);

  ### Unit types

  Units are organised into **unit types**. The main types that run at boot time
  are as follows:

  - service units (`.service`)
    - control service daemons
  - socket units (`.socket`)
    - handle incoming network connection request locations
  - device units (`.device`)
    - disks and other devices
  - mount units (`.mount`)
    - handle the attachment of filesystems to the system
  - target units
    - control other units by organising them into groups

  For example, at boot, a target unit called `default.target` groups together a
  number of service and mount units as dependencies. These then run in a
  graph-like dependency structure where a unit that comes late in the boot process
  can depend on several previous units making earlier branches of a dependency
  tree join back together.

  ## systemd configuration files

  Units are managed via `systemd` configuration files.

  ### Configuration file locations

  System level `systemd` config files are located in the _system unit directory_
  at `/usr/lib/systemd/system`. You shouldn't change or manipulate these files or
  attempt to add new config files here since they will be overwritten by the
  system.

  ![](/img/systemd-global-files.png) _`systemd` global unit files_

  Local definitions that relate to the specific user and where the user herself
  can define units are located in the _system configuration_ directory:
  `/etc/systemd/system`.

  ![](/img/systemd-local-files.png)

  _`systemd` local unit files, specific to the currently logged-in user_

  ### Example files

  Below is the systemd service file for the creation of UUIDs:

  ```plain
  [Unit]
  Description=Daemon for generating UUIDs
  Documentation=man:uuidd(8)
  Requires=uuidd.socket

  [Service]
  ExecStart=/usr/sbin/uuidd --socket-activation
  Restart=no
  User=uuidd
  Group=uuidd
  ProtectSystem=strict
  ProtectHome=yes
  PrivateDevices=yes
  PrivateUsers=yes
  ProtectKernelTunables=yes
  ProtectKernelModules=yes
  ProtectControlGroups=yes
  MemoryDenyWriteExecute=yes
  ReadWritePaths=/var/lib/libuuid/
  SystemCallFilter=@default @file-system @basic-io @system-service @signal @io-event @network-io

  [Install]
  Also=uuidd.socket
  ```

  - The `Unit` section provides metadata about the unit including which `systemd`
    dependencies it has
  - `Service` constitutes the main specification for the unit
  - `Install` is the call to set the dependencies running before the `Service`
    functions are accessible.

  ## systemd operations: systemctl

  The `systemctl` command is the chief way of interacting with `systemd`. You use
  it to activate and deactivate services, list their status, reload the
  configuration and so.

  ### View the dependency graph

  `systemctl status` by itself will print a long list of units grouped by their
  dependency relations. It will also provide some metadata about the current
  systemd boot context.

  ### Viewing active units

  Below I have listed the running units pertaining to bluetooth:

  ```
  $ systemctl list-units | grep bluetooth
   sys-devices-pci0000:00-0000:00:14.0-usb3-3\x2d10-3\x2d10:1.0-bluetooth-hci0-hci0:3585.device                                   loaded active plugged   /sys/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.0/bluetooth/hci0/hci0:3585
    sys-devices-pci0000:00-0000:00:14.0-usb3-3\x2d10-3\x2d10:1.0-bluetooth-hci0.device                                             loaded active plugged   /sys/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.0/bluetooth/hci0
    sys-subsystem-bluetooth-devices-hci0.device                                                                                    loaded active plugged   /sys/subsystem/bluetooth/devices/hci0
    sys-subsystem-bluetooth-devices-hci0:3585.device                                                                               loaded active plugged   /sys/subsystem/bluetooth/devices/hci0:3585
    bluetooth.service                                                                                                              loaded active running   Bluetooth service
    bluetooth.target                                                                                                               loaded active active    Bluetooth Support
  ```

  ### List jobs

  Requests to activate, reactivate and restart units are called **jobs** in
  `systemd`. They can be thought of as unit state changes. Current jobs can be
  listed with `systemctl list-jobs`.

  This will most likely return `No jobs running` if the computer has been running
  for a while. Most jobs execute at boot.

  ### Enable/disable, start/stop units

  If a unit has dependencies it is necessary to _enable_ it before starting it.
  This installs the dependencies.

  ```bash
  systemctl enable mongodb.service
  Created symlink /etc/systemd/system/multi-user.target.wants/mongodb.service → /usr/lib/systemd/system/mongodb.service.
  ```

  Then we can start:

  ```
  systemctl start mongodb.service
  ```

  To stop the service:

  ```
  systemctl stop mongodb.service
  ```

  After this we should disable it, in order to remove any symbolic links it has
  created on the system as part of the install process:

  ```bash
  systemctl disable mongodb.service
  Removed "/etc/systemd/system/multi-user.target.wants/mongodb.service".
  ```

  ## Why use systemd over cron ?

  https://mark.stosberg.com/2016-08-26-rsnapshot-and-systemd/

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

  # systemd

  Once the [boot process](Boot_process.md) has completed and the bootloader has
  located the kernel and injected it into memory the first user space program
  runs: `init` (for _initialisation_). `init` is a [daemon](Daemons.md)
  [process](./Processes.md) that continues running until shutdown and is
  responsible for starting all the processes that are prerequisites for user
  space. For example: network connections, disk access, user logins etc. `init` is
  the parent of all processes: PID1. Whilst it does a lot of its work in quick
  succession at boot time it is not limited to the this stage of the lifescycle
  but runs continuously in reponse to new user events.

  On Linux systems `systemd` is used to implement `init` and therefore `systemd`
  will always be listed as PID1.

  `systemd` is directly accessible from user space and provides a straightforward
  way to enable/disable, start/stop system level processes

  > `systemd` can track individual service daemons after they start, and group
  > together multiple processes associated with a service, giving you more power
  > and insight into exactly what is running on the system _How Linux Works: Third
  > Edition_, Brian Ward 2021

  ## How systemd works

  ### Goal-directed units

  `systemd` works on the basis of _goals_. Each goal is system task defined as a
  **unit**. A unit contains instructions and a specification of dependencies to
  other units.

  When activating a unit, `systemd` first activates the dependencies and then
  moves onto the details of the unit itself. `init` as implemented by `systemd`
  does not follow a rigid sequence every time, initialising units in the same
  sequence and waiting for one to complete before starting another. Instead it
  activates units whenever they are ready. This, its parallelized nature, is one
  of the main advantages over previous programs that managed the `init` sequence
  on Linux (such as for example System V);

  ### Unit types

  Units are organised into **unit types**. The main types that run at boot time
  are as follows:

  - service units (`.service`)
    - control service daemons
  - socket units (`.socket`)
    - handle incoming network connection request locations
  - device units (`.device`)
    - disks and other devices
  - mount units (`.mount`)
    - handle the attachment of filesystems to the system
  - target units
    - control other units by organising them into groups

  For example, at boot, a target unit called `default.target` groups together a
  number of service and mount units as dependencies. These then run in a
  graph-like dependency structure where a unit that comes late in the boot process
  can depend on several previous units making earlier branches of a dependency
  tree join back together.

  ## systemd configuration files

  Units are managed via `systemd` configuration files.

  ### Configuration file locations

  System level `systemd` config files are located in the _system unit directory_
  at `/usr/lib/systemd/system`. You shouldn't change or manipulate these files or
  attempt to add new config files here since they will be overwritten by the
  system.

  ![](/img/systemd-global-files.png) _`systemd` global unit files_

  Local definitions that relate to the specific user and where the user herself
  can define units are located in the _system configuration_ directory:
  `/etc/systemd/system`.

  ![](/img/systemd-local-files.png)

  _`systemd` local unit files, specific to the currently logged-in user_

  ### Example files

  Below is the systemd service file for the creation of UUIDs:

  ```plain
  [Unit]
  Description=Daemon for generating UUIDs
  Documentation=man:uuidd(8)
  Requires=uuidd.socket

  [Service]
  ExecStart=/usr/sbin/uuidd --socket-activation
  Restart=no
  User=uuidd
  Group=uuidd
  ProtectSystem=strict
  ProtectHome=yes
  PrivateDevices=yes
  PrivateUsers=yes
  ProtectKernelTunables=yes
  ProtectKernelModules=yes
  ProtectControlGroups=yes
  MemoryDenyWriteExecute=yes
  ReadWritePaths=/var/lib/libuuid/
  SystemCallFilter=@default @file-system @basic-io @system-service @signal @io-event @network-io

  [Install]
  Also=uuidd.socket
  ```

  - The `Unit` section provides metadata about the unit including which `systemd`
    dependencies it has
  - `Service` constitutes the main specification for the unit
  - `Install` is the call to set the dependencies running before the `Service`
    functions are accessible.

  ## systemd operations: systemctl

  The `systemctl` command is the chief way of interacting with `systemd`. You use
  it to activate and deactivate services, list their status, reload the
  configuration and so.

  ### View the dependency graph

  `systemctl status` by itself will print a long list of units grouped by their
  dependency relations. It will also provide some metadata about the current
  systemd boot context.

  ### Viewing active units

  Below I have listed the running units pertaining to bluetooth:

  ```
  $ systemctl list-units | grep bluetooth
   sys-devices-pci0000:00-0000:00:14.0-usb3-3\x2d10-3\x2d10:1.0-bluetooth-hci0-hci0:3585.device                                   loaded active plugged   /sys/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.0/bluetooth/hci0/hci0:3585
    sys-devices-pci0000:00-0000:00:14.0-usb3-3\x2d10-3\x2d10:1.0-bluetooth-hci0.device                                             loaded active plugged   /sys/devices/pci0000:00/0000:00:14.0/usb3/3-10/3-10:1.0/bluetooth/hci0
    sys-subsystem-bluetooth-devices-hci0.device                                                                                    loaded active plugged   /sys/subsystem/bluetooth/devices/hci0
    sys-subsystem-bluetooth-devices-hci0:3585.device                                                                               loaded active plugged   /sys/subsystem/bluetooth/devices/hci0:3585
    bluetooth.service                                                                                                              loaded active running   Bluetooth service
    bluetooth.target                                                                                                               loaded active active    Bluetooth Support
  ```

  ### List jobs

  Requests to activate, reactivate and restart units are called **jobs** in
  `systemd`. They can be thought of as unit state changes. Current jobs can be
  listed with `systemctl list-jobs`.

  This will most likely return `No jobs running` if the computer has been running
  for a while. Most jobs execute at boot.

  ### Enable/disable, start/stop units

  If a unit has dependencies it is necessary to _enable_ it before starting it.
  This installs the dependencies.

  ```bash
  systemctl enable mongodb.service
  Created symlink /etc/systemd/system/multi-user.target.wants/mongodb.service → /usr/lib/systemd/system/mongodb.service.
  ```

  Then we can start:

  ```
  systemctl start mongodb.service
  ```

  To stop the service:

  ```
  systemctl stop mongodb.service
  ```

  After this we should disable it, in order to remove any symbolic links it has
  created on the system as part of the install process:

  ```bash
  systemctl disable mongodb.service
  Removed "/etc/systemd/system/multi-user.target.wants/mongodb.service".
  ```

  ## Why use systemd over cron ?

  https://mark.stosberg.com/2016-08-26-rsnapshot-and-systemd/


path: systemd_status.md
sortable_path: systemd_status.md
title: Get status of a specific unit
lead: Here I have requested the status of the currently running `mongodb` unit:
checksum: 9e8922e6a289c24e65832dd419b4392ebc960a9cbbd729bef55efb3fce5146ce
created: 2024-10-22 16:53:00.315090824+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
body:
  Here I have requested the status of the currently running `mongodb` unit:

  ```
  $ systemctl status mongod
  mongodb.service - MongoDB Database Server
       Loaded: loaded (/usr/lib/systemd/system/mongodb.service; enabled; preset: disabled)
       Active: active (running) since Wed 2022-08-17 07:25:27 BST; 24h ago
         Docs: https://docs.mongodb.org/manual
     Main PID: 931 (mongod)
       Memory: 304.1M
          CPU: 2min 18.630s
       CGroup: /system.slice/mongodb.service
               └─931 /usr/bin/mongod --config /etc/mongodb.conf
  Aug 17 07:25:27 archbish systemd[1]: Started MongoDB Database Server.****
  ```

  In addition to the core info it tells us the unit type. In this case it is a
  service.

  We can also view the journal entry for the given unit. This provides you with
  its diagnostic log messages:

  ```
  journalctl --unit=mongodb.service
  - Boot b9565dfe8aca4d069143209b3aa84d8e --
  Aug 05 18:31:30 archbish systemd[1]: Started MongoDB Database Server.
  Aug 06 14:27:33 archbish systemd[1]: mongodb.service: Deactivated successfully.
  Aug 06 14:27:33 archbish systemd[1]: mongodb.service: Consumed 3min 17.598s CPU time.
  -- Boot 01922f84c3bd4b3a8f11824cf05f7320 --
  Aug 07 11:58:09 archbish systemd[1]: Started MongoDB Database Server.
  Aug 08 14:43:01 archbish systemd[1]: mongodb.service: Deactivated successfully.
  Aug 08 14:43:01 archbish systemd[1]: mongodb.service: Consumed 5min 28.760s CPU time.
  -- Boot e52b735e115c43bdad8c00462aaff395 --
  Aug 10 13:13:22 archbish systemd[1]: Started MongoDB Database Server.
  Aug 11 07:46:40 archbish systemd[1]: mongodb.service: Deactivated successfully.
  Aug 11 07:46:40 archbish systemd[1]: mongodb.service: Consumed 2min 16.629s CPU time.
  ```

  Each entry is organised around specific boots.

raw_content:
  ---
  id: v5f6xkdv
  tags:
    - systemd
    - systems-programming
    - Linux
    - procedural
  created: Thursday, February 29, 2024 | 18:19
  ---

  # Get status of a specific unit

  Here I have requested the status of the currently running `mongodb` unit:

  ```
  $ systemctl status mongod
  mongodb.service - MongoDB Database Server
       Loaded: loaded (/usr/lib/systemd/system/mongodb.service; enabled; preset: disabled)
       Active: active (running) since Wed 2022-08-17 07:25:27 BST; 24h ago
         Docs: https://docs.mongodb.org/manual
     Main PID: 931 (mongod)
       Memory: 304.1M
          CPU: 2min 18.630s
       CGroup: /system.slice/mongodb.service
               └─931 /usr/bin/mongod --config /etc/mongodb.conf
  Aug 17 07:25:27 archbish systemd[1]: Started MongoDB Database Server.****
  ```

  In addition to the core info it tells us the unit type. In this case it is a
  service.

  We can also view the journal entry for the given unit. This provides you with
  its diagnostic log messages:

  ```
  journalctl --unit=mongodb.service
  - Boot b9565dfe8aca4d069143209b3aa84d8e --
  Aug 05 18:31:30 archbish systemd[1]: Started MongoDB Database Server.
  Aug 06 14:27:33 archbish systemd[1]: mongodb.service: Deactivated successfully.
  Aug 06 14:27:33 archbish systemd[1]: mongodb.service: Consumed 3min 17.598s CPU time.
  -- Boot 01922f84c3bd4b3a8f11824cf05f7320 --
  Aug 07 11:58:09 archbish systemd[1]: Started MongoDB Database Server.
  Aug 08 14:43:01 archbish systemd[1]: mongodb.service: Deactivated successfully.
  Aug 08 14:43:01 archbish systemd[1]: mongodb.service: Consumed 5min 28.760s CPU time.
  -- Boot e52b735e115c43bdad8c00462aaff395 --
  Aug 10 13:13:22 archbish systemd[1]: Started MongoDB Database Server.
  Aug 11 07:46:40 archbish systemd[1]: mongodb.service: Deactivated successfully.
  Aug 11 07:46:40 archbish systemd[1]: mongodb.service: Consumed 2min 16.629s CPU time.
  ```

  Each entry is organised around specific boots.

metadata:
  {"created":"Thursday, February 29, 2024 | 18:19","id":"v5f6xkdv","tags":["systemd","systems-programming","Linux","procedural"]}


path: v8.md
sortable_path: v8.md
title: v8
checksum: b10a7303881a96d28e9623865c847d3ad3c39bf9056b9d8c1820f7236db2b385
created: 2024-10-22 16:53:00.322343438+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["node-js"]}
lead:
  The v8 module is useful for providing information about the resource-usage of
  your Node.js apps, such as memory usage and capacity.

body:
  The v8 module is useful for providing information about the resource-usage of
  your Node.js apps, such as memory usage and capacity.

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

  # v8

  The v8 module is useful for providing information about the resource-usage of
  your Node.js apps, such as memory usage and capacity.


path: 861cc26e_ARPA_origins.md
sortable_path: 861cc26e_ARPA_origins.md
title: 861cc26e_ARPA_origins
checksum: 7e12e88545f4b0811358fa463ec06c85aeda890751517aac1fe0e08e2e0d2058
created: 2024-10-24 05:55:55.365241747+00:00
modified: 2024-10-24 06:30:17.167329532+00:00
metadata: {"created":"Monday, October 14, 2024","tags":["fleeting","computer-history","ARPA"]}
lead:
  After Sputnik, Eisenhower wanted a dedicated, cross-departmental division within
  the US government to focus on advanced technological research so that they would
  not be caught out again. ARPA was the result.

body:
  After Sputnik, Eisenhower wanted a dedicated, cross-departmental division within
  the US government to focus on advanced technological research so that they would
  not be caught out again. ARPA was the result.

  Originally under the control of the Department of Defence (Pentagon).

  Once projects reached maturity, they would be handed over to the armed forces.

  It's name switched back and forth between ARPA/DARPA depending on congressional
  factors and funding. The switch to DARPA (and the associated re-focusing on
  military applications) indirectly precipitated a brain-drain in the late 1970s
  which contributed to the birth of the personal computer around the same time.

  Main technologies it contributed to or invented include the #ARPANET,
  time-sharing and GPS systems.

raw_content:
  ---
  tags: [fleeting, computer-history, ARPA]
  created: Monday, October 14, 2024
  ---

  # 861cc26e_ARPA_origins

  After Sputnik, Eisenhower wanted a dedicated, cross-departmental division within
  the US government to focus on advanced technological research so that they would
  not be caught out again. ARPA was the result.

  Originally under the control of the Department of Defence (Pentagon).

  Once projects reached maturity, they would be handed over to the armed forces.

  It's name switched back and forth between ARPA/DARPA depending on congressional
  factors and funding. The switch to DARPA (and the associated re-focusing on
  military applications) indirectly precipitated a brain-drain in the late 1970s
  which contributed to the birth of the personal computer around the same time.

  Main technologies it contributed to or invented include the #ARPANET,
  time-sharing and GPS systems.


path: cfbef1c4_web_precursors.md
sortable_path: cfbef1c4_web_precursors.md
title: cfbef1c4_web_precursors
lead: ## Early precursors to the commercial internet
checksum: f5f842b9d92e189c4cfc0cab9ff8cf03050a627d4e9daceee266e21473d8bf2c
created: 2024-10-24 05:55:55.373430773+00:00
modified: 2024-10-24 16:28:51.550341174+00:00
body:
  ## Early precursors to the commercial internet

  Prior to the commercial internet and the advent of the web, there were services
  in the 1980s which allowed home computers to connect to limited public networks.
  Example services: checking one's bank balance, paying bills, transferring money,
  booking holidays.

  (Presumably, the services would be hosted on a providers'
  [mainframe](Mainframe_computers.md) or mini-computer, which the consumer would
  access through something like time-sharing?)

  ![BT telephone modem](../img/modem-bt.png)

  The mechanism of connection was via a modem. A device that connected to your
  phone's cradle. Digital signals from your home computer would be converted into
  sound signals ("tones") that would travel through the phone transmitter to a
  receiver at the service provider which you would "dial-up" beforehand.

  In the US the main provider was Micronet in the 1970s, which became Compuserve
  in the 1980s and eventually AOL in the 1990s. In the UK there was Prestel
  provided by BT, first introduced in 1971.

  These services had an expensive subscription model and consequently uptake was
  limited.

  In addition there were bulletin board systems (BBS), typically administrated by
  hobbyists. Their personal computers acted as servers that other users would
  dial-up to access. They permitted the sharing of news and messages and later
  chat-rooms for realtime communication. Some of the biggest BBSs later became the
  first ISPs.

raw_content:
  ---
  tags: [fleeting, modems, computer-history, internet, bulletin-boards]
  created: Monday, October 14, 2024
  ---

  # cfbef1c4_web_precursors

  ## Early precursors to the commercial internet

  Prior to the commercial internet and the advent of the web, there were services
  in the 1980s which allowed home computers to connect to limited public networks.
  Example services: checking one's bank balance, paying bills, transferring money,
  booking holidays.

  (Presumably, the services would be hosted on a providers'
  [mainframe](Mainframe_computers.md) or mini-computer, which the consumer would
  access through something like time-sharing?)

  ![BT telephone modem](../img/modem-bt.png)

  The mechanism of connection was via a modem. A device that connected to your
  phone's cradle. Digital signals from your home computer would be converted into
  sound signals ("tones") that would travel through the phone transmitter to a
  receiver at the service provider which you would "dial-up" beforehand.

  In the US the main provider was Micronet in the 1970s, which became Compuserve
  in the 1980s and eventually AOL in the 1990s. In the UK there was Prestel
  provided by BT, first introduced in 1971.

  These services had an expensive subscription model and consequently uptake was
  limited.

  In addition there were bulletin board systems (BBS), typically administrated by
  hobbyists. Their personal computers acted as servers that other users would
  dial-up to access. They permitted the sharing of news and messages and later
  chat-rooms for realtime communication. Some of the biggest BBSs later became the
  first ISPs.

metadata:
  {"created":"Monday, October 14, 2024","tags":["fleeting","modems","computer-history","internet","bulletin-boards"]}


path: 385af4b4_Baran_distributed_networks.md
sortable_path: 385af4b4_Baran_distributed_networks.md
title: 385af4b4_Baran_distributed_networks
lead: ## Paul Baran's concept of a distributed network and influence on architecture of the ARPANET
checksum: 5137724945b6cbe0f9574feae0b76780c6a3f2a0aa0ada179ce335c4dc3d1c74
created: 2024-10-24 16:16:23.111121593+00:00
modified: 2024-10-24 16:17:48.081609842+00:00
metadata: {"created":"Tuesday, October 22, 2024","tags":["fleeting","networks","ARPANET","packet-switching"]}
body:
  ## Paul Baran's concept of a distributed network and influence on architecture of the ARPANET

  Baran worked for RAND not ARPA and his work predated the commencement of the
  ARPANET. However his model of a distributed network was highly influential on
  the design of the ARPANET and served as a consultant during the initial
  construction.

  Unlike ARPA, Baran was concerned with command and control and how a
  communication network could be resiliant enough to withstand nuclear conflict.
  He analysed the weaknesses in AT&T's telephone network and proposed a more
  robust and resiliant network model.

  If we think of the nodes of the phone network as being loci for the transferral
  of data, the phone network was highly centralised. The message would go from its
  transmitter to a central switch which would route it to the intended receiver:

  ![Centralised network](../img/centralised.png)

  The problem is obvious: if the central switch went down, all data transferance
  stop.

  In fact this is an over-simplification. The phone network at the time was
  actually decentralised:

  ![Decentralised network](../img/decentralised.png)

  This system is an improvement because each region has a switch and not all nodes
  are dependent on the central switch at all times. If the central switch goes
  down, the peripheral switches can still manage connections within their regions.
  It just means that trans-regional messages are not longer possible. Conversely,
  if one regional switch goes down, the other regions are not affected and can
  communicate between themselves and each other. So there is greater resiliance
  but still points of failure - at the central and regional switches.

  Baran conceived a _distributed_ network as his model for the most resiliant
  communications network. Instead of centralised or decentralised switches, nodes
  would connect to eachother in aggregate:

  ![Distributed network](../img/distributed.png)

  Under this system, every node can eventually access every other node through
  intermediary nodes, without going through a main switch that establishes the
  connection.

  This is far more resiliant than the previous two network types because if a
  single node or multiple nodes go down, the message can be re-routed through
  others.

  Furthermore, by distributing the connections between nodes, the overall transfer
  of data can be more efficient as there isn't a bottleneck of centralised
  switches that can block up at times of high network activity. If one pathway is
  blocked, another route can be taken.

  The distributed model also opens up the opportunity to optimise transmission.
  For a given node in the network, it becomes possible to find the shortest route
  to another node. This would be very important in designing the IMP nodes of the
  ARPANET.

raw_content:
  ---
  tags: [fleeting, networks, ARPANET, packet-switching]
  created: Tuesday, October 22, 2024
  ---

  # 385af4b4_Baran_distributed_networks

  ## Paul Baran's concept of a distributed network and influence on architecture of the ARPANET

  Baran worked for RAND not ARPA and his work predated the commencement of the
  ARPANET. However his model of a distributed network was highly influential on
  the design of the ARPANET and served as a consultant during the initial
  construction.

  Unlike ARPA, Baran was concerned with command and control and how a
  communication network could be resiliant enough to withstand nuclear conflict.
  He analysed the weaknesses in AT&T's telephone network and proposed a more
  robust and resiliant network model.

  If we think of the nodes of the phone network as being loci for the transferral
  of data, the phone network was highly centralised. The message would go from its
  transmitter to a central switch which would route it to the intended receiver:

  ![Centralised network](../img/centralised.png)

  The problem is obvious: if the central switch went down, all data transferance
  stop.

  In fact this is an over-simplification. The phone network at the time was
  actually decentralised:

  ![Decentralised network](../img/decentralised.png)

  This system is an improvement because each region has a switch and not all nodes
  are dependent on the central switch at all times. If the central switch goes
  down, the peripheral switches can still manage connections within their regions.
  It just means that trans-regional messages are not longer possible. Conversely,
  if one regional switch goes down, the other regions are not affected and can
  communicate between themselves and each other. So there is greater resiliance
  but still points of failure - at the central and regional switches.

  Baran conceived a _distributed_ network as his model for the most resiliant
  communications network. Instead of centralised or decentralised switches, nodes
  would connect to eachother in aggregate:

  ![Distributed network](../img/distributed.png)

  Under this system, every node can eventually access every other node through
  intermediary nodes, without going through a main switch that establishes the
  connection.

  This is far more resiliant than the previous two network types because if a
  single node or multiple nodes go down, the message can be re-routed through
  others.

  Furthermore, by distributing the connections between nodes, the overall transfer
  of data can be more efficient as there isn't a bottleneck of centralised
  switches that can block up at times of high network activity. If one pathway is
  blocked, another route can be taken.

  The distributed model also opens up the opportunity to optimise transmission.
  For a given node in the network, it becomes possible to find the shortest route
  to another node. This would be very important in designing the IMP nodes of the
  ARPANET.


path: e470bf3d_IMPs_in_the_ARPANET.md
sortable_path: e470bf3d_IMPs_in_the_ARPANET.md
title: e470bf3d_IMPs_in_the_ARPANET
checksum: 8e2cc9c8714a45239d8cb1752f4f5ed10ded442706db964d1a40286f7fafed8f
created: 2024-10-24 16:16:23.118044914+00:00
modified: 2024-10-25 08:09:17.144428339+00:00
metadata: {"created":"Friday, October 18, 2024","tags":["ARPANET","networks","computer-history"]}
lead:
  Within the #ARPANET, IMPs (Information Message Processors) were nodes
  distinguished from [hosts](Network_hosts.md). They were computers designed to
  receive and route the the packets from one host to another, described as
  "adaptive routing".

body:
  Within the #ARPANET, IMPs (Information Message Processors) were nodes
  distinguished from [hosts](Network_hosts.md). They were computers designed to
  receive and route the the packets from one host to another, described as
  "adaptive routing".

  Their job was to:

  - packetize or reassemble the data from the host
  - find the most efficient pathway from one host to another
  - store packets in their buffers whilst attending to other requests
  - retry the transmission a set number of times in the case of error or data
    corruption
  - confirm successful transmission

  These responsibilities required the IMP to "know" the location and addresses of
  other IMPs and hosts on the [network](Network_fundamentals.md) and their
  proximity to them.

  From the point of view of the host user, the existence of the IMPs would be
  hidden. Hosts would connect to eachother transparently - it would appear as if
  you were directly connecting to the other host rather than an IMP.

  Thus an IMP was a subnetwork within the broader ARPANET: the IMP would be the
  core of this subnetwork and the links to its direct hosts, the periperhy.
  Initially, in its full extent, the ARPANET comprised a network of these
  subnetworks.

  ![IMP diagram](../img/IMP-network.png)

  IMPs were later known as 'gateways' and were the precursors to what we today
  call routers.

raw_content:
  ---
  tags: [ARPANET, networks, computer-history]
  created: Friday, October 18, 2024
  ---

  # e470bf3d_IMPs_in_the_ARPANET

  Within the #ARPANET, IMPs (Information Message Processors) were nodes
  distinguished from [hosts](Network_hosts.md). They were computers designed to
  receive and route the the packets from one host to another, described as
  "adaptive routing".

  Their job was to:

  - packetize or reassemble the data from the host
  - find the most efficient pathway from one host to another
  - store packets in their buffers whilst attending to other requests
  - retry the transmission a set number of times in the case of error or data
    corruption
  - confirm successful transmission

  These responsibilities required the IMP to "know" the location and addresses of
  other IMPs and hosts on the [network](Network_fundamentals.md) and their
  proximity to them.

  From the point of view of the host user, the existence of the IMPs would be
  hidden. Hosts would connect to eachother transparently - it would appear as if
  you were directly connecting to the other host rather than an IMP.

  Thus an IMP was a subnetwork within the broader ARPANET: the IMP would be the
  core of this subnetwork and the links to its direct hosts, the periperhy.
  Initially, in its full extent, the ARPANET comprised a network of these
  subnetworks.

  ![IMP diagram](../img/IMP-network.png)

  IMPs were later known as 'gateways' and were the precursors to what we today
  call routers.


path: f0f70a3c_ARPANET_motivations.md
sortable_path: f0f70a3c_ARPANET_motivations.md
title: f0f70a3c_ARPANET_motivations
checksum: 40f8c1e073b574aba95598ac1905ef9b369eecac748284bac8c8785e583d949a
created: 2024-10-24 16:16:23.133708498+00:00
modified: 2024-10-24 16:26:12.714119183+00:00
metadata: {"created":"Friday, October 18, 2024","tags":["ARPA","ARPANET","networks","computer-history"]}
lead:
  It is inaccurate to say that the chief motivation in creating the ARPANET was to
  create a network that could withstand nuclear conflict, where mulitple nodes
  could be hit and the network could still go on functioning.

body:
  It is inaccurate to say that the chief motivation in creating the ARPANET was to
  create a network that could withstand nuclear conflict, where mulitple nodes
  could be hit and the network could still go on functioning.

  Ideas that originated from this concern _did_ influence the project (chiefly
  [Paul Baran's work on distributed networks](385af4b4_baran_distributed_networks.md)
  at RAND), but this was not the concern of the ARPANET.

  ARPA's objectives were focused more on reducing efficiency in the academic and
  military projects it sponsored.

  Different models of computer were used throughout the
  military-industrial-academic complex. Users required different logins and a
  knowledge of different operating systems and programming languages in order to
  be able to use them.

  At the same time, there was a duplication of hardware. Different projects would
  all require their own computer to do their research. It would be better if the
  same computer could be accessed by multiple teams through time-sharing (already
  in existene) and a network.

  Finally, results from different research projects were being duplicated.
  Different teams would be producing the same data independently of one another
  because the data could not be readily shared. If this redundancy could be
  removed, and the data accessed and shared between multiple projects, teams could
  focus on new research and the production of novel applications.

raw_content:
  ---
  tags: [ARPA, ARPANET, networks, computer-history]
  created: Friday, October 18, 2024
  ---

  # f0f70a3c_ARPANET_motivations

  It is inaccurate to say that the chief motivation in creating the ARPANET was to
  create a network that could withstand nuclear conflict, where mulitple nodes
  could be hit and the network could still go on functioning.

  Ideas that originated from this concern _did_ influence the project (chiefly
  [Paul Baran's work on distributed networks](385af4b4_baran_distributed_networks.md)
  at RAND), but this was not the concern of the ARPANET.

  ARPA's objectives were focused more on reducing efficiency in the academic and
  military projects it sponsored.

  Different models of computer were used throughout the
  military-industrial-academic complex. Users required different logins and a
  knowledge of different operating systems and programming languages in order to
  be able to use them.

  At the same time, there was a duplication of hardware. Different projects would
  all require their own computer to do their research. It would be better if the
  same computer could be accessed by multiple teams through time-sharing (already
  in existene) and a network.

  Finally, results from different research projects were being duplicated.
  Different teams would be producing the same data independently of one another
  because the data could not be readily shared. If this redundancy could be
  removed, and the data accessed and shared between multiple projects, teams could
  focus on new research and the production of novel applications.


path: 653c0b1d_host_protocols_of_ARPANET.md
sortable_path: 653c0b1d_host_protocols_of_ARPANET.md
title: 653c0b1d_host_protocols_of_ARPANET
checksum: b73ca9d5f96d7c34a742f9769cd75e1c91208f05f5a176784e63462be7724e76
created: 2024-10-24 19:01:28.82315652+00:00
modified: 2024-10-25 08:01:54.245487764+00:00
metadata: {"created":"Wednesday, October 23, 2024","tags":["ARPANET","computer-history","networks"]}
lead:
  The responsibility for enabling communication between nodes fell to the hosts,
  not the [IMPs](e470bf3d_IMPs_in_the_ARPANET.md). At the lowest level (equivalent
  to the [link layer](Link_Layer_of_Internet_Protocol.md) in the subsequent
  internet?), this consisted in moving raw bits between hosts regardless of what
  specific type of data they encoded.

body:
  The responsibility for enabling communication between nodes fell to the hosts,
  not the [IMPs](e470bf3d_IMPs_in_the_ARPANET.md). At the lowest level (equivalent
  to the [link layer](Link_Layer_of_Internet_Protocol.md) in the subsequent
  internet?), this consisted in moving raw bits between hosts regardless of what
  specific type of data they encoded.

  In the book (_Where Wizards Stay Up Late_), there is a great description by
  analogy:

  > Some water out of the tap is used for making coffee, some for washing dishes,
  > some for bathing. The pipe and the faucet do not care about this; they convey
  > the water regardless.

raw_content:
  ---
  tags: [ARPANET, computer-history, networks]
  created: Wednesday, October 23, 2024
  ---

  # 653c0b1d_host_protocols_of_ARPANET

  The responsibility for enabling communication between nodes fell to the hosts,
  not the [IMPs](e470bf3d_IMPs_in_the_ARPANET.md). At the lowest level (equivalent
  to the [link layer](Link_Layer_of_Internet_Protocol.md) in the subsequent
  internet?), this consisted in moving raw bits between hosts regardless of what
  specific type of data they encoded.

  In the book (_Where Wizards Stay Up Late_), there is a great description by
  analogy:

  > Some water out of the tap is used for making coffee, some for washing dishes,
  > some for bathing. The pipe and the faucet do not care about this; they convey
  > the water regardless.


path: c8173d17_TIMPs.md
sortable_path: c8173d17_TIMPs.md
title: c8173d17_TIMPs
checksum: 8229d083e090ae2c63d332d19e6c4c8f093943706d8e20948dbdc7310a98fbef
created: 2024-10-25 08:01:22.030508631+00:00
modified: 2024-10-25 08:09:15.987739105+00:00
metadata: {"created":"Friday, October 25, 2024","tags":["ARPANET","networks","computer-history"]}
lead:
  After the initial ARPANET was complete, the next major milestone was to enable
  access to the network regardless of one's proximity to a host node with an IMP
  connection.

body:
  After the initial ARPANET was complete, the next major milestone was to enable
  access to the network regardless of one's proximity to a host node with an IMP
  connection.

  The idea was to allow a computer to access resources on another computer
  directly without connecting to a host first. This connective computer would
  connect to an IMP directly (but still transparently) as a 'dumb terminal' as
  with time-sharing and would not be a fully-fledged computing device. These
  devices would be called _Terminal_ Information Processors (TIMPs) for this
  reason.

  The development of TIMPs makes it clearer that the host machines on the ARPANET
  were not just connection and transmission nodes for their own purpose, they were
  loci for other non-host computers to gain access to an IMP, and thereby, the
  broader network. In other words computers would connect to a host which
  sustained a connection to an IMP.

raw_content:
  ---
  tags: [ARPANET, networks, computer-history]
  created: Friday, October 25, 2024
  ---

  # c8173d17_TIMPs

  After the initial ARPANET was complete, the next major milestone was to enable
  access to the network regardless of one's proximity to a host node with an IMP
  connection.

  The idea was to allow a computer to access resources on another computer
  directly without connecting to a host first. This connective computer would
  connect to an IMP directly (but still transparently) as a 'dumb terminal' as
  with time-sharing and would not be a fully-fledged computing device. These
  devices would be called _Terminal_ Information Processors (TIMPs) for this
  reason.

  The development of TIMPs makes it clearer that the host machines on the ARPANET
  were not just connection and transmission nodes for their own purpose, they were
  loci for other non-host computers to gain access to an IMP, and thereby, the
  broader network. In other words computers would connect to a host which
  sustained a connection to an IMP.


path: Writing_to_files_in_Python.md
sortable_path: Writing_to_files_in_Python.md
title: Writing to files in Python
lead: We create a file object with `open()` and use the `write` method:
checksum: 8449ca06c0ffa866293f7f3fb684a537bc7be485d9ca62c0c64350fad0555531
created: 2024-10-25 08:18:18.503647492+00:00
modified: 2024-10-25 08:34:09.192824747+00:00
metadata: {"created":"Friday, October 25, 2024","tags":["python","file-system","procedural"]}
body:
  We create a file object with `open()` and use the `write` method:

  ```py
  # Open file in write mode
  file = open("example.txt", "w")

  # Write some text to the file
  file.write("Hello, this is an example text written using Python.")

  # Close the file
  file.close()
  ```

  Alternatively we use `with open` which automatically closes the file:

  ```py

  with open("example.txt", "w") as file
      file.write('some lines')

  ```

  > Note that in the above example, if the file does not already exist, it will
  > create it. If it does exist, it will overwrite its contents with the new data.
  > So we use `write` to create new files as well as to write to existing files.

raw_content:
  ---
  tags: [python, file-system, procedural]
  created: Friday, October 25, 2024
  ---

  # Writing to files in Python

  We create a file object with `open()` and use the `write` method:

  ```py
  # Open file in write mode
  file = open("example.txt", "w")

  # Write some text to the file
  file.write("Hello, this is an example text written using Python.")

  # Close the file
  file.close()
  ```

  Alternatively we use `with open` which automatically closes the file:

  ```py

  with open("example.txt", "w") as file
      file.write('some lines')

  ```

  > Note that in the above example, if the file does not already exist, it will
  > create it. If it does exist, it will overwrite its contents with the new data.
  > So we use `write` to create new files as well as to write to existing files.


path: Appending_to_files_in_Python.md
sortable_path: Appending_to_files_in_Python.md
title: Appending to files in Python
checksum: 86df2885240356b35bb9a5ca0451e1352fbf4df4c9e3cd916d9c29ad8e92fd14
created: 2024-10-25 08:22:31.5155199+00:00
modified: 2024-10-25 08:28:57.623299509+00:00
metadata: {"created":"Friday, October 25, 2024","tags":["python","file-system","procedural"]}
lead:
  ```py
  file = open("example.txt", "a")
  file.write('Add something to the end of the file')
  file.close()
  ```

body:
  ```py
  file = open("example.txt", "a")
  file.write('Add something to the end of the file')
  file.close()
  ```

  ```py
  with open("example.txt", "a"):
      file.write("Add something to the end of the file")
  file.close()
  ```

raw_content:
  ---
  tags: [python, file-system, procedural]
  created: Friday, October 25, 2024
  ---

  # Appending to files in Python

  ```py
  file = open("example.txt", "a")
  file.write('Add something to the end of the file')
  file.close()
  ```

  ```py
  with open("example.txt", "a"):
      file.write("Add something to the end of the file")
  file.close()
  ```


path: File_system_error_handling_in_Python.md
sortable_path: File_system_error_handling_in_Python.md
title: File system error handling in Python
lead:
  The [error handler](Error_handling_in_Python.md) in the case of a file not being
  found:

checksum: 4af222636234d404e53c1a463ab6687a73dea80ae94161e2ea811569c1ea9ae5
created: 2024-10-25 08:29:32.499927658+00:00
modified: 2024-10-25 08:31:07.462477033+00:00
metadata: {"created":"Friday, October 25, 2024","tags":["python","file-system","procedural"]}
body:
  The [error handler](Error_handling_in_Python.md) in the case of a file not being
  found:

  ```py
  try:
      with open('filename.txt', 'r') as file:
      contents = file.readlines()
      for line in lines:
          print(line)
  except FileNotFoundError as err:
      print("File does not exist")
      print(err)
  ```

raw_content:
  ---
  tags: [python, file-system, procedural]
  created: Friday, October 25, 2024
  ---

  # File system error handling in Python

  The [error handler](Error_handling_in_Python.md) in the case of a file not being
  found:

  ```py
  try:
      with open('filename.txt', 'r') as file:
      contents = file.readlines()
      for line in lines:
          print(line)
  except FileNotFoundError as err:
      print("File does not exist")
      print(err)
  ```


path: With_open_in_Python.md
sortable_path: With_open_in_Python.md
title: With open in Python
checksum: 2de8f5b1da55138664ebf60cb770e8e97086f2fd1e888d75313b2c6ef83c26bc
created: 2024-10-25 08:32:29.167088979+00:00
modified: 2024-10-25 08:47:03.18778379+00:00
metadata: {"created":"Friday, October 25, 2024","tags":["python"]}
lead:
  We use the `open()` method to create a **file object** that allows us to read,
  write and append to files.

body:
  We use the `open()` method to create a **file object** that allows us to read,
  write and append to files.

  The general syntax is as follows:

  ```py
  file_object = open(<file_name>, <access_mode>)
  ```

  `<file_name>` is a path to the file you want to read, create or modify. The
  `<access_mode>` denotes the mode in which to open the file. The most frequently
  used are:

  - `r`
    - [read](Reading_files_in_Python.md)
  - `w`
    - [write](Writing_to_files_in_Python.md)
  - `a`
    - [append](Appending_to_files_in_Python.md)

  When we have finished with an I/O operation, such as reading from or writing to
  a file, we must call `file.close()` to terminate the process. This removes the
  reference to the file from memory.

  A more pythonic and concise way of reading files and closing them is to use
  `with...as` syntax. When this phrasing is used, a self-contained context is
  created for the I/O operation that closes the file automatically.

  ```py
  with open('filename.txt', 'r') as file:
      contents = file.read()
      print(contents)
  ```

raw_content:
  ---
  tags: [python]
  created: Friday, October 25, 2024
  ---

  # With open in Python

  We use the `open()` method to create a **file object** that allows us to read,
  write and append to files.

  The general syntax is as follows:

  ```py
  file_object = open(<file_name>, <access_mode>)
  ```

  `<file_name>` is a path to the file you want to read, create or modify. The
  `<access_mode>` denotes the mode in which to open the file. The most frequently
  used are:

  - `r`
    - [read](Reading_files_in_Python.md)
  - `w`
    - [write](Writing_to_files_in_Python.md)
  - `a`
    - [append](Appending_to_files_in_Python.md)

  When we have finished with an I/O operation, such as reading from or writing to
  a file, we must call `file.close()` to terminate the process. This removes the
  reference to the file from memory.

  A more pythonic and concise way of reading files and closing them is to use
  `with...as` syntax. When this phrasing is used, a self-contained context is
  created for the I/O operation that closes the file automatically.

  ```py
  with open('filename.txt', 'r') as file:
      contents = file.read()
      print(contents)
  ```


path: Reading_files_in_Python.md
sortable_path: Reading_files_in_Python.md
title: Reading files in Python
checksum: d4054381a2834c5d28cb3849e60d29f8763192767e0cef52c0c2b3c0856c510c
created: 2024-10-25 08:34:16.855774779+00:00
modified: 2024-10-25 08:51:17.685917996+00:00
metadata: {"created":"Friday, October 25, 2024","tags":["python","file-system","procedural"]}
lead:
  Once a file object has been intialised with `open()` there are several ways in
  which the content can be read:

body:
  Once a file object has been intialised with `open()` there are several ways in
  which the content can be read:

  | Read method | Behaviour                                                                                                                                |
  | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
  | `read`      | Return the entire contents of the file as a single string.                                                                               |
  | `readline`  | Read the contents of a file a line at a time. You would combine this with a loop so that you can do something with each individual line. |
  | `readlines` | Return a list of all the lines in a file. Each line will be an element in the list.                                                      |

  ## Read

  `read` reads the entire contents of a file and returns it as a single string.

  `read()` reads the entire file into memory at once, so it may not be suitable
  for very large files that exceed the available memory of your system. In such
  cases, you can use the `readline()` method to read the file line by line
  instead.

  The basic syntax is as follows:

  ```py
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Read the entire contents of the file
  contents = file.read()

  # We could also limit the read to a number of characters:
  contents = file.read(100)

  # Close the file
  file.close()
  ```

  Once we have the file contents stored, we can then interact with it. The
  standard way of doing this is to parse it line by line. For example, say we were
  reading a CSV:

  ```py
  lines = content.split(',')
  for line in lines:
      if line: # if the line is not empty
          # do something with line
  ```

  ## Readline

  > The readline() method in Python is used to read a single line from a file. It
  > is typically used when you want to process a file line by line, rather than
  > reading the entire file into memory at once.

  `readline()` returns the line including the newline character at the end of the
  line, so you may need to strip this character off using the strip() method
  before processing the line further.

  ```python
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Loop through the file, reading one line at a time
  line = file.readline()
  while line:
      # Strip off the newline character
      line = line.strip()

      # Do something with the line
      print(line)

      # Read the next line
      line = file.readline()

  # Close the file
  file.close()
  ```

  ## Readlines

  The `readlines()` method is used to read all the lines of a file and return them
  as a list of strings, where each element of the list is a line from the file.

  ```py
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Read all the lines of the file and store them in a list
  lines = file.readlines()

  # Loop through the lines and print them to the console
  for line in lines:
      print(line)

  # Close the file
  file.close()
  `
  ```

raw_content:
  ---
  tags: [python, file-system, procedural]
  created: Friday, October 25, 2024
  ---

  # Reading files in Python

  Once a file object has been intialised with `open()` there are several ways in
  which the content can be read:

  | Read method | Behaviour                                                                                                                                |
  | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
  | `read`      | Return the entire contents of the file as a single string.                                                                               |
  | `readline`  | Read the contents of a file a line at a time. You would combine this with a loop so that you can do something with each individual line. |
  | `readlines` | Return a list of all the lines in a file. Each line will be an element in the list.                                                      |

  ## Read

  `read` reads the entire contents of a file and returns it as a single string.

  `read()` reads the entire file into memory at once, so it may not be suitable
  for very large files that exceed the available memory of your system. In such
  cases, you can use the `readline()` method to read the file line by line
  instead.

  The basic syntax is as follows:

  ```py
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Read the entire contents of the file
  contents = file.read()

  # We could also limit the read to a number of characters:
  contents = file.read(100)

  # Close the file
  file.close()
  ```

  Once we have the file contents stored, we can then interact with it. The
  standard way of doing this is to parse it line by line. For example, say we were
  reading a CSV:

  ```py
  lines = content.split(',')
  for line in lines:
      if line: # if the line is not empty
          # do something with line
  ```

  ## Readline

  > The readline() method in Python is used to read a single line from a file. It
  > is typically used when you want to process a file line by line, rather than
  > reading the entire file into memory at once.

  `readline()` returns the line including the newline character at the end of the
  line, so you may need to strip this character off using the strip() method
  before processing the line further.

  ```python
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Loop through the file, reading one line at a time
  line = file.readline()
  while line:
      # Strip off the newline character
      line = line.strip()

      # Do something with the line
      print(line)

      # Read the next line
      line = file.readline()

  # Close the file
  file.close()
  ```

  ## Readlines

  The `readlines()` method is used to read all the lines of a file and return them
  as a list of strings, where each element of the list is a line from the file.

  ```py
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Read all the lines of the file and store them in a list
  lines = file.readlines()

  # Loop through the lines and print them to the console
  for line in lines:
      print(line)

  # Close the file
  file.close()
  `
  ```


path: File_CRUD_operations_in_Python.md
sortable_path: File_CRUD_operations_in_Python.md
title: File operations in Python
lead: Most create, delete, move etc operations invoke the inbuilt `os` module.
checksum: e7393a7ec678ac542ec3616a78389b1e511cb26818f32893019cce37097c8da6
created: 2024-10-25 08:48:17.103921249+00:00
modified: 2024-10-25 12:17:17.239363379+00:00
metadata: {"created":"Friday, October 25, 2024","tags":["python","file-system","procedural"]}
body:
  Most create, delete, move etc operations invoke the inbuilt `os` module.

  // Add directory CRUD operations in Python

  ## Renaming files (moving)

  ```py
  import os
  os.rename('original-file-name.txt', 'new-file-name.txt')
  ```

  ## Deleting files

  ```py
  import os
  os.remove('file-name.txt')
  ```

raw_content:
  ---
  tags: [python, file-system, procedural]
  created: Friday, October 25, 2024
  ---

  # File operations in Python

  Most create, delete, move etc operations invoke the inbuilt `os` module.

  // Add directory CRUD operations in Python

  ## Renaming files (moving)

  ```py
  import os
  os.rename('original-file-name.txt', 'new-file-name.txt')
  ```

  ## Deleting files

  ```py
  import os
  os.remove('file-name.txt')
  ```


path: File_operations_in_Python.md
sortable_path: File_operations_in_Python.md
title: File operations in Python
lead: Most create, delete, move etc operations invoke the inbuilt `os` module.
checksum: 48e81f9ab7a9010fd29a04828557d98a39a76c70a53132e45849ebe4348a93a2
created: 2024-10-25 12:17:17.250189136+00:00
modified: 2024-11-03 14:55:44.056312533+00:00
metadata: {"created":"Friday, October 25, 2024","tags":["python","file-system","procedural"]}
body:
  Most create, delete, move etc operations invoke the inbuilt `os` module.

  // Add directory CRUD operations in Python

  ## Renaming files (moving)

  ```py
  import os
  os.rename('original-file-name.txt', 'new-file-name.txt')
  ```

  ## Deleting files

  ```py
  import os
  os.remove('file-name.txt')
  ```

  ## Check file exists

  ```python
  import os

  file_exists = os.exists('/file/path')

  ```

raw_content:
  ---
  tags: [python, file-system, procedural]
  created: Friday, October 25, 2024
  ---

  # File operations in Python

  Most create, delete, move etc operations invoke the inbuilt `os` module.

  // Add directory CRUD operations in Python

  ## Renaming files (moving)

  ```py
  import os
  os.rename('original-file-name.txt', 'new-file-name.txt')
  ```

  ## Deleting files

  ```py
  import os
  os.remove('file-name.txt')
  ```

  ## Check file exists

  ```python
  import os

  file_exists = os.exists('/file/path')

  ```


path: Working_with_directories_in_Python.md
sortable_path: Working_with_directories_in_Python.md
title: Working with directories in Python
lead: ## List files in a directory
checksum: 2734ba7b36c0c0c6edd882a554f6aef2fa44a1c6043e58cff78e880870b9ca00
created: 2024-10-25 12:19:22.201874957+00:00
modified: 2024-10-25 12:27:31.879977908+00:00
metadata: {"created":"Friday, October 25, 2024","tags":["python","file-system","procedural"]}
body:
  ## List files in a directory

  ```py
  # Here we list the file exts of files in a dir
  import os
  from pathlib import Path

  with os.scandir("/a/directory") as dir:
      for entry in dir:
          print(Path(entry).suffix)
  ```

  ## Create a directory

  ```py
  import os

  os.makedirs("/name/of/directory")
  ```

  ### Check if directory exists, remove if so

  ```py
  import os
  import shutil

  if os.path.isdir("/name/of/directory")
      shutil.rmtree(target_dir)
  ```

  ## Copy a nested directory from location to another

  ```py
  import shutil

  shutil.copytree("/source/dir", "/target/dir", dir_exist_ok=True)
  ```

raw_content:
  ---
  tags: [python, file-system, procedural]
  created: Friday, October 25, 2024
  ---

  # Working with directories in Python

  ## List files in a directory

  ```py
  # Here we list the file exts of files in a dir
  import os
  from pathlib import Path

  with os.scandir("/a/directory") as dir:
      for entry in dir:
          print(Path(entry).suffix)
  ```

  ## Create a directory

  ```py
  import os

  os.makedirs("/name/of/directory")
  ```

  ### Check if directory exists, remove if so

  ```py
  import os
  import shutil

  if os.path.isdir("/name/of/directory")
      shutil.rmtree(target_dir)
  ```

  ## Copy a nested directory from location to another

  ```py
  import shutil

  shutil.copytree("/source/dir", "/target/dir", dir_exist_ok=True)
  ```


path: 4da22434_from_ARPANET_to_Internet.md
sortable_path: 4da22434_from_ARPANET_to_Internet.md
title: 4da22434_from_ARPANET_to_Internet
lead:
  By the mid-1970s many additional computer networks had emerged which also used
  #packet-switching.

checksum: 7e52f41b102cdf98731fa8105feb6c0d0d6384401194b8b0264211a08f8959b2
created: 2024-10-28 13:09:11.374652525+00:00
modified: 2024-10-28 15:54:00.664499304+00:00
metadata: {"created":"Sunday, October 27, 2024","tags":["internet","ARPANET"]}
body:
  By the mid-1970s many additional computer networks had emerged which also used
  #packet-switching.

  It was the success of ARPANET as a "proof of concept" for packet-switching that
  led to these additional networks being created in other countries and contexts.
  Some used different modes of transmission than the #ARPANET's phone-wire based
  system such as radio waves and satellites. However this created a problem: these
  networks were siloed from each other and a bridge was needed to connect them.

  Those working on the ARPANET steering committee (the Information Processing
  Techniques Office (IPTO) within ARPA/DARPA) began to discuss what they called a
  "concatenated network" or "CATENET" that would connect these different networks
  together. Thus emerged the idea of a _network of networks_ or _inter-network_.

  For this to work it would be necessary to construct "gateways": routing
  computers that would hand-off messages from one network to another.

  This required a new protocol because the Network Control Protocol was only
  designed to work _within_ the ARPANET with its IMPs. Unlike the IMPs, the
  gateways would not be responsible for reconstituting packets or ensuring
  reliability, this would be shifted to the hosts. The protocol was the
  [Transmission Control Protocol/Internet Protocol](Internet_Layer_of_Internet_Protocol.md).
  TCP would be responsible for packet assembly and disassembly and IP for routing.

  The "common language" of TCP/IP and the growth in networks in addition to the
  ARPANET is what formed the Internet. Different commercial groups and research
  enterprises inaugurated their own networks and, one-by-one, they adopted TCP and
  became accessible to each other.

  As the ARPANET was retired, its individual sites either became nodes within the
  Internet or they split off into sub-networks such as MILNET (which was part of
  the ARPANET retained for classified military communication). The IMPs were
  retired in favour of TCP/IP routers.

  _The ARPANET in 1973_

  ![](../img/Arpanet_map_1973.jpg)

  _The ARPANET as part of an inter-network 1977_

  ![](../img/internetworking_1977.jpg)

  _The ARPANET in 1977_

  ![](../img/Arpanet_logical_map,_march_1977.png)

  _The Internet in 1984_

  ![](../img/internet_1984.jpg)

raw_content:
  ---
  tags: [internet, ARPANET]
  created: Sunday, October 27, 2024
  ---

  # 4da22434_from_ARPANET_to_Internet

  By the mid-1970s many additional computer networks had emerged which also used
  #packet-switching.

  It was the success of ARPANET as a "proof of concept" for packet-switching that
  led to these additional networks being created in other countries and contexts.
  Some used different modes of transmission than the #ARPANET's phone-wire based
  system such as radio waves and satellites. However this created a problem: these
  networks were siloed from each other and a bridge was needed to connect them.

  Those working on the ARPANET steering committee (the Information Processing
  Techniques Office (IPTO) within ARPA/DARPA) began to discuss what they called a
  "concatenated network" or "CATENET" that would connect these different networks
  together. Thus emerged the idea of a _network of networks_ or _inter-network_.

  For this to work it would be necessary to construct "gateways": routing
  computers that would hand-off messages from one network to another.

  This required a new protocol because the Network Control Protocol was only
  designed to work _within_ the ARPANET with its IMPs. Unlike the IMPs, the
  gateways would not be responsible for reconstituting packets or ensuring
  reliability, this would be shifted to the hosts. The protocol was the
  [Transmission Control Protocol/Internet Protocol](Internet_Layer_of_Internet_Protocol.md).
  TCP would be responsible for packet assembly and disassembly and IP for routing.

  The "common language" of TCP/IP and the growth in networks in addition to the
  ARPANET is what formed the Internet. Different commercial groups and research
  enterprises inaugurated their own networks and, one-by-one, they adopted TCP and
  became accessible to each other.

  As the ARPANET was retired, its individual sites either became nodes within the
  Internet or they split off into sub-networks such as MILNET (which was part of
  the ARPANET retained for classified military communication). The IMPs were
  retired in favour of TCP/IP routers.

  _The ARPANET in 1973_

  ![](../img/Arpanet_map_1973.jpg)

  _The ARPANET as part of an inter-network 1977_

  ![](../img/internetworking_1977.jpg)

  _The ARPANET in 1977_

  ![](../img/Arpanet_logical_map,_march_1977.png)

  _The Internet in 1984_

  ![](../img/internet_1984.jpg)


path: 82f9c228_dissolution_of_ARPANET.md
sortable_path: 82f9c228_dissolution_of_ARPANET.md
title: 82f9c228_dissolution_of_ARPANET
checksum: 1213ce22718441dcb3e35d33c411d9e4fd3318a07cf2302548756d0cda3eeac4
created: 2024-10-28 14:58:22.963252411+00:00
modified: 2024-10-28 15:16:38.371200628+00:00
metadata: {"created":"Sunday, October 27, 2024","tags":["internet","ARPANET"]}
lead:
  By 1984, the US military became concerned about the expansion of the ARPANET and
  its growing non-military applications (see the film _War Games_ released in
  1983!). They split the network into the _ARPANET_ for academic research and the
  _MILNET_ for non-civilian users where transmission was encrypted and protected.

body:
  By 1984, the US military became concerned about the expansion of the ARPANET and
  its growing non-military applications (see the film _War Games_ released in
  1983!). They split the network into the _ARPANET_ for academic research and the
  _MILNET_ for non-civilian users where transmission was encrypted and protected.

  Throughout the 1980s, the US National Science Foundation began promoting private
  networks that used the ARPANET as their _backbone_. (Private in the sense of not
  publicly-funded.)

  Later, these became commercial and
  [subscription-based](cfbef1c4_web_precursors.md). A plethora of ..._NETS_ grew
  up (NSFNET, USENET, CSNET, TELENET etc.), all based on #packet-switching, the
  ARPANET nodes as the backbone and [TCP/IP](Link_Layer_of_Internet_Protocol.md)
  host communication.

  These networks, although accessible to the public through subscriptions,
  remained unwieldy and inconsistent to access until the Web.

raw_content:
  ---
  tags: [internet, ARPANET]
  created: Sunday, October 27, 2024
  ---

  # 82f9c228_dissolution_of_ARPANET

  By 1984, the US military became concerned about the expansion of the ARPANET and
  its growing non-military applications (see the film _War Games_ released in
  1983!). They split the network into the _ARPANET_ for academic research and the
  _MILNET_ for non-civilian users where transmission was encrypted and protected.

  Throughout the 1980s, the US National Science Foundation began promoting private
  networks that used the ARPANET as their _backbone_. (Private in the sense of not
  publicly-funded.)

  Later, these became commercial and
  [subscription-based](cfbef1c4_web_precursors.md). A plethora of ..._NETS_ grew
  up (NSFNET, USENET, CSNET, TELENET etc.), all based on #packet-switching, the
  ARPANET nodes as the backbone and [TCP/IP](Link_Layer_of_Internet_Protocol.md)
  host communication.

  These networks, although accessible to the public through subscriptions,
  remained unwieldy and inconsistent to access until the Web.


path: 4a3dc316_key_figures_ARPANET.md
sortable_path: 4a3dc316_key_figures_ARPANET.md
title: 4a3dc316_key_figures_ARPANET
checksum: 0b781afb66f4731f64577a49ea652afc95660de8a0054f8c9728ef2baaf05c61
created: 2024-10-28 15:17:18.058792444+00:00
modified: 2024-10-28 15:39:04.780108677+00:00
metadata: {"created":"Monday, October 28, 2024","tags":["internet","ARPANET","ARPA"]}
lead:
  - [Paul Baran](385af4b4_Baran_distributed_networks.md) for his work at RAND on
    distributed networks as an alternative to the extant
    centralised/de-centralised phone networks.

body:
  - [Paul Baran](385af4b4_Baran_distributed_networks.md) for his work at RAND on
    distributed networks as an alternative to the extant
    centralised/de-centralised phone networks.

  - Donald Davies at the UK Physical Laboratory for developing ideas on
    #packet-switching, similar to but derived independently of Baran's work.

  - Bob Kahn as the principle designer of the ARPANET, who with Vint Cerf, wrote
    the TCP/IP protocols. Became director of the Information Processing Techniques
    Office within ARPA/DARPA.

  - Vint Cerf was head of the International Networking Group and worked on the
    Network Control Protocol (the first protocol for packet switching using the
    IMPs). Later joined up with Kahn on TCP/IP and the network of packet-switching
    networks (early internet).

raw_content:
  ---
  tags: [internet, ARPANET, ARPA]
  created: Monday, October 28, 2024
  ---

  # 4a3dc316_key_figures_ARPANET

  - [Paul Baran](385af4b4_Baran_distributed_networks.md) for his work at RAND on
    distributed networks as an alternative to the extant
    centralised/de-centralised phone networks.

  - Donald Davies at the UK Physical Laboratory for developing ideas on
    #packet-switching, similar to but derived independently of Baran's work.

  - Bob Kahn as the principle designer of the ARPANET, who with Vint Cerf, wrote
    the TCP/IP protocols. Became director of the Information Processing Techniques
    Office within ARPA/DARPA.

  - Vint Cerf was head of the International Networking Group and worked on the
    Network Control Protocol (the first protocol for packet switching using the
    IMPs). Later joined up with Kahn on TCP/IP and the network of packet-switching
    networks (early internet).


path: 3f1b9fe5_motivation_Berners_Lee.md
sortable_path: 3f1b9fe5_motivation_Berners_Lee.md
title: 3f1b9fe5_motivation_Berners_Lee
checksum: 18f3ace9839c6f27213b3ec1e7513adc9c4addb1bc0c71000f94872daab49de1
created: 2024-10-28 15:39:42.595989386+00:00
modified: 2024-10-28 15:41:28.425479134+00:00
metadata: {"created":"Monday, October 28, 2024","tags":["world-wide-web","internet"]}
lead:
  TBL describes his motivation in creating the WWW as the desire to marry two
  separate concepts/technologies: hypertext and the Internet, after both had come
  of age.

body:
  TBL describes his motivation in creating the WWW as the desire to marry two
  separate concepts/technologies: hypertext and the Internet, after both had come
  of age.

raw_content:
  ---
  tags: [world-wide-web, internet]
  created: Monday, October 28, 2024
  ---

  # 3f1b9fe5_motivation_Berners_Lee

  TBL describes his motivation in creating the WWW as the desire to marry two
  separate concepts/technologies: hypertext and the Internet, after both had come
  of age.


path: Bolt_Beranek_and_Newman.md
sortable_path: Bolt_Beranek_and_Newman.md
title: Bolt, Beranek and Newman
lead: ![BBN logo](../img/bbn-logo.png)
checksum: e3935d8817b1b318f5d4efe6dd46cb6b44a06917db8e4e11f713b6b8239528a5
created: 2024-10-30 13:15:43.321292419+00:00
modified: 2024-10-30 13:24:38.057881197+00:00
metadata: {"created":"Wednesday, October 30, 2024","tags":["computer-history","ARPANET"]}
body:
  ![BBN logo](../img/bbn-logo.png)

  Founded by Bolt and Beranek, two MIT students and hired some of the leading
  scientists of the day to lead departments. E.g. J.C.R Licklider, John McCarthy
  and Marvin Minsky.

  - Tested the original prototype of the PDP-1 on behalf of DEC.
  - Working for BBN, McCarthy developed one of the first time-sharing systems (the
    "BBN Time-Sharing System") for the PDP-1 provided by DEC.
  - Sponsored by DARPA to create the IMP computers which served as the main
    routers of the ARPANET.

  Eventually subsumed into Raytheon and now known as "Raytheon BBN".

raw_content:
  ---
  tags: [computer-history, ARPANET]
  created: Wednesday, October 30, 2024
  ---

  # Bolt, Beranek and Newman

  ![BBN logo](../img/bbn-logo.png)

  Founded by Bolt and Beranek, two MIT students and hired some of the leading
  scientists of the day to lead departments. E.g. J.C.R Licklider, John McCarthy
  and Marvin Minsky.

  - Tested the original prototype of the PDP-1 on behalf of DEC.
  - Working for BBN, McCarthy developed one of the first time-sharing systems (the
    "BBN Time-Sharing System") for the PDP-1 provided by DEC.
  - Sponsored by DARPA to create the IMP computers which served as the main
    routers of the ARPANET.

  Eventually subsumed into Raytheon and now known as "Raytheon BBN".


path: Bell_System.md
sortable_path: Bell_System.md
title: Bell System
checksum: 37b58f2882e93d9305793ec7d2e58ce6939f3804336be2a4ae3d8472f9c4864b
created: 2024-10-30 13:25:24.210592069+00:00
modified: 2024-10-30 13:34:21.877642405+00:00
metadata: {"created":"Wednesday, October 30, 2024","tags":["computer-history"]}
lead:
  Denotes the family of companies run by the Bell Telephone Company during its 100
  year outright monopoly on the telecommunications industry and infrastructure in
  the US. Inclusive of the American Telephone and Telegraph Company (AT&T), AT&T
  Long Lines (line network), the Western Electric Company (manufacturing
  telephonic equipment), and Bell Labs (the R&D section). Each major city/ region
  had a subsidiary, e.g. Michigan Bell, Northwestern Bell, the New York Telephone
  Company etc.

body:
  Denotes the family of companies run by the Bell Telephone Company during its 100
  year outright monopoly on the telecommunications industry and infrastructure in
  the US. Inclusive of the American Telephone and Telegraph Company (AT&T), AT&T
  Long Lines (line network), the Western Electric Company (manufacturing
  telephonic equipment), and Bell Labs (the R&D section). Each major city/ region
  had a subsidiary, e.g. Michigan Bell, Northwestern Bell, the New York Telephone
  Company etc.

  Colloquially known as "Ma Bell" ("mother Bell").

  The monopoly was broken up in 1983. Today, after the breakup the remaining (now
  separate) companies are AT&T (phone network, mobile network), Verizon (mobile
  network), and Lumen (cloud computing, networking services).

raw_content:
  ---
  tags: [computer-history]
  created: Wednesday, October 30, 2024
  ---

  # Bell System

  Denotes the family of companies run by the Bell Telephone Company during its 100
  year outright monopoly on the telecommunications industry and infrastructure in
  the US. Inclusive of the American Telephone and Telegraph Company (AT&T), AT&T
  Long Lines (line network), the Western Electric Company (manufacturing
  telephonic equipment), and Bell Labs (the R&D section). Each major city/ region
  had a subsidiary, e.g. Michigan Bell, Northwestern Bell, the New York Telephone
  Company etc.

  Colloquially known as "Ma Bell" ("mother Bell").

  The monopoly was broken up in 1983. Today, after the breakup the remaining (now
  separate) companies are AT&T (phone network, mobile network), Verizon (mobile
  network), and Lumen (cloud computing, networking services).


path: AWS_CloudWatch.md
sortable_path: AWS_CloudWatch.md
title: AWS CloudWatch
checksum: 540c152b7939fd5f47af1a556124dbb2790ac5296b155eb89f2046352170ba31
created: 2024-11-04 15:20:40.547321253+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["AWS"]}
lead:
  AWS CloudWatch is a monitoring and observability service offered by Amazon Web
  Services (AWS). It is designed to help users collect, track, and analyze various
  metrics, logs, and events related to their AWS resources and applications
  running on the AWS cloud. CloudWatch enables users to gain operational insights
  and take timely actions to maintain the performance, availability, and health of
  their infrastructure and applications.

body:
  AWS CloudWatch is a monitoring and observability service offered by Amazon Web
  Services (AWS). It is designed to help users collect, track, and analyze various
  metrics, logs, and events related to their AWS resources and applications
  running on the AWS cloud. CloudWatch enables users to gain operational insights
  and take timely actions to maintain the performance, availability, and health of
  their infrastructure and applications.

  ## Key features

  - Metrics
    - CloudWatch collects and stores metrics from various AWS services, such as
      EC2 instances, Lambda functions, and RDS databases. Users can monitor these
      metrics in real-time, set alarms based on thresholds, and visualize them
      through customizable dashboards.
  - Logs
    - CloudWatch Logs enables users to collect, store, and analyze log data from
      AWS resources and applications.
  - Events
    - Events allows users to respond to changes in their AWS resources
      automatically by triggering actions such as sending notifications, invoking
      Lambda functions, or starting/stopping EC2 instances.
  - Alarms
    - Users can set alarms based on specified metric thresholds. When an alarm is
      triggered, CloudWatch can send notifications or automatically take
      corrective actions.
  - Dashboards
    - Dashboards provide customizable, at-a-glance visualizations of various
      metrics and logs, making it easier to monitor the health and performance of
      AWS resources and applications.

  ## Accessing CloudWatch

  CloudWatch can usually be accessed directly from within the dashboard for the
  given AWS service under the "Monitor" tab. For example with AWS Lambda:

  ![](/img/lambda-cloudwatch.png)

  In addition to graphical metrics, we can also view specific logs for each event
  and execution of the given service. In the case of a Lambda function:

  ![](/img/cloudwatch-logs.png)

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

  # AWS CloudWatch

  AWS CloudWatch is a monitoring and observability service offered by Amazon Web
  Services (AWS). It is designed to help users collect, track, and analyze various
  metrics, logs, and events related to their AWS resources and applications
  running on the AWS cloud. CloudWatch enables users to gain operational insights
  and take timely actions to maintain the performance, availability, and health of
  their infrastructure and applications.

  ## Key features

  - Metrics
    - CloudWatch collects and stores metrics from various AWS services, such as
      EC2 instances, Lambda functions, and RDS databases. Users can monitor these
      metrics in real-time, set alarms based on thresholds, and visualize them
      through customizable dashboards.
  - Logs
    - CloudWatch Logs enables users to collect, store, and analyze log data from
      AWS resources and applications.
  - Events
    - Events allows users to respond to changes in their AWS resources
      automatically by triggering actions such as sending notifications, invoking
      Lambda functions, or starting/stopping EC2 instances.
  - Alarms
    - Users can set alarms based on specified metric thresholds. When an alarm is
      triggered, CloudWatch can send notifications or automatically take
      corrective actions.
  - Dashboards
    - Dashboards provide customizable, at-a-glance visualizations of various
      metrics and logs, making it easier to monitor the health and performance of
      AWS resources and applications.

  ## Accessing CloudWatch

  CloudWatch can usually be accessed directly from within the dashboard for the
  given AWS service under the "Monitor" tab. For example with AWS Lambda:

  ![](/img/lambda-cloudwatch.png)

  In addition to graphical metrics, we can also view specific logs for each event
  and execution of the given service. In the case of a Lambda function:

  ![](/img/cloudwatch-logs.png)


path: Associative_Property_of_Addition_and_Multiplication.md
sortable_path: Associative_Property_of_Addition_and_Multiplication.md
title: Associative Property of Addition and Multiplication
lead: **Let $a$, $b$ , $c$ represent members of $\mathbb{W}$ or $\mathbb{Z}$ then:**
checksum: 4eccab31c4dcc3f59c42ce1db49e5e987ec2857302427d40823da2625b759b08
created: 2024-11-04 15:20:40.556832548+00:00
modified: 2024-11-04 14:20:39.45610386+00:00
metadata: {"tags":["theorems","prealgebra"]}
body:
  **Let $a$, $b$ , $c$ represent members of $\mathbb{W}$ or $\mathbb{Z}$ then:**

  $$ (a + b) + c = a + (b + c) $$

  $$ a \cdot (b \cdot c) = (a \cdot b) \cdot c $$

  When grouping symbols (parentheses, brackets, braces) are used with the
  multiplication and addition of whole numbers and integers, the particular
  placement of the grouping symbols relative to each of the addends or
  multiplicands does not change the sum/product.

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

  # Associative Property of Addition and Multiplication

  **Let $a$, $b$ , $c$ represent members of $\mathbb{W}$ or $\mathbb{Z}$ then:**

  $$ (a + b) + c = a + (b + c) $$

  $$ a \cdot (b \cdot c) = (a \cdot b) \cdot c $$

  When grouping symbols (parentheses, brackets, braces) are used with the
  multiplication and addition of whole numbers and integers, the particular
  placement of the grouping symbols relative to each of the addends or
  multiplicands does not change the sum/product.


path: Commutative_Property_of_Addition_and_Multiplication.md
sortable_path: Commutative_Property_of_Addition_and_Multiplication.md
title: Commutative Property of Addition and Multiplication
lead: **Let $a$, $b$ represent members of $\mathbb{W}$ or $\mathbb{Z}$ then:**
checksum: 7855d10eab7e519fe596362d3d5ede96b7069b87f14650aab2eb570579c4aa12
created: 2024-11-04 15:20:40.617906022+00:00
modified: 2024-11-04 14:25:25.351055085+00:00
metadata: {"tags":["theorems","algebra","number-theory"]}
body:
  **Let $a$, $b$ represent members of $\mathbb{W}$ or $\mathbb{Z}$ then:**

  $$ a + b = b + a $$

  $$ a \cdot b = b \cdot a $$

raw_content:
  ---
  tags:
    - theorems
    - algebra
    - number-theory
  ---

  # Commutative Property of Addition and Multiplication

  **Let $a$, $b$ represent members of $\mathbb{W}$ or $\mathbb{Z}$ then:**

  $$ a + b = b + a $$

  $$ a \cdot b = b \cdot a $$


path: MongoDB_connection.md
sortable_path: MongoDB_connection.md
title: MongoDB connection
lead:
  ```js
  const mongoose = require("mongoose");

checksum: f4de15004137d56e214f1e0ef4b780799b7af6c7019bafcd8910bfd4064d5ec7
created: 2024-11-04 15:20:40.636392747+00:00
modified: 2024-11-04 14:26:19.288659167+00:00
metadata: {"tags":["mongo-db","node-js","mongoose","databases"]}
body:
  ```js
  const mongoose = require("mongoose");

  mongoose..connect("mongodb://127.0.0.1/[databse_name]");

  const courseSchema = new mongoose.Schema({
      name: String,
      author: String,
      tags: [String],
      data: Date,
      isPublished: Boolean,
      price: Number
  })

  const Course = mongoose.model("Course", courseSchema);

  async function getCourses(){
      return await Course
          .find({isPublished: true, tags: "backend"})
          .sort({name: 1})
          .select({name: 1, author: 1});
  }
  ```

raw_content:
  ---
  tags:
    - mongo-db
    - node-js
    - mongoose
    - databases
  ---

  # MongoDB connection

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

  mongoose..connect("mongodb://127.0.0.1/[databse_name]");

  const courseSchema = new mongoose.Schema({
      name: String,
      author: String,
      tags: [String],
      data: Date,
      isPublished: Boolean,
      price: Number
  })

  const Course = mongoose.model("Course", courseSchema);

  async function getCourses(){
      return await Course
          .find({isPublished: true, tags: "backend"})
          .sort({name: 1})
          .select({name: 1, author: 1});
  }
  ```


path: React_application_structure.md
sortable_path: React_application_structure.md
title: React application structure
checksum: f15ba5994809bb2ef31999f98ec2752f4b0a5e62f14fd2ae8222d460951746fb
created: 2024-11-04 15:20:40.643566774+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["javascript","react"]}
lead:
  When you use `create-react-app` it creates a default directory structure that is
  a sensible one to follow:

body:
  When you use `create-react-app` it creates a default directory structure that is
  a sensible one to follow:

  ```
  build/
  public/
  	index.html
  src/
  	index.js
  	App.js
  ```

  ## `public/`

  - `public` constains the `index.html` file via which React connects to the
    traditional DCOM. Within `index.html` you have `<div id="root">...</div>` as
    the entry point where your application is injected into the standard DOM.

  - Everything in this folder is as per standard web development practice.

  - You shouldn't include React code here but you can paste necessary links and
    metadata to the `head` of `index.html`

  ## `src/`

  - It automatically contains `index.js` . This imports `React` and `ReactDOM` and
    contains the `ReactDOM.render()` method:
    ```js
    ReactDOM.render(
      <App />
    document.getElementById('root')
    );
    ```
  - The `App` component is your parent component and the container for your app.
    It is where you will import your sub-components.
  - If you are using React Router `App` becomes the index for the different page
    components. See [Routing](Routing.md) for more detail.

  ## `build`

  `build` houses the compiled version of your application, ready to be deployed to
  a server. All contents are minified, compressed and optimised using Webpack.

  Once you have executed `npm run build` you can test the output by using `serve`
  via the console:

  ```bash
  serve -s build
  ```

  `build` will comprise a minified version of `index.html` and a `static/`
  directory which holds your compiled CSS and JS that `index.html` links to, as
  per a standard static website.

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

  # React application structure

  When you use `create-react-app` it creates a default directory structure that is
  a sensible one to follow:

  ```
  build/
  public/
  	index.html
  src/
  	index.js
  	App.js
  ```

  ## `public/`

  - `public` constains the `index.html` file via which React connects to the
    traditional DCOM. Within `index.html` you have `<div id="root">...</div>` as
    the entry point where your application is injected into the standard DOM.

  - Everything in this folder is as per standard web development practice.

  - You shouldn't include React code here but you can paste necessary links and
    metadata to the `head` of `index.html`

  ## `src/`

  - It automatically contains `index.js` . This imports `React` and `ReactDOM` and
    contains the `ReactDOM.render()` method:
    ```js
    ReactDOM.render(
      <App />
    document.getElementById('root')
    );
    ```
  - The `App` component is your parent component and the container for your app.
    It is where you will import your sub-components.
  - If you are using React Router `App` becomes the index for the different page
    components. See [Routing](Routing.md) for more detail.

  ## `build`

  `build` houses the compiled version of your application, ready to be deployed to
  a server. All contents are minified, compressed and optimised using Webpack.

  Once you have executed `npm run build` you can test the output by using `serve`
  via the console:

  ```bash
  serve -s build
  ```

  `build` will comprise a minified version of `index.html` and a `static/`
  directory which holds your compiled CSS and JS that `index.html` links to, as
  per a standard static website.


path: Testing_basic_prop_passing_in_React.md
sortable_path: Testing_basic_prop_passing_in_React.md
title: Testing basic prop passing in React
lead:
  ```js
  import { render, screen } from "@testing-library/react";

checksum: 34575567e1cfa73384ee771f05a338abb12b0e70f2b64ff32c0317a0e85fdbb8
created: 2024-11-04 15:20:40.659123986+00:00
modified: 2024-11-04 14:21:35.357069193+00:00
metadata: {"tags":["javascript","react","testing"]}
body:
  ```js
  import { render, screen } from "@testing-library/react";

  describe("<CertificateHtml />", () => {
    it("should render the values passed as props", () => {
      // Arrange:
      const stub = {
        titleOfActivityOrProgramme: "Filming",
        nameOfDepartment: "The film department",
      };

      // Act:
      render(<CertificateHtml {...stub} />);

      // Assert:
      expect(screen.getByText("Filming")).toBeInTheDocument();
      expect(screen.getByText("The film department")).toBeInTheDocument();
    });
  });
  ```

  Or, loop:

  ```js
  for (const entry of Object.values(stub)) {
    expect(screen.getByText(entry)).toBeInTheDocument();
  }
  ```

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

  # Testing basic prop passing in React

  ```js
  import { render, screen } from "@testing-library/react";

  describe("<CertificateHtml />", () => {
    it("should render the values passed as props", () => {
      // Arrange:
      const stub = {
        titleOfActivityOrProgramme: "Filming",
        nameOfDepartment: "The film department",
      };

      // Act:
      render(<CertificateHtml {...stub} />);

      // Assert:
      expect(screen.getByText("Filming")).toBeInTheDocument();
      expect(screen.getByText("The film department")).toBeInTheDocument();
    });
  });
  ```

  Or, loop:

  ```js
  for (const entry of Object.values(stub)) {
    expect(screen.getByText(entry)).toBeInTheDocument();
  }
  ```


path: Typing_built_in_React_hooks.md
sortable_path: Typing_built_in_React_hooks.md
title: Typing built-in React hooks
lead: ## `useState`
checksum: bf784ae842ad366733d7363e90c2b591524805c04410cb062cf6c9561b98564e
created: 2024-11-04 15:20:40.665390311+00:00
modified: 2024-11-04 14:24:05.242997853+00:00
metadata: {"tags":["typescript","react"]}
body:
  ## `useState`

  ```tsx
  const [amount, setAmount] = useState<number | string>(3);
  ```

  ### With a ustom type

  ```tsx
  interface IState {
    people: IPerson[];
  }

  interface IPerson {
    name: string;
    age: number;
  }

  const [people, setPeople] = useState<IState>({});

  // Alternative declaration

  interface IState {
    people: {
      name: string;
      age: number;
    }[];
  }

  const [people, setPeople] = useState<IState["people"]>({});
  ```

  ## `useReducer`

  Mostly the same. They key thing to type is the reducer function itself. You
  don't need to type the intitial state unless it is a pre-existing type, you can
  just use type assertion:

  ```jsx
  interface IAction {
    | { type: "increment"; payload: number }
    | { type: "decrement"; payload: string };
  }

  const initialState = { count: 0 };

  function reducer(state: typeof initialState, action: IAction) {
    switch (action.type) {
      case "increment":
        return { count: state.count + action.payload };
      case "decrement":
        return { count: state.count - Number(action.payload) };
      default:
        throw new Error();
    }
  }
  ```

  Then the syntax is as normal, i.e:

  ```js
  const [state, dispatch] = useReducer(reducer, initialState);
  ```

  ## `useEffect`

  > Limited potential for typing here as this hook does not return a value. See if
  > there is useful inferred typing from use and update.

  ## `useContext`

  We can use generics but mostly this is untyped:

  ```jsx
  type Theme = "light" | "dark";
  const ThemeContext = createContext < Theme > "dark";

  const App = () => (
    <ThemeContext.Provider value="dark">
      <MyComponent />
    </ThemeContext.Provider>
  );

  const MyComponent = () => {
    const theme = useContext(ThemeContext);
    return <div>The theme is {theme}</div>;
  };
  ```

  ## `useCallback` / `useMemo`

  You must only type the parameters that are passed to the callback, the return
  value will be inferred.

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

  # Typing built-in React hooks

  ## `useState`

  ```tsx
  const [amount, setAmount] = useState<number | string>(3);
  ```

  ### With a ustom type

  ```tsx
  interface IState {
    people: IPerson[];
  }

  interface IPerson {
    name: string;
    age: number;
  }

  const [people, setPeople] = useState<IState>({});

  // Alternative declaration

  interface IState {
    people: {
      name: string;
      age: number;
    }[];
  }

  const [people, setPeople] = useState<IState["people"]>({});
  ```

  ## `useReducer`

  Mostly the same. They key thing to type is the reducer function itself. You
  don't need to type the intitial state unless it is a pre-existing type, you can
  just use type assertion:

  ```jsx
  interface IAction {
    | { type: "increment"; payload: number }
    | { type: "decrement"; payload: string };
  }

  const initialState = { count: 0 };

  function reducer(state: typeof initialState, action: IAction) {
    switch (action.type) {
      case "increment":
        return { count: state.count + action.payload };
      case "decrement":
        return { count: state.count - Number(action.payload) };
      default:
        throw new Error();
    }
  }
  ```

  Then the syntax is as normal, i.e:

  ```js
  const [state, dispatch] = useReducer(reducer, initialState);
  ```

  ## `useEffect`

  > Limited potential for typing here as this hook does not return a value. See if
  > there is useful inferred typing from use and update.

  ## `useContext`

  We can use generics but mostly this is untyped:

  ```jsx
  type Theme = "light" | "dark";
  const ThemeContext = createContext < Theme > "dark";

  const App = () => (
    <ThemeContext.Provider value="dark">
      <MyComponent />
    </ThemeContext.Provider>
  );

  const MyComponent = () => {
    const theme = useContext(ThemeContext);
    return <div>The theme is {theme}</div>;
  };
  ```

  ## `useCallback` / `useMemo`

  You must only type the parameters that are passed to the callback, the return
  value will be inferred.


