# entries

title: Set_DNS_settings
last_modified: 2024-10-18 18:55:56
body:
# Set DNS settings

## Change the default DNS server

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

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

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

```
#wlan.network

[Match]
Name=wl*

[Link]
RequiredForOnline=routable

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

[DHCPv4]
UseDNS=no
RouteMetric=600

[IPv6AcceptRA]
RouteMetric=600

```

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

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

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

```

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

```

## Related notes

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


title: Reading_files_in_Python
last_modified: 2024-10-25 09:51:17
body:
# 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()
`
```


title: Functions_in_Python
last_modified: 2024-10-18 18:55:56
body:
# Functions in Python

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

## Basic examples

```py

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

print_msg()
# Hello World!

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

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

print_my_msg('Good day')
# Good day

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

print(square(2))
# 4

result = square(4)
print(result)

# 16

```

## Default parameters

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

greeter('Eloise')

# Welcome Eloise - Live Long and Prosper
```

## Optional parameters

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


## Function with arbitrary parameter list

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

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

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

## Scoping

Function variables are locally scoped by default.

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

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

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

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


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

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

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

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

print(myfunc1())

# hello
```

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


title: Setting_up_and_configuring_TypeScript
last_modified: 2024-10-18 18:55:56
body:
# Setup and configuration

## Installation and configuration

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

## Installation

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

## Initialise project

```
npx tsc --init
```

## Basic configuration

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

### Specify output paths (for production code)

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

### Compile-time commands

```
tsc --noEmit
```

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

## Global requirements

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

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

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

## Imports and exports

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

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

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

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

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

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


title: Conditional_Introduction
last_modified: 2024-10-18 18:55:56
body:
# Conditional Introduction

If we can show that $Q$ follows from $P$ (typically via a sub-proof) than we can
assert that P implies Q. This is also sometimes known as _Conditional Proof_

![](/img/cond-intro.png)


title: Docker_Compose
last_modified: 2024-10-18 18:55:56
body:
# Docker Compose

- Docker Compose (DC) is a tool for defining and running multi-container
  applications. A typical example would be combining a FE with a BE
  implementation and database as three related but distinct containers.

- DC is a prime example of _infrastructure as code_.

- A YAML file defines all the required configuration and **all services can be
  started with a single command**.

- It provides a consistent configuration accross environments: development,
  test, production

## Composition

Three step process:

1. Define your application image in a Dockerfile
2. Define the services in `docker-compose.yaml` (backend services, networks,
   volumes)
3. Start the entire application with `docker compose up`

### `docker-compose.yml`

- Must always begin with `version`
- Must have `services` map
  - This defines the different components that comprise your application
  - Services can be either an image reference (the one that generates the
    container you want to include) or a build context

Look at the following example. It runs a Python Flask frontend connected to a
Redis key-value pair database for backend.

```yml
# docker-compose.yml

version: "3.9"
services:
  web:
    build: . # here we are using the current directory as the build context
    image: myapp:1.0
    ports:
      - "5050:5000"
    depends_on:
      - redis
  redis:
    image: "redis:alpine"
```

`depends_on` is important - it means this container must be running first before
any others. If this is a DB, it means the data is accessible before the frontend
request start.

## Main commands

```sh
# start the composition:
docker compose up -d

# start a specific service within the composition:
docker compose up -d web
docker compose up -d redis

# view logs for a specific service
docker compose logs --follow web

# view processes by activity
docker compose top
```

## Environment variables

You can define them in the compose file or name them and leave them blank. If
you do this, Docker will look in the host environment for the variable which is
really handy:

```yml
...
services:
  redis:
    ...
    environment:
      ENV1: 'some value'
      ENV2:
```

## See also

[NodeJS and MySQL Docker backend](Node_and_MySQL_db.md)


title: Dates_in_Python
last_modified: 2024-10-18 18:55:56
body:
# Dates in Python

Python's built-in `datetime` module provides various classes for manipulating
dates and times.

### Importing `datetime`

```python
import datetime
```

### Getting Current Date and Time

Get the current date and time using `datetime.datetime.now()`.

```python
current_datetime = datetime.datetime.now()
print("Current datetime:", current_datetime)
```

### Creating Date Objects

To create a date object, use `datetime.date`, specifying the year, month, and
day.

```python
some_date = datetime.date(2021, 9, 30)
print("Some date:", some_date)
```

### Creating Time Objects

To create a time object, use `datetime.time`, specifying the hour, minute,
second, and optionally microsecond.

```python
some_time = datetime.time(13, 24, 56)
print("Some time:", some_time)
```

### Creating Datetime Objects

To create a datetime object, use `datetime.datetime`.

```python
some_datetime = datetime.datetime(2021, 9, 30, 13, 24, 56)
print("Some datetime:", some_datetime)
```

### Extracting components

Extract components from a datetime object:

```python
print("Year:", some_datetime.year)
print("Month:", some_datetime.month)
print("Day:", some_datetime.day)
print("Hour:", some_datetime.hour)
print("Minute:", some_datetime.minute)
print("Second:", some_datetime.second)
```

### Formatting `datetime` objects: `strftime`

The `strftime` method (_string format time_) converts a datetime object to a
string according to the specified format.

This would typically be used when we have been working with a computer-friendly
format of a date such as unix seconds which we then want to output in a more
readable format.

Use `strftime` to express the current date as YYYY-MM:

```python
now = datetime.now()
formatted = now.strftime('%Y-%m')
print(formatted)
# 2024-06
```

Express the current date as YYYY-MM-DD H:M:S:

```python
formatted_datetime = some_datetime.strftime('%Y-%m-%d %H:%M:%S')
print("Formatted datetime:", formatted_datetime)
```

Express a [unix timestamp](./Time_and_computers.md) as DD-MM-YYYY:

```py
def convert_timestamp(timestamp):
    date_object = datetime.datetime.fromtimestamp(timestamp)
    formatted_date = date_object.strftime("%d-%m-%Y")
    return formatted_date

converted = convert_timestamp(1689023491)
print(converted)
# 10-07-2023
```

### Parsing `datetime` strings: `strptime`.

The `strptime` (_string_parse_time) method_ parses a string representing a date
and/or a time according to a specified format and returns a `datetime` object.

Typically be used when want to carry out some sort of transformation on time
data that we are sourcing in a particular format.

Receive a date in the format DD-MM-YYYY and convert to datetime object:

```python
date_string = "18-06-2024"
datetime_object = datetime.strptime(date_string, '%d-%m-%Y')
print(date_object) # 2024-06-18 00:00:00
```

Extract the year from a UTC date format:

```python
datetime_obj = datetime.strptime("2024-03-27T10:44:28Z", "%Y%m%dT%H%M%SZ")
print(datetime_obj.strftime('%Y'))
# 2024

```


title: Monitoring_processes_and_resources
last_modified: 2024-10-18 18:55:56
body:
# Monitoring processes and resources

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

### `top`/`htop`

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

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

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

![](/img/htop.png)

### Main commands

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

### Understanding the categories

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

### `vmstat`

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

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

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

```

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

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

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

![](/img/lsof.png)

## System calls: `strace`

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

## CPU performance

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

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

`Uptime` gives you three load averages:

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

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

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

## Memory status

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

### System page size

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

```bash
$ getconf PAGE_SIZE
4096
```

This will typically be the same for all Linux systems.

### `free` : available physical memory

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

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


title: Conditional_statements_in_Python
last_modified: 2024-10-18 18:55:56
body:
# Conditional statements in Python

## Basic example

```python
input_string = input('Please input a number: ')

if input_string.isnumeric():
    print('The number is accepted')
else:
    print('The input is invalid')

# 5
# The number is accepted

# Using an and in the condition
print('-' * 25)
age = 15
status = None
if age > 12 and age < 20:
    status = 'teenager'
else:
    status = 'not teenager'
print(status)
```

## Else if

```python
savings = float(input("Enter how much you have in savings: "))

if savings == 0:
    print("Sorry no savings")
elif savings < 500:
    print('Well done')
elif savings < 1000:
    print('That is a tidy sum')
elif savings < 10000:
    print('Welcome Sir!')
else:
    print('Thank you')
```

## Nested conditions

```python
snowing = True
temp = -1
if temp < 0:
    print('It is freezing')
    if snowing:
        print('Put on boots')
    print('Time for Hot Chocolate')
print('Bye')
```

## Ternaries/ shorthand conditionals

```python
status = 'teenager' if age > 12 and age < 20 else 'not teenager'
print(status)

num = int(input('Enter a simple number: '))
result = -1 if num < 0 else 1
print('Result is ', result)
```


title: Apollo_Server
last_modified: 2024-10-18 18:55:56
body:
# Apollo Server

> Apollo Server is the part of the Apollo suite that we use to create the
> backend of a GraphQL project: a GraphQL server.

It is able to do the following:

- Receive an incoming GraphQL query from a client
- Validate that query against the server schema
- Populate the queried schema fields
- Return the fields as a JSON response object

## Example schema

We will use the following schema in the examples.

```js
// schema.js

const typeDefs = gql`
  " Our schema types will be nested here
`;
module.exports = typeDefs;
```

```js
type Query {
  tracksForHome: [Track!]!
}

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

type Author {
  id: ID!
  name: String!
  photo: String
}
```

## Setting up the server

We instantiate an `ApolloServer` instance and pass our schema to it. We then
subscribe to it with a
[listener](Node_JS_events_module.md#extending-the-eventemitter-class).

```js
// index.js

const { ApolloServer } = require("apollo-server");
const typeDefs = require("./schema");
const server = new ApolloServer({ typeDefs });

server.listen().then(() => {
  console.log(`
        Server is running!
        Listening on port 4000
        Query at http://localhost:4000
  `);
});
```

When we access the local URL we are able to access the Apollo server using the
Explorer GUI. This automatically loads our schema and is basically a more fancy
version of GraphiQL:

![](/img/apollo-explorer.png)

It makes it easy to read descriptions of the dataypes and to construct queries
by clicking to insert fields.

### Adding some mock data

We are not connected to a database yet but we can create a mock that will enable
us to run test queries.

We do this just by updating the Apollo Server options. We can either use generic
dummy data or provide our own mock.

#### Generic mock

```js
const server = new ApolloServer({ typeDefs, mocks: true });
```

#### Custom mock

```js
const mocks = {
  Track: () => ({
    id: () => "track_01",
        author: () => {
      return {
        name: "Grumpy Cat",
        photo:
          "https://res.cloudinary.com/dety84pbu/image/upload/v1606816219/kitty-veyron-sm_mctf3c.jpg",
      };
    },
    thumbnail: () =>
      "https://res.cloudinary.com/dety84pbu/image/upload/v1598465568/nebula_cat_djkt9r.jpg",
    length: () => 1210,
    modulesCount: () => 6,
  }),
};

const server = new ApolloServer({ typeDefs, mocks });
```

We can now
[run queries](Apollo_Client.md#running-a-query)
against our server.

## Implementing resolvers

A resolver is a [function](Creating_a_GraphQL_server.md#resolvers) that
populates data for a given query. It should have **the same name as the field
for the query**. So far we have one query in our schema: `tracksForHome` which
returns the tracks to be listed on the home page. We must therefore also name
our resolver for this query `tracksForHome`.

It can fetch data from a single data source or multiple data sources (other
servers, databases, REST APIs) and present this as a single integrated resource
to the client, matching the shape requested.

As per the [generic example](Creating_a_GraphQL_server.md#resolvers), you
write write your resolvers as keys on a `resolvers` object, e.g:

```js
const resolvers = {};
```

The `resolvers` object's keys will correspond to the schema's types and fields.
You distinguish resolvers which directly correspond to a query in the schema from
other resolver types by wraping them in `Query {}`.

```js
const resolvers = {
  Query: {
    tracksForHome: () => {},
  },
};
```

### Resolver parameters

Each resolver function has the same standard parameters that you can invoke when
implementing the resolution: `resolverFunction(parent, args, context, info)`.

- `parent`
  - Used with
    [resolver chains](Using_arguments_with_Apollo_Client.md#resolver-chains)
    ---add example
- `args`
  - an object comprising arguments provided for the given field by the client.
    For instance if the client requests a field with an accompanying `id`
    argument, `id` can be parsed via the `args` object
- `context`
  - shared state between different resolvers that contains essential connection
    parameters such as authentication, a database connection, or a
    `RESTDataSource` (see below). This will be typically instantiated via a
    class which is then invoked within the `ApolloServer` instance under the
    `dataSources` key.
- `info`
  - not used so frequently but employed as part of caching

Typically you won't use every parameter with every resolver. You can ommit them
with `_, __`; the number of dashes indicating the argument placement.

### `RESTDataSource`

A resolver can return data from multiple sources. One of the most common sources
is a RESTful endpoint. Apollo provides a specific class for handling REST
endpoints in your resolvers: `RESTDataSource`.

REST APIs fall victim to the "n + 1" problem: say you want to get an array of
one resource type, then for each element returned you need to send another
request using one of its properties to fetch a related resource.

This is implicit in the case of the `Track` type in the schema. Each `Track` has
an `author` key but the `Author` type isn't embedded in `Track` it has to be
fetched using an `id`. In a REST API, this would require a request to a separate
end point for each `Track` returned, increasing the time complexity of the
request.

Here is an example of `RESTDataSource` being used. It is just a class that can
be extended and which provides inbuilt methods for running fetches against a
REST API:

```js
const { RESTDataSource } = require("apollo-datasource-rest");

class TrackAPI extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = "https://odyssey-lift-off-rest-api.herokuapp.com/";
  }

  getTracksForHome() {
    return this.get("tracks");
  }

  getAuthor(authorId) {
    return this.get(`author/${authorId}`);
  }
}
```

### Using our `RESTDataSource` in our resolver

As our GraphQL server is sourcing data from a REST API, we can now integrate the
`RESTDataSource` class with our resolver.

First thing, we need to instantiate an instance of our `TrackApi` class,
otherwise we won't be able to use any of its methods in the resolver.

We will create an instance of this class and pass it into `ApolloServer` object
we established at the beginning. We will pass it to the `dataSources` key.
**This will allow us to access it from within the `context` parameter in our
resolver function**

We can also get rid of the `mocks` object since we don't need it any more. We
will replace it with our `resolvers` constant:

```diff
const server = new ApolloServer({
  typeDefs,
-  mocks,
+  resolvers,
+  dataSources: () => {
+  return {
+    trackApi: new TrackApi()
+  }
  }
})
```

Now we can complete our resolver:

```js
const resolvers = {
  Query: {
    tracksForHome: (_, __, {dataSources}) => {},
      return dataSources.trackApi.getTracksForHome()
  },
};
```

So we destructure the `dataSources` object from the parent Apollo Server
instance (in the place of the `context` parameter) which gives us access to our
`trackApi` class. This resolver will now make the API request and return the
tracks.

The `tracksForHome` query returns `Track` objects and these have a required
`author` key that returns an `Author` type. So we are also going to need a
resolver that can return the author data that will be populated along with
`Track`.

We already have this functionality in our class: `getAuthor` so we just need to
integrate it:

```js
const resolvers = {
  Query: {
    tracksForHome: (_, __, { dataSources }) => {
      return dataSources.trackApi.getTracksForHome();
    },
  },
  Track: {
    author: ({ authorId }, _, { dataSources }) => {
      return dataSources.trackApi.getAuthor(authorId);
    },
  },
};
```

- Just as we nest the `tracksForHome` resolver under `Query`, we must nest
  `author` under `Track` to match the structure of the schema. This resolver
  doesn't respond to a query that is exposed to the client so it shouldn't go
  under `Query`.

* We invoke the `context` again when we destructure `dataSources` from the
  `ApolloServer` instance.
* This time we utilise the `args` parameter in the resolver since an `id` will
  be provided as a client-side
  [argument](Using_arguments_with_Apollo_Client.md) to
  return a specific author.

## The `useMutation` hook

We invoke the `useMutation` hook to issue mutations from the client-side.

As with queries and
[query constants](Apollo_Client.md#query-constants)


title: e470bf3d_IMPs_in_the_ARPANET
last_modified: 2024-10-25 09:09:17
body:
# 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.


title: Ping
last_modified: 2025-01-13 20:54:42
body:
# Ping

`ping` is perhaps the most basic network debugging tool. It sends
[ICMP](./ICMP_Protocol.md) echo request packets to a recipient host.

If the recipient receives the packet and is configured to reply, it sends an
ICMP echo response packet in return.

For example:

```
$ ping google.com
PING google.com (142.250.200.14) 56(84) bytes of data.
64 bytes from lhr48s29-in-f14.1e100.net (142.250.200.14): icmp_seq=1 ttl=116 time=19.1 ms
64 bytes from lhr48s29-in-f14.1e100.net (142.250.200.14): icmp_seq=2 ttl=116 time=19.2 ms
64 bytes from lhr48s29-in-f14.1e100.net (142.250.200.14): icmp_seq=3 ttl=116 time=19.1 ms
64 bytes from lhr48s29-in-f14.1e100.net (142.250.200.14): icmp_seq=4 ttl=116 time=21.1 ms
```

The first indicates the IP address of the host you are pinging (once per second,
by default). The subsequent lines are the response packets, echoed back from
this host.

The `icmp_seq` is the number of the request in the sequence. If there was to be
a gap in the sequence, this would be the clearest indication of a connection
problem.The packets should not arrive out of order since there is only one
packet being sent per second.

`time` is the round-trip time - from the sending of the packet to its return to
the sender.

If the packet cannot reach the destination host, the final router to see the
packet retunes a "host unreachable" packet to `ping`.


title: Reciprocals
last_modified: 2024-10-18 18:55:56
body:
# Recipricols

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

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

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

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

For example:

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

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

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

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


title: Why_computers_use_binary
last_modified: 2024-10-18 18:55:56
body:
# 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.


title: AWS_SAM_and_Docker
last_modified: 2024-10-18 18:55:56
body:
# AWS SAM and Docker

_How would you use docker containers in AWS. How would this integrate with SAM.
What are the benefits over just deploying from local via SAM_

Amazon Web Services (AWS) provides several services for running Docker
containers, such as Amazon Elastic Container Service (ECS) and AWS Fargate. To
integrate Docker containers with AWS Serverless Application Model (SAM), you can
use the AWS Lambda container image support feature, which allows you to package
and deploy Lambda functions as container images.

Here's a brief overview of how to use Docker containers in AWS with SAM:

1. Create a Dockerfile for your Lambda function, using one of the AWS-provided
   base images for your preferred runtime (e.g., Python, Node.js, etc.).
2. Build the Docker image using the docker build command.
3. Push the Docker image to Amazon Elastic Container Registry (ECR) using the
   docker push command.
4. In your AWS SAM template (template.yaml), define a Lambda function with the
   ImageUri property pointing to the Docker image stored in ECR.
5. Deploy the serverless application using the sam deploy command.

## Benefits of using Docker containers with AWS SAM over deploying from local:

- Consistency:

  - Packaging your Lambda function as a container image ensures that the
    environment and dependencies are consistent across development, testing, and
    production stages. It eliminates the "it works on my machine" problem.

- Custom runtimes:

  - AWS Lambda provides a set of pre-defined runtimes, but with container image
    support, you can use custom runtimes, giving you more flexibility and
    control over the execution environment.

- Easier dependency management:

  - For complex applications with numerous dependencies or large package sizes,
    using container images simplifies dependency management and can help you
    stay within the Lambda package size limits.

- Reuse of existing Docker workflows:

  - If you already have Docker workflows in place for building, testing, and
    deploying containerized applications, you can easily extend these workflows
    for your Lambda functions, reducing the learning curve and operational
    overhead.

- Ease of local testing:
  - With AWS SAM CLI, you can test your Lambda functions locally as container
    images, closely mimicking the actual execution environment and reducing the
    chances of unexpected issues when deploying to AWS.

> While using Docker containers with AWS SAM has its benefits, it may not be
> suitable for every use case. If your Lambda functions have minimal
> dependencies and simple execution environments, deploying directly from local
> using SAM might be more straightforward and faster. However, for more complex
> applications, using Docker containers can provide better consistency,
> flexibility, and control over the execution environment.


title: Concise_subfield_mapping_JS
last_modified: 2024-11-04 15:20:00
body:
# Concise mapping of object subfields in JS

## Scenario

You have an array of objects and you want to return the objects with only a
subset of the fields.

## Implementation

Standard approach with a map:

```js
const arrayOfObjs = [
  { id: 12, name: "Thomas" },
  { id: 3, name: "Gerald" },
];

// We just want the `name` property

const subset = arrayOfObjs.map((obj) => {
  name: obj.name;
});
```

More concise approach with destructuring:

```js
const subset = arrayOfObjs.map(({ name }) => ({ name }));
```

## Related notes


title: Magnetic_drum_memory
last_modified: 2024-10-18 18:55:56
body:
# Magnetic drum memory

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

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

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

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

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

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


title: fs
last_modified: 2024-10-18 18:55:56
body:
# 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)


title: AWS_SQS
last_modified: 2024-11-25 06:50:57
body:
# AWS SQS

SQS ("Simple Queue Service") is a service that allows you to send, store and
receive messages between apps and software components built in AWS. It helps
with decoupling and scaling.

> Amazon SQS is a distributed queue system that enables web service applications
> to quickly and reliably queue messages that one component in the application
> generates to be consumed by another component where a queue is a temporary
> repository for messages that are awaiting processing.

As the name indicates, its operating mode is that of a [queue](Queue.md) data
structure offering first-in, first-out and other queue implementations.

## Example use case

![SQS example diagram](../img/SQS-example.png)

A request is made to an [API Gateway](API_Gateway.md) endpoint with a body. The
body is then parsed and inserted into a database.

The benefit of adding SQS as a buffer between the request and the updating of
the database:

- It can better handle spikes in activity, buffering requests to the database
  until it is ready to handle them. This prevents the messages getting lost if
  the server is overloaded

- There is a retry mechanism built into SQS. If the database write fails, the
  message stays in the queue allowing for retries

- It facilitates decoupling. Without SQS as the middleman the responsibilites of
  the lambda would be compounded - it would receive requests and update the DB,
  plus any additional processes such sending a message to SNS. In the solution
  we have two lambdas co-ordinating actions in a decouped manner via SQS.

## Dead letters

As SQS allows for multiple retries we could end up in a situation where a
malformed message is continually processed in a loop. To avoid this you can set
a maxiumum retry limit and, when this is exceeded, shift the problematic message
to the dead letter queue (DLQ) and remove it from the main queue.

![SQS deadletter example](../img/SQS-deadletter.png)

If the DLQ reaches a certain threshold this can trigger additional handling such
as raising an Alert in [CloudWatch](./AWS_CloudWatch.md) or other monitoring
tool.

Note that a DLQ is not a distinct entity within the SDK, it is just another SQS
queue that is designated to store failures.

See [AWS SDK Syntax](AWS_SQS_SDK.md).


title: Semantic_versioning
last_modified: 2024-10-18 18:55:56
body:
# Semantic versioning

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

- Major

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

- Minor

  - New features that do not break the existing API

- Patch

  - Bug fixes for the current minor version

## Glob patterns for versioning

### Caret

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

This is equivalent to `4.x`

### Tilde

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

### No tilde or caret

Use the _exact_ version specified


title: fbbfbc32-political-accommodation-inveigelment-surveillance-capitalism
last_modified: 2025-01-09 19:03:40
body:
# fbbfbc32-political-accommodation-inveigelment-surveillance-capitalism

The reason why surveillance capitalism was able to take root when it did was not
merely down to technological forces. Google, as the originator of the form, was
able to exploit the political currents of the time to further innoculate itself
against scrutiny and oversight.

The discovery of behavioural surplus coincided with September 11 and the
subsequent "global war on terror". The quest for domestic "security" led to the
sweeping erosion of previous generations' privacy rights.

Prior to 911, the FCC had been arranging a series of legal challenges to Google
on the question of privacy. This went out the window in the post-911 era where
Google's unique ability to track and surveil online behaviour became a coveted
commodity for the intelligence apparatus. Google began working closely with both
the CIA and the NSA, with the CIA establishing an office and forward base in
Silicon Valley.

There was an deep interdependency between the Pentagon, intelligence agencies
and Google:

_The US intelligence community's incubation of Google from inception occurred
through a combination of direct sponsorship and informal networks of financial
influence, themselves closely aligned with Pentagon interests._

_The Age of Surveillance Capitalism_ (2019): 118

Another key aspect was cosying up to politicians during the electoral cycle.
Most notably during Obama's elections:

_Google demonstrated that the same predictive knowledge derived from behavioural
surplus that had made the surveillance capitalists wealthy could also help
candidates win elections_

_Ibid_: 122

Eric Schmidt took an active role in making Google's dataset and technologies
available to the Obama campaign in both of his elections in order to target
voters more accurately. He then was swiftly appointed to advisory roles within
the administration throughout both terms.


title: Handling_negative_fractions
last_modified: 2024-10-18 18:55:56
body:
# Negative fractions

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

## Fractions with unlike terms

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

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

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

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

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

Therefore:

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

## Fractions with like terms

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

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


title: Truth_trees
last_modified: 2024-10-19 10:00:39
body:
# 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.


title: Expressing_whole_numbers_as_fractions
last_modified: 2024-10-18 19:13:45
body:
# Expressing whole numbers as fractions

Being asked to express a natural number as a fraction seems confusing at first
but you need to just know that for any whole number $n$, you express it as a
fraction with $\frac{n}{1}$.

_Express 8 as an equivalent fraction having the denominator 5_

$$
8 = \frac{8}{1} = \frac{8 \cdot 5}{1 \cdot 5} = \frac{40}{5}
$$


title: Passing_arguments_and_options_to_Bash_scripts
last_modified: 2024-11-17 15:08:27
body:
# Passing arguments and options to Bash scripts

## Relation between commands and programs

Whenever we issue a command in bash we are running an executable program that
the command token references. This is why when we create our own bash scripts we
must run `chmod` to make them executable. When we issue a command like
`./do_something.sh` we are running an executable program.

When we run a program like `cd` or `npm` we don’t have to type `./cd.sh` or
`./npm.sh`. This is because a reference to the program file is already in our
[`$PATH`](The_PATH.md).

In the case of `cd`, this is an in-built program and as such it will be sourced
from a binary and we have a reference to the binary in path. In the case of
`npm`, this is not an in-built program however in order to run it we must
already have it in our `PATH`.

## Passing arguments

A script is really just a function that runs when you source it. As such there
needs to be a way for you to pass data to the function so that it can actually
act like a function and take arguments. When we use for example `cd ./Desktop`
we are passing a directory name as an argument to the `cd` program. We can do
the same thing with our custom bash scripts.

To pass an argument we simply add the values after the script in the command.
For example:

```bash
./arguments.sh Thomas 33
```

We can use built-in variables to return information about the invocation of this
script:

```bash
#!/bin/bash

echo "File is called $0"
echo "The arguments provided are $@"
echo "The first argument is $1"
echo "The second argument is $2"
echo "Your name is $1 and you are $2 years old"
echo "There were $# arguments"
```

This outputs:

```
File is called ./arguments.sh
The arguments provided are Thomas 33
The first argument is Thomas
The second argument is 33
Your name is Thomas and you are 33 years old
There were 2 arguments.
```

Key points:

- `$0` designates the script or function that is being executed
- `$@` designates a list of all the arguments that are passed to the script
- `$1...` designates each individual argument
- `$#` gives us a count of the number of arguments

## Passing options

Options differ from arguments in that they are prepended with `-` and they can
be passed in any order. We use the `getops` program to parse the options that
the user inputs with their command.

We can demonstrate this with a script that takes in a username and password as
options.

We can handle an option-based input like:

```
some-program -u thomas -p password123
```

With the following construct:

```bash
while getopts u:p: option; do
  case $option in
    u) user=$OPTARG;;
    p) pass=$OPTARG;;
  esac
done

echo "user: $user / pass: $pass"
```

Which returns:

```
user: thomas / pass: password123
```

Here we set up a `while` loop against the `getopts` command to parse the options
in the stdin and store each one in a `option`. We run each `option` through a
case statement and individuate each one, storing them in dedicated local
variables via the global `$OPTARG` variable

### The role of the colons

Using a colon after each option means that the script will expect the given
option to have a value. If we put a colon _before_ each of the options we expect
this will allow us to view any option the user provides in our option handler.
This is useful when you are working with very many options, as with a
command-line program.

This version demonstrates a more exhaustive use case:

```bash
while getopts :u:p: option; do
  case $option in
    u) user=$OPTARG;;
    p) pass=$OPTARG;;
    a) echo "got the 'a' flag";;
    b) echo "got the 'b' flag";;
    ?) echo "Don't know what $OPTARG is!"
  esac
done

```


title: Bash_colour_output
last_modified: 2024-11-17 15:06:52
body:
# Bash colour output

Define colours:

```bash
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NO_COLOR='\033[0m'
```

Interpolate in string:

```bash
echo -e "${BLUE}INFO Uploading graph file...${NO_COLOR}"
```

Note: must end with the blank colour otherwise everything subsequent will be in
blue.


title: Soundness
last_modified: 2024-10-18 18:55:56
body:
# Soundness

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

#### Demonstration

Therefore this argument is valid:

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

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

This argument is also valid:

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

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

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

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

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

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

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

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

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


title: Relays
last_modified: 2024-10-18 18:55:56
body:
# Relays

A relay is an electrically operated switch.

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

## Core components

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

## Operation

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

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

## Usage

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


title: DynamoDB_SDK_commands
last_modified: 2024-10-18 18:55:56
body:
# DynamoDB SDK commands

The following commands are for using `@aws-sdk/client-dynamodb` (the JS SDK).

## Create client

```js
import DynamoDBClient from "@aws-sdk/client-dynamodb";

const client = new DynamoDBClient({
  region: "eu-west-2",
  accessKeyId: process.env.ACCESS_KEY_ID,
  secretAccessKey: process.env.ACCESS_KEY_ID,
});
```

### DynamoDB Document Client

The Document Client returns the data as a standard JSON document in the manner
of a document-based NoSQL database. Without it, DynamoDB will return the data
with type data which is unwieldy and hard to parse.

To transform into a document structure:

```js
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({
  region: "eu-west-2",
  accessKeyId: process.env.ACCESS_KEY_ID,
  secretAccessKey: process.env.ACCESS_KEY_ID,
});

const dynamoDbDocumentClient = DynamoDBDocumentClient.from(client);
```

## Query a table

Querying is the most performant and cost-effective method since it is an O(1)
lookup against a partition key or secondary index.

```js
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const params = {
  TableName: "YourTableName",
  KeyConditionExpression: "#pk = :pk",
  ExpressionAttributeNames: { "#pk": "PrimaryKeyAttributeName" },
  ExpressionAttributeValues: { ":pk": "PrimaryKeyValue" },
};

const dynamoDbDocumentClient = DynamoDBDocumentClient.from(client);
const data = await dynamoDbDocumentClient.send(new QueryCommand(params));
console.log(data);
```

## Scan a table

Scanning is less performant (O(n)) and most expensive since it requires checking
every item in the database.

```js
import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";

const params = {
  TableName: "YourTableName",
};

const dynamoDbDocumentClient = DynamoDBDocumentClient.from(client);
const data = await dynamoDbDocumentClient.send(new ScanCommand(params));
```

## Related notes


title: Electromagnetism
last_modified: 2024-10-18 18:55:56
body:
# Electromagnetism

> Electromagnetism is the physical interaction among **electric charges,
> magnetic moments and the electromagnetic field**.

For a long time electricity and magnetism were thought to be separate forces. In
the 19th century Maxwell demonstrated that they were interrelated phenomena then
Einstein proved with the Special Theory of Relativity that they are aspects of
one unified phenomenon.

The core of the relationship is that a changing magnetic field produces an
electric field and conversely, a changing electric field produces a magnetic
field.

## Electric charge

We know that charge is an innate property of all charged fundamental particles.
If a particle is charged it has a positive or negative charge. The most common
charged particles in the universe are negatively charged electrons or positively
charged protons. When charged particles are moving, they are known as electric
currents.

## Magnetism

> Magnetism is a physical property produced by the _motion_ of electric charge,
> which of course, is the same thing as
> [electric current](Current.md)

A **magnet** is a material or object that produces a magnetic field. This field
is invisible but visible by its effects: pulling on other magnetic materials
such as iron, steel, nickel, cobalt etc and attracting or repelling other
magnets.

All magnets have two ends where their magnetic effects are strongest. These
regions are called the **poles** of the magnet. Materials can be _magnetic_ but
they are not _magnetized_ until another magnetic material has entered into their
field. At this point, the attraction and repulsion behaviour can be observed.

This behaviour is a function of the **magnetic force** which is
**_transmitted_** via the **magnetic field**.

### Relation to electrons

Magnetism, understood as the effect of a magnetic field, arises from the
properties of the electrons in an atom. We know that atoms 'orbit' the nucleus
of the atom but as they circle the nucleus they also spin or rotate on their own
axis.

As they spin they produce a **magnetic dipole**: the two poles noted above. We
call this propensity of electrons the **intrinsic magnetic moment** of the
electron. It is aggregates of these miniature magnetic behaviours that produce
the overall magnetic property of the material.

![](/img/dipole-again.svg)

In most materials, equal numbers of electrons spin in opposite directions. As a
result, their magentic effects are cancelled out. However **in strongly magnetic
materials an overall majority of electrons spin in one particular direction**.
This breaks the equilibrium and produces the magnetic field.

If you have material A where the electrons all spin in one direction and
material B where the electrons all spin in a direction opposite to A, then B
will be attracted to A and you can observe the effects of the magnetic force in
action.

## Fields and forces

### What is a force in physics?

At its most abstract, divorced from the specific types of force, a force is **an
influence that can change the motion of an object**. It is an external agent
capable of changing the velocity of a body with mass. A force has both a
magnitude and a direction.

### What is a field

A field is a property of space. It means that a physical quantity is assigned to
every point in space. This quantity has a numerical value and may vary over
time.

## The electric field

There are different types of field. The electric field is an instance of a
**vector field**. With vector fields there is more than one number for each
point in space:

- a **magnitude**: a size value, i.e. being larger or greater than something
  else
- a **direction**

> The value of the electric field at a point in space equals the force that
> would be exerted on a unit of charge at that position in space.

Every charged object sets up an electric field in the surrounding space. A
second charge “feels” the presence of this field. The second charge is either
attracted toward the initial charge or repelled from it, depending on the signs
of the charges. Of course, since the second charge also has an electric field,
the first charge feels its presence and is either attracted or repelled by the
second charge too. The electric field from a charge is directed away from the
charge when the charge is positive and toward the charge when it is negative.

## The magnetic field

As already noted, the magnetic field is the field created by a magnetic
material: a material where the spin of the electrons is in disequillibrium. As
with the electric field, a magnetic field exerts an attraction or repulsion on
other spinning electrons. This is the **magnetic force**; the force is
transmitted by the field. Attraction occurs when the two sets of electrons spin
in opposite directions to each other. Repulsion occurs when the two sets of
electrons spin in the same direction.

> Crucially the magnetic force influences only those charges that are already in
> motion.

The magnetic field and force is more complex than the electric field/force.
Whereas the electric field and force point either towards or away from the
charge, the magnetic field is different:

- The magnetic field points perpendicular to its source
- The magentic force points perpendicular to the magnentic field

This is illustrated below which shows the magnetic field operating at right
angles to the flow of charge within a wire.

<img src="/home/thomas/repos/eolas/img/magnetic_field.png" width="300" />

## The electromagnetic field

Although we have described the electric and magnetic fields separately, they are
in fact a single unified and inseparable field created by charged particles and
particles with a magentic moment. In describing electricity and magnetism we
have focusing on different attributes of the same phenomenon.

The electromagnetic field does not carry charge or magnetic moment, it carries
energy and momentum. This energy and momentum can be transferred to charged
particles and particles with magnetic moment.

### Maxwell's Equations

The equations express four laws that offer a complete account of the
electromagnetic field. The equations integrate separate discoveries and laws
identified by other scientists with a common mathematical representation. This
is why they are not called 'Maxwell's Laws' since he didn't originate the laws,
he unified them into a common expression. These collective laws capture
everything we have discussed so far in relation to electric and magnetic fields.

1. An electrically charged particle creates an electric field. (Gauss' Law for
   Electricity)
2. Magnetically charged particles do not exist. (Gauss' Law for Magentism)
3. A changing magnetic field creates an electric field. (Faraday's Law of
   Induction)
4. A changing electric field creates a magnetic field. (Ampere's Law)

## The electromagnetic force

Maxwell's Equations describe how electromagnetic fields are generated and behave
but they don't specify how the fields interact with charged particles. An
account of this is provided by Lorentz's force law which specifies what we have
already described above:

> An electric field exerts a forward or backward force on a charged particle and
> a magnetic field exerts a sideways/perpendicular force on a moving charged
> particle.

## Electromagnetic radiation / waves

> Electromagnetic radiation consists in waves of the electromagnetic field
> propagating through space carrying electromagnetic radiant energy.

Electromagnetic waves consist of rapidly changing electric and magnetic fields.
They are emitted any time an electrically charged particle accelerates. These
waves are generally referred to as light. However, more accurately, 'light'
refers to the types of electromagnetic wave that we can see. Electromagnetic
waves form a spectrum based on their frequency and wavelength. For example,
'radio waves' are low-frequency / long wavelength electromagnetic waves and
gamma rays are high-frequency / short wavelength waves:

![](/img/em-spectrum.jpg)

The image below shows the propagation of an electromagnetic wave through space.
We can identify the core components as follows

- The vector of the magnetic field $B$ propagates outwards along the $z$ axis
- The magenetic field is perpendicular to the vector of the electric field $E$
  which propagates upward along the $y$ axis
- The directionality of both waves is forward along the $x$ axis

![](/img/em-wave.gif)

## Using magnetism to generate electricity

## Understand better:

- How do EM waves relate to simple electrical circuits.
- Link discussion here to discussion of Hertz

https://www.britannica.com/science/electromagnetism/Coulombs-law


title: Working_with_CSVs_in_Python
last_modified: 2024-10-19 10:02:12
body:
# 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)
```


title: Sliding_window
last_modified: 2024-10-18 18:55:56
body:
# Sliding window

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

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

## Example: maximum sum of sub-array

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

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

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

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

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

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

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


title: Nodemon
last_modified: 2024-10-18 18:55:56
body:
# Nodemon

We don't want to have to restart the local server every time we make a change to
our files. We can use `nodemon` instead of `node` when running our `index.js`
file so that file-changes are immediately registered without the need for a
restart.

This is a wrapper around the `node` command so it doesn't require any
configuration. Once installed, update your start script from `node index.js` to
`nodemon index.js`.


title: Flip_flops
last_modified: 2024-10-18 18:55:56
body:
# Flip-Flops

A flip-flop is a type of
[latch](Latches.md) that is connected
to a [clock signal](Clock_signals.md)
and which executes in time with the clock's pulse. (Sometimes "latch" and
"flip-flop" are used interchangeably but technically a latch is flip-flop
without a clock connection.)

## JK Flip-Flop

The JK Flip-Flop (the letters are meaningless) is basically an SR latch in
functionality. It has a "set" input (_J_) and a "reset" input (_K_) and _Q_ and
inverted-_Q_ outputs.

Where it differs from the SR is that it the SR will change state just if the
voltage is high (this, afterall, is all that a bit is) whereas for the JK to set
it **must receive a clock pulse** and it will only set on the pulse. Hence in
addition to _J_ and _K_ inputs it has a _CLK_ input for "clock".

In addition the JK Flip-Flop has a **toggle** function. When this is executed,
whatever _Q_ currently is will be flipped to its opposite value:
$1 \rightarrow 0$, $0 \rightarrow 1$. The toggle executes when both _J_ and _K_
are set to high.

The possible state changes for the JK Flip-Flop are detailed below:

| J   | K   | Clock | Q state                   | Operation |
| --- | --- | ----- | ------------------------- | --------- |
| 0   | 0   | Pulse | Maintain previous value   | Hold      |
| 0   | 1   | Pulse | 0                         | Reset     |
| 1   | 0   | Pulse | 1                         | Set       |
| 1   | 1   | Pulse | Inverse of previous value | Toggle    |

A JK Flip-Flop can execute on either the positive or negative pulse. Below are
the diagrams for a rising and falling pulse respectively:

![](/img/jk-flip-flops.png)

## T Flip-Flops

Another type of Flip-Flop can be created by connecting the _J_ and _K_ inputs of
a JK Flip-Flop to each other. This reduces the possible states to two: toggle
current state or maintain current state. Its overall logic is therefore reduced
to just two lines of the previous state table: the state that obtains when _J_
and _K_ are $0 0$ or $1 1$.

Thus the state table for the T Flip-Flop is:

| T   | Clock | Q state                   | Operation |
| --- | ----- | ------------------------- | --------- |
| 0   | Pulse | Maintain previous value   | Hold      |
| 0   | Pulse | Inverse of previous value | Toggle    |

![](/img/t-flip-flops.png)


title: Reset_to_remote_version
last_modified: 2024-10-18 18:55:56
body:
# Reset to remote version of a branch

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

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


title: Stack_memory
last_modified: 2024-10-18 18:55:56
body:
# Stack memory

## Summary

- Along with [[Heap_memory|heap memory]] heap memory, programs make use of
  _stack memory_ during runtime.

- Best suited for small-sized singular values that are temporary/ephemeral
  rather than large or complex data types that need to persist for a longer
  time.

- Specifically used to keep track of memory used during function executions
  within a given scope: control flow, local variables, returned value.

- Uses a LIFO data-structure

Similarly to the [[Stacks|stack data structure]], it works on the basis of LIFO:
the last item put on the stack is the first item to be withdrawn.

Note, this doesn't mean that the data at the memory addresses that comprise the
stack can _only_ be accessed in a LIFO manner. Any item currently on the stack
can be accessed (be read or modified) at any time. LIFO applies when it is is
time to clear memory from the stack: the most recent addition is cleared first
from the top down.

### Stack pointer

A **stack pointer** is used to manage the items in the stack. This value is
stored in a [[CPU_architecture#registers|register]] and its value is the memory
address of the item currently on the top of the stack. Note: _not_ the value
itself, the _address_ of the value.

When a new value is added to the top of the stack, the pointer's value is
adjusted to increase the size of the stack and make room for the new value. So
this will be a new memory address for the latest item. When an item is removed,
the reverse happens. The previously top address is cleared and the pointer
points to the new top item.

## Threads

For every thread that a program spawns, it gets its own stack or "stack frame".
Stack memory within the thread can only be accessed by processes within that
thread which is what makes it well suited for managing scoped function memory.

### Role in program execution

1. Function call management: a stack records the key information pertaining to
   the execution of a function: parameters, local variables, the location of the
   address for the returned value.

2. Local variable storage: storing the variables local to a function's scope and
   removing them once the function returns, avoiding
   [[Memory_leaks|memory leaks]]

3. Control flow: record from where a function is called and where its return
   value should be stored, meaning the right value is always returned no matter
   how nested it is.

4. Recursion: individuating each call of a function that calls itself and its
   given step to avoid infinite loops


title: Useful_operators_in_SQL
last_modified: 2024-10-18 18:55:56
body:
# 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
```


title: Whole_numbers
last_modified: 2024-10-18 18:55:56
body:
# 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.


title: Module_wrapping_at_runtime
last_modified: 2024-10-18 19:17:23
body:
# Module wrapping at runtime in NodeJS

## The Module Wrapper Function

When Node runs each of our module files are wrapped within an
immediately-invoked function expression that has the following parameters:

```js
(function (exports, require, module, __filename, __dirname))

```

This is called the **module wrapper function**

Note that one of these parameters is the
[module object](Modules_in_NodeJS.md#structure-of-a-module).

Within any module we can access these parameters: you can think of them as
metadata about the module itself. `__filename` and `__dirname` are particularly
useful when writing to files and modifying directories.


title: Full_Node_API_example
last_modified: 2024-10-18 18:55:56
body:
# Creating a RESTful API: Full example

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


title: Resistance
last_modified: 2024-10-18 18:55:56
body:
# Resistance

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

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

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

## Relationship between _resistance_ and _insulation_

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

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

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

## Factors affecting resistance

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

## Notation and scientific expression

### Resistance

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

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

### Conductance

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

> Conductance is the ability of a material to pass electrons

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

## Ohm's Law

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

## Resistors

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


title: Delete_records_in_an_SQL_table
last_modified: 2024-10-18 18:55:56
body:
# Deleting data in SQL

## Deleting records

We use `DELETE` to delete existing records in a table. Once this is applied, the
records are permanently deleted.

To delete **all records** whilst maintaining the table:

```sql
DELETE FROM sales
```

We can also use `TRUNCATE` to achieve this. It is faster but it doesn't keep a
log of each deletion:

```sql
TRUNCATE TABLE sales
```

To delete a **specifc entry** by field:

```sql
DELETE FROM sales WHERE sale_id = 1;
```

## Deleting tables

We use `DROP` to remove an entire table. This will delete the table definition,
and all the data, indexes, triggers, constraints and permission specifications
for that table.

```sql
DROP TABLE sales
```


title: Variables_and_data_types_in_Bash
last_modified: 2024-10-19 10:01:58
body:
# 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.


title: Role_of_memory_in_computation
last_modified: 2024-10-18 18:55:56
body:
# The role of memory in computation

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

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

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

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

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


title: Creating_a_Mongo_schema_and_model
last_modified: 2024-10-18 18:55:56
body:
# Creating a schema and model

## Schema

In order start adding
[collections and documents](MongoDB_Introduction.md) to our database,
we use Mongoose's **schema** structure. (This is specific to Mongoose and is not
a structure that is a part of Mongo in general.)

We use a schema to define the shape of documents in a MongoDB collection. To do
this we instantiate an instance of the Mongoose `Schema` class and set our
properties:

### Creating a 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,
});
```

> This is similar to declaring a type or interface in TypeScript

#### Available data types

The following data types are available:

- `String`
- `Number`
- `Boolean`
- `Array`
- `Date`
- `Buffer`
- `ObjectID` (for UUIDs)

> Note that we set our validation criteria as the second property for each
> schema value. There is more info info on validation in a
> [separate entry](Validating_Mongoose_schemas.md);

## Models

Once we have established our schema we can then create a **model** of it. A
model is basically a class representation of the interface we define in the
schema:

```js
const Course = mongoose.model("Course", courseSchema);
```

Now we can start adding specific courses as documents to our collection. We do
this by referring to the model, i.e.

```js
const course = new Course({
  name: "Node.js Course",
  author: "Ozzy Osbourne",
  tags: ["node", "backend"],
  isPublished: true,
});
```

![](/img/mongoose-hierarchy.svg)

## Outcome

Having created a database, connected to it with Mongoose, and created a model we
will see our collection reflected in Compass:

![](img/../../../img/mongo-collection.png)


title: ROM_versus_disk_loading
last_modified: 2024-10-18 18:55:56
body:
# ROM versus disk loading

I will explore the concept via the practical examples of cartridge-based and
disk-based video games console.

When you insert a disk into an Xbox, the game data is typically read from the
disk and transferred into the console's memory by the OS. If the game is
installed to the hard drive, this initially involves transferring substantial
amounts of data to speed up future access. When you insert a cartridge into a
console like the SNES, the data is not transferred in the same way. Instead, the
cartridge contains a ROM chip that, when inserted, integrates directly with the
motherboard. From the operating system’s point of view, this ROM chip behaves as
an extension of the system’s memory architecture.

The primary advantage of a ROM-based game is significantly reduced latency. The
game data can be accessed as rapidly as any other data moving along the buses of
the motherboard. With a disk, the data must first be read into a buffer and then
transferred to a memory address where it can be accessed by the CPU.

With a ROM, while the console CPU still uses its own RAM to run the game
(similar to disk-based systems), the game data on the ROM can be loaded into
this RAM more quickly because it is directly attached to the motherboard. This
makes the ROM cartridge effectively a fast, directly connected storage device.

However, despite its speed, the limitation of a ROM cartridge is its storage
capacity—it can hold only as much data as the chip can accommodate. In contrast,
a disk can store much more data. This larger storage capacity allows not just
for bigger and more complex games but also for the possibility of expanding the
game through additional data downloaded to the console's hard drive. Such
expansions or patches represent additional data from the console’s storage that,
combined with the original disk data, are synthesized in memory at runtime for
enhanced or updated gameplay experiences.

(Note however that modern cartridge-based consoles like the Nintendo Switch can
also combine ROM-data with expanded data from the console HDD, sourced from the
internet.)

Both disks and ROMs are forms of static storage in the context of a game
console. While disks can technically be overwritten with new data, in practice,
manufacturers implement various locks and protections to prevent this. Any
changes or additions to the game, such as patches or expansions, are not written
back to the original disk. Instead, these updates are stored on the console's
hard drive.

Therefore, if you download an expansion for a game on your Xbox, the original
disk remains unchanged as when you first purchased it. Consequently, if you were
to take that disk and load it on another Xbox, it would not include any of the
expansions or updates. The game would function in its original form, as the
additional content and updates reside solely on the hard drive of the first
console where they were downloaded.

## Related notes


title: Headless_Raspi_network_setup
last_modified: 2024-10-18 18:55:56
body:
# Headless network setup

If you are using a headless system and you aren't using an ethernet connection,
you face a chicken-egg issue of how to connect the device to WiFi via `ssh` when
the device isn't yet on the network.

You can get around this by saving a file called `wp_supplicant.conf` to the
`/boot` directory of the device. This specifies the SSID name and password and
will be used to auto-connect when the device boots.

For example:

```
country=GB
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
     ssid="SKYVL7XP"
     scan_ssid=1
     psk="WIFI_PASSWORD"
     key_mgmt=WPA-PSK
}
```

## Related notes


title: NodeJS_scripts
last_modified: 2024-10-18 19:18:42
body:
# NodeJS scripts

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

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

const process = require("process");

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

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

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

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

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

## `require.main`

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

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

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


title: Usenet
last_modified: 2024-11-12 08:26:14
body:
# Usenet

A decentralised system of computer servers that allowed users to post messages
to topic-based discussion groups called "newsgroups" which other users could
read and reply to.

Arguably the first social network and the oldest internet network that is still
operational.

It was designed by staff at two separate universities. Both used Unix systems
and wanted a way to communicate with each other.

## Technology

Usenet comprised a [distributed network](385af4b4_Baran_distributed_networks.md)
of dedicated servers that exchanged messages over the **Network News Transfer
Protocol** (NNTP).

A user would post a message to their local news server. The server would assign
it a UUID and then share it with other peer servers that it was connected to,
who would also share it, repeating the process. This created a ripple effect
that would propagate the message over the global network. Because of the
distributed nature of this network it meant that messages would have a high
longevity and retention rate and that it was effectively impossible to delete a
message once posted, since it would be stored across a multitude of servers.

## Relation to the Internet

Usenet developed separately and in parallel with the Internet. Originally it did
not operate over the Internet: servers would connect to each other over phone
lines using the Unix-to-Unix Copy Protocol. In the late 1980s when the Internet
became more widespread and not just limited to DARPA-adjacent universities,
Usenet transitioned to running over
[TCP/IP](Transport_Layer_of_Internet_Protocol.md) networks.

## Newsgroup hierarchy

Usenet used a structured, dot-separated naming system for newsgroups that got
more specific with each subdivision, e.g:

- comp.sys.mac
- sci.physics.particle
- alt.music.nirvana

Users would subscribe to the newsgroups that interested them and their
newsreader (which connected to the servers) would only download messages from
these groups.

## Influence

- Created the concept of threaded discussions in its reply structure
- Reddit appropriated the concept of topic-based communities in its sub-reddits
  and also used threading
- RSS adopted Usenet's model of subscribing to specific information sources and
  getting updates: the concept of "pulling" new content from feeds mirrors how
  newsreaders worked
- Distributed social networks like Mastodon/ActivityPub use a similar
  server-to-server propagation model that Usenet pioneered
- [BitTorrent's](Torrenting.md) distributed file-sharing model has similarities
  to how binary files were distributed across Usenet servers

## "Eternal September"

For most of its early history Usenet was limited to university students whose
university was part of the network. Each September new students would join the
university and this led to an influx of inexperienced Usenet users who didn't
yet know the etiquette and culture. They would have to be schooled and inducted.

In the mid-1980s Usenet became accessible to the general public as ISPs started
granting access. In 1993-1994, AOL granted access to subscribers. This changed
the nature of discussions from primarily academic and technical topics to
include more general interest and entertainment content, leading to the creation
of the "alt." hierarchy which allowed anyone to create new newsgroups without
formal approval processes that were required in the traditional hierarchies.
Hence it was "always September".

This phrase is often used as a shorthand for the way in which the Internet and
Web has moved from a niche community of technical users to a demotic public
space with an attendant decline in perceived value.


title: Formatting_output_text_in_Bash
last_modified: 2024-10-18 18:55:56
body:
# Formatting output text in Bash

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

## Output text in columns

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

Name		Number
Thomas		123
```

## Break text over several lines

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

This text
breaks over
three lines
```

## Colour outputs

![](/img/terminal_colours.png)

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

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

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

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

This underlines the output.

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

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

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


title: 82f9c228_dissolution_of_ARPANET
last_modified: 2024-10-28 15:16:38
body:
# 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.


title: a4601796_trapdoor_functions
last_modified: 2024-12-20 13:12:24
body:
# a4601796_trapdoor_functions

How does [public key cryptography](./bbdcb54f_public_key_cryptography.md)
achieve the feat of making the encyrpted data only decipherable in one
direction. In other words how come the same data can be encrypted with one key
but only decryptable by another?

It is acheived by the use of **trapdoor functions**. These are functions that
are easy to compute in one direction but extremely difficult to reverse. But
they also contain trapdoors which, if known, make the functions trivially easy
to compute in both directions.

The public key holder can compute the encryption but only the private key holder
has the trapdoor that makes reversing the encryption trivial.

As an example we can look at a common method for generating trapdoor functions
using prime numbers.

First we need to convert the message into numerical form. One way would be to
convert the characters to their ASCII numbers. These numbers then become input
into the mathematical encryption function.

Next we would pick two very large prime numbers. Using RSA-like encryption they
would be need to be 2048+ bits in length. For the example we will use 17 and 23.

For simplicity let's say that our message is one character equivalent to `5` in
ASCII. We multiply our two primes to get 391 (17 x 23 = 391) and then find the
remainder of: our message cubed and divided by the prime sum. This is `125`
(`5^3 / 391`) which is now our encrypted message which we send.

Unless you know the two primes which were used, it is extremely difficult and
time consuming to reverse the calculation and find the factors that would get
you back to the starting point (remember the numbers will be much larger in
reality). The owner of the private key knows these primes however, so can do
this.


title: Devices
last_modified: 2024-10-18 18:55:56
body:
# Devices

Devices are hardware that require access to the CPU in order to function.
Devices can either be external and plugged-in or internal to the motherboard.
The most common type of device that you will work with are [disks](Disks.md).

Devices are files but they have some different capabilities than ordinaryq
files. There are two types: **block** and **stream**. Device files reside in the
`/dev/` directory.

Some of the most important device files are:

- `hda` : a harddisk on a port
- `sda` : a harddisk on another port

> Device files are an interface to a driver which accesses the hardware. A
> driver is therefore part of the Linux kernel.

So when you see `sda` listed for example, this isn't the harddisk itself, it is
a file that communicates with a driver that controls how the kernel can interact
with it. This is why when you add a new piece of hardware (such as a mouse for
example) you have to install drivers (typically provided by the manufacturer) so
that your kernel is able to interact with it and provide it with access to the
CPU.

## Listing devices

The following `ls` within the `/dev/` directory shows my main harddrive
partitions:

```
brw-rw----   1 root   disk   259,     1 Jun  4 11:00 nvme0n1p1
brw-rw----   1 root   disk   259,     2 Jun  4 11:00 nvme0n1p2
brw-rw----   1 root   disk   259,     3 Jun  4 11:00 nvme0n1p3
```

> Since device files are files we can interact with them using standard file
> programs like `ls` and `cat`.

The
[mode](File_permissions_and_execution_in_Bash.md#what-the-output-means)
is different from ordinary files. Each device file is prepended with
`b, p, c, s` before the standard permissions. These stand for the major types of
devices: _block, character, pipe_ and _socket_.

<dl>
  <dt>block</dt>
  <dd>Block devices transfer data as large fixed-size blocks. These are the most common device type and include harddrives and filesystems. As the data can be split up into discrete blocks of data, this facilitates quick random access from the kernel. </dd>
  <dt>character</dt>
  <dd>Character devices transfer data one character at a time.The data is not in discrete chunks, it is a continuous stream of characters. An example of a stream device file is a printer however many character devices (such as `/dev/null`) are not physically connected to the machine.</dd>
  <dt>pipe<dt>
  <dd>Pipe devices allow two or more processes to communicate with each other. They work in the same way as character devices (transferring data as a stream) but instead of the output being sent to a device, it is sent to another process.</dd>
  <dt>socket<dt>
  <dd>The same as pipe devices, facilitating communication between processes however they can communicate with many processes at once, not just a single process.</dd>
</dl>

## /dev/null

`/dev/null` is a virtual device: it doesn't actually exist as a piece of
hardware on the system. It can be useful when
[bash scripting](Redirect_to_dev_null.md) as a
place to direct output that you don't care about, for example errors or verbose
program read-outs.

I like to think of `/dev/null` as being like earth in a circuit. It's an outlet
to safely dispose of things you are not interested in.

> ! Make notes on this. Base on : https://linuxhint.com/what_is_dev_null/


title: Prototypical_inheritance
last_modified: 2024-10-18 18:55:56
body:
# Prototypical inheritance

## Object oriented basis of JavaScript and nomenclature

As we know, everything in JS that isn't a primitive (i.e. a primitive data type:
string, number, boolean, null) is an object. Where an object is a data structure
that consists in key:value pairs.

The key is always a string and the value can be: a primitive value, a method
(function within an object), an array, another object etc.

> In other object-oriented languages like C# and Java, **functions are called
> methods** however in JS the term **method** is reserved for function that
> exists as a property of an objects that contains it.

### Lack of classes in JS

In contrast to more advanced object-oriented languages that are compiled (Java,
C#), classes do not really exist in JavaScript. This is due to the
prototype-based nature of JS (as we will see) however class-like functionality
can be achieved through the use of functions.

Remember in JS that functions can be simple code blocks that execute when called
(which is what we mean most of the time by 'function') **or they can be
constructor functions which create generic object types from which specific
token instances can be generated as objects.**

It is through these 'generic object types' that something akin to a class can be
created in JavaScript. (These are sometimes called function-objects )

The following is a good account of the differences between prototypal and
class-based languages:

> JavaScript is a prototype-based language, **meaning object properties and
> methods can be shared through generalized objects that have the ability to be
> cloned and extended**. This is known as **prototypical inheritance** and
> differs from **class inheritance**. Among popular object-oriented programming
> languages, JavaScript is relatively unique, as other prominent languages such
> as PHP, Python, and Java are class-based languages, which instead **define
> classes as blueprints for objects**.

### Example of a constructor function

In order to understand prototypes in a moment, it will be helpful to have an
example of a constructor function that can be used to generate object instances:

```jsx
/* Create constructor function: */
function Band(mainAlbum, subgenre) {
  // use Pascal for constructor function names
  this.mainAlbum = mainAlbum;
  this.subgenre = subgenre;
}

/* Then to create an object instance: */

var slayer = new Band("Reign in Blood", "thrash metal");
```

## Prototypes

Now that we know class-type functionality enters JS via constructor functions
and we have an example, we can look at prototypes.

> Prototypes are a special type of object that exist as a property within
> function-objects.

Whenever we create an instance-object from constructor function using `new`, the
object is given a property with the key `_proto_` . It's value is the
`[[prototype]]` property of the function that created, i.e the class of which
the object is an instance:

```jsx
{
  _proto_: prototype;
}
```

Note that we use the syntax `[[prototype]]` to indicate that **whilst the value
exists, it is not accessible via JavaScript code**, in the manner of a normal
object-literal or function-object, because it is the lower-level foundation of
first-order JS code.

So how would this work for the `slayer` object we created above?

```jsx
Slayer._proto_ === Band.prototype;
```

The thing is, you do not tend to see this prototype functionality and it is not
possible to refer to it as a key:value directly. This is because it is something
under-the-hood - it is a process that makes the object-oriented aspects of JS
possible but it is not apparent on the first-order level of everyday coding.

The prototype is like a hidden hook that facilitates the generation of objects
from class-like constructor functions. With our new awareness of it, we are in a
position _to understand how JavaScript works in foundational terms._

## Every non-primitive data type in JS possesses the `Object` prototype

The title above indicates how and why JavaScript establishes its object-oriented
data structure.

When we create an object literal (i.e an every day object not a class-like
function), under-the-hood JS is making a `new Object()` call in much the same
way as we are calling the function-object `Band` when we declare
`var slayer = new Band(...)` .

All instance-objects in JavaScript (which, remember is the set of everything
that is not a primitive) **inherit from the `Object` keyword**. And, to do this,
prototypes are necessary. Now we see how integral prototypes are to JavaScript.

### Examples

Here we create an object-literal and uncover how it has it's basis in the
overall `Object` constructor.

```jsx
var slayerMembers = {
  bassist: "Tom Araya",
  guitarist: "Kerry King",
  guitarist: "Jeff Hanneman",
  drummer: "Dave Lombardo",
};

//  slayerMembers._proto_ === Object.prototype
```

Here we create an instance-object off of our `Band` constructor function and
trace it back to `Object`

```jsx
var deicide = new Band("Legion", "death metal");

/* deicide object relies on Band prototype:
		
		deicide._proto_=== Band.prototype

Band prototype in turn relies in turn on Object prototype: 

 Band.prototype._proto_=== Object.prototype

*/
```

As the above examples show, ultimately all objects in JavaScript (whether
variables, arrays, methods, or functions) eventually lead back to
`Object.prototype` .

### How the browser uses prototypes when interpreting code

When the browser interprets your code attempting to access a property or method
of an object it runs through the following process:

1. Search on the object itself. If fails →
2. Search the object's prototype. If fails →
3. Search the prototype of the linked object. If fails→
4. Keep going until `Object.prototype` is reached

Clearly with each step we are going to a lower level of abstraction, drawing the
curtain back more and more until we got the point where JavaScript effectively
stops because any lower would take you into its lower-level parent language
(C/C++ depending on how the specific browser executes ECMAScript)

Note (3) is basically the prototype of the component itself. So if you started
at (1) with a custom array. At (3) the browser would be querying
`Array.prototype` i.e the basic foundational array object.

This explains why the full-name of certain methods on the global objects,
includes reference to 'prototype', for instance:

- `toFixed` → `Number.prototype.toFixed()`
- `.trim` → `String.prototype.trim()`.

### Using the function-object of `Object` to create an object literal

As a side-effect of uncovering the prototypical basis of `Object()` we can use
it to create an object literal in another way.

Standardly to create a new, empty object literal we would use:

```jsx
var slayerMembers = {...}
```

But now we could just as easily direct the code to `Object()` prototype and
achieve the same outcome:

```jsx
let slayerMembers = new Object();
```

### Finding the `[[Prototype]]` of a JS object with `getPrototypeOf()`

We can find out the `[[prototype]]` of a given object in JavaScript with a
special method applied to the object in question. The method we use is one of
the inherent methods that belong to the `Object()` constructor function:
`getPrototypeOf()` .

Here is an example:

```jsx
console.log(Object.getPrototypeOf(slayer));

/*

Returns: 

Object {constructor: , __defineGetter__: ,
 __defineSetter__: , hasOwnProperty: , __lookupGetter__: , …}

*/
```

We could also use the following approach to get the same output however this is
not recommended for production:

```jsx
console.log(slayer.__prototype__);
```

The output we get when querying the prototype is the same in each instance.

## But why bother using prototypes in your code?

It is informative to learn how prototypes are behind the object behaviour we
observe at a high level in JavaScript and to know that there is a common,
lower-level root but when would you actually need to use prototype methods?

The reason is mainly to do with performance and optimisation

> When we create a `deicide` and `slayer` object using the constructor function
> `Band()` JS creates two _copies_ of the constructor function, one for each
> generated object. This means there are two instances of `mainAlbum` for each
> object even though each do exactly the same thing. This wastes memory and
> performance time every time the script runs.

As a prototype object is an object, we can attach properties and methods to the
prototype object. Thus, enabling all the objects created using the constructor
function to share those properties and methods. However, the beauty is that the
method is applied to the prototype, so it is only stored in the memory once, but
every instance of the object has access to it.

```jsx
/* So say I wanted to add an age property to the Band()constructor that all objects of this type have access to. I could do this without wasting memory and without the need to change the original constructor function by referring to the prototype: */

Band.prototype.age = null;

Slayer.age = 25;
```

## Further steps

We are going to stop here for the time being since the primary aim of uncovering
prototypes and understanding how they underly all objects and object methods in
JS has been achieved.

However it is important to note that there is more that you can do with
prototypes. The main additional thing is using the prototypical basis of
constructor functions (i.e pseudo-classes) to add and remove key value pairs in
the resulting objects. This is useful when you want to create behaviour that is
much more like classes in compiled OOP languages.


title: Validation_in_NodeJS
last_modified: 2024-10-18 18:55:56
body:
# 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`.


title: Node_and_MySQL_db
last_modified: 2024-10-18 18:55:56
body:
# Docker example: NodeJS backend with MySQL database

We will utilise [Docker Compose](Docker_Compose.md) to combine
two containers:

- A container for the NodeJS backend
- A container for the MySQL database

We will only create a Dockerfile for the NodeJS part since the existing `mysql`
image is sufficient for most needs and does not require a specific
configuration.

Each of the files listed below would be saved to the same source directory which
would then form the basis of the
[build context](Creating_a_Docker_image.md#creating-a-docker-image).

## Docker Compose file

```yml
# docker-compose.yml
version: "3.8"
services:
  db:
    image: mysql:8.0
    container_name: mysql_container
    environment:
      MYSQL_ROOT_PASSWORD: your_root_password
      MYSQL_DATABASE: your_database_name
      MYSQL_USER: your_database_user
      MYSQL_PASSWORD: your_database_password
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "3306:3306"
  app:
    build: .
    container_name: node_app
    volumes:
      - .:/usr/src/app
    environment:
      MYSQL_HOST: db
      MYSQL_USER: your_database_user
      MYSQL_PASSWORD: your_database_password
      MYSQL_DB: your_database_name
    depends_on:
      - db
    ports:
      - "3000:3000"

volumes:
  mysql-data:
```

## Dockerfile for the NodeJS backend

```Dockerfile
# Dockerfile
FROM node:14

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD [ "node", "app.js" ]
```

## NodeJS project setup

```json
// package.json
{
  "name": "node-mysql-docker",
  "version": "1.0.0",
  "description": "Node.js and MySQL with Docker",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.17.1",
    "mysql2": "^2.3.2"
  }
}
```

```js
// app.js
const express = require("express");
const mysql = require("mysql2/promise");

const app = express();

const { MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DB } = process.env;

const createConnection = async () => {
  return await mysql.createConnection({
    host: MYSQL_HOST,
    user: MYSQL_USER,
    password: MYSQL_PASSWORD,
    database: MYSQL_DB,
  });
};

app.get("/", async (req, res) => {
  const connection = await createConnection();
  const [rows] = await connection.query("SELECT 1 + 1 AS solution");
  res.send(`Hello World! The solution is ${rows[0].solution}`);
});

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
```

To start up the environment you would then run:

```
docker-compose -up
```

## Environments

In the example, the database connection information in the Node source is coming
from the
[`process.env`](Managing_environments_in_NodeJS.md)
object, which itself is sourcing the values `MYSQL_HOST`, `MYSQL_PASSWORD` etc
from the Docker compose file. Therefore these values are hardcoded there.

This is not good practice as it exposes sensitive information and make managing
different deployment environments (development, stage, test etc.) difficult.

To get around this we would create an `.env` file in the project directory that
is Git ignored:

```sh
# .env
MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_DATABASE=your_database_name
MYSQL_USER=your_database_user
MYSQL_PASSWORD=your_database_password
```

Then the `docker-compose.yml` file can be updated to use these variables:

```yml
version: "3.8"
services:
  db:
    image: mysql:8.0
    container_name: mysql_container
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "3306:3306"
  app:
    build: .
    container_name: node_app
    volumes:
      - .:/usr/src/app
    environment:
      MYSQL_HOST: db
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      MYSQL_DB: ${MYSQL_DATABASE}
    depends_on:
      - db
    ports:
      - "3000:3000"

volumes:
  mysql-data:
```

`${VARIABLE_NAME}` syntax is used to reference environment variables from the
.env file in the `docker-compose.yml` file. Docker Compose will automatically
load the variables from the .env file when starting the services.

### Development, staging, production environments

To specify different connection details for different environments you would
create different `.env` files for each:

- `.env.development`
- `.env.staging`
- `.env.production`

Each file will contain **environment-specific variables**, such as database
credentials, API keys, and other configuration details.

For example, development and production:

```yml
# docker-compose.development.yml
version: '3.8'
services:
  db:
    ...
  app:
    ...
    environment:
      MYSQL_HOST: db
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      MYSQL_DB: ${MYSQL_DATABASE}
      NODE_ENV: development

```

```yml
# docker-compose.production.yml
version: '3.8'
services:
  db:
    ...
  app:
    ...
    environment:
      MYSQL_HOST: db
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
      MYSQL_DB: ${MYSQL_DATABASE}
      NODE_ENV: production
`
```

Then you would select the specific environment with your run command:

```
docker-compose -f docker-compose.development.yml up -d
```

Docker won't know by default which `.env` file to use from that command however.
Assuming all the files are in the same directory you can use Bash substitution
to specify the source of the environment specific variables:

```sh
export $(cat .env.development | xargs) && docker-compose -f docker-compose.development.yml up -d
```


title: 738d0481_sound_recording_as_wave_phenomena
last_modified: 2025-01-02 18:02:28
body:
# 738d0481_sound_recording_as_wave_phenomena

When the microphone captures alterations in sound pressure and converts it into
an electric signal, we have a scenario where the same formal or mathematical
relationships in the phenomena (amplitude, frequency, wavelength) are being
expressed in two different modes of matter.

This is quite profound especially as the resulting electrical waveform is
_identical_ to the original sound wave. This is effectively what enables
accurate playback. We can convert between the two mediums seamlessly providing
we captured their initial expression accurately.

It's like two different people doing the same dance:

In air:

- The dancers are air molecules
- They move back and forth in waves of compression and rarefaction
- Their "dance pattern" is the sound wave

In wire:

- The dancers are electrons
- They move back and forth creating waves of voltage
- They perform the exact same "dance pattern"


title: Secrets_or_env_vars_in_AWS
last_modified: 2024-10-27 11:21:46
body:
# Secrets or environment variables?

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

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


title: HTTPS
last_modified: 2024-12-13 14:12:33
body:
# HTTPS

The problem with standard, unencypted HTTP requests is that the data can be
captured in transit and be observed or modified by malicious actors.

HTTPS is the solution. It encrypts the entirety of the HTTP request and response
(URL, headers, body).

It utilises two types of encryption to send messages securely:

- symmetric encryption
  - a single shared key between client and server to encrypt the messages sent
    between them
- asymmetric encryption
  - two keys are used: a public key to encrypt the data and a private key to
    decrypt it. The public key can be shared freely so anyone can encrypt and
    send data to a peer but only the peer can receive and decrypt it

The encryption protocol used is TLS - the **Transport Layer Security Protocol**.
Previously SSL (Secure Sockets Layer) was used but has been deprecated over
security concerns.

## How communication over HTTPs works

- An initial handshake is shared between client and server, agreeing that
  communication will be encrypted. This is public. "Client hello" and "Server
  hello". Some random numbers are exchanged at this point.
- The server sends a digital certificate to the client along with its public
  key. The client checks that the certificate is valid.
- If valid, the client encrypts a string of bytes using the public key and sends
  it to the server. This is called the "premaster secret".
- This is decrypted by the server.
- The premaster secret, along with the random bytes from the handshake is then
  used to compute a shared secret key (symmetric) which is used to encrypt all
  subsequent messages for the duration of the session.


title: Hardware_simulation
last_modified: 2024-10-18 18:55:56
body:
# Hardware simulation

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

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

There are several simulation options:

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

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

## Interactive

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

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

## Script-based

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

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

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

## Comparison-based

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

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

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

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


title: Internet_Layer_of_Internet_Protocol
last_modified: 2024-10-18 18:55:56
body:
# Internet Layer of the Internet Protocol Suite

The Internet Layer is concerned with establishing the basis for the connection
and communication between hosts accross the internet.

In order for the data to get from a machine on one local network to a machine on
another local network, each machine must have a way of identifying and
interfacing with the other.

The Internet Layer utilises the **Internet Protocol** to determine how devices
will be publicly identified to devices on other networks and how different
devices on the same local network will be distinguished from outside of this
network. This is achieved through
[Internet Protocol (IP) addresses](./IP_addresses.md).

## Packets

Data is sent accross the internet between hosts as _packets_. These packets,
when sent and received within the
[Link Layer](Link_Layer_of_Internet_Protocol.md) on the local network, are
enclosed within a Link Layer frame (in its payload section):

![Internet Layer packet](../img/internet-layer-packet-revised.png)

Like a frame, an IP packet has a header and a payload. The payload comprises the
data between sent between hosts. The header contains a source IP address and a
destination IP address and also specifies which version of the Internet Protocol
is being used.

// Need to find out more about packets work.


title: Restructure_URLs_lambda
last_modified: 2024-10-18 18:55:56
body:
# AWS Lambda examples

## Reconstruct CloudFront URLs

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

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

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

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

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

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

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


title: AWS_CLI
last_modified: 2024-10-18 18:55:56
body:
# AWS CLI frequent commands

### Retrieve current user

```
aws-sts get-caller-identity
```

### List users

```
aws configure list
aws configure list-profiles
```

### View profile data

```
vim ./aws/credentials
```

## CloudFormation

### List stacks

```
aws cloudformation --list-stacks
```

### Delete a stack

```
aws cloudformation delete-stack --stack-name <my_stack> --region <region>
```

`region` is optional especially if only deployed to one region

## Billing ("Cost explorer")

### Usage for month

```
 aws ce get-cost-and-usage --time-period Start=$(date +%Y-%m-01),End=$(date -d @$(( $(date +%s) + 86400 )) +%Y-%m-%d) --granularity MONTHLY --metrics "UnblendedCost" "UsageQuantity
```

### Retrieve current user

```
aws-sts get-caller-identity
```

### List users

```
aws configure list
aws configure list-profiles
```

### View profile data

```
vim ./aws/credentials
```


title: 241fe1a3_the_Web_versus_modem_BBSs
last_modified: 2024-11-04 18:33:45
body:
# 241fe1a3_the_Web_versus_modem_BBSs

As access to the Web and the demand for public access grew, services like AOL,
which provided dial-up access to a subset of the broader Internet (along with
email and chat-rooms) became increasingly redundant.

In response to this, they pivoted in their marketing to presenting their service
as a safe, curated means of accessing the information: the Web was confusing, it
was hard to know what was out there and how to find useful information.

By using AOL, you would know which sites to visit for different tasks and which
of these were reputable. They adopted a kind of friendly gatekeeper persona in
the era of unhampered and universal access to information that the Web heralded
and embodied.

(This isn't so strange if we consider that outside of work (and the communities
of computer enthusiasts), personal computers were still relatively new and
computers themselves still rather forbidding entities.)

This function of a gateway to the overwhelming content of the web would be taken
up first by browsers
([Mosaic's](c301a0b3-1d8_Mosaic_Netscape_and_Browser_Wars.md) landing page would
list websites for specific sources of information and user tasks and links to
other directories) and then of course search engines which would allow users to
search for what they needed against an index (usually a subset) of the Web's
total content. (Interestingly in this regard, AOL would eventually buy
Netscape.)

AOL et al made sense when users wanted access to the internet and the web didn't
yet exist. Different networks had different access criteria and were often
poorly signposted, accessible only to those in the know. Private commercial
networks were set up to service typical consumer needs (news, banking, weather
reports etc) but the whole point of the Web as conceived by TBL was to make
everything available to anyone who wanted it through a streamlined and universal
set of protocols.


title: Link_Layer_of_Internet_Protocol
last_modified: 2024-10-18 18:55:56
body:
# Link Layer of the Internet Protocol Suite

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

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

Link Layer protocols goven communication between devices. Examples include:

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

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

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

## MAC addresses

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

A device receives data addressed to its MAC address.

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

## Frames

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

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

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


title: The_Pragmatic_Programmer_1999
last_modified: 2024-10-19 10:00:04
body:
# The Pragmatic Programmer (Hunt/Thomas, 1999)

## General

### Meyer's Uniform Access Principle

> All services offered by a module should be available through a uniform
> notation, which does not betray whether they are implemented through storage
> or through computation

This is a clear recommendation for using getters and setters with classes. You
should not see method calls outside of the class, they should appear as
properties of the object.

## Don't Repeat Yourself

> Every piece of knowledge must have a single, unambiguous, authoritative
> representation within a system

## The Principle of Orthogonality

This notion comes from geometry. Two lines are orthogonal to each other if they
form a right-angle when they meet.

Their meeting isn't the important part. Think of a simple x, y graph:

> If you move along one of the lines, **your position projected onto the other
> doesn't change**

In computing this is expressed in terms of **decoupling** and is implemented
through modular, component-based architectures. As much as possible code should
be scoped narrowly so that a change in one area does not cause changes in
others. By keeping components discrete it is easier to make changes, refactor,
improve and extend the codebase.

> We want to design components that are self-contained: independent and with a
> single, well-defined purpose. When components are isolated from one another,
> you know that you can change one without having to worry about the rest. As
> long as you don't change that component's external interfaces, you can be
> comfortable that you won't cause problems that ripple through the entire
> system.

### Benefits of orthogonality: productivity

- Changes are localised so development time and testing time are reduced
- Orthogonality promotes reuse: if components have specific, well-defined
  responsibilities, they can be combined with new components in ways that were
  not envisioned by their original implementors. The more loosely coupled your
  systems, the easier they are to reconfigure and reengineer.
- Assume that one component does _M_ distinct things and another does _N_
  things. If they are orthogonal and you combine them, the result does _M x N_
  things. However if the two components are not orthogonal, there will be
  overlap, and the result will do less. You get more functionality per unit
  effort by combining orthogonal components.

### Benefits of orthogonality: reduced risk

- Diseased sections of code are isolated. If a module is sick, it is less likely
  to spread the symptoms around the rest of the system.
- Overall the system is less fragile: make small changes to a particular area
  and any problems you generate will be restricted to that area.
- Orthogonal systems are better tested because it is easier to run and design
  discrete tests on modularised components.

> Building a unit test us itself a an interesting test of orthogonality: what
> does it take to build and link a unit test? Do you have to drag in a large
> percentage of the rest of the system just to get a test to compile or link?
> **If so, you've found a module that is not well decoupled from the rest of the
> system**

### Relationship between DRY and orthogonality

With DRY you're looking to minimize duplication within a system, whereas with
orthogonality, you reduce the interdependency among the system's components. If
you use the principle of orthogonality combined closely with the DRY principle,
you'll find that the systems you develop are more flexible, more understandable
and easier to debug, test, and maintain.

### Reversibility

The principles of orthogonality and DRY result in code that is reversible. This
means it is able to change in an agile way when the circumstances of its use and
deployment change. This is important because when developing software in a
business setting, the best decisions are not always made the first time around.
By following the principles it should be relatively easy to change your
program's interfaces, platform and scale. In other words, with the principle of
orthogonality and DRY, refactoring becomes less of a chore.

## Prototyping and Tracer Bullets

'Tracer bullets' are used by the military for night warfare. They are
phosphorous bullets that are included in the magazines of guns alongside normal
bullets. They are not intended to kill but instead light-up the surrounding area
making it easier to see the terrain and target more efficiently.

The authors use the notion of tracer bullets as a metaphor for developing
software at the early stages of a project. This is **not** the same thing as
prototyping. A tracer bullet model is useful for building things that haven't
been built before. They exist to 'shed light' on the project's needs and to help
the client understand what they want.

They differ from prototypes in that they include integrated overall
functionality but in a rough state. Whereas prototypes are more for singular,
specific subcomponents of the project. Because tracer bullet models are
joined-up in this way, even if they turn out to be inappropriate in some regard,
they can be adapted and developed into a better form, without losing the core
functionality.

> Tracer bullets work because they operate in the same environment and under the
> same constraints as the real bullets. They get to the target fast, so the
> gunner gets immediate feedback. And from a practical standpoint they are a
> relatively cheap solution. To get the same effect in code, we're looking for
> something that gets us from a requirement to some aspect of the final system
> quickly, visibly and repeatably.

> Tracer code is not disposable: you write it for keeps. It contains all the
> error-checking, structuring, documentation and self-checking that a piece of
> production code has. It simply is not fully functional. However, once you have
> made an end-to-end connection among the components of your system, you can
> check how close to the target you are, adjusting as necessary.

### Distinguishing from prototyping

> Prototyping generates disposable code. Tracer code is lean but complete, and
> forms part of the skeleton of the final system. Think of prototyping as the
> reconnaissance and intelligence gathering that takes place before a single
> tracer bullet is fired.

## Design by contract

To understand DBC we have to think of a computational process as involving two
stages: the call and the execution of the routine that happens in response to
the call (henceforth **caller** and **routine**).

- the caller could be a function expression that invokes a function and passes
  arguments to it expecting a given output. The function that executes is the
  routine
- the caller could be an object instantiation that calls a method belonging to
  its parent class
- the caller could be a parent React component that passes props to a child
  component

Design by contract means specifying clear and inviolable rules detailing what
must obtain at both the call stage and the routine stage if the process is to
execute.

Every function and method in a software system does something. Before it starts
that something, the routine may have some expectation of the state of the world
and it may be able to make a statement about the state of the world when it
concludes. These expectations are defined in terms of preconditions,
postconditions, and invariants. They form that basis of a **contract** between
the caller and the routine. Hence _design by contract\*\*._\*\*

### Preconditions

Preconditions specify what must be true in order for the routine to be called.
In other words, the requirements of the routine. What it needs and what should
be the case before it even considers executing the task. A **routine should
never get called when its preconditions would be violated**.

### Postconditions

Providing the preconditions are met, this is what the routine is guaranteed to
do. In other words: the state of affairs that must obtain after the routine has
ran.

### Invariants

Once established, the preconditions and postconditions should not change. If
they need to change, that is a separate process and contract. In the processing
of a routine, the data may be variant relative to the contract, but by the end
the overall conditions establish the equilibrium of the contract.

There is an analogue here with functional programming philosophy: the function
should always return the same sort of output, without ancillary processes
happening, i.e side-effects.

One way to achieve this is to be miserly when setting up the contract, which
overlaps with orthogonality. Only specify the minimum return on a contract
rather than multiple postconditions. This only increases the likelihood that the
contract will be breached at some point. If you need multiple postconditions,
spread them out an achieve them in a compositional way, with multiple separate
and modular processes.

> Be strict in what you will accept before you begin, and promise as little as
> possible in return. If your contract indicates that you'll accept anything and
> promise the world in return, then you've got a lot of code to write!

### Division of responsibilities

> If all the routine's preconditions are met by the caller, the routine shall
> guarantee that all postconditions and invariants will be true when it
> completes.

Note that the emphasis of responsibilities is on the caller.

Imagine that we have a function that returns the count of an array of integers.
It is not the job of the count routine to verify that it has been passed
integers and then to execute the count. Or, in the event that it is not passed
integers, to mutate the data to integers and then execute.

This should be resolved by the caller: it is the responsibility of the caller to
pass integers. If it doesn't, the routine simply crashes or raises an exception.
It doesn't try to accommodate the input because that does not come down on its
side of the contract. The caller has failed to meet the preconditions. If, due
to some bug, the routine receives integers and fails to output the sum, then it
has failed on its side

### Example: type checking

An obvious example of this philosophy is when you perform checks or validation
within your code (although validation is more of an issue when you are dealing
with user data, not your own internal code). For instance using type checking
with dynamically-typed languages.

When we use the `prop-types` library with React we are specifying preconditions:
so long at the prop (effectively the caller) passed to the component
(effectively the routine) is of type X, the component will render invariantly as
R. If the prop is of type Y, an exception will be raised highlighting a breach
in the contract.

Another example would be more advanced type checking with Javascript written
using Typescript.

## The Law of Demeter

Demeter's Law has applicability chiefly when programming with classes.

It's a fancy name for a simple principle summarised by 'don't talk to
strangers'. Demeter's law is violated when code has more than one step between
classes. You should avoid invoking methods of an object returned by another
method. You should only use your own methods when dealing with it.

### Formal

A method _m_ of object _O_ may only invoke the methods of the following kinds of
objects:

- _O_ itself
- _m_'s parameters
- any objects created or instantiated within _m_
- _O_'s direct component objects (in other words nested objects)
- a global variable (over and above _O_) accessible by _O_, within the scope of
  _m_

## Model, View, Controller design pattern

The key concept behind the MVC idiom is separating the model from both the GUI
that represents it and the controls that manage the view.

- **Model**
  - The abstract data model representing the target object
  - The model has no direct knowledge of any views or controllers
- **View**
  - A way to interpret the model. It subscribes to changes in the model and
    logical events from the controller
- **Controller**
  - A way to control the view and provide the model with new data. It publishes
    events to both the model and the view

For comparison, distinguish React from MVC. In React data is unidirectional: the
JSX component as controller cannot change the state. The state is passed down to
the controller. Also MVC lends itself to separation of technologies: code used
to create the View is different from Code that manages Controller and data
Model. In React it's all one integrated system.

## Refactoring

> Rewriting, reworking, and re-architecting code is collectively known as
> refactoring

### When to refactor

- **Duplication**: you've discovered a violation of the DRY principle
- **Non-orthogonal design**: you've discovered some code or design that could be
  made more orthogonal
- **Outdated knowledge**: your knowledge about the problem and you skills at
  implementing a solution have changed since the code was first written. Update
  and improve the code to reflect these changes
- **Performance: y**ou need to move functionality from one area of the system to
  another to improve performance

### Tips when refactoring

- Don't try to refactor and add new functionality at the same time!
- Make sure you have good tests before you begin refactoring. Run the tests as
  you refactor. That way you will know quickly if your changes have broken
  anything
- Take short, deliberative steps. Refactoring often involves making many
  localised changes that result in a larger-scale change.

## Testing

> Most developers hate testing. They tend to test-gently, subconsciously knowing
> where the code will break and avoiding the weak spots. Pragmatic Programmers
> are different. We are _driven_ to find our bugs _now_, so we don't have to
> endure the shame of others finding our bugs later.

### Unit testing

A unit test is code that exercises a module. It consists in testing each module
in isolation to verify its behaviour. Unit testing is the foundation of all
other forms of testing. If the parts don't work by themselves, they probably
won't work well together. All the modules you are using must pass their own unit
tests before you can proceed.

We can think of unit testing as **testing against contract** (detailed above).
We want to test that the module delivers the functionality it promises over a
wide range of test cases and boundary conditions.

Scope for unit testing should cover:

- Obviously, returning the expected value/outcome
- Ensuring that faulty arguments/ types are rejected and initiate error handling
  (deliberately breaking your code to ensure it is handled appropriately)
- Pass in the boundary and maximum value
- Pass in values between the zero and the maximum expressible argument to cover
  a range of cases

Benefits of unit testing include:

- It creates an example to other developers how to use all of the functionality
  of a given module
- It is a means to build **regression tests** which can be used to validate any
  future changes to the code. In other words, the future changes should pass the
  older tests to prove they are consistent with the code base

### Integration testing

Integration testing shows that the major subsystems that make up the project
work and play well with each other.

Integration testing is really just an extension of the unit testing described,
only know you're testing how entire subsystems honour their contracts.

## Commenting your code

In general, comments should detail **why** something is done, its purpose and
its goal. The code already shows _how_ it's done, so commenting on this is
redundant, and violates the DRY principle.

> We like to see a simple module-level comment, comments for significant data
> and type declarations, and a brief class and per-method header describing how
> the function is used and anything it does that is not obvious

```js
/*
Find the highest value within a specified data range of samples

Parameter: aRange = range of dates to search for data
Parameter: aThreshold = minimum value to consider

Return: the value, or null if no value found that is greater than or equal to the threshold
*/
```


title: Bash_store_status_of_command_execution
last_modified: 2024-11-17 15:07:07
body:
# Bash store status of command execution

We can store the outcome of a command in a variable, then check this variable to
derive the status and then provide custom outputs or other branching logic.

Here is an example of adding an object to a bucket in S3:

```bash
aws s3api put-object --bucket "${BUCKET_NAME}" --key "${FILE}" --body "${FILE_PATH}" >/dev/null 2>&1
STATUS=$?

if [ $STATUS -eq 0 ]; then
	echo -e "SUCCESS File uploaded."
else
	echo -e "Upload failed with status $STATUS."
fi
```


title: DynamoDB_data_structure
last_modified: 2024-10-18 18:55:56
body:
# DynamoDB

## Data structure

### Non-relational tables

DynamoDB is "NoSQL" because it does not support #SQL queries and is
non-relational meaning there cannot be JOIN operations via
[foreign_keys](Foreign_keys_in_SQL.md)

![](../img/dynamodb.png)

### Primary key

Although the data is stored as a table, one of the attributes is a primary key
and the rest of the attributes are effectively the "value" associated with it.

Because DynamoDB is schemaless, other than the primary key, neither the
attributes or their data types need to be defined beforehand and each item can
have its own distinct attributes.

Each item in the table is uniquely identifiable by its primary key.

There are two types of primary key available:

- partition key: a simple primary key composed of one attribute only. Because
  the primary key is hash-mapped items can be retrieved very rapidly using the
  primary key. This would be the `personId` alone.

- composite key: this comprises a partition key and a _sort key_ both of which
  are attributes. In a table that has a partition key and a sort key, it's
  possible for multiple items to have the same partition key value. However,
  those items must have different sort key values. You could then query by
  either key or both. For instance using the `PersonId` along with `LastName`

### Secondary index

As well as the index provided by the primary key, you can set one or more
**secondary indices**. A secondary index lets you query the data in the table
using an alternate key.

A **global secondary index** is useful for querying data that needs to be
accessed using non-primary key attributes. For example, if you have a Users
table with `UserID` as the primary key but often need to fetch users by their
`Email`, a GSI on `Email` would be appropriate.

There are also **local secondary indices** but I don't understand the
difference.

## Real example

Below is a specification of the DynamoDB table I am using for my time-entries
project:

```json
{
  "TableName": "TimeEntries",
  "KeyAttributes": {
    "PartitionKey": {
      "AttributeName": "activity_start_end",
      "AttributeType": "S"
    }
  },
  "NonKeyAttributes": [
    {
      "AttributeName": "activity_type",
      "AttributeType": "S"
    },
    {
      "AttributeName": "start",
      "AttributeType": "S"
    },
    {
      "AttributeName": "end",
      "AttributeType": "S"
    },
    {
      "AttributeName": "duration",
      "AttributeType": "N"
    },
    {
      "AttributeName": "description",
      "AttributeType": "S"
    },
    {
      "AttributeName": "year",
      "AttributeType": "S"
    }
  ],
  "GlobalSecondaryIndexes": [
    {
      "IndexName": "YearIndex",
      "KeyAttributes": {
        "PartitionKey": {
          "AttributeName": "year",
          "AttributeType": "S"
        },
        "SortKey": {
          "AttributeName": "start",
          "AttributeType": "S"
        }
      },
      "Projection": {
        "ProjectionType": "ALL"
      }
    }
  ]
}
```

This defines the attribute `activity_start_end` as the primary key. This string
(`S`) value is a concatenation of three attributes, which is a way of ensuring
each entry for the attribute will be unique.

The `NonKeyAttributes` are all the other attributes in addition to the primary
key. As mentioned these do not actually need to be defined when setting up the
table but they are listed here for clarity.

I have also defined a GSI. This is derived from the `Year` attribute. This will
group all the items by their `Year`, allowing me to query directly by year but
also helping to chunk the entries which will make look-ups quicker and less
expensive.

## Related notes


title: Terraform
last_modified: 2024-11-17 11:45:30
body:
# Terraform

## Authentication

Terraform will default to the currently active AWS user providing you have your
[AWS credentials](/zk/AWS_CLI.md) set-up.

## Creating resources

> Resources are specified in the `main.tf` file.

Create a new S3 bucket:

```hcl
resource "aws_s3_bucket" "my_new_bucket" {
    bucket = "my-new-bucket"
}
```

At this point the resource doesn't exist so there is no ARN or ID. When the
resource is created (through `terraform apply`) Terraform receives this info
from AWS and store it in a state file.

This info can be retrieved with `terraform show`, e.g:

```
resource "aws_s3_bucket" "my_new_bucket" {
    id                    = "my-new-bucket"
    arn                   = "arn:aws:s3:::my-new-bucket"
    bucket                = "my-new-bucket"
    bucket_domain_name    = "my-new-bucket.s3.amazonaws.com"
 }

```

## Standard sequence

Make your `terraform` directory, then `cd` and:

### Initialise project

```
terraform init
```

This will create the file `.terraform.lock.hcl` which should be committed. Stuff
in `.terraform/` can be git-ignored.

### Check changes

```
terraform plan
```

Providing you have written a `main.tf`. The command will list the proposed
changes to your resources.

### Apply changes

```
terraform apply
```

### Destroy the resource

Typically when you want to do a clean recreate.

```
terraform destoy
```

## Run with debug/logger

```
TF_LOG=DEBUG terraform apply
```

Or use with other Terraform command, e.g. `destroy`, `refresh` - whatever is
causing the problem.


title: Voltage
last_modified: 2024-10-18 18:55:56
body:
# 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.


title: Factory_pattern
last_modified: 2024-10-18 18:55:56
body:
# Factory pattern

The factory pattern pertains to how objects are created in the course of the
code. It is class based.

In this pattern we create a class interface that delegates the responsibility of
object instantiation to its subclasses.

You have a high level class that sits on top of two or more lower level classes
that are semantically related but different in function. Depending on the input
the higher level class receives upon instantiation, it will generate one of the
lower level classes.

### Advantages

- allows for dynamic creation
  - we may not know what objects are required until runtime: the factory allows
    us to cover several eventualities
- decoupling
  - in cases where the lower level classes combine to create something greater
    than their individual parts (see the Alligator example below), they do not
    have to know about each other.
- maintenance
  - allows us to add/remove new object classes easily without changing a lot of
    code.

## Examples

We have two types of notification: email and push. The creation of these two is
handled by a factory class called `NotificationFactory` :

```jsx
class PushNotification {
  constructor(sendTo, message) {
    this.sendTo = sendTo;
    this.message = message;
  }
}

class EmailNotification {
  constructor(sendTo, cc, emailContent) {
    this.sendTo = sendTo;
    this.cc = cc;
    this.emailContent = emailContent;
  }
}
```

```jsx
class NotificationFactory {
  createNotification(type, props) {
    switch (type) {
      case "email":
        return new EmailNotification(
          props.sendTo,
          props.cc,
          props.emailContent
        );
      case "push":
        return new PushNotification(props.sendTo, props.message);
    }
  }
}
```

We first instantiate an instance of the factory and then pass into it the
parameters for either of the two lower level classes.

```jsx
const factory = new NotificationFactory();

// create email notification
const emailNotification = factory.createNotification("email", {
  sendTo: "receiver@domain.com",
  cc: "test@domain.com",
  emailContent: "This is the email content to be delivered.!",
});

// create push notification
const pushNotification = factory.createNotification("push", {
  sendTo: "receiver-device-id",
  message: "The push notification message",
});
```

This example doesn't use a constructor on the factory class, but we could easily
do so. This would remove the need to call a method on the factory class when we
want to instantiate one of the subclasses. Here is another example that does
this with an alligator factory:

```jsx
class ReptilePartFactory {
  constructor(type, props) {
    if (type === "tail") return new TailFactory(props);
    if (type === "torso") return new TorsoFactory(props);
    if (type === "head") return new HeadFactory(props);
  }
}

let alligator = {};
let alligatorProps = {
  tailLength: 2.5,
  color: "green",
  snoutLenth: 1,
};

//gets a tail from the tail factory
alligator.tail = new ReptilePartFactory("tail", alligatorProps);

//gets a torso from the torso factory
alligator.torso = new ReptilePartFactory("torso", alligatorProps);

//gets a head from the head factory
alligator.head = new ReptilePartFactory("head", alligatorProps);
```


title: Split_into_array_in_Bash
last_modified: 2024-10-18 18:55:56
body:
# Splitting input into an array

## readarray

`readarray` makes it really easy to split input into an array based on new
lines. Say we have this file as input:

```
123
456
789
```

Then we can split like so:

```bash
readarray -t name_for_array < ./input.text

# Print all elements
echo "${name_for_array[@]}"

# Print element by index

echo "${name_for_array[1]}"
456
```

If we want to read a string directly:

```bash
readarray -t new_name_for_array <<< "here
is
some
text"

echo "${new_name_for_array[1]}"
is
```

> The _-t_ flag removes the trailing newline

See more: https://linuxhint.com/split-string-array-bash/

## read

For delimiters other than a space we have to use `read`, combined with `IFS` the
**Internal Field Separator**.

For example, to split by comma:

```plaintext
# comma-input.txt
something,something else,something more
```

```bash
IFS=',' read -a arr < ./comma_inputs.txt
```

> We use the `-a` flag to signal that we want each item read returned as an
> element in an array

## mapfile

We can use `mapfile` to read the lines of a input file in set segments.

For the following file:

```
line one
line two
line three
line four
```

We could generate the following output:

```
-----SNIP-----
line one
line two
-----SNIP-----
line three
line four
```

With:

```bash
while mapfile -t -n 2 ary && ((${#ary[@]})); do
  printf '%s\n' "${ary[@]}"
  printf -- '--- SNIP ---\n'
 done < $testInput
```

So, while there are input lines, map each line into an array and return it in
groups of 2.


title: Maps_and_sets_in_JS
last_modified: 2024-10-18 18:55:56
body:
# Maps and Sets

> Both `Map` and `Set` are new data structures introduced with ES6. They combine
> the properties of the pre-existing data structures: arrays and objects and
> offer advantages in certain non-typical use cases.

## Maps

### Concepts

A map (not to be confused with the array map method) introduces something like
the associative array model to JavaScript. Prior to ES6 the closest thing JS had
to this was an object.

Maps are like objects in that their structure consists in key/map pairs but like
arrays in that the order in which the entries are placed is retained and can be
referred to throughout the lifecycle of the Map. Also, the length of the Map is
a known and easily returnable quantity, with objects we have to do some
wrangling with `Object.keys` and `Object.entries` to retrieve meta data about
the object itself. Moreover, with an object the order in which the keys are
stored is not recorded.

At the same time Maps are different from arrays because the index of an array
element is not unique, it will always be a number of the form `[0], [1], [2]...`
.

Finally, the key can be any data type not just a string. This is an important
characteristic of maps because although you can use numbers or booleans as the
keys in an object, they are not actually numbers, they are converted to strings
at execution. With Maps, whatever data type you assign as a key, retains its
type.

Maps

- Have key/value pair structure
- Have length property
- Order of entries and insertions is maintained
- Can use any data type as a key
- Disallow duplicate keys

### Creating a Map and adding entries

```jsx
const map = new Map();

// We then pass in key/value pairs using `set`:
map.set("firstName", "Luke");
map.set("lastName", "Skywalker");
map.set("occupation", "Jedi Knight");
```

There is an alternative method for defining a map:

```jsx
const map = new Map([
  ["firstName", "Luke"],
  ["lastName", "Skywalker"],
  ["occupation", "Jedi Knight"],
]);
```

Internally, this data will be represented as follows:

```
Map(3)
0: {"firstName" => "Luke"}
1: {"lastName" => "Skywalker"}
2: {"occupation" => "Jedi Knight"}
```

### Working with map elements

```jsx
// check for the existence of an item in a map by searching for existence of key
map.has("firstName"); // return boolean

// return all keys in a Map as iterator
map.keys(); // MapIterator {'firstName', 'lastName', 'occupation'}

// return all values as iterator
map.values();

// return array of key/value pairs
map.entries(); // {"firstName" => "Luke", "lastName" => "Skywalker", "occupation" => "Jedi Knight"}
// retrieve an item from a Map
map.get("lastName"); // Skywalker

// delete an item from a map by key, returns boolean as confirmation
map.delete("occupation"); // true

//return the length of a map (just like array.length)
map.size; // 3
// clear all values
map.clear();
```

### Looping

```jsx
// Map has its own .forEach method, like with arrays. Hewre they are side by side

// Map
Map.prototype.forEach((value, key, map) = () => {})

// Array
Array.prototype.forEach((item, index, array) = () => {})

// You can also use a for of loop
// Destructure the key and value out of the Map item
for (const [key, value] of map) {
  // Log the keys and values of the Map with for...of
  console.log(`${key}: ${value}`)
}
```

### Interfacing between Maps and other reference types

Given that we can create a map with both object and array-style syntax, its easy
to convert maps to and from the more established data structures.

It's easy to convert an object to a map:

```jsx
const luke = {
  firstName: "Luke",
  lastName: "Skywalker",
  occupation: "Jedi Knight",
};

const map = new Map(Object.entries(luke));
```

And a map to an object:

```jsx
const obj = Object.fromEntries(map);
```

A map to an array:

```jsx
const arr = Array.from(map);
```

We saw earlier that a Map is a multidimensional array, so any multidimensional
array can be used in the Map constructor invocation to turn it into a Map.

## Sets

A set is special kind of array, an atypical supplement to arrays. It is
basically an array of unique values. If you try to add a duplicate entry to a
set it will be ignored.

```jsx
const set = new Set();
set.add("Beethoven");
set.add("Bach");

// Or:
const set = new Set(["Beethoven", "Bach"]);

// Convert array to set (note, this will remove duplicates on initialisation)
const arr = [...set];
```

We can use `keys()`, `values()`, and `entries()` with sets but obviously, `keys`
and `values` will be identical.


title: Fetch_decode_execute
last_modified: 2024-10-28 13:23:37
body:
# Fetch, decode, execute, store

_Fetch, decode, execute_ is the operating cycle of the CPU. We will run through
how this works with reference to the [CPU architecture](CPU_architecture.md).

## Fetch

### Overview

First, instructions and data are fetched from outside of the CPU chip (usually
from the DRAM). In some cases, the data used by an instruction will be included
with it. In other instance the instruction will reference the location where the
data are held as an address.

### Specifics

- The Program Counter register needs to keep track and sequence the different
  instructions that the CPU will work on. The first place it will look for an
  instruction is at the DRAM address `0000`, equivalent to 0 in the Program
  Counter register: the starting point. This is address therefore copied to the
  Memory Address Register for future reference.
- This memory-storing event constitutes an instruction so it is copied to the
  Instruction Register.
- As the first instruction has been fetched, the system reaches the end of the
  first cycle. Thus the Program counter increments by 1 to log this.
- The next fetch cycle begins.

## Decode

### Overview

Once the CPU has received the instruction, the CPU controller chip analyses it
to determine which of its circuits should be used for processing.

### Specifics

- Now that the instruction is fetched and stored in the RAM it needs to be
  decoded. It is therefore sent from the RAM to the Control Unit of the CPU.
  There are two parts to the instruction:
  1. The operation code ("op code"): the command that the computer will carry
     out.
  1. The operand: an address in RAM where the data will be read and written to
     as part of the execution
- The Control Unit converts the operation code and operand into an instruction
  that is fed to the next stage in the cycle: execution. For example, for an add
  function, `add` would be the op code and `address x` and `address y` would be
  the operands, thus the instruction would be: `add` the value in `address x` to
  the value in `address y`.

## Execute

Now the command will be executed. The operand is copied to the Memory Address
Register and then passed to the Memory Data Register and the command is carried
out by the ALU. The activities of ALU are covered in
[CPU Architecture](CPU_architecture.md#arithmetic-logic-unit) and the notes on
[Logic Gates](Logic_gates.md).

## Store

### Overview

As well as telling the CPU what operation to perform, the instructions must
specify where to store the result.

### Specifics

- Data is stored differently depending on the instruction.
- If the instruction is iterative (e.g. adding two numbers and then multiplying
  by another number), the instruction will tell the CPU to store the interim
  first value in the CPU's [registers](CPU_architecture.md#registers). As these
  are part of the CPU, the data can be retrieved more readily. If the value is
  not expected to be used again immediately, it goes to the DRAM or harddisk.


title: Reiteration
last_modified: 2024-10-18 18:55:56
body:
# Reiteration

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

![](/img/reiteration.png)


title: MAC_addresses
last_modified: 2024-10-18 18:55:56
body:
# MAC addresses

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

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

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


title: Update_a_Mongo_document
last_modified: 2024-10-18 18:55:56
body:
# 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


title: Controlled_components_in_React
last_modified: 2024-10-18 18:55:56
body:
# Controlled components

Controlled components relate to how you use forms in React.

You can use standard form HTML within a React component but applying React state
to the form via controlled components gives you greater power over what you do
with the inputs and how you handle the data that is received.

Standard HTML forms have a separate state from the rest of the document object
and this is highly mutable. It can send requests to a server and reload the
page. This mutability can obviously interfere with the state you maintain in
your React components; this is why it is best to manage form state along with
React state.

We can combine the two [native HTML form state and React state] by making the
React state be the “single source of truth”. Then the React component that
renders a form also controls what happens in that form on subsequent user input.
An input form element whose value is controlled by React in this way is called a
“controlled component”.


title: Routing_on_link_click_in_React
last_modified: 2024-10-18 18:55:56
body:
# Test: Routing on link click

## Component

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

## Test

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

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

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

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


title: Validity_and_entailment
last_modified: 2024-10-19 10:01:40
body:
# 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   |            |


title: Lambda_directory_structure
last_modified: 2024-10-18 18:55:56
body:
# Lambda directory structure

I am using the following structure for my lambdas:

```
lambda-name/
    events/
    function-name/
        src/
            index.js
        tests/
        local-env.json
        production-env.json
        package.json
    template.yaml
    README.md
```


title: Connect_to_Mongo_database
last_modified: 2024-10-18 18:55:56
body:
# Connect to a database with Mongoose

Now that we have installed and configured MongoDB, we need to connect to it via
Node.js. Mongoose is a simple API for interacting with a Mongo database via
Node.

With Mongoose installed we can connect to a database. We don't have any Mongo
databases yet beyond the defaults but the following Mongoose connection logic
will create and connect to a new database called `playground`:

Providing the Mongo server is running (execture `mongod`), we will see the
confirmation message in the console.

```js
mongoose
  .connect("mongodb://127.0.0.1/playground")
  .then(() => console.log("Connected to MongoDB"))
  .catch((err) => console.error(err));
```


title: Assembly
last_modified: 2024-10-18 18:55:56
body:
# Assembly

- Assembly is one level up from machine code and provides a more human-friendly
  abstraction layer

- An assembly instruction is a _mneumonic_ that comprises an "op code" plus
  operands

- It is translated back to machine code by an assembler for the computer to
  execute.

## Example instruction

We can translate the machine instruction `e3a07004` into the assembly
instruction `mov r7, #4`. This translates to: _move the value number 4 to the
register r7_.

## Assemblers

Although assembly is useful for humans, computers still work at the level of
machine code. Therefore you need an **assembler** to translate the assembly code
to machine code. An assembly language text file is fed into an assembler and a
binary object file containing machine code is returned.

A disassembler does the opposite: translate machine code into assembly.

## Relation to instruction set architectures

- the ISA defines the hardware capabilities and the instructions that can be run
  on the hardware
- machine code is a binary representation of these instructions and can be
  directly executed by the CPU
- humans use an assembly language version of the machine code which is then
  translated back to machine code for the computer to execute.

## Related notes

[[Hexadecimal_number_system]], [[Instruction_set_architectures]],
[[CPU_architecture]]


title: Logic_gates
last_modified: 2024-10-18 18:55:56
body:
# Logic gates

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

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

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

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

## Elementary and composite gates

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

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

## Gate interface / gate implementation

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

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

// Add: Interface diagram

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

// Add: Implementation diagram

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

## NOT gate

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

### Truth conditions

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

### Interactive circuit

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

### Truth conditions

tion) truth functional connective

### Symbol

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

### Truth conditions

### Truth conditions

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

### Interactive circuit

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

## NAND gate

> The NAND gate inverts the truth conditions of AND.

### Symbol

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

### Truth conditions

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

### Interactive circuit

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

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

## OR gate

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

### Symbol

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

### Truth condition

### Truth conditions

s

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

### Interactive circui

### Truth conditions

t

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

## XOR gate

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

### Symbol

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

### Truth conditions

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

### Interactive circuit

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

## NOR gate

> The NOR gate inverts the function of an OR gate

### Symbol

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

### Truth conditions

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

### Interactive circuit

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


title: What_are_disks
last_modified: 2024-10-18 18:55:56
body:
# 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.


title: Comparing_React_classes_to_hooks
last_modified: 2024-10-18 18:55:56
body:
# Comparing class components to hook-based components

We will look at the same component written with hooks and classes. Both use this
child component:

```jsx
const CounterBox = (props) => {
  return <div className="output">{props.counterValue}</div>;
};
```

## Class component

```jsx
import CounterBox from "./CounterBox";

class CounterClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      counterValue: 0,
    };
  }

  addOne() {
    this.setState({
      counterValue: this.state.counterValue + 1,
    });
  }

  render() {
    return (
      <React.Fragment>
        <button onClick={() => this.addOne()}>Add One</button>
        <CounterBox counterValue={this.state.counterValue} />
      </React.Fragment>
    );
  }
}
```

## Hook version

```jsx
import React, { useState } from "react";
import CounterBox from "./CounterBox";

function CounterHook() {
  const [counter, setCounter] = useState(0);
  const handleClick = () => setCounter(counter + 1);
  return (
    <React.Fragment>
      <button onClick={handleClick}>Add One</button>
      <CounterBox counterValue={counter}></CounterBox>
    </React.Fragment>
  );
}
```

## Main differences

- Obviously with hooks we use a function, not a class. We can therefore think of
  them as **simple components** that are able to handle state as well as props.
- We have written the hook as a declared function but we could just as easily
  write it as a function expression or arrow function.
- With the class, the state versioning is managed via the following properties
  on the `React.Component` class: `this.state` (start state) and `this.setState`
  (the change).
- In both cases the function that actually creates the change is handled via an
  arrow function. With the hook we use the customary name `handleClick` .

- The same process is managed in the hook via the `useState()` method. We
  effectively set 'before' and 'after' destructured variables on this method:

  ```jsx
  const [counter, setCounter] = useState(0);

  // Schematically:
  const [val, setVal] = useState(initialVal);
  ```

## Binding with class component state

In the class component example we use an inline arrow function to execute
`addOne()` however the official way to do it is to bind the event via the
constructor method to avoid the problems created by `this` scope in classes.
Using arrows is mostly fine and is less verbose, but it is important to
understand the 'official' approach.

The way to write the state change in this way is:

```jsx
class CounterClass extends React.Component{
  constructor(props){
    super(props);
		this.state = { counterValue: 0};
    this.handleClick = this.handleClick.bind(this); // bind the click to the class scope
  }

  handleClick(event){
    this.setState({
            counterValue: this.state.counterValue + 1;
    });
  }

  render(){
    return (
      <button type="button"
      onClick={this.handleClick}> // reference the class scope with `this`
      Click Me
      </button>
			<CounterBox counterValue={this.state.counterValue}></CounterBox>
    );
  }
}
```

## How hooks improve on class components

- State, when managed via hooks can be easily decoupled from a specific
  component and reused elsewhere. This also means it can be tested separately
  from any specific component where it is applied. This is much harder to do
  with classes which are closely entangled with the non-state aspects of a
  component through `this` and binding:

  > With Hooks, you can extract stateful logic from a component so it can be
  > tested independently and reused. Hooks allow you to reuse stateful logic
  > without changing your component hierarchy. This makes it easy to share Hooks
  > among many components or with the community.

- Hooks simplify lifestyle methods and the use of `componentDidMount` etc
  through the `useEffect` hook
- Classes and `this` are confusing generally. Functions are easier to grasp and
  read.

## Relation to function components

You can think of hooks as function components (previously known as stateless
components) plus state. Previously function components were 'dumb' components.
That is, they didn't allow for state management and generally used for layout.
They could receive data as props but not state. This changes with hooks: you can
add state to functions and no longer need to use classes exclusively for state.
In fact you no longer need to use classes at all, unless you want to.

> The stateful portion of a function component must be placed before the
> `return()` statement in the function.


title: Update_existing_data_in_SQL_table
last_modified: 2024-10-18 18:55:56
body:
# 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;
```


title: Creating_a_Linux_partition_table
last_modified: 2024-10-18 19:12:13
body:
# Creating a Linux partition table

## Creating a partition table

To demonstrate the process of partitioning a harddrive I am going to repartition
an external SATA drive as if it were being primed for a fresh Linux install.

Let's take a look at the disk in its current form:

```
$ fdisk -l

  Disk /dev/sda: 465.74 GiB, 500079525888 bytes, 976717824 sectors
  Disk model: My Passport 071Aumount /dev/sda2
  Disklabel type: gpt
  Disk identifier: 9993F1BB-626C-485F-8542-3CC73BB40953

  Device      Start       End   Sectors   Size Type
  /dev/sda1      40    409639    409600   200M EFI System
  /dev/sda2  409640 976455639 976046000 465.4G Apple HFS/HFS+
```

(This disk was previously used as a backup disk for MacOS so in addition to the
extended partition which has a proprietary file system type (Apple HFS) it has a
primary partition which would load the recovery OS. In contrast to my main
harddrive this uses the standard SCSI prototcol and thus the partitions are
prepended with `sda`.)

#### 1. Unmount existing partitions

```bash
umount /dev/sda1
umount /dev/sda2
```

#### 2. Deleting the existing partitions

```bash
# Load the disk into fdisk
$ sudo fdisk /dev/sda
Command (m for help): d
Partition number (1,2, default 2): 1
Partition 1 has been deleted.
Command (m for help): d
Selected partition 2
Partition 2 has been deleted.

# Verify deletion with p(rint) command
Command (m for help): p
Disk /dev/sda: 465.74 GiB, 500079525888 bytes, 976717824 sectors
Disk model: My Passport 071A
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 9993F1BB-626C-485F-8542-3CC73BB40953

# Write the changes to disk with w(rite)
w

# Check execution:
$ lsblk

NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda           8:0    0 465.7G  0 disk
```

So now the disk has no partitions, just the physical disk space and no
mountpoints. We are ready to re-partition.

#### 3. Re-partition the disk

We are going to create a GPT partition table

```bash
$ sudo fdisk /dev/sda
Command (m for help): g
Created a new GPT disklabel (GUID: E316B1A9-6F73-FB41-8CBB-FA4E3C448F2B).

Command (m for help): n
Partition number (1-128, default 1):
First sector (2048-976717790, default 2048):

# Just press enter here

# The last sector is where we put the actual size we want for the partition
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-976717790, default 976715775): +100G
Created a new partition 1 of type 'Linux filesystem' and of size 100 GiB.

# For the second and final partition, we follow the same steps but don't add a partition size, since it will default to what is left.

Command (m for help): n
Partition number (2-128, default 2):
First sector (209717248-976717790, default 209717248):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (209717248-976717790, default 976715775):

Created a new partition 2 of type 'Linux filesystem' and of size 365.7 GiB.

# Check the partitions with p(rint)
Command (m for help): p
Disk /dev/sda: 465.74 GiB, 500079525888 bytes, 976717824 sectors
Disk model: My Passport 071A
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 92739978-F7D2-5042-9758-C1429B5C8E11

Device         Start       End   Sectors   Size Type
/dev/sda1       2048 209717247 209715200   100G Linux filesystem
/dev/sda2  209717248 976715775 766998528 365.7G Linux filesystem

# Then write with w
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
```

We can now confirm our new partitions:

```bash
$ lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda           8:0    0 465.7G  0 disk
├─sda1        8:1    0   100G  0 part
└─sda2        8:2    0 365.7G  0 part
```

#### Naming a partition

By default each partition will have its GUID as its name, but you can add a
human friendly name with:

```bash
sudo e2label /dev/sda1 my_human_name
```

> Whilst we have created our partitions we cannot yet mount them. This is
> because we have not yet set up a filesystem on the partitions. This is the
> next step.

## Related notes

![Linux disk partitions](./Linux_disk_partitions.md)


title: Associative_Property_of_Addition_and_Multiplication
last_modified: 2024-11-04 14:20:39
body:
# 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.


title: User_management_on_AWS
last_modified: 2024-10-18 18:55:56
body:
# 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`.


title: Primitive_types
last_modified: 2024-10-18 18:55:56
body:
# Primitive types

The most basic type level is the one already present in JavaScript itself: the
primitive data types: `boolean`, `string` and `number` . These are the types
that would be returned from a `typeof` expression in ordinary JavaScript

## Type inference

You can explicitly declare these data types yourself when you create a variable
with `var`, `const`, or `let` if you like, but it is generally unnecessary since
Typescript is intelligent enough to perform **type inference** and will know
from what you write which type you mean.

```ts
const age: number = 32;
const name: string = "Thomas";
const one: boolean = true;
const zero: boolean = false;
```

## Arrays

With arrays that you directly define the type will also be inferred but it can
be helpful to explicitly declare them. If you declare an empty array, it is
necessary to assign empty brackets.

```ts
const store: number[] = [1, 2, 3]; // Populated array
const store: string[] = []; // Empty array
```

## Object

`Object` is a valid type declaration in TS but it is not particularly helpful
since it becomes similar to using [any](Any.md) given that most primitive
types in JavaScripts prototypically inherit from an Object.

Generally, when you use objects in TypeScript you type them as
[custom types](Custom_types_in_TypeScript.md).

## Array of (untyped) objects

If we just know that it is going to be an array of objects we can use:

```ts
const arrOfObj = {}[]
```

If we wish to define a particular shape but without defining a type:

```ts
const arrOfObj = { name: string, age: number }[]
```

But better for reusability to do:

```ts
type ArrayOfObj = {
  name: string,
  age: number
}

const arrOfObj: ArrayOfObj[] = [{}, ...]

```


title: Python_execution
last_modified: 2024-10-18 18:55:56
body:
# Python execution

For immediately executable scripts, we have to have a Python shebang at the top:

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

With programs we can just run the `main` file with `python main.py`.


title: S3
last_modified: 2024-10-18 18:55:56
body:
# S3

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

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

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

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


title: AWS_CloudWatch
last_modified: 2024-10-18 18:55:56
body:
# 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)


title: Binary_colour_encoding
last_modified: 2024-10-18 18:55:56
body:
# Binary encoding of colours

The approach to encoding binary representations of colour is very similar to the
approach we explored when looking at the encoding of
[alphanumeric values](Text_encoding.md).

We begin by determining the total number of colours and colour shades we want to
represent. With this value established we then decide on the bit-length required
that will accomodate this number of variations. Finally, we assign a binary
number to each representation.

### Greyscale

We can start with a limited palette: greyscale. Here there is black and white
and number of interim shades of grey. The convention is to use an 8-bit number
which, given $2^8 = 256$ provides 256 shade variations.

In decimal, 0 is equal to black (zero light intensity) and 255 is equal to white
(full light intensity). Some examples of this (including binary and hex
representations are below):

![](/img/greyscale-encoding.svg)

### Colour encoding

A single 8-bit number is sufficient for 256 greyscale combinations but with
colour, the convention is to use three 8-bit numbers where each byte corresponds
to a value for red, green and blue (RGB). Thus the overall bit-length is 24
($2^3 + 2^3 + 2^3$). Each value corresponds to a light intensity for the given
colour. Combined they are capable of representing all colours.

Some examples below

#### Red

Red is represented in RGB with all 8 red bits to set to 1 and the remaining 16
bits for the other two colours set to 0.

![](/img/red-encoding.svg)

#### Yellow

Yellow is represented in RGB with both red and blue set to 1 and the remaining 8
green bits set to ):

![](/img/yellow-encoding.svg)

## Binary encoding of images


title: Rename_a_branch
last_modified: 2024-10-18 18:55:56
body:
# Rename a branch

### In local development

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

### Update the remote

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


title: Accessing_metadata_about_SQL_database
last_modified: 2024-10-18 18:55:56
body:
# Accessing metadata about SQL database

```sql
SHOW DATABASES

SHOW TABLES

-- View column names and data types
DESCRIBE [table_name]

-- View disk usage per column
SHOW TABLE STATUS

```


title: Theorems_and_empty_sets
last_modified: 2024-10-19 10:00:27
body:
# 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. )


title: Creating_a_RESTful_API_Integrating_the_Database
last_modified: 2024-10-18 18:55:56
body:
# Creating a RESTful API: Integrating the database

So far we have set up the application and an `/api/courses` route which handles
requests for RESTful API operations on a local array of course objects. We now
want to have the endpoints operate on a MongoDB `courses` collection rather than
the array.

## Set-up

We will follow the routine for establishing a MongoDB instance as detailed in
[my notes](Connect_to_Mongo_database.md) on Mongo:

- [Create MongoDB database](Create_Mongo_database.md)
- [Connect to MongoDB database](Connect_to_Mongo_database.md)

Our `index.js` now looks like the following:

```js
// index.js

// Connect to database
mongoose
  .connect("mongodb://127.0.0.1/playground")
  .then(() => console.log("Connected to MongoDB"))
  .catch((err) => console.error(err));

app.use(express.json());

// Link to `courses` route which contains our REST request handlers for this part of the API
app.use("/api/courses", courses);
```

## Integrating Mongo with our our `courses` module

### Create the schema

Now we go the router module for `courses` and start to use Mongoose, defining
our `Course` schema:

```diff
// index.js

// Connect to database
mongoose
  .connect("mongodb://127.0.0.1/playground")
  .then(() => console.log("Connected to MongoDB"))
  .catch((err) => console.error(err));

app.use(express.json());

// Link to `courses` route which contains our REST request handlers for this part of the API
app.use("/api/courses", courses);

+ 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,
+ });

```

### Create a model

```diff
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,
 });

+ const Course = new mongoose.model('Course', courseSchema);
```

With this established we can remove our local array as we are ready to start
getting our data from the database:

```diff
const Course = mongoose.model('Course', courseSchema);

- const courses = [
-  {
-    id: 1,
-    name: "First course",
-  },
-  ...
-];
```

We could actually simplify the syntax here and combine our schema and model
declaration into a single block:

```js
const Course = mongoose.model(
  "Course",
  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,
  })
);
```

> N.B In a real project we wouldn't keep our models in the same file as our
> handlers. We would keep them in the dedicated `/models/` directory. We should
> stick to the single responsibility principle and keep `/routes/` for API
> handlers and `/model/` for schema declarations and models.

## Rewriting the REST handlers

Now we need to rewrite our RESTful request handlers so that the data is sourced
from and added to the database. We will mainly be using the Mongo syntax defined
at [Querying a collection](Querying_a_Mongo_collection.md) and
[Adding documents to a collection](Adding_documents_to_a_Mongo_collection.md).
We will also keep API validation within the `/model/` file.

### GET

Instead of simply returning the array, we use the Mongoose `find` method.

```diff
- router.get("/", (req, res) => {
-  res.send(courses);
 });

+ router.get("/", async (res, res) => {
+  const courses = await Courses.find();
  res.send(courses)
})
```

### POST

Now we make our new course an instance of the `Courses` model:

```js
// Original formulation

router.post("/", (req, res) => {
-  const course = {
-    id: courses.length + 1,
-    name: req.body.name,
-  };
  courses.push(course);
  res.send(course);
});
```

```diff
router.post("/", async (req, res) => {
+  let course = new Course({ // make new course instance of Course model
-   id: courses.length + 1, // not needed as DB automatically adds an id
    name: req.body.name,
  });
- courses.push(course); // not pushing to the array anymore
+ await course.save() // save to Mongo
  res.send(course);
});

```

### PUT

When updating a value in the database we are going to use the
[query-first](Update_a_Mongo_document.md#query-first-document-update)
approach to updating a Mongo document.

```jsconst 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,
 });
router.put("/:id", (req, res) => {
  const course = courses.find((c) => c.id === parseInt(req.params.id));

  if (!course)
    return res.status(404).send("A course with the given ID was not found");

  const { error } = validateCourse(req.body);
  if (error)
    return error.details.map((joiErr) => res.status(400).send(joiErr.message));
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,
 });
  course.name = req.body.name;
  res.send(course);
});
```

```diff
router.put("/:id", async (req, res) => {
- const course = courses.find((c) => c.id === parseInt(req.params.id));
 const { error } = validateCourse(req.body);
 if (!course) return res.status(404).send("A course with the given ID was not found");
+ const updatedCourse = await Course.findByIdAndUpdate(req.params.id,
+  { name: req.body.name },
+  { new: true}
+ )

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

- course.name = req.body.name;
  res.send(course);
```

### DELETE

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

  courses.indexOf(course);
  courses.splice(index, 1);
  res.send(course);
});
```

```diff

router.delete("/:id", async (req, res) => {
 const courseToDelete = await Course.findByIdAndRemove(req.params.id)

 if (!course) return res.status(404).send("A course with the given ID was not found");

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

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


title: Prefixes_for_units_of_electrical_measurement
last_modified: 2024-10-18 18:55:56
body:
# Prefixes for unit of electrical measurement

In electronics we are often dealing with units that are very large or very
small, thus we rely on [exponents](Exponents.md) for formal
expression.

| Prefix | Symbol | Expression as exponent | Expression as decimal value | English word |
| ------ | ------ | ---------------------- | --------------------------- | ------------ |
| Terra- | T      | $10^{12}$              | 1,000,000,000,000           | trillion     |
| Giga-  | G      | $10^9$                 | 1,000,000,000               | billion      |
| Mega-  | M      | $10^6$                 | 1,000,000                   | million      |
| Kilo-  | k      | $10^3$                 | 1,000                       | thousand     |
| Centi- | c      | $10^{-2}$              | 0.01                        | hundredth    |
| Milli- | m      | $10^{-3}$              | 0.001                       | thousandth   |
| Micro- | $\mu$  | $10^{-6}$              | 0.0000001                   | millionth    |
| Nano-  | n      | $10^{-9}$              | 0.0000000001                | billionth    |
| Pico-  | p      | $10^{-12}$             | 0.0000000000001             | trillionth   |

For example, with Amps we tend not to use 1 whole amp as this is far too large
for most electronics. More common is the milliampere (mA) and the microampere
($\mu$A).

A mA is equal to one thousandth of an ampere: 0.001 A. It takes 1000
milliamperes to equal one ampere.

A $\mu$A is equal to one millionth of an ampere: 0.0000001 A. It takes one
million micoramperes to equal one ampere.


title: Naming_conventions
last_modified: 2024-10-18 18:55:56
body:
# Python naming conventions

## Underscores

To name a variable or method with multiple words we use underscores to separate
each word.

```python
an_int = 32
print(an_integer.is_integer) # true
```

## Constants

There are no constants in Python. However there a pseudo-constants established
by convention. To denote that a variable should not change you use `UPPER_CASE`.


title: f0f70a3c_ARPANET_motivations
last_modified: 2024-10-24 17:26:12
body:
# 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.


title: Dynamic_and_static_websites
last_modified: 2024-12-20 11:32:47
body:
# Dynamic and static websites

When we talk about dynamic and static websites we are talking about the role of
the server, not anything to do with how the website behaves for the user.

A static site's content comes unchanged from the files on the server whereas a
dynamic site's content is generated on the server.

With a static website, the content of the site has been build before a request
for it is made. It's HTML, CSS and JS are stored on the server and are simply
returned to the client in this form when they are requested.

When content is requested on a dynamic website the HTML is generated on-the-fly
on the server-side and returned to the client. Typically a request will be made
for an item of content, the data will be fetched on the backend from a DB and
then formatted as HTML and sent back to the client.

Static websites can be more performant because of the lack of BE processing
however it means that any runtime processing or special rendering must be
handled by the frontend JavaScript, without relying on server languages.

Take the sourcing of remote data. With a dynamic site this can be handled in a
single request because the backend can fetch the data and return to the client
with the rendering applied. On the frontend, JS would have to make a request to
an endpoint that calls a backend service that returns the data. Having received
the raw data it would then need to render it.

Nowadays there is generally a mixed approach. This distinction was more
important in the early days of the web.


title: Integrated_circuits
last_modified: 2024-10-18 18:55:56
body:
# Integrated circuits

An integrated circuit (IC) is a single unit that comprises several logic gates
designed for the easy construction of [digital circuits](Digital_circuits.md).
The terms "integrated circuit" and "chip" are often used interchangeably.

An IC puts the gates on a single piece of silicon that has electrical contact
points called pins. There are two types of pins on an IC: input pins and output
pins. Input pins are used to receive signals from other components in the
circuit, while output pins are used to send signals to other components. The
function of a pin is determined by the connection it has to the rest of the
circuit and the type of signal it is intended to transmit or receive.

The type we will look at are called **dual in-line packages** (DIPs). They are
rectangular wth two parallel rows of pins. The pins make it easy to connect DIPs
to a breadboard.

_An integrated circuit and its use on a breadboard:_

![](../img/integrated-circuit.jpeg)

![](../img/breadboard-DIP.jpg)


title: Distributivity
last_modified: 2024-10-18 18:55:56
body:
# The Distributive Property of Multiplication

**Let $a$, $b$ represent members of $\mathbb{W}$ or $\mathbb{Z}$ then:**

$$ a \cdot (b + c) = a \cdot b + a \cdot c $$

### Demonstration

When faced with $4(2\cdot3)$ we may proceed with the official order of
operations in algebra, namely:

```
4 x (2 + 3) = 4 x (5)
= 20
```

In other words we find the sum of the values in parentheses and then multiply
this by the value outside of the brackets.

When we use distributive property we _distribute_ each value in the parentheses
against the value outside of the parentheses:

```
4 x (2 + 3) = (4 x 2) + (4 x 3)
8 + 12 = 20
```


title: Identify_merged_branches
last_modified: 2024-10-18 18:55:56
body:
# Identify merged branches

```sh
# View merged
git branch --merged

# View merged remote
git branch -r --merged

# View unmerged
git branch --no-merged

# View unmerged remote

git branch -r --no-merged
```

The above cases run the merge check from the point of view of HEAD as this is
the default.

But we can also run it from any branch.

```
git branch --merged non_head_branch
```

## Demonstration

We have the following branches

```
git branch

* main
key_feature
another_feature
```

The branches other than `main`, have not been merged:

```
git branch --merged

* main
```

Just to confirm:

```
git branch --no-merged

key_feature
another_feature
```


title: Working_with_JSON_in_Python
last_modified: 2024-12-29 11:55:12
body:
# 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.dump()`

Not to be confused with the above. Output a Python dictionary as JSON, for
instance to create a `.json` file:

```py
data = { "key": "value"}
with open("some/directory/output.json", "w") as f:
    json.dump(data, f, indent=4)
```


title: Boot_process
last_modified: 2024-11-04 14:22:36
body:
# Boot process

The primary or boot
[partition](Disks.md#primary-extended-and-logical-partitions) of a harddisk
contains a bootloader. It is the job of the bootloader to locate the
[kernel](The_kernel.md) on the harddrive and inject it into memory so that they
operating system can start. This is the boot process.

## Boot loaders

The boot loader loads the kernel into memory from the disk and then starts the
kernel with a set of kernel parameters.

Like the kernel itself, the boot loader requires a driver in order to access the
disk but it can't use the same one as the kernel since at this point, the kernel
is not yet loaded into memory. So it has its own special driver: this is either
the BIOS or the UEFI firmware.

A boot loader's core functionality includes the ability to do the following:

- select from multiple kernels
- switch between sets of kernel parameters
- provide support for booting other operating systems

### BIOS and UEFI

BIOS and UEFI are both firmware that is installed directly on the motherboard of
the computer. They are firmware because they are software that is permanent and
programmed into read-only memory.

In the context of disks, their most crucial role is locating the operating
system on the harddisk and loading it into memory so that the bootstrapping
process can begin. However they are also responsible for the computer clock and
the management of peripherals.

As we can see from the `fdisk` readout, the boot partition uses EFI, the storage
partition associated with UEFI.

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

Whilst UEFI is installed on the hardware, most of its configuration is stored in
the EFI partition on the disk, whereas with BIOS, everything is on the chip.
This makes booting faster with UEFI.

Even though most modern computers use UEFI, it may still be referred to as BIOS
for user-continuity (for instance on WIndows). With Linux you have to explicitly
create your boot process so the two are clearly distinguishable.

### GRUB

The de facto standard boot loader for Linux is GRUB: Grand Unified Boot Loader.

![](/img/grub.jpg)

You see the GRUB default menu when you first start a Linux machine. It will
offer you various options for loading your installed OS or other OSs. GRUB is a
[filesystem](./Filesystems.md) like the main disk. If you press `e` in this
screen you can view and edit specific boot parameters. Pressing `c` gives you
access to the GRUB command line interface. This allows you to interact with GRUB
in the same way as you would with any other filesystem, allowing for advanced
configuration.

## The boot sequence

1. The machine's BIOS or boot firmware (in the case of UEFI) loads and runs a
   boot loader. It finds the bootloader by looking for the part of the disk that
   contains either the MBR or GPT sector.
2. The boot loader finds the kernel image on disk, loads it into memory and
   starts it.
3. The kernel initializes the devices and its drivers.
4. The kernel mounts the root filesystem.
5. The kernel starts a program called **init**. It has a
   [process id](Processes.md#processes-ps) of 1. This is the point at which
   [user space](User_Space.md) starts.
6. Init sets the rest of the system processes in motion.
7. At the end of the boot process, init starts a process allowing you to log in.

## Boot diagnostics

Linux keeps a log of the boot process as part of the [journal](./journald.md)
log of everything that happens on disk. We can use the command `journalct -k` to
view the diagnostics generated during the boot. Here is a selection:

```
Jul 07 07:12:58 archbish kernel: Linux version 5.18.5-arch1-1 (linux@archlinux) (gcc (GCC) 12.1.0, GNU ld (GNU Binutils) 2.38) #1 SMP PREEMPT_DYNAMIC Thu, 16 Jun 2022 20:40:45 +0000
Jul 07 07:12:58 archbish kernel: Command line: BOOT_IMAGE=/boot/vmlinuz-linux root=UUID=2ee6b834-0857-49dc-b8ba-a24d46d228ae rw loglevel=3 quiet
Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x020: 'AVX-512 opmask'
Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x040: 'AVX-512 Hi256'
Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[2]:  576, xstate_sizes[2]:  256
Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[5]:  832, xstate_sizes[5]:   64
Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[6]:  896, xstate_sizes[6]:  512
Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[7]: 1408, xstate_sizes[7]: 1024
Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[9]: 2432, xstate_sizes[9]:    8
Jul 07 07:12:58 archbish kernel: x86/fpu: Enabled xstate features 0x2e7, context size is 2440 bytes, using 'compacted' format.
Jul 07 07:12:58 archbish kernel: signal: max sigframe size: 3632
Jul 07 07:12:58 archbish kernel: BIOS-provided physical RAM map:
Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000000000000-0x000000000009efff] usable
Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x000000000009f000-0x00000000000fffff] reserved
Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000000100000-0x000000008ef6efff] usable
Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x000000008ef6f000-0x000000008f03efff] type 20
Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x000000008f03f000-0x0000000093f2efff] reserved
Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000093f2f000-0x0000000094b2efff] ACPI NVS
Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000094b2f000-0x0000000094bfefff] ACPI data
Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000094bff000-0x0000000094bfffff] usable
Jul 07 07:12:58 archbish kernel: NX (Execute Disable) protection: active
Jul 07 07:12:58 archbish kernel: efi: EFI v2.70 by Lenovo
Jul 07 07:12:58 archbish kernel: efi: ACPI=0x94bfe000 ACPI 2.0=0x94bfe014 SMBIOS=0x90cbd000 SMBIOS 3.0=0x90cb0000 TPMFinalLog=0x9498f000 MEMATTR=0x8aa30018 ESRT=0x8ab9c000
Jul 07 07:12:58 archbish kernel: SMBIOS 3.2.0 present.
Jul 07 07:12:58 archbish kernel: DMI: LENOVO 20W4000NUK/20W4000NUK, BIOS N34ET40W (1.40 ) 06/25/2021
Jul 07 07:12:58 archbish kernel: tsc: Detected 2400.000 MHz processor
Jul 07 07:12:58 archbish kernel: tsc: Detected 2419.200 MHz TSC
Jul 07 07:12:58 archbish kernel: e820: update [mem 0x00000000-0x00000fff] usable ==> reserved
Jul 07 07:12:58 archbish kernel: e820: remove [mem 0x000a0000-0x000fffff] usable
Jul 07 07:12:58 archbish kernel: last_pfn = 0x45f800 max_arch_pfn = 0x400000000
Jul 07 07:12:58 archbish kernel: x86/PAT: Configuration [0-7]: WB  WC  UC- UC  WB  WP  UC- WT
Jul 07 07:12:58 archbish kernel: last_pfn = 0x94c00 max_arch_pfn = 0x400000000
Jul 07 07:12:58 archbish kernel: esrt: Reserving ESRT space from 0x000000008ab9c000 to 0x000000008ab9c150.
Jul 07 07:12:58 archbish kernel: e820: update [mem 0x8ab9c000-0x8ab9cfff] usable ==> reserved
```

We can see that the first thing that is registered is the OS image that contains
the kernel. From this point on, the kernel is active and doing its work.

## Initial kernel actions

The diagnostics above are a running commentary provided by the kernel itself
about its actions. The details will be specific to the machine in question but
the general sequence is as follows for all operating systems:

1. CPU inspection
2. Memory inspection
3. Device bus discovery
4. Device discovery
5. Auxiliary kernel subsystem setup (networks etc)
6. Mounting of the root filesystem
7. Starting of user space


title: Docker_architecture
last_modified: 2024-10-18 18:55:56
body:
# Docker architecture

## Overview

![](/img/dock-architecture.png)

- The Docker Client is a thin API for making
  [REST API](RESTful_APIs.md) to the Docker Server. Any CLI
  command beginning `docker...` is an API request to the server.
- The internal process name for the server is `dockerd`.
- On `docker run...`, `dockerd` calls `containerd`. This process starts the
  container runtimes and configures container-level storage and networking.
- This then spawns `container-shim` procecesses which takes any output from you
  container and puts it in a logfile. (A redirection of `stdout` and `stderr`)
  One shim is spawned for each container.
- The shim then launches a container runtime called `runc`. The `runc` performs
  all the complex configuration of cgroups and namespaces to create the
  container environment.
- `containerd` is purely a bootstrapper. Once it has fulfilled its function by
  initiating `runc`, the Docker instance is up and running and the kernel takes
  it from there. It is like a launch scaffold that can be discarded.

> If you are running Docker desktop on Mac or Windows, Docker creates a Linux
> virtual machine, since it cannot run natively on Windows or Mac. In these
> cases the the Docker Server runs inside this VM adding an additional layer of
> abstraction.

> Docker server also creates an internal virtual network and assigns IP
> addresses to the containers. (This can be used to get containers to
> communicate to each other over TCP/IP, since they are isolated by default.)

## Images

Containers are created from images stored in a registry (either the public
Docker Hub or privately). `containerd` uses the image to tell `runc` how to
construct and build the image.

```sh
docker run [image_name]
```

The images you have pulled from the registry are stored in a local registry on
your machine.

When you run `docker run`, Docker will look first for the image in your local
registry. If it cannot find it there, it will attempt to pull it from the remote
registry.

This assumes you are using a third-party image. You can also create your own
images by writing a Dockerfile:

```sh
docker build [docker_file]
docker run [resulting_image]
```


title: Three_bit_counter
last_modified: 2024-10-18 18:55:56
body:
# 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


title: Foreign_keys_in_SQL
last_modified: 2024-10-18 18:55:56
body:
# Creating views with foreign keys

We utilise **foreign** and
[primary keys](Primary_key.md) to create
relationships between tables in SQL. Foreign keys link data in one table to the
data in another table and are how we cross-reference data in SQL.

In essence you use the primary key of one table to access data in another table.
When one table _A_ contains the primary key of another table _B_ as a field,
that key is "foreign" to _A_ hence the name.

Let's say we have a `sales` table:

| saleId | modelId | saleDate   | employeeId | price  |
| ------ | ------- | ---------- | ---------- | ------ |
| 1      | 44      | 2020-07-27 | tbishop    | 399.99 |
| 2      | 22      | 2021-02-05 | tbishop    | 200.99 |

In SQL this would be set up as follows:

```sql
CREATE TABLE sales (
  saleId integer PRIMARY KEY,
  modelId integer,
  saleDate date,
  employeeId text,
  price float
)
```

For every model that is sold it is possible for the customer to return it. This
data will be kept in another table `returns`. Every model sold will have an
entry in `sales` but may or may not have an entry in `returns` (not every
customer will return an item).

We want to establish a relationship between the two tables so that if an item is
returned we can trace it back to its original sale.

As the `saleId` is the primary key in `sales` this means it is a unique
identifier for each model sold. We will therefore use this in our `returns`
table to track the sale data.

Here's our `returns` table:

| returnId | saleId | returnDate  | reason                                         |
| -------- | ------ | ----------- | ---------------------------------------------- |
| 7899     | 1      | 2020-11-218 | New device issued under warranty due to defect |
| 6711     | 2      | 2022-09-02  | Returned gift                                  |

In this table `saleId` is identical to `saleId` in sales. It is the primary key
in `sales` but a foreign key in `returns`. If a device has been returned it must
have an entry in `returns` and the `salesId` of the entry in `returns` must
match the `salesId` in `sales`.

This is the primary benefit of utilising foreign keys: they add a restriction.
Entries to the table with a foreign key **must** have a value that corresponds
with the foreign key column.

We call this a **foreign key contraint**. More explicitly, our contraint is as
follows:

> Any value entered into returns.saleId must already exist in sales.salesId

A secondary benefit is that they save us the trouble of repeating data. Without
foreign keys we would have to input `saleId` twice in two different tables.

We establish the foreign key reference with ther following SQL:

```sql
CREATE TABLE returns (
	returnId integer PRIMARY KEY,
	saleId integer NOT NULL,
	returnDate date,
	reason text,
	FOREIGN KEY (sale_id) REFERENCES sales(sale_id)
	);
```

A table can have more than one foreign key.

If you delete the source of the foreign key, you also delete its references in
tables for which it is a foreign key. This is important to remember. So if a row
was deleted from `sales` the row in `returns` with the corresponding `saleId`
would also be deleted.


title: Printing_values_in_C
last_modified: 2024-10-18 18:55:56
body:
# Printing values in C

## To print a literal

```c
printf("hello world")
```

## To print a variable

```c
int age = 25
print("%d", age)

```

The `%d` is an example of a [format specifier](./zk/format-specifiers-in-c.md).
It is the decimal integer format specifier.


title: Primes_and_composites
last_modified: 2024-10-18 19:19:17
body:
# Prime and composite numbers

Definition of a **prime number**:

> For any whole number $n$ where $n \neq 1$, $n$ is prime if and only if its
> sole [factors](Factors%20and%20divisors.md) are $1$ and $n$

Definition of a **composite number**:

> For any whole number $n$, $n$ is composite just if $n$ is not prime


title: Defining_a_computer
last_modified: 2024-10-18 18:55:56
body:
# Defining a computer

> A general-purpose computer is one that, given the appropriate instructions and
> required time, should be able to perform most common computing tasks.

This sets a general purpose computer aside from a special-purpose computer, like
the one you might find in your dishwasher which may have its instructions
hardwired or coded into the machine. Special purpose computers only perform a
single set of tasks according to prewritten instructions. We’ll take the term
_computer_ to mean general purpose computer.

Simplified model of what a computer is:
![1.4-Input-Process-Output.png](1.4-Input-Process-Output.png)

Although the input, output and storage parts of a computer are very important,
they will not be the focus of this course. Instead we are going to learn all
about the process part, which will focus on how the computer is able to follow
instructions to make calculations.

## **Supplementary Resources**

### Early computing (_Crash Course Computer Science)_

[Early Computing: Crash Course Computer Science #1](watch?v=O5nskjZ_GoI)

- The abacus was created because the scale of society had become greater than
  what a single person could create and manipulate in their mind.
  - Eg thousands of people in a village and tens of thousands of cattle
- In a basic abacus each row of beads (say its coloured) represents a different
  power of ten
- As well as aiding calculation, the abacus acts as a primitive storage device
- Similar early computing devices: astrolabe, slide rule, sunrise clocks, tide
  clocks

> As each increase in knowledge, as well as on the contrivance of every new
> tool, human labour becomes abridged. **Charles Babbage**

- One of the first computers of the modern era was the Step Reckoner built by
  Leibniz in 1694.
  - In addition to adding, this machine was able to multiply and divide
    basically through hacks because from a mechanical point of view,
    multiplications and divisions are just many additions and subtractions
  - For example, to divide 17/5, we just subtract 5, then 5, then 5 again until
    we can't do anymore hence two left over
- But as these machines were expensive and slow, people used pre-computed tables
  in book form generated by human computers. Useful particularly for things like
  square roots.
- Similarly range tables were created that aided the military in calculating
  distances for gunboat artillery which factored in contextual factors like
  wind, drift, slope and elevation. These were used well into WW2 but they were
  limited to the particular type of cannon or shell

![Screenshot_2020-08-09_at_21.32.54 1.png](Screenshot_2020-08-09_at_21.32.54%201.png)

![Screenshot_2020-08-09_at_21.34.48.png](Screenshot_2020-08-09_at_21.34.48.png)

> Before the invention of actual computers, 'computer' was a job-title denoting
> people who were employed to conduct complex calculations, sometimes with the
> aid of machinery, but most often not. This persisted until the late 18th
> century when the word changed to include devices like adding machines.

- Babbage sought to overcome this by designing the **Difference Engine** which
  was able to compute polynomials. Complex mathematical expressions that have
  constants, variables and exponent. He failed to complete it in his lifetime
  because of the complexity and number of intricate parts required. His model
  was eventually successfully created in the 90s using his designs and it
  worked.
  - But while he was coming up with this he also conceived of a better and
    general purpose computing device that wasn't limited to polynomial
    calculations → the Analytical Engine.
  - It could run operations in sequence and had memory and a primitive printer.
    It was way ahead of its time and was never completed.
  - Ada #Lovelace wrote hypothetical programs for the Analytical Engine, hence
    she is considered the world's first computer programmer.
- At this point then, computing was limited to scientific and engineering
  disciplines but in 1890, the US govt needed a computer in order to comply with
  the constitutional stipulation to have a census every ten years. This was
  getting increasingly difficult with the growing population - it would take
  more than 13 years to complete. This led to punch cards designed by Herman
  Hollereth. From this IBM was born


title: Match_statements_in_Python
last_modified: 2024-10-18 18:55:56
body:
# Match statements in Python

> A `match` statement is the equivalent of a switch or case statement in Python

```python
command = input("What are you doing next? ")

match command:
    case "quit":
        print("Goodbye!")
    case "look":
        print("Looking out")
    case "up" | "down":
        print("up or down")
    case _:
        print("The default")

"""
What are you doing next? up
up or down
"""

match command.split():
    case ["go", "left"]:
        print("go left")
    case ["go", ("fast" | "slow")]:
        print("go fast or slow")

point = (3, 3)
match point:
    case (x, y) if x == y:
        print(f"The point is located on the diagonal Y=X at {x}.")
    case (x, y):
        print(f"Point is not on the diagonal.")
"""
The point is located on the diagonal Y=X at 3.
"""
```


title: The_PATH
last_modified: 2024-10-18 18:55:56
body:
# The `$PATH`

We know that `$PATH` is an
[environment variable](Environmental_and_shell_variables.md).
This variable keeps track of directories **where executables are found**.

Whenever any command is run, the shell looks up the directories contained in the
`PATH` for the target executable file and runs it. We can see this is the case
by using the `which` command which traces the executable of bash commands. Take
the `echo` program:

```bash
which echo
# echo: shell built-in command
```

Or `npm` :

```bash
which npm
/home/thomas/.nvm/versions/node/v19.4.0/bin/npm
```

By default the path will always contain the following locations:

- `/usr/bin`
- `/usr/sbin`
- `/usr/local/bin`
- `/usr/local/sbin`
- `/bin`
- `/sbin`

All the inbuilt terminal programs reside at these locations and most of them are
at `/usr/bin`. This is why they run automatically without error. If you attempt
to run a program that doesn’t reside at these locations then you will get an
error along the lines of `program x is not found in PATH`.

## Structure of the PATH

```bash
/home/thomas/.nvm/versions/node/v19.4.0/bin:
/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:
/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl
```

## Adding to the PATH

Only the default directories load to the PATH on every session. How then can we
add custom directories to the path without them being lost every time we start a
new session? Remember that the user config `.bashrc` loads on init for every
bash session. Therefore, if we set the custom path in this file, it will be
created every time we start a session. This is why when you add a new program it
often ask you to append a script to the bottom of your `.bashrc` .

For example, at the bottom of my `.zshrc` on my work computer I have:

```bash
export CHROME_BIN=/mnt/c/Program\\ Files\\ \\(x86\\)/Google/Chrome/Application/chrome.exe
```

This enables me to access the Chromium binaries from my terminal session (needed
for running Angular tests) but it doesn’t add it to the path, it creates an
environment variable on every session.

For demonstration, let’s add a user’s desktop directory to the PATH.

First we go to the `.bashrc` and add the `export` command. Remember that this is
the command for creating a new environment variable:

```bash
export PATH="$PATH=:~/Desktop"
```

We force a reload of the `.bashrc` with the command:

```bash
source ~/.bashrc
```

Then we can check this directory has been added to the path with an echo

```bash
echo $PATH
...:~/Desktop
```


title: JavaScript_module_types
last_modified: 2024-11-24 10:46:32
body:
# JavaScript module types

The following info is useful when you wish to use modern JavaScript tooling
outside of a build system.

For instance I wanted to use a bundled React application in a static website not
created using NPM.

Great account by
[Julia Evans](https://jvns.ca/blog/2024/11/18/how-to-import-a-javascript-library/).

In essence there are three main module formats:

- CommonJS

  - Native Node JS module system using `require()` and `module.exports()`

- ESModules

  - The defacto standard that works in both the browser and Node.js

- AMD

  - A browser-friendly version of CommonJS since superseded by ESModules

Then there is UMD, _Universal Module Definition_. This is a wrapper that
combines compatibility for multiple module systems and works with AMD, CommonJS,
as well as global variables.


title: Apollo_Client
last_modified: 2024-10-18 18:55:56
body:
# Apollo Client

Apollo Client is the client-side counterpart to
[Apollo Server](Apollo_Server.md). We use it for
managing queries and mutations from the frontend to our Apollo GraphQL server.
It is specifically designed to work with React.

> We will be working with the
> [schema](Apollo_Server.md#example-schema) we defined
> when working on the server

## Initializing the client

We initialise the client and set-up in memory caching to reduce network
requests:

```js
const client = new ApolloClient({
  uri: "http://localhost:4000",
  cache: new InMemoryCache(),
});
```

> The `uri` property must match the location of our Apollo server.

## Utilising the provider

Apollo provides a top level application context that we can wrap our React app
in. This will provide access to the client object from anywhere within the app,
eg:

```jsx
ReactDOM.render(
  <ApolloProvider client={client}>
    <GlobalStyles />
    <Pages />
  </ApolloProvider>,
  document.getElementById("root")
);
```

## Running a query

### Queries as entry points

From the client point of view, the queries in the schema are _entry points_.
Although the queries exist in the schema, this alone is not sufficient for them
to be entry points. Remember a schema is just a specification or contract
between the frontend and the backend, it is not itself executable code.

Therefore, for each query in the schema we must write a frontend implementation.
We do this with **query constants**. The frontend implementation has a backend
analogue: the
[resolver](Apollo_Server.md#implementing-resolvers)
that is invoked when the frontend issues a query. The schema standardises this
relationship so that every query on the client must have a corresponding
resolver on the backend.

### Query constants

To run a query against our server we must define a query contant first. We use a
`gql` literal again:

```js
import { gql } from "@apollo/client";
const TRACKS = gql`
  query GetTracks {
    tracksForHome {
      id
      title
      thumbnail
      length
      modulesCount
      author {
        name
        photo
      }
    }
  }
`;
```

The convention is to name the query constant in `ALL_CAPS`.

> Note that the name of the query on the client doesn't have to match the query
> type defined in the schema (there is no `GetTracks` in the schema), this is
> just a client-side designator. However it should reference the schema on the
> second line (`tracksForHome`).

### `useQuery` hook

The `useQuery` hook provides a straightforward wrapper for sending queries and
receiving data back from the server.

When a component renders, `useQuery` returns an object from the Apollo Client
that contains loading, error, and data properties.

```jsx
const { loading, error, data } = useQuery(TRACKS);

const Tracks = () => {
  const { loading, error, data } = useQuery(TRACKS);

  if (loading) return "Loading...";

  if (error) return `Error! ${error.message}`;

  return <Layout grid>{JSON.stringify(data)}</Layout>;
};
```

- We destructure the `loading, error, data` variables that are returned from the
  hook
- We pass in our query constant as an argument.
- In the example we just render the serialized data but we could of course pass
  the data as a prop and map through it in an embedded child component.


title: Threads
last_modified: 2024-10-24 17:25:42
body:
# 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)


title: Capturing_user_input_in_Bash
last_modified: 2024-10-18 18:55:56
body:
# Capturing user input in Bash

We use [read](Read_command%20_in_Bash.md) to gather user input.

## Capturing raw inputs

```bash
# Wait for the input
echo "What is your name?"
read name

# Silently gather the input and proceed (the input won't show up in the terminal)
echo "What is your password?"
read -s pass

# Combine in single line
read  -p "What's your favourite animal? " animal

echo name: $name, pass $pass, animal: $animal
```

## Offering a selection

We can use `select` to get the user to choose from a series of options.

```sh
select animal in "bird" "dog" "cow"
do
    echo "You selected $animal"
    break
done
```

This will present each option as number that can be inputted:

```
1) bird
2) dog
3) cow
#? 1
You selected bird
```

We could make this more robust by using a case statement:

```sh
select animal in "bird" "dog" "cow"
do
    case $animal in
        bird) echo "Birds like to tweet";;
        dog) echo "Dogs like to woof";;
        cow) echo "Cows like to moo";;
        quit) break;;
        *) "I'm not sure what that means"
    esac
done
```

## Ensuring you capture a valid response from the user

### Set a default response value with `-i`

If the user doesn't enter anything, the value will be set to the default.

```sh
read -ep "Favourite colour? " -i "blue" favecolour
echo "$favecolour"
# blue
```

Alternative formulation:

```sh

read -p "Favourite colour? [blue]" fave

# If response empty...
if [[ -z $fave ]]; then
    fave="blue"
fi
echo "$fave was selected"
```

### Check right number of arguments supplied

Here we test that the right number of
[arguments](Passing_arguments_and_options_to_Bash_scripts.md)
have been passed to the script:

```sh
if (( $#<3 )); then
    echo "This command requires three arguments:"a
    echo "username, userId, and favouriteNumber"
else
    echo "username: $1"
    echo "userid: $2"
    echo "favourite number: $3"
fi
```

## Check the response is valid

The following example demonstrates checking an input for a pattern (a data-link
integer sequence):

```sh
read -p "Which year? [nnnn] " year
until [[ $year =~ [0-9{4} ]];
    read -p "A four-digit year, please! [nnnn] " year
done
echo "Selected year: $year"

```


title: Docker_CLI
last_modified: 2024-10-18 19:12:43
body:
# Docker CLI

```sh
# List images
docker image ls

# List container
docker container ls

# Remove container
docker rm container-name

# Remove image

docker rmi image_name:version

```


title: DynamoDB_CLI_commands
last_modified: 2024-10-18 18:55:56
body:
# DynamoDB CLI commands

## Connecting to a local (Docker)/prod (AWS) DynamoDB instance

In order to distinguish between local and production accounts you should keep
seperate profiles for each (via `.aws/config` and `.aws/credentials`).

When connecting to a local DB use the local profile and the local URL. Without
the `--profile` flag, AWS will default to the `default` profile which will
typically be your credentials for accessing AWS on the remote.

It also sometimes required to add the endpoint-url when working locally:

```
--endpoint-url http://localhost:800

> When using the commans below locally, assume `--endpoint-url` and `--profile`
> have been appended

```

For example:

```sh
aws dynamodb list-tables \
--profile timetracking_dev \
--endpoint-url http://localhost:800
```

## Delete a table

```sh
aws dynamodb delete-table \
--table-name TableName
```

## Create table from JSON schema

```sh
aws dynamodb create-table \
--cli-input-json file://create-timeentries-table.json \

```

## Describe the table

View the table schema:

```sh

aws dynamodb describe-table \
--table-name TimeEntries
```

## Scan the table

```sh
aws dynamodb scan \
--table-name TimeEntries \
--output table
```

## Related notes


title: None_in_Python
last_modified: 2024-10-18 18:55:56
body:
# None in Python

`None` is not `null`, it is closer to `undefined` in JS. If you define a
variable as `None`, the variable exists, it is just not yet defined.

Using `None` is a pattern similar to using `let` in JS to name a variable and
definine it later on.

```python
temperature = None
```

If we logged `temperature` it would give us `None` rather than a null pointer
error.

With None we can use `is None` and `is not None`, special predicates for working
with `None` only. This is a akin to using `if (x !== undefined)` in TypeScript

```python
winner = None
print('winner:', winner)
# winner: None
print('winner is None:', winner is None)
# winner is None: True
print('winner is not None:', winner is not None)
# winner is not None: False
print(type(winner))
# <class 'NoneType'>
# Now set winner to be True
print('Set winner to True')
# Set winner to True
winner = True
print('winner:', winner)
# winner: True
print('winner is None:', winner is None)
# winner is None: False
print('winner is not None:', winner is not None)
# winner is not None: True
print(type(winner))
# <class 'bool'>
```


title: Pacman
last_modified: 2024-11-17 15:37:26
body:
# Pacman

## List all installed packages

```
pacman -Qe
```

## List all packages installed for current user

```
pacman -Qqe
```

## Uninstall, and list dependencies for package to be uninstalled

```
pacman -Rcns
```


title: Heap_memory
last_modified: 2024-10-18 18:55:56
body:
# Heap memory

Along with [[Stack_memory|Stack memory]], programs make use of _heap memory_
during runtime.

Heap memory does not use a standardised data structure and can be accessed from
any point within the program.

Whereas stack memory with it's LIFO structure has memory management built-in
when programs allocate memory from the heap they must manually deallocate it
when it is no longer required. This process of "freeing memory" is known as
_garbage collection_. In a language like C, this is the explicit concern of the
programmer and is not abstracted away. Failure to properly manage garbage
collection is what causes [[Memory_leaks]].

Heap memory is used in combination with the stack since a given heap memory
allocation address is stored as a stack variable during runtime. It points to
the heap memory address whilst not being that memory itself.

Here is an example of managing heap memory allocation in C:

```c
void * data;
data = malloc(512)
```

The first line assigns a special _pointer_ variable (indicated by `void *`
rather than `int` or `str`) . This is a variable only holds a memory address.
The `malloc` method requests 512 bytes that it wants to assign to the `data`
variable. It will return the address of the first byte in the newly allocated
memory. `data` will then refer to the address on the stack that holds the
address allocation on the heap.

## Related notes


title: f6564af9_AOL_and_early_internet_enclosure
last_modified: 2025-01-09 18:51:36
body:
# f6564af9_AOL_and_early_internet_enclosure

We might view AOL, with its ubiquitous 'free trial' CD-ROMs, as the first
attempt by a corporate entity to try and enclose the Internet/Web. To take the
vast informational expanse and reduce it to a single, filtered and contained
space controlled by a single company.

![Early AOL interface](../img/aol-on-windows.jpg)

_AOL interface from 1993_

This would later be achieved or approximated more effectively by Google (under
the guise of doing the opposite - people are "empowered" to access the
information they otherwise would not find, granted a key to the Internet etc.
When in fact, making everyone go through the same gate enables surveillance and
the monetisation of the use of a public resource) and then of course by the
social media companies.

AOL in fact started this even before there was a Web, pioneering the concept of
a web portal, with their [modem-based](cfbef1c4_web_precursors.md) private-BBS
business model. This provided access to a circumscribed subset of the Internet
at a time when accessing information was opaque and confusing to the average
non-technical user. The advent of the web first threatened and then basically
obviated this model, when access to information via browsers and HTTP servers
made the process easier and more democratized. Hence why AOL then tellingly
pivoted to the browser and search-engine market (purchasing
[Netscape](c301a0b3-1d8_Mosaic_Netscape_and_Browser_Wars.md) in 1999), the next
available mechanism of enclosure.

![AOL homepage](../img/aol-original.jpg)

_AOL interface from 1999_


title: Strings_in_Bash
last_modified: 2024-10-18 18:55:56
body:
# Strings in bash

## Return a substring by index

```bash
myString="hello"
substring=${myString:0:3}
# hel
```

This is often used when looping through each character in a string.

## Loop through each character in a string

```bash
str="hallelujah"
stringLength=$(expr length str)

for (( i=0; i<=${stringLength}; i++ )); do
    echo "${str:$i:1}"
done
```


title: Conditionals_in_Bash
last_modified: 2024-10-18 18:55:56
body:
# Conditionals in Bash

## If statements

- Conditional blocks start with `if` and end with the inversion `fi` (this is a
  common syntactic pattern in bash)
- The conditional expression must be placed in square brackets with spaces
  either side. The spaces matter: if you omit them, the code will not run
- We designate the code to run when the conditional is met with `then`
- We can incorporate else if logic with `elif`

## Basic example

```bash
if [ -e $var ]; then
  # Do something
else
  # Do something else
fi
```

## If, else

```bash
if [ "$myMove" -eq "$opponentMove" ]; then
    (( totalScore+=myMove+3 ))
  elif [ $absDiff -eq 2 ] && [ "$myMove" -gt "$opponentMove" ]; then
    (( totalScore+=myMove))
  elif [ $absDiff -eq 2 ] && [ "$opponentMove" -gt "$myMove" ]; then
    (( totalScore+=myMove+6))
  elif [ $absDiff -eq 1 ] && [ "$opponentMove" -gt "$myMove" ]; then
    (( totalScore+=myMove))
  elif [ $absDiff -eq 1 ] && [ "$myMove" -gt "$opponentMove" ]; then
    (( totalScore+=myMove+6))
  fi
```

## Nested conditionals

```bash
if [[ "$line" =~ ^$ ]]; then
  if [[ "$runningTotal" -gt "$highest" ]]; then
    (( highest=runningTotal ))
  fi
  # Reset running sum
  (( runningTotal=0 ))
fi
```


title: Delay_line_memory
last_modified: 2024-10-18 18:55:56
body:
# Delay line memory

- First described in the _First Draft_ by #vonNeumann based on work by Eckert
  and Mauchley.

- Information is stored as acoustic waves travelling through a medium, typically
  mercury. As sound travels more slowly than electric charge (light), if
  electric signals are converted to sound, the delay time involved can be used
  as a type of store.

- It worked as follows:
  - Imagine we have an 8-bit number. The number is input as a sequence of pulses
    where a pulse is 1 and the absence of a pulse is 0.
  - The pulses are converted into sound waves (using a transducer, as with a
    speaker) and sent through a mercury-filled tube.
  - The length of the tube is calculated so that it takes exactly the time of
    one machine cycle for a pulse to travel from one end to the other
  - At the other end of the tube, another transducer converts the sound waves
    back to electrical pulses
  - This operates in a loop: the pulses go back into the input end of the tube
    and the cycle runs again.

To read the data, the computer would "listen" to the pulses coming out of the
receiving end. To write new data, the computer would inject new pulses at the
precise moment when it wants to change a '0' to a '1' or vice versa.

For an 8-bit number there would be 8 pulses for each bit and it would be
necessary to keep track of 8 specific positions in the delay line where each bit
would be stored.

The limitations were timing - difficult to keep track of each precise bit and
when it would arrive at the output. Also it was sequential rather than random
access. Not every bit could be accessed at once, you had to wait for its time
slot to come around to access it.


title: Syllogism
last_modified: 2024-10-18 18:55:56
body:
# Syllogism

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

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

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

### Demonstration

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


title: Chipset_and_controllers
last_modified: 2024-10-18 18:55:56
body:
# Chipset and controllers

A **controller** is simply a circuit that controls a process. The **chipset** is
a combination of controllers placed on the same piece of silicon.

The chipset manages the data flow between the different components that comprise
the [motherboard](Motherboard.md): processor,
[memory](Memory.md),
[harddisk](What_are_disks.md) and peripherals.

Buses run in and out of the chipset into these key motherboard components. The
main chipset is a kind of junction that sits between the memory and CPU through
which the [system bus](Bus.md) passes.

The chipset is sometimes called the "glue" or "traffic controller" of the
motherboard or _an intelligent intersection of buses_.


title: Grep
last_modified: 2024-10-18 18:55:56
body:
# Grep

## Purpose of `grep`

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

## Syntax

### Schematic

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

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

### Applied

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

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

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

## Useful options

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

## `ripgrep`

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

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

Most of the standard `grep` options transfer over.


title: Boolean_function_synthesis
last_modified: 2024-10-18 18:55:56
body:
# Boolean function synthesis

When we looked at
[boolean functions](Boolean_functions.md) we were
working in a particular direction: from a function to a truth table. When we do
Boolean function synthesis we work in the opposite direction: from a truth table
to a function.

This is an important skill that we will use when constructing
[logic circuits](Digital_circuits.md).
We will go from truth conditions (i.e. what we want the circuit to do and when
we want it to do it) to a function expression which is then reduced to its
simplest form and implemented with
[logic gates](Logic_gates.md).
Specifically, NAND gates.

We will show here that a complex logical expression can be reduced to an
equivalent expression that uses only the NAND operator.

## The process

The process proceeds as follows:

1. Work out the truth conditions for the circuit we want to construct
2. Identify the rows where the output is equal to 1
3. For each of these rows construct a Boolean expression that evaluates to that
   output
4. Join each expression with OR
5. Reduce these expressions to a single expression in its simplest form

## Example

Let's say we have the following truth table:

| Line | $x$ | $y$ | $z$ | $f$ |
| ---- | --- | --- | --- | --- |
| 1    | 0   | 0   | 0   | 0   |
| 2    | 0   | 0   | 1   | 0   |
| 3    | 0   | 1   | 0   | 1   |
| 4    | 0   | 1   | 1   | 0   |
| 5    | 1   | 0   | 0   | 1   |
| 6    | 1   | 0   | 1   | 0   |
| 7    | 1   | 1   | 0   | 0   |
| 8    | 1   | 1   | 1   | 0   |

We only need to focus on lines 1, 3, and 5 since they have the output 1:

| Line | $x$ | $y$ | $z$ | $f$ |
| ---- | --- | --- | --- | --- |
| 1    | 0   | 0   | 0   | 1   |
| 3    | 0   | 1   | 0   | 1   |
| 5    | 1   | 0   | 0   | 1   |

For each line we construct a Boolean expression that would result in the value
in the $f$ column. In other words we construct the function:

| Line | $x$ | $y$ | $z$ | $f$                                       |
| ---- | --- | --- | --- | ----------------------------------------- |
| 1    | 0   | 0   | 0   | $\lnot(x) \land \lnot (y) \land \lnot(z)$ |
| 3    | 0   | 1   | 0   | $\lnot(x) \land y \land \lnot(z)$         |
| 5    | 1   | 0   | 0   | $x \land \lnot(y) \land \lnot(z)$         |

We can now join each expression to create a complex expression that covers the
entire truth table using OR:

$$
(\lnot(x) \land \lnot (y) \land \lnot(z)) \\ \lor \\ (\lnot(x) \land y \land \lnot(z)) \\ \lor \\  (x \land \lnot(y) \land \lnot(z))
$$

It's clear that we have transcribed the truth conditions accurately but that we
are doing so in a rather verbose way. We can simplify by just looking at the
position of the 1s in the truth table. Notice:

- $z$ is always 0
- $x$ and $y$ are either 0 or 1 but never both 1 in the same row

So we simplify:

$$
    (\lnot(x) \land \lnot(z)) \lor (\lnot(y) \land \lnot(z))
$$

Notice that $\lnot(z)$ is repeated so we can remove the repetition through
[idempotence](Boolean_algebra.md#idempotent-law):

$$
    \lnot z \land (\lnot(x) \lor \lnot(y))
$$

The upshot is that we now have a simpler expression that uses only NOT, OR and
AND. These are the fundamental logic gates thus we are able to construct a
circuit that embodies the logic of the expression.

> This is important and is an instance of the general theorem that _any Boolean
> function_ can be represented using an expression containing AND, OR and NOT
> operations

But even this is too complex. We could get rid of the OR and just use AND and
NOT:

We can prove this theorem by showing that an expression with AND, NOT, and OR
can be formulated as an equivalent expression using just NOT and AND:

$$
  x \lor y = \lnot(\lnot(x) \land \lnot(y))
$$

| $x$ | $y$ | $x \lor y$ | $\lnot(\lnot(x) \land \lnot(y)$ |
| --- | --- | ---------- | ------------------------------- |
| 0   | 0   | 0          | 0                               |
| 0   | 1   | 1          | 1                               |
| 1   | 0   | 1          | 1                               |
| 1   | 1   | 1          | 1                               |

Finally, we can simplify even further by doing away with AND and NOT and using a
single
[NAND gate](Logic_gates.md#nand-gate)
which embodies the logic of both, being true in all instances where AND would be
false: $\lnot (x \land  y)$.

Let's prove the theorem that every logical expression can be formulated as a
NAND function. To do this we need to show that both NOT and AND can be converted
to NAND.

NOT:

$$
  \lnot(x) = x \lnot\land x
$$

AND:

$$
  x \land y = \lnot(x \lnot\land y)
$$


title: Logical_possibility_and_necessity
last_modified: 2024-10-18 18:55:56
body:
# Logical possibility and necessity

## Logical possibility

In distinguishing the properties of
[logical consistency](Logical_consistency.md) and
[validity](Validity_and_entailment.md) we make tacit use
of the notion of **possibility**. This is because when we consider the validity
of an argument we are assessing truth-conditions and this consists in asking
ourselves what could or could not be the case: were it such that _P_, then it
would be the case that _Q_. It is important to understand what possibility means
in the context of logic and how it differs from what we might mean ordinarily
when we use the term.

It is evident from the case of arguments that are valid but not sound that logic
operates with a specialised notion of possibility. For example it has to be the
case that the proposition _Every woman can levitate_ is logically possible since
the following argument is valid:

```
1.  P: Janice is a woman.
2.  P: Every woman can levitate.
3.  C: Janice can levitate.
```

But we know of course that women cannot levitate. When we assert that this is
impossible we are relying on a stronger notion of possibility than logical
possibility. It follows that the concept of possibility can have different
degrees. The scope of the concept of possibility has been the concern of
logicians and philosophers since at least the time of Plato and numerous
different formulations exist. The notion that we mostly work with unreflectively
in everyday life is nomological possibility. This means ‘governed by the
application of laws’ where these laws pertain to our current understanding of
the natural world as determined by physics. Levitation is therefore
nomologically impossible but logically possible.

If logical possibility is not constrained by the laws of physics does it place
any restrictions on what is possible? Logic applies a single restriction, the
law of non-contradiction: a proposition cannot both be true and false at once.
The following propositions are examples of a contradictory propositions.

Some examples of contradictions:

- There is a dog that is not a dog
- Today is Tuesday and today is not Tuesday
- The cat that is dead is alive

From this we can derive the following property of logical possibility:

> A proposition is logically possible just if it does not imply a contradiction.

## Logical necessity

A proposition is _logically necessary_ if it is true in every logically possible
circumstance which is to say: true on every possible truth functional
assignment. Necessity and
[logical truth](Logical_truth_and_falsity.md#logical-truth)
are therefore synonyms: anything that is logically true (a tautology) is true by
necessity (could not be otherwise.)

Further, every logical truth is logically possible but not everything that is
logically possible is logically true. It is possible that it is raining but this
is not logically necessary - it could be otherwise, i.e not raining. However it
is not possible that it could be both raining and not raining.


title: Magnetic_core_memory
last_modified: 2024-10-18 19:16:37
body:
# Magnetic core memory

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

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

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

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


