# entries

title: Memory_versus_processor
last_modified: 2024-10-18 19:17:01
body:
# Memory versus processor

> Would a more powerful processor with average or reduced memory capacity
> perform better than, equal to, or less than a less powerful processor with
> more memory, in principle?


title: Using_arguments_with_Apollo_Client
last_modified: 2024-10-18 18:55:56
body:
# Using arguments with Apollo Client

## What are arguments?

- An argument is a value you provide for a particular field in a query.
- The schema must define the arguments that a field accepts (if any)
- The resolver invokes a field's provided arguments to determine how to return
  specific data
- Some example use cases of arguments:
  - retrieve specific objects
  - filter through a set of objects
  - transform a field's returned value

## Updated schema

In order to demonstrate arguments we need to expand the
[original schema](Apollo_Server.md#example-schema).

Remember a Track is a group of modules that teaches about a specific topic. We
are going to add:

- `description` and `numberOfViews` fields to the original `Track` type
- A new `Module` type
- A field `modules` to the `Track` type that will be an array of type `Module`

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

type Track {
    id: ID!
        authorId: String! // They added this on the sly
    thumbnail: String
    length: Int
    modulesCount: Int
    description: String
    numberOfViews: Int
    modules: [Module!]!
}

type Module {
    id: ID!
        length: Int
}

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

## Adding additional query

> Currently we only have one query in the schema (`tracksForHome`) and this
> returns an array of all the tracks. To demonstrate arguments we want to return
> a specific track by its `id`. We are going to add a query that enables this.

```js
type Query {
    track(id: ID!):  Track
}
```

This query will take an `id` as an argument and the resolver will match the `id`
to a given track and return that `Track`.

To define an argument for a field in the schema we add parentheses after the
field name. In the parentheses we write the name of the argument and its type.
If we have more than one argument we can separate them with commas.

## Create resolver for new query

Now we have to create a resolver for our new `track` query. We will quickly run
through the [server-side process](Apollo_Server.md).

### Update our `RESTDataSource`

```js
class TrackAPI extends RESTDataSource {
  constructor() {...}
  getTracksForHome() {...}
  getAuthor(authorId) {...}

  getTrack(trackId){
    return this.get(`track/${trackId}`);
  }
}

```

### Add resolver to `resolvers.js`

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

### Making a query

With the server changes complete, we can now issue a query with an argument from
the client:

```js
query track(id: 'xyz'){
  title
}
```

This will return the `title` field from the track with the specific id. This
query complies with the schema since the `Track` type has the field `title`

### Resolver chains

What about the following query:

```js
query track(id: 'xyz'){
  title
  author {
    name
  }
}
```

It's not obvious how the resolver should respond to the nested query here since
author name is not a field on `Track`, it is a field on `Author`:

```js
type Track {
  id: ID!
    authorId: String!
  thumbnail: String
  length: Int
  modulesCount: Int
  description: String
  numberOfViews: Int
  modules: [Module!]!
}

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

This is where we use a **resolver chain**. Because `authorId` exists on `Track`
we can use this to get the `name` part of the query. We already have a resolver
for `author` under the `Track` resolver:

```js
Track: {
    author: ({ authorId }, _, { dataSources }) => {
      return dataSources.trackApi.getAuthor(authorId);
    },
  },
```

Notice that `authorId` is used in the place of the `parent` parameter. It
already exists on the `Track` type that wraps the resolver. So this can be
invoked to fulfill `author` and thereby access the author name from the graph.

This process is also required for our extended schema. The `Track` type now has
a `modules` field that comprises an array of the `Module` type.

Here's a reminder of the `Module` type:

```js
type Module {
    id: ID!
        length: Int
}
```

The modules for a `Track` can be sourced from the track `id`. So we can again
add a resolver chain function to the resolvers file:

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

Once again we are using a property on the parent `Track` type to use as a
parameter in the resolver function.

This setup would enables like the following to be run:

```js
query track(id: 'xyz'){
  title
  modules
}
```

The `id` parameter would be used by the `modules` resolver to return the array
of the `Modules` type.

## Variables in arguments

Instead of writing the following within our query constants on the client-side:

```js
query GetTrack {
  track(id: 'xyz'){
    title
  }
}
```

We can make the code more reusable by using variables instead of the hardcoded
`id` argument:

```js
query GetTrack($trackId: ID!) {
  track(id: $trackId){
    title
  }
}
```

This way we do not need to write a new query constant every time we want to
return a specific track.

## Send query with arguments using Apollo `useQuery`

We can now write a proper query using the
[useQuery hook](Apollo_Client.md#usequery-hook) from
Apollo Client, with variables.

First define our query constant:

```js
export const GET_TRACK = gql`
  query GetTrack($trackId: ID!) {
    track(id: $trackId) {
      id
      title
      author {
        name
      }
      description
      modules {
        id
        title
        length
      }
    }
  }
`;
```

Then to employ in React:

```jsx
const trackId = "xyz";

const { loading, error, data } = useQuery(GET_TRACK, {
  variables: {
    id: trackId,
  },
});
```

Note that in contrast to the
[simple example](Apollo_Client.md#query-constants)
because we are using variables, we have to pass-in an additional options object
with the query constant that specifies our variables.


title: Memory_addresses
last_modified: 2024-10-18 18:55:56
body:
# Memory addresses

> Computers assign numeric addresses to bytes of memory and the CPU can read or
> write to those addresses

We can think of the internals of RAM as grids of memory cells.

Each single-bit cell in the grid can be identified using two dimensional
coordinates, like in a graph. The coordinates are the location of that cell in
the grid.

Handling one bit at a time isn't very efficient so RAM accesses **multiple
grids** of 1-bit memory cells in parallel. This allows for reads or writes of
multiple bits at once, such as a whole byte.

The location of a set of bits in memory is known as a **memory address**.

### Demonstration

Let's imagine we have a computer system that can address up to 64KB of memory
and our system is byte addressable. This means there are
$64 \cdot 1024 = 65,536$ bytes of memory because 1KB = 1024 bytes.

We therefore have 65,536 addresses and each address can store one byte. So our
addresses go from 0 to 65, 535.

We now need to consider how many bits we need to uniquely represent an address
on this system.

What does this mean? Although there are approximately 64 thousand bytes of
memory, to refer to each byte we can't just use 1, 2, 3... because computers use
binary numbers. We need a binary number to refer to a given byte in the the 64KB
of memory. The question we are asking is: how long does this binary number need
to be to be able to represent each of the 64 thousand bytes?

1 bit can represent two addresses: 0 and 1. 2 bits can represent four addresses:
00, 01, 10, 11. The formula is as follows: number of addresses = $2^n$ where $n$
is the number of bits.

We need to reverse this formula to find out how many bits we need to represent a
given number of addresses. We can do this with a [logarithm](Logarithms.md).

We can reverse the formula as follows: number of bits = $\log_2$ (number of
addresses).

In our case we have 65,536 addresses so we need $\log_2(65,536)$ bits to
represent each address. This is approximately 16 bits. Thus a 16 bit memory
iaddress is needed to address 65, 546 bytes.

Using memory addresses we end up with tables like the following:

| Memory address   | Data             |
| ---------------- | ---------------- |
| 0000000000000000 | 1010101010101010 |
| 0000000000000001 | 0010001001001011 |
| 0000000000000010 | 0010001001001010 |

This is hard to parse so we can instead use
[hexadecimal numbers](Hexadecimal_number_system.md) to represent the addresses:

| Memory address (as hex) | Data (as binary) |
| ----------------------- | ---------------- |
| 0x0000                  | 1010101010101010 |
| 0x0001                  | 0010001001001011 |
| 0x0002                  | 0010001001001010 |

By itself, the the data is meaningless but we know from
[binary encoding](Binary_encoding.md) that the binary data will correspond to
some meaningful data, such as a character or a colour, depending on the encoding
scheme used. The above table could correspond to the characters for 'A', 'B' and
'C' in the ASCII encoding scheme:

| Memory address (as hex) | Data (as binary) | Data (as ASCII) |
| ----------------------- | ---------------- | --------------- |
| 0x0000                  | 1010101010101010 | A               |
| 0x0001                  | 0010001001001011 | B               |
| 0x0002                  | 0010001001001010 | C               |


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: Create_Mongo_database
last_modified: 2024-10-18 18:55:56
body:
# Creating a MongoDB database

First create a directory for the database and set permissions:

```bash
$ mkdir /data/db
$ sudo chown -R `id -un` /data/db
```

Then start the Mongo daemon

```bash
mongod
```

This will run continuously in the terminal and should say somewhere that it is
waiting for connections on port `27017`. This command must be executed before
you run any backend that interacts with the Mongo database.

## MongoDB Compass

_Compass_ is a graphical interface for viewing and interacting with the data in
your Mongo database. It will automatically load to the default Mongo port:
`27017`.

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


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: The_kernel
last_modified: 2024-10-18 18:55:56
body:
# The kernel

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

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

## Process management

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

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

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

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

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

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

## Memory management

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

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

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

## Device drivers

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

## System calls

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

In Linux there are two particularly important system calls:

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

Example with a terminal program like `ls`:

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

## Controlling processes

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


title: Analogue_and_digital
last_modified: 2024-10-18 18:55:56
body:
# Analogue and digital

Analogue and digital are paradigms for recording information, specifically
information about an object or state that obtains in the world.

![](/img/analog-digital.svg)

## Analogue

Here are two examples of analogue measurement/ capture:

- Measuring the temperature of a room with a mercury barometer
  - The mercury in the barometer expands or contracts in response to the heat of
    the ambient environment.
  - We say that the position of the mercury relative to the container represents
    the current temperature
- Measuring the weight of an object by placing it on a weighing scale
  - We say the position of the needle on the scale represents the weight of the
    object

> In each case, the position of the needle or the mercury isn't actually the
> temperature or the weight, it is a representation of this property. Thus in
> both cases, the analogue device works by analogy hence why we call it
> analogue.

This isn't the only sense in which "analogue" is used however. It doesn't always
mean an analagous representation of a physical property. We talk about
**analogue signals** as signals that vary continuously and which do not align to
digital values.

## Digital

### Limitations of analogue for computation

From a computing point of view, the data that can be captured and recorded by
analogue devices is simply too diverse and non-uniform. Computers are universal
machines whereas analogue devices typically only measure one property well.
Whereas a computer can record and playback music and take the temperature, to do
this in an analogue way would require a complex machine that can represent two
unrelated data sources. Imagine if a computer was able to achieve universality
through analogue means, it would most likely be very large physically and very
complex computationally since the computer would need to be able process data
from, e.g. for grooves on vinyl, needles on weighing sensors and mercury in
vacuum tubes.

> The types of analogue systems used are so different and variable that creating
> a common computing device that can understand all of them is nearly
> impossible.

Furthermore:

> Computers require highly reliable and accurate representations of certain
> types of data. Analogue representations of data can be difficult to measure
> precisely (and are potentially infinite), tend to decay over time, and lose
> fidelity when copied.

**Computers need a way to represent all types of data in a format that can be
accurately processed, stored and copied.**

### Why digital is better suited to computational requirements

A digital system represents data as a sequence of symbols where each symbol is
one of a limited set of values.

Computers only use two symbols for each value: 0 and 1.

Although a digital system could use more than two symbols, adding more would
[increase the complexity](Why_computers_use_binary.md#from-circuits-to-programs)
and cost of the system. A set of only two symbols allows for simplified hardware
and improved reliability.

All data within a computer from images to sounds are sequences of 0s and 1s. In
practice, the sequences are stored in various ways. On a CD, the binary digits
are stored as bumps (0) or flat spaces (1). On a flash drive, the 1s and 0s are
electrical charges etc.


title: Creating_a_RESTful_API_POST
last_modified: 2024-10-18 18:55:56
body:
# Creating a RESTful API: `POST` requests

To demonstrate the handling of POST requests, we will create a handler that add
a new element to the array of courses.

```js
router.post("/", (req, res) => {
  const course = {
    id: courses.length + 1,
    name: req.body.name,
  };
  courses.push(course);
  res.send(course);
});
```

Here we use the body that is sent from the client and isolate the field `name`.
This presupposes that the client is sending us data with the following shape as
the body:

```json
{
  "name": "Biology and Life Sciences"
}
```

To execute the PUT request from the frontend:

```js
const addCourse = async (newCourse) => {
  try {
    const resp = await axios.post("http://localhost:3000/api/courses", {
      name: newCourse,
    });
    console.log(resp.data);
  } catch (err) {
    console.error(err);
  }
};
addCourse("Biology and Life Sciences");
```

Which returns:

```js
{ id: 4, name: 'Biology and Life Sciences' }
```

The `id` is added by the server, not the client. Having created the new value we
add it to our `courses` array. (In reality we would be creating a new entry in a
database.) Then we follow the convention of returning the new value back to the
client.

## Validation

We should accept alterations to the database that are not first validated. We
can use the
[Joi validator](Validation_in_NodeJS.md) to vet
the request:

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

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

We can then add the validation as part of our general error handling:

```js
router.post("/", (req, res) => {
  const course = {
    id: courses.length + 1,
    name: req.body.name,
  };
  const { error } = schema.validate(req.body);
  if (error)
    return error.details.map((joiErr) => res.status(400).send(joiErr.message));

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


title: Ohms_Law
last_modified: 2024-10-18 18:55:56
body:
# Ohm's Law

The relationship between
[current](Current.md),
[voltage](Voltage.md), and
[resistance](Resistance.md) is
defined by Ohm's Law:

> The current flowing from one point to another is equal to the voltage accross
> these points divided by the resistance between them.

$$
I = \frac{V}{R}
$$

As an application, consider a circuit with a 9V battery with a 10,000 $\Omega$
resistor attached across its terminals.

The current flowing through the resistor can be calculated as follows:

$$
\frac{9V} {10,000\Omega} = 0.0009 \textsf{A} (0.9 \textsf{mA})
$$


title: With_open_in_Python
last_modified: 2024-10-25 09:47:03
body:
# With open in Python

We use the `open()` method to create a **file object** that allows us to read,
write and append to files.

The general syntax is as follows:

```py
file_object = open(<file_name>, <access_mode>)
```

`<file_name>` is a path to the file you want to read, create or modify. The
`<access_mode>` denotes the mode in which to open the file. The most frequently
used are:

- `r`
  - [read](Reading_files_in_Python.md)
- `w`
  - [write](Writing_to_files_in_Python.md)
- `a`
  - [append](Appending_to_files_in_Python.md)

When we have finished with an I/O operation, such as reading from or writing to
a file, we must call `file.close()` to terminate the process. This removes the
reference to the file from memory.

A more pythonic and concise way of reading files and closing them is to use
`with...as` syntax. When this phrasing is used, a self-contained context is
created for the I/O operation that closes the file automatically.

```py
with open('filename.txt', 'r') as file:
    contents = file.read()
    print(contents)
```


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

![](../img/breadboard-diagram.jpg)

## Contact holes

Beneath the plastic casing, the contact holes are connected to one another
making a continuous circuit connection

## Terminal strips

The contact holes in the middle section, arranged in groups of five and
designated by letters

The terminal strips are not connected accross the ditch thus each of the two
strips are electrically separate regions (A-E and F-J).

## Bus strips

The contact holes on the outer edges of the breadboard, designated by numbers
(1-30). The chief role of the bus strips is to manage the power to the circuit.
You will typically use the positive bus strip on one side for the voltage source
and the negative bus strip on the other side for the ground outlet.


title: Zip_function_in_Python
last_modified: 2024-10-18 18:55:56
body:
# `zip`

The `zip` function returns a zip object when you pass in two iterator data types
(typically two lists). This is an iterator of tuples where the first item in
each passed iterator is paired together, and then the second item in each passed
iterator are paired together, and so on.

## Example

```py
a = ("John", "Charles", "Mike")
b = ("Jenny", "Christy", "Monica", "Vicky")

x = zip(a,b)

print(x)
# <zip object at 0x1514af34c340>

# To display the resulting tuple in a readable format:

print(tuple(x))

(('John', 'Jenny'), ('Charles', 'Christy'), ('Mike', 'Monica'))
```

As indicated above, if the input iterators are of equal lengths, Python will
ignore the value in iterator B that doesn't have an equivalent index in iterator
A.

## Real-life example

```py
prompts = [
    "Enter title",
    "Enter author",
    "Enter publication date",
    "Enter year you read the book"
]

keys = ["Title", "Author", "Publication_date", "Date_read"]

book = {}

for key, prompt in zip(keys,prompts):
    book[key] = input(prompt + ": ")

```

Here I take the `prompts` and `keys` lists, collate them via `zip` and then loop
through them to populate the `book` dictionary, which contains properties
sourced from the `keys` list and values from the user's answers to the
`prompts`.


title: Frequency_counters
last_modified: 2024-10-18 18:55:56
body:
# Frequency counters

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

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

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

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

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


title: Docker_storage
last_modified: 2024-10-18 18:55:56
body:
# Docker storage

The following problems related to storage face containerized environments:

- Container storage is ephemeral. When the container is removed the read-write
  layer is removed. How can we persist data?

- Container storage is isolated: containers cannot access each other's file
  systems. If they need to share data, this could be a problem

### Docker volumes

Docker volumes are the solution to this. Volumes:

- store data on the host file system (i.e. outside the container)
- persist data beyond the lifecycle of the container

> A Docker volume is a logical volume that exists outside the container file
> system. It maps to a directory on the host and this location is managed by
> Docker - not much control over this.

Can be named or anonymous. Anonymous vols are created by Docker as needed. Named
come from the user.

Volumes can be **named** or **anonymous**. Anonymous volumes are created by
Docker as needed. Named volumes are named by the user. In both cases Docker
decides where to create the volume.

### Creating a volume

```sh
docker run -d -rm -v /usr/share/nginx/html nginx:latest
```

The `-v` flag indicates volume. The file reference is the mount point _within_
the container. This is how the persistent storage it will appear. The _actual_
volume exists outside of the container anonymously in a place decided by Docker.

To create a named volume you first have to create:

```
docker volume create volume_name
```

Then you can designate it:

```
docker run -d -rm -v volume_name:/usr/share/nginx/html nginx:latest
```

The actual location is still hidden and decided by Docker.

As these are anonymous they will show ups as GUIDs in the volume viewer in
Docker Desktop:

// Add: screenshot

### Commands

```
docker volume ls
docker volume rm
docker volume inspect
docker volume prune // remove all unused volumes
```

Inspect is only really informative if you are on Linux because it will show you
the actual physical path. With Windows and Mac the referernce is to a virtual
machine.


title: Fetch_from_Secrets_Manager
last_modified: 2024-10-18 18:55:56
body:
# Fetch from Secrets Manager

## TypeScript

```ts
const getSecret = async (): Promise<SomeCredentials> => {
  const secretsManager = new AWS.SecretsManager();
  const response = await secretsManager
    .getSecretValue({ SecretId: process.env.SECRET_ARN as string })
    .promise();
  const secretValues = JSON.parse(response.SecretString as string);

  if (secretValues) {
    return {
      accessToken: secretValues.POCKET_ACCESS_TOKEN,
      consumerKey: secretValues.POCKET_CONSUMER_KEY,
    };
  } else {
    throw new Error("Failed to return Pocket credentials");
  }
};

type SomeCredentials = {
  accessToken: string;
  consumerKey: string;
};
```

## Python

```py
import json
import boto3

def get_secret():
    python_env = os.environ.get("PYTHON_ENV", "local")
    """In production, source creds from SecretsManager"""
    if python_env == "production":
        secrets_manager = boto3.client("secretsmanager")
        response = secrets_manager.get_secret_value(SecretId=os.environ["SECRET_ARN"])
        secret_values = json.loads(response["SecretString"])
        return secret_values["GOOGLE_CREDS"]
```


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: 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: Map_and_filter_to_reduce_in_JS
last_modified: 2024-10-18 18:55:56
body:
# Expressing combined map and filter operations to a single reduce

## `map` + `filter`

The following combines a `map` and a `filter` to square a list of numbers and
then filter the ones that are greater than or equal to 10:

```js
const arr = [1, 2, 3, 4];

const filteredSquaredNumbers = arr.map((x) => x * x).filter((y) => y >= 10);
```

Using `reduce` we can combine these operations more concisely:

```js
const filteredSquaredNumbers = arr.reduce((acc, x) => {
  const squared = x * x;
  if (squared >= 10) {
    acc.push(squared);
  }
  return acc;
}, []);
```

## `filter` + `map`

Here we use a filter and then a map to filter the even numbers and then double
them:

```js
const arr = [1, 2, 3, 4];

const evenDoubled = arr.filter((x) => x % 2 === 0).map((y) => y * 2);
```

Using `reduce` :

```js
const evenDoubled = arr.reduce((acc, x) => {
  if (x % 2 === 0) {
    acc.push(x * 2);
  }
  return acc;
}, []);
```

> The empty array [] is the initial value of the accumulator. As the callback
> function processes each element of the array, it can add or modify the
> accumulator based on the desired logic. By providing an empty array as the
> initial value for the accumulator, we ensure that the results will be
> collected in a new array without modifying the original array.


title: Push_buttons
last_modified: 2024-10-18 18:55:56
body:
# Push buttons

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

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

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

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

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

![](../img/correct_push_button.jpg)

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

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

![](../img/incorrect_push_button.jpg)

## Related notes

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


title: Basic_properties_of_sets
last_modified: 2024-10-18 19:11:22
body:
# Basic properties of sets

## Set theory

Set theory is a sub-discipline of both mathematics and formal logic. In
mathematics it is used as a universal framework for describing other
mathematical theories. It is also utilised in computer science and linguistics.

It is useful because it provides tools for modelling an extraordinary variety of
structures.

> Set theory and the theory of infinite sets was created by Georg Cantor
> (1845-1918), a German mathematician.

## Method of formalisation

We can use the symbols of predicate logic to simplify and clarify natural
language expression of set-theoretic principles. There are different ways to do
this but we will use the standard quantifiers and:

- variables $a,b,c,...$ to range over sets
- variables $x,y,z$ to range over ordinary objects as well as sets.

More generally we will use capital Latin letters ($A, B, ...$) to denote some
specific set, i.e not a generalised/quantified notion of a set.

### Example

'Everything is a member of some set or another:

$$ \forall x \exists a (x\in a) $$

## What are sets?

A set is a collection of objects. In mathematics the objects are mathematical
objects.

A **finite set:**

$$ BG = { \textsf{Barry, Maurice, Robin}} $$

An **infinite set:**

$$ I = {1, 2, 3, 4, ...} $$

> When we use braces to indicate the members of a set we are providing a **list
> description** of the set.

## Set membership

If a set S is a collection of objects, to say that object x is a member of S is
just to say that x is one of those objects.

We might also express this in natural language as:

- the object x is an element of the set S
- the object x belongs to S
- the set S contains the object x

Formally, we use epsilon to express set membership:

$$ x \in A $$

This asserts that x is a member of the set A.

The negation of this proposition is expressed:

$$ x \notin A $$

This asserts that x is not a member of the set A.

### Subsets

> Set A is a subset of set B if every member of A is also a member of B.

For example the set of women is a subset of the set of humans because every
woman is a human. We express subset relations like so:

$$ A \subseteq B $$

This asserts that set A is a subset of set B.

The negation of this proposition is expressed:

$$ A \not\subset B $$

We must not confuse the relation of being a subset with being a member. Jane is
a member of the set of women but Jane is not a subset of the set of women since
Jane is not herself a set, she is an object/individual member.

There is also the notion of a **proper subset.**

> If subset _A_ of _B_ is a proper subset of _B_ then _B_ contains some elements
> that are not in _A_.

In other words, if B contains objects other than/ in addition to A.

$$ A \subset B $$

This asserts that set A is a proper subset of set B.

For example, the set of women is a proper subset of the set of humans because
the set of humans also includes the set of men. If there were only women and no
men, then the set of women would be a subset of the set of humans.

### Supersets

If A is a subset of B then we say that B is a **superset** of A. Being a
superset, B contains every object of A and may also contain other objects in
addition to A. This is just a different way of asserting that A is a proper
subset of B.

$$ B \supseteq A $$

This asserts B is a superset of A. The negation:

$$ B \not\supset A $$

This asserts that B is not a superset of A.

## Resources

[Set symbols](symbols.html)


title: Turing_completeness
last_modified: 2024-10-18 18:55:56
body:
# Turing Completeness

We know that a [Turing machine](Turing_machines.md) is a theoretical construct
of a computer that:

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

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

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

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

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

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

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

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


title: Atomic_and_molecular_propositions
last_modified: 2024-10-18 18:55:56
body:
# Atomic and molecular propositions

Propositions are expressions **that have truth values**, either true or false.

We call a proposition which does not contain a logical connective (or
'sentential connective') a **simple proposition**.

We call a proposition that does contain a logical connective, a **compound
proposition**.

Simple propositions are represented within a formal language of sentential logic
with a single character, customarily _P_ or _Q_. When we refer to the formal
representation of such propositions in our system of sentential logic (SL) we
call them **atomic propositions**.

Compound propositions consist in single characters for each atomic proposition
that they comprise, combined with a symbol for the logical connective. When we
refer to the formal representation of such propositions in SL we call them
**molecular propositions**.

### Demonstration

Atomic proposition:

```
Socrates was a philosopher.
(P)
```

Molecular proposition:

```
Socrates was a philosopher and a drinker.
(P & Q)
```

Connectives in natural language often obscure the logical basis of the
proposition being expressed (where such a proposition contains a proposition,
i.e. excluding propositions that are _logically indeterminate_. The molecular
proposition is above is such an example. In this instance the proposition can be
expressed more precisely as:

```
Socrates was a philosopher and Socrates was a drinker.
```

Where propositions in natural language cannot be elucidated by the addition of
implied logical connectives in the manner above, they must be treated not as
molecular propositions but as atomic proposition. Example:

```
Two splashes of gin and a few drops of vermouth make a great martini.
```

If we were to formalise this as:

```
Two splashes of gin make a great martini and a few drops of vermouth make a great martini.
```

We would lose the sense of the original and we would not be uncovering any logic
that is in the original.


title: Add_Subtract_Fractions
last_modified: 2024-10-18 18:55:56
body:
# Adding and subtracting fractions

## Adding/ subracting fractions with common denominators

For two fractions $\frac{a}{c}$ and $\frac{b}{c}$ with a common denominator,
their sum is defined as:

$$
    \frac{a}{c} + \frac{b}{c} = \frac{a + b}{c}
$$

For example:

$$
    \frac{2}{8} + \frac{3}{8} = \frac{5}{8}
$$

The same applies to subtraction:

$$
    \frac{a}{c} - \frac{b}{c} = \frac{a - b}{c}
$$

## Adding/ subracting fractions without common denominators

- Find the lowest common denominator for the two fractions
- Use this to create two equivalent fractions
- Add/subtract
- Reduce

### Lowest common denominator and lowest common multiple

Given the symmetry between
[factors and divisors](Factors_and_divisors.md) these
properties are related. Note however that the LCM is more generic: it applies to
any set of numbers not just fractions. Whereas the LCD is explicitly to do with
fractions (hence 'denominator').

- For two fractions $a, b$ (or a set), the LCD is the smallest number divisble
  by both the denominator of $a$ and the denominator of $b$ (or each member of
  the set).

- For two fractions $a, b$ (or a set), the LCM is the smallest number that is a
  multiple of the denominator of $a$ and the denominator of $b$ (or each member
  of the set).

In order to find the LCM of the set $\{12, 16\}$ we list the multiples of both:

$$
12, 24, 36, 48 \\
16, 32, 48
$$

Until we identify the smallest number common to both lists. In this case it
is 48. Thus the LCM of 12 and 16 is 48.

The relationship between LCM and LCD is that _the least common denominator is
the least common multiple of the fractions' denomintors_.

### Demonstration: addition

We can now use this to calculate the addition of two fractions without common
denominators: $\frac{4}{9} + \frac{1}{6}$.

First identify the common multiples of 9 and 6:

$$
9, 18, ... \\
6, 12, 18, ...
$$

The least common multiple is 18. We then think: what do we need to multiply each
denominator by to get 18?

In the case of the first fraction ($\frac{4}{9}$) it is 2:

$$
    \frac{4}{9 \cdot 2}  = \frac{4}{18}
$$

But what we do to the denominator, we must also do to the numerator, hence:

$$
    \frac{4 \cdot 2}{9 \cdot 2}  = \frac{8}{18}
$$

We then do the same to the second fraction ($\frac{1}{6}$). We need to multiply
its denominator by 3 to get 18 and we apply this also to the numerator.

$$
    \frac{1 \cdot 3}{6 \cdot 3}  = \frac{3}{18}
$$

We now have two fractions that share a common denominator so we can sum:

$$
    \frac{8}{18} + \frac{3}{18} = \frac{11}{18}
$$

### Demonstration: subtraction

Calculate:

$$
    \frac{3}{5} - \frac{2}{3}
$$

Once again we need to find the least common denominator for the two fractions.
We start by listing the common multiples for the two denominators 5 and 3:

$$
5, 10, 15, ... \\
3, 6, 9, 12, 15,...
$$

The lowest common multiple is 15. From the first fraction we get 15 by
multiplying by 3. With the second fraction we get 15 by multiplying by 5. Thus:

$$
    \frac{3 \cdot 3}{5 \cdot 3}  = \frac{9}{15}
$$

$$
    \frac{2 \cdot 5}{3 \cdot 5}  = \frac{10}{15}
$$

We can now carry out the subtraction:

$$
    \frac{9}{15}  -  \frac{10}{15} = -\frac{1}{15}
$$


title: Variable_indirection_in_Bash
last_modified: 2024-10-18 18:55:56
body:
# Variable indirection

> Write proper notes on this

```sh
function array_empty() {
    declare -n arr=$1
    # Proceed if array not empty:
    if [ ${#arr[@]} -gt 0 ]; then
        return 1 # array is not empty
    else
        return 0 # array is empty
    fi
}

my_array=(1 2 3)

function push() {
    # $1 = stack name
    local stack_name="$1"
    if [ "${stack_name}" ]; then # stack exists
        if array_empty "${!stack_name}"; then
            echo "stack is empty"
        else
            echo "stack is not empty"
        fi
    else
        echo "Error: ${stack_name} does not exist."
    fi
}

push "my_array"

```

In Bash scripting, ${!stack_name} is an example of indirect variable referencing
or variable indirection. It's a way to use a variable whose name is stored in
another variable.

Let's break it down:

`${stack_name}`: This syntax is used to reference the value of a variable. So if
stack_name="my_array", then ${stack_name} would return "my_array".

`${!stack_name}`: Adding the ! introduces indirection. Now, instead of getting
the value of stack_name, you get the value of the variable whose name is stored
in stack_name. So if stack_name="my_array" and my_array=(1 2 3), then
${!stack_name} would return (1 2 3).

So, in the context of your script, `${!stack_name} allows you to pass the name
of an array (as a string) to your push function, and then use that string to
indirectly reference the actual array within the function.


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: Intersection_types_in_TypeScript
last_modified: 2024-10-18 18:55:56
body:
# Intersection types

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

### Creating an intersection

Say you have the following three types:

```ts
type IConcert = {
  date: number;
  location: string;
  band: string;
};

type IConference = {
  date: number;
  location: string;
  keynoteSpeaker: string;
};

type IMeeting = {
  date: number;
  location: string;
  agenda: string[];
};
```

Each type corresponds to a different event and has two properties in common with
the other events: `date` and `location`.

Rather than repeat these properties for each individual type, we could create a
general `IEvent` type and apply this as an intersection to each of the
individual event types:

```ts
// The common event type
type IEvent = {
  date: number;
  location: string;
};
```

```ts
// The intersection

type IConcert = IEvent & {
  band: string;
};

type IConference = IEvent & {
  keynoteSpeaker: string;
};
```

> In the case of interfaces, instead of using `&` we would write
> `interface IConcert extends IEvent`

### Alternative method

There is another equivalent way of intersecting the types. We could first define
`IConcert` and `IConference` as their own types (without `extends` or `&`) and
then intersect with, e.g., type IConcert = SelfStandingConcertType & IEvent.

### Benefits of intersections

- It is more concise and we do not repeat ourselves needlessly
- Common properties can be modelled **in one place** which makes updates and
  changes much easier: we do not have to change multiple type declarations, just
  change the 'master' type that is intersected with the variants.
- It is more readable: we can see which properties are unique to the type
  without having to parse repeated properties.

## Set theory: understanding errors

With intersections we literally use the & operator which discloses that the
logic is equivalent to logical AND: a member of the intersection must have the
properties of both types. In the case of primitive values this is impossible or,
more accurately, it results in an empty set because, for example, `string` and
`boolean` do not share any common values. This means you will get an error if
you assign an intersection type to a value that does not have all the features
of each custom type in the intersection.


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

<img src="../img/motherboard-pi.jpg" width="400px"/>

The motherboard is the foundation of a computer. It allocates power and allows
communication to and between the
[CPU](CPU_architecture.md),
[RAM](Memory.md),
[harddisk](What_are_disks.md) and all other hardware
components.

It is a printed circuit board and is always the largest board within the
computer chassis.


title: Dictionaries_in_Python
last_modified: 2024-10-31 14:57:09
body:
# Dictionaries in Python

Dictionaries are basically the Python equivalent of objects in JS.

Dictionaries:

- Are ordered (in contrast to JS)
- Are mutable
- Are indexed by a key which references a value
- Can be increased/decreased in length by adding/removing new members.

## Basic usage

Dictionaries are declared with `{...}`:

```python
cities = {
  'Wales': 'Cardiff',
  'England': 'London',
  'Scotland': 'Edinburgh',
  'Northern Ireland': 'Belfast',
  'Ireland': 'Dublin'
}

print(type(citites))
# <class 'dict'>
```

## Accessing entries

```python
print(cities['Wales'])
# Cardiff

print(cities.get('Ireland'))
# Dublin

print(cities.values())
# ['Cardiff', 'London', 'Edinburgh', 'Belfast', 'Dublin']

print(cities.keys())
# ['Wales', 'England', 'Scotland', 'Northern Ireland', 'Ireland']

print(cities.items())
# [('Wales', 'Cardiff'), ('England', 'London'), ('Scotland', 'Edinburgh'), ('Northern Ireland', 'Belfast'), ('Ireland', 'Dublin')]
```

## Predicates

```py
print('Wales' in cities)
# True

print('France' not in cities)
# True
```

## Looping

```py
for country in cities:
    print(country, end=', ')
    print(cities[country])

"""
Wales, Cardiff
England, London
Scotland, Edinburgh
Northern Ireland, Belfast
Ireland, Dublin
"""

for e in country.values():
    print(e)
```

## Updating values

```py
cities['Wales'] = 'Swansea'
print(cities)
```

## Removing values

```py
# Remove last item
cities.popitem()
print(cities)
# {'Wales': 'Cardiff', 'England': 'London', 'Scotland': 'Edinburgh', 'Northern Ireland': 'Belfast'}

# Remove specific entry by key

cities.pop('Northern Ireland')
print(cities)
# {'Wales': 'Cardiff', 'England': 'London', 'Scotland': 'Edinburgh'}

del cities['Scotland']
print(cities)
{'Wales': 'Cardiff', 'England': 'London'}
```

## Containers as values

```py
seasons = {
'Spring': ('Mar', 'Apr', 'May'),
'Summer': ('June', 'July', 'August'),
'Autumn': ('September', 'October', 'November'),
'Winter': ('December', 'January', 'February')}

print(seasons['Spring'])
print(seasons['Spring'][1])

"""
('Mar', 'Apr', 'May')
Apr
"""
```

## Merging two Dictionaries

Two separate dictionaries can be merged into a single dictionary with shared
properties with this shorthand:

```python
merged_dictionary = dictionary_one | dictionary_two

```


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: Mixed_and_improper_fractions
last_modified: 2024-10-18 18:55:56
body:
# Mixed and improper fractions

## Improper fractions

- Proper fraction:
  - The numerator is smaller than the denominator
  - E.g. $\frac{2}{3}$, $-\frac{5}{10}$
- Improper fraction
  - The numerator is greater than the denominator
  - E.g. $\frac{3}{2}$, $-\frac{5}{4}$

## Mixed fractions

A mixed fraction is part whole number, part fraction. For example:
$5 \frac{3}{4}$.

It means, in effect: $5 + \frac{3}{4}$

## Converting mixed fractions into improper fractions

Mixed fractions are hard to calculate with. We need some way to convert them to
fractions. We can do this by converting them to improper fractions.

With the example $4 \frac{7}{8}$, we know this means $4 + \frac{7}{8}$. We need
to express the amount 4 in terms of eighths. It would be 4 lots of $\frac{8}{8}$
given that 4 is a whole number not a fractional amount. Thus the process would
be:

$$
    \frac{8}{8} + \frac{8}{8} + \frac{8}{8} + \frac{8}{8} + \frac{7}{8}
$$

But we know that when we
[add fractions with a common denominator](Add_Subtract_Fractions.md#adding-subracting-fractions-with-common-denominators),
we only add the numerators, not the denominators. Thus the calculation would
actually be:

$$
\frac{8 + 8 + 8 + 8 + 7}{8} = \frac{39}{8}
$$

Addition helps to explain the concepts underlying the procedure but it is more
efficient to use multiplication.

The procedure is as follows:

1. Multiply the whole number part by the denominator
2. Add the numerator
3. Place the result over the denominator

Thus:

$$
    4 \frac{7}{8} = \frac{(4 \cdot 8) + 7 }{8}
$$

## Converting improper fractions into mixed fractions

It is quite obvious how to reverse the process and turn an improper fraction
into a mixed fraction.

Take $\frac{27}{5}$. We work out how many times the numerator is divisible by
the denominator and make that the whole number. The remainder is then left as
the fractional part.

$$
\begin{split}
\frac{27}{5} = 27 \div 5  \\
= 5 r 2 \\
= 5 \frac{2}{5}
\end{split}
$$

## Multiplying and dividing by mixed fractions

Now that we know how to convert mixed fractions into improper fractions, it is
straight forward to multiply and divide with them. We convert the mixed fraction
into an improper fraction and then divide and multiply as we would with a proper
fraction.

### Demonstration of multiplication

Calculate $-2\frac{1}{12} \cdot 2 \frac{4}{5}$:

1. First convert each mixed fraction into an improper fraction:
   $$
     \begin{split}
     -2\frac{1}{12} = -2 \cdot -12  \\
     = 24 + 1 \\
     = - \frac{25}{12}
     \end{split}
   $$

$$
\begin{split}
  2 \frac{4}{5} =2 \cdot 5 \\
  = 10 + 4 \\
  = \frac{14}{5}
\end{split}
$$

2. Then carry out the multiplication
   [using factorization](Multiplying_fractions.md#prime-factorisation-in-place):

   $$
     \begin{split}
     - \frac{25}{12} \cdot \frac{14}{5} = \\
    - \frac{(5 \cdot 5) \cdot (7 \cdot 2)}{(3 \cdot 2 \cdot 2) \cdot (5)} = - \frac{5 \cdot 7 }{2 \cdot 3} \\
     \end{split}
   $$

3. Then simplify:

   $$
     - \frac{35}{6}
   $$

4. Finally, convert back into a mixed fraction:

$$
  \begin{split}
  - \frac{35}{6} = -35 \div 6 \\
  -  5 r 5 = \\
  - 5 \frac{5}{6}
  \end{split}
$$

## Demonstration of division

Again we convert the mixed fraction into an improper fraction and then follow
the requisite rule, in the case of division this is to
[invert and multiply](Dividing_fractions.md#formal-specification-of-how-to-divide-fractions').

Calculate $-4 \frac{4}{5} \div 5 \frac{3}{5}$.

1. Convert to improper fraction:
   $$
      \begin{split}
        -4 \cdot 5 = \\
    -20
   \end{split}
   $$

## Adding and subtracting mixed fractions


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

## List all installed packages

```
pacman -Qe
```

## List all packages installed by current user

```
pacman -Qqe
```

## Uninstall, and list dependencies for package to be uninstalled

```
pacman -Rcns
```


title: Magnetic_tape
last_modified: 2024-10-18 18:55:56
body:
# Magnetic tape

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

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

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

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

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

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

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

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

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


title: Step_functions_and_state_machines
last_modified: 2024-10-18 18:55:56
body:
# AWS Step functions and state machines

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

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

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

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

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

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

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

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

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

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

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


title: Aggregate_functions_in_SQL
last_modified: 2024-10-18 18:55:56
body:
# Aggregate functions in SQL

Aggregate functions enable us to return data about the data that a table holds.
For example the sum of a given column or its average.

## Count return with custom variable

```sql
SELECT COUNT(*) AS total_sales
FROM SALES
```

## Sum

```sql
SELECT SUM(price) as total_value
FROM sales
```

## Average

```sql
SELECT AVG(price) as average_income
FROM sales
```

## Applying aggregate function with sorting applied

```sql
SELECT COUNT(*) AS total_sales
FROM sales
GROUP BY employee_id
```


title: Union_types_in_TS
last_modified: 2024-10-18 18:55:56
body:
# Union types

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

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

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

## Demonstration

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

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

type Car = Petrol | Electric;

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

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

## Usage with functions

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

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

## Unifying primitive and custom types

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

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

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

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

## Set theory: understanding errors

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

This is not true in the case of unions.

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

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

Here is an example of this:

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


title: Containerization
last_modified: 2024-10-18 18:55:56
body:
# Containerizaton

> In this entry we look at containerization as a general technology that is
> distinct from its particular implementation by Docker.

We can understand the role and significance of containers and containerization
by analogy with shipping containers. Before the invention of shipping containers
as a **standardised modular unit** for transporting goods, shipping and supply
chains were chaotic: inefficient, complex, frictional. With the advent of the
shipping container, the same artefact could be used for transportation via
ships, trains, lorries etc.

With containers we can share and transport software via a single standardised
unit that works regardless of the host operating system or server. We can also
iterate different software environments easily: local, production, test etc. As
with the shipping of goods, standardisation has driven down running costs and
increased the speed of transport.

## Containers and software

- Containers are a technology that allow you to package and isolate applications
  with their entire runtime environment. This makes it easy to move the
  contained application between various environments whilst retaining full
  functionality.

- The technology of containerization is distinct from _Docker_. Docker is a
  particular implementation of containerization that simplifies the process and
  bases it on a standardised specification.

- Containers are native to the Linux [kernal](The_kernel.md)
  and are key part of how it works. Thus when you run containers on Linux, you
  are using native capability. When you use containers on Windows or Mac you
  have to run a virtual version of Linux in order to exploit the capabilities of
  that kernel. (Docker provides this)

## How containers work

A key feature of the Linux kernal is the existence of **cgroups** (control
groups).

> Containerization = resource control + process isolation

- A cgroup **groups a series of processes together and contrains their access to
  system resources (CPU, memory, disk)**
- Cgroups use namespaces to provide **process isolation**: groups of processes
  are isolated so that they cannot access other processes on the system. For
  example a container is ignorant of the underlying operating system and
  network, by default.

In ordinary [user space](User_Space.md) applications share
the _same_ processor, memory and file system resources. This increases the
likelihood of resourcing challenges, dependency conflicts and security threats.
Without modularisation and the titration of resources, you are opened up to much
greater possibility of failure.

For example one application could fill up the harddrive preventing other
applications from writing to it. One application can "bring down" another
applicaton.

Prior to containerization, in an enterprise environment most of the system
administration consisted in managing resouces to avoid excessive resource
expenditure and security challenges.

_Standard userspace_

![](/img/standard-userspace.png)

_Userspace with containerization_

![](/img/containers-in-userspace.png)

## Differences with virtual machines

Containers are similar in many regards to VMs. They both offer a virtualized
environment in which to run software and they both provide isolation of
resources. (When you start up a VM you specify the available RAM and disk
space.)

The central difference is that VMs require the existence of a full operating
system. Containers can run without an OS although it is often convenient to run
them with stripped-down Linux distributions.

A VM contains a complete installation of a guest OS and requires a hypervisor on
the host OS to manage the different operating environments. This makes them
slower and consume more resources.

A containerized system radically reduces this overhead. It just starts up the
container and after that it's job is done since the Linux kernel handles the
rest as it would any other process on the OS.

| Virtual Machine                             | Container                               |
| ------------------------------------------- | --------------------------------------- |
| Isolation of resources and operating system | Isolation of resources only             |
| Require a hypervisor to run                 | Require a container runtime to run      |
| Slower due to additional abstraction        | Faster due to less overhead             |
| Less portable                               | More portable                           |
| Slower and more difficult to run            | Scale rapidly due to lightweight nature |

![](/img/container-versus-vm.png)

## Why use containers?

- portability
- scalability
- efficiency
- isolation
- DevOps agility


title: Effective_logging_in_Git
last_modified: 2024-10-18 18:55:56
body:
# Effective logging in Git

| Root command      | Variant      | Output                                                   |
| ----------------- | ------------ | -------------------------------------------------------- |
| `git log`         | filename.txt | List the commits that changed the given file             |
| `git log --patch` | filename.txt | Show the diff changes for each commit for the given file |
| `git log --patch` | null         | Show the diff changes for each commit for all files      |


title: Bolt_Beranek_and_Newman
last_modified: 2024-10-30 13:24:38
body:
# Bolt, Beranek and Newman

![BBN logo](../img/bbn-logo.png)

Founded by Bolt and Beranek, two MIT students and hired some of the leading
scientists of the day to lead departments. E.g. J.C.R Licklider, John McCarthy
and Marvin Minsky.

- Tested the original prototype of the PDP-1 on behalf of DEC.
- Working for BBN, McCarthy developed one of the first time-sharing systems (the
  "BBN Time-Sharing System") for the PDP-1 provided by DEC.
- Sponsored by DARPA to create the IMP computers which served as the main
  routers of the ARPANET.

Eventually subsumed into Raytheon and now known as "Raytheon BBN".


title: DeMorgan's_Laws
last_modified: 2024-10-18 18:55:56
body:
# DeMorgan's Laws

DeMorgan's laws express some fundamental equivalences that obtain between the
Boolean
[connectives](Truth-functional_connectives.md).

## First Law

> The negation of a conjunction is logically equivalent to the disjunction of
> the negations of the original conjuncts.

$$
\lnot (P \land Q) \leftrightarrow \lnot P \lor \lnot Q
$$

The equivalence is demonstrated with the following truth-table

| $P$ | $Q$ | $ \lnot (P \land Q)$ | $ \lnot P \lor \lnot Q$ |
| --- | --- | -------------------- | ----------------------- |
| T   | T   | F                    | F                       |
| T   | F   | T                    | T                       |
| F   | T   | T                    | T                       |
| F   | F   | T                    | T ### Truth conditions  |

> The negation of a disjunction is equivalent to the conjunction of the negation
> of the original disjuncts.

$$
\lnot (P \lor Q) \leftrightarrow \lnot P \land \lnot Q
$$

| $P$ | $Q$ | $ \lnot (P \lor Q)$ | $ \lnot P \land \lnot Q$ |
| --- | --- | ------------------- | ------------------------ |
| T   | T   | F                   | F                        |
| T   | F   | F                   | F                        |
| F   | T   | F                   | F                        |
| F   | F   | T                   | T                        |


title: Numbers_in_Python
last_modified: 2024-10-18 18:55:56
body:
# Numbers in Python

## Distinguishing `int` and `float`

- In Python we have floats and integers and we can coerce one into the other
- A `//` as an operator means float division. This obviously provides greater
  precision than int division `/`.
- There is no increment (`++`) or decrement (`--`) operator in Python

```python
# Integers and floats
count = 1
print(count)
# 1
print(type(count))
# <class 'int'>

exchange_rate = 1.83
print(exchange_rate)
# 1.83
print(type(exchange_rate))
# <class 'float'>

print(float(count))
# 1.0

print(int(exchange_rate))
# 1
```


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

Morgan is middleware that is used to log HTTP requests to the Express instance.

```js
app.use(morgan("dev"));
```

With Morgan in place, every time we run a request it will be logged on the
console that is running our Node application, e.g:

```plain
GET /api/courses 200 95 - 1.774 ms
```

This uses the `tiny` default which logs the bare minimum giving us: request
type; endpoint; response code; and time to execute. But there are more verbose
settings.

It defaults to logging on the console but can also be configured to write to a
log file.


title: Modelling_relationships_in_MongoDB
last_modified: 2024-10-18 18:55:56
body:
# Modelling relationships between data

So far we have taken the values of a document to be simple: the `author` value
is just a string but what if `author` was itself another collection for instance
a collection with the proprties `age` and `publications` and the author name,
say `Tosh Gnomay` was a document in this collection? This means we would have an
interaction between two collections. In this entry we will look at how to work
with interrelated collections. This is equivalent to establishing
[joins](10_Joins.md) in a relational database.

There are two main approaches to modelling relationships between data:
**normalisation** and **denormalisation**.

## Modelling relationships with normalisation: using references

In the case of normalisation we use a UUID from one document to connect it to
another document.

Let's say that we have a course document as follows which is an instance of the
`courses` collection:

```js
{
    _id: "ceds89e"
    name: "Java course",
    author: "Tosh Gnomay"
}
```

And we have a document which is an instance of another type of collection called
`authors`. One instance of it could be:

```js
{
    _id: "de89w9",
    name: "Tosh Gnomay",
    courses: ["Java course", "C# course"]
}
```

Using normalisation, we would establish the relationship by using the value of
the `_id` propety in the `courses` document as the value of the `name` property
in the `authors` document. We would rewrite the `courses` document like so:

```js
{
    _id: "ceds89e"
    name: "Java course",
    author: "de89w9"
}
```

## Modelling relationships with denormalisation: embedding

With denormalisation we achieve the same outcome but instead of using a
reference we embed one value in another:

```js
{
    _id: "ceds89e"
    name: "Java course",
    author: {
        name: "Tosh Gnomay",
        courses: ["Java course", "C# course"]
    }
}
```

## Tradeoffs: which approach to use?

There are drawbacks and benefits with each approach.

- One benefit of normalisation is consistency. Because we are using the UUID and
  referencing it in another document, if we make a change to the values in the
  source document, it will automatically apply to all documents that reference
  it. We make a change in one place and it is reflected everywhere.

- Conversely, a drawback of denormalisation is that it invites inconsistency
  between documents. Embedded values can easily go out of sync with their usage
  elsewhere as you don't have a direct reference correspondence.

- On the other hand a drawback of normalisation is performance when querying.
  Any time you have a document that references another document than this
  document also has to be retrieved. Therefore for every query you are returning
  two documents. As denormalised references embed the second document, only one
  document is being returned on each query.


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: Lists_in_Python
last_modified: 2024-11-01 16:03:11
body:
# Lists in Python

Lists are the equivalent of a simple array in JavaScript.

Lists have the following properties:

- They are **ordered**
- They are **mutable** and can be modified
- They **allow duplicate** members
- They are **indexed**
- You can increase/decrease their length by adding/removing new members

> Lists are denoted with `[...]`

## Duplicating lists

When we want to duplicate a list, we can't just reassign the list to a new
variable and expect this to be a copy.

If we edit this "copy" it will update the original list since it copies the
pointer and will therefore point to the same address in memory. Instead we have
to use the List `copy()` function which returns a new list and doesn't modify
the original list.

Relatedly, we should distinguish those List methods and functions which create a
new list (non-destructive) and those that mutate the existing list.

## Basic usage

```python
# Defining a list
list1 = ['John', 'Paul', 'George', 'Ringo']
list2 = [4]

# Empty list
list3 = []  # empty list
list3 = list()  # Also empty list

# Nested list
list5 = [[2, 3], [6, 8]]
```

## Slicing

```python
list1 = ['John', 'Paul', 'George', 'Ringo']

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

"""
'John'
'Ringo'
['Paul', 'George']
['John', 'Paul', 'George']
['Paul', 'George', 'Ringo']
"""
```

## Adding additional values to existing list

### Single value

```py
list1 = ['John', 'Paul', 'George', 'Ringo']

# Add single element to the end of a list
list1.append('Pete')
# ['John', 'Paul', 'George', 'Ringo', 'Pete']
```

### Multiple values

```py
list1.extend(['Albert', 'Bob'])
list1 += ['Ginger', 'Sporty']
# ['John', 'Paul', 'George', 'Ringo', 'Pete', 'Albert', 'Bob', 'Ginger', 'Sporty']
```

### Specific index

```python

## Insert at specific index

a_list = ['Adele', 'Madonna', 'Cher']
a_list.insert(1, 'Paloma')
print(a_list)

# ['Adele', 'Paloma', 'Madonna', 'Cher']
```

## Removing elements

We distinguish `del` from `remove` when removing elements from lists:

- `del` requires an index value
- `remove` requires a value reference (i.e. the name of the element rather than

`del` is simple deletion whereas `remove` searches the list. Therefore `del` is
more efficient.

### `del`

```py
my_list = [10, 20, 30, 40, 50]
del my_list[1]
print(my_list)  # Output will be [10, 30, 40, 50]
```

We can remove multiple items at once via a slice:

```py
my_list = [10, 20, 30, 40, 50]

# Delete the elements from index 1 to 3 (inclusive of start index and exclusive of end index)
del my_list[1:4]

# Print the updated list
print(my_list)  # Output will be [10, 50]
```

### `remove()`

```py
my_list = [10, 20, 30, 40, 50]

# Remove the element with value 30
my_list.remove(30)

# Print the updated list
print(my_list)  # Output will be [10, 20, 40, 50]
```

> If the value is not found in the list,
> `remove()`` will raise a ValueError. To avoid this, you can check whether the value exists in the list before calling `remove()`

### pop()

Remove and return the element removed

```python
list6 = ['Once', 'Upon', 'a', 'Time']
print(list6.pop(2))
# a

```

## Return index of a list element

```python
list7 = [2, 3, 6, 8]
print(list7.index(8))
# 3

list6 = ['Once', 'Upon', 'a', 'Time']
print(list6.index('a'))
# 2
```

## Nesting lists

```python
l1 = [1, 43.5, 'Phoebe', True]
l2 = ['apple', 'orange', 31]
root_list = ['John', l1, l2, 'Denise']
print(root_list)
# ['John', [1, 43.5, 'Phoebe', True], ['apple', 'orange', 31], 'Denise']
```

## Merging lists

```py
list1 = [1, 2, 3]
list2 = [4, 5, 6]

merged_list = list1 + list2
print(merged_list)  # Output: [1, 2, 3, 4, 5, 6]
```

## Flattening a two-dimensional list (matrix)

Use [list comprehension](List_comprehension_in_Python.md) for each row in the
matrix:

```python
 matrix = [
    [9, 3, 8, 3],
    [4, 5, 2, 8],
    [6, 4, 3, 1],
    [1, 0, 4, 5],
]

flat = [item for row in matrix for item in row]
# [9, 3, 8, 3, 4, 5, 2, 8, 6, 4, 3, 1, 1, 0, 4, 5]
```

## See also

[Sorting lists in Python](Sorting_lists_in_Python.md)


title: Logical_equivalence
last_modified: 2024-10-18 18:55:56
body:
# Logical equivalence

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

### Informal expression

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

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

### Formal expression

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

### Truth-tables

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

### Derivation

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

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

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


title: User_management_in_Linux
last_modified: 2024-10-18 18:55:56
body:
# User management

## Switch user

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

## Login as root

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

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


title: Mutations_with_Apollo_Client
last_modified: 2024-10-18 18:55:56
body:
# Mutations with Apollo Client

Queries are read-only operations. Mutations are write-only operations.

Just like the `Query` type, the `Mutation` type serves as an entrypoint to the
schema.

## Naming convention

- Use verb: `add`, `create`, `delete`
- Refer to the datatype

For example `addSpaceCat(){}`

## Demonstration mutation

We are going to create a mutation that increments the `numberOfViews` field on
the `Track` type:

## Updating the schema

```js
type Mutation {
    incrementTrackViews(id: ID!): IncrementTrackViewsResponse!
}


// Define a specific response type that specifically matches our needs
type IncrementTrackViewsResponse {
    code: Int! // status code
    success: Boolean! // whether mutation was successful
    message: String! // what to say if mutation successful
    track: Track   // not nullable because might error
}
```

Based on this schema, the mutation will recieve a `Track` id and increment the
specified `Track`. It will return an object comprising the newly updated `Track`
and a bundle of properties that provide feedback on the status of the
operations: a status code, whether it succeeded, and a message.

## Updating the data source

Remember that our sole data source in the demonstration project is a REST API.
We handle it within GraphQL using Apollos `RESTDataSource` class. We need to add
a method to this class that will increment the track views. We wil use the
`PATCH` REST method:

```js
class TrackAPI extends RESTDataSource {
  constructor() {...}
  getTracksForHome() {...}
  getAuthor(authorId) {...}
  getTrack(trackId){...};

  incrementTrackViews(trackId) {
    return this.patch(`track/${trackId}/numberOfViews`);
  }
}
```

The `patch()` method is procided by the `RESTDataSouce` class that `TrackAPI`
inherits from

## Adding resolver

Next we need a resolver that corresponds to the mutation we have defined in the
schema. We will need to handle successful responses as well as errors.

### Success case

As always we match the shape of the schema:

```js
const resolvers = {
  Query: {
    // ...query resolvers
  },
  Mutation: {
    // increments a track's numberOfViews property
    incrementTrackViews: async (_, { id }, { dataSources }) => {
      const track = await dataSources.trackAPI.incrementTrackViews(id);
      return {
        code: 200,
        success: true,
        message: `Successfully incremented number of views for track ${id}`,
        track,
      };
    },
  },
};
```

There's more going on with this resolver than the previous one. As is standard,
we call the API using the `TrackAPI` class. However we don't just immediately
return this when it executes. This is because the schema specifies that the
return type `IncrementTrackViewsResponse` requires more than just the updated
`Track`. So we wait this and return it with the cluster of metadata about the
mutation response (`code`, `success`, and `message`).

### Error case

We can extend the Mutation resolver to allow for errors. We'll do this by
refactoring the resolver into a `try...catch` block and adding the error
handling in the `catch`.

We'll harness the details that are provided by Apollos' own `err` object which
is returned by the `RESTDataSource` class that our resolver ultimately traces
back to:

```js
const resolvers = {

    Query: {
        // ...query resolvers
    }

    Mutation: {
        incrementTrackViews: async (_, {id}, {dataSources}) => {
            try {
                const track = await dataSources.trackAPI.incrementTrackViews(id);
                return {
                    code: 200,
                    success: true,
                    message: `Successfully incremented number of views for track ${id}`,
                    track
                };
            } catch (err) {
                return {
                    code: err.extensions.response.status,
                    success: false,
                    message: err.extensions.response.body,
                    track: null
                };
            }
        },
    }
}
```

## 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) we
wrap our mutation in a `gql` template string:

```js
const INCREMENT_TRACK_VIEWS = gql`
  mutation IncrementTrackViews($incrementTrackViewsId: ID!) {
    incrementTrackViews(id: $incrementTrackViewsId) {
      code
      success
      message
      track {
        id
        numberOfViews
      }
    }
  }
`;
```

We then pass it to the `useMutation` hook including an options object with our
variables. (This time the specific variable is named):

```js
import { gql, useMutation } from "@apollo/client";

useMutation(INCREMENT_TRACK_VIEWS, {
  variables: { incrementTrackViewsId: id },
});
```

`useMutation` returns an array of two elements:

1. The mutation function that actually executes
2. An object comprising (`loading`, `error`, `data`) - this is the same as is
   the return value of `useQuery`.

So we can destructure like so (we don't always need the second element);

```js
const [incrementTrackViews, dataObject] = useMutation(INCREMENT_TRACK_VIEWS...)
```

Given that we can isolate the mutation function as the first destructured
element of the array, we could then attach `incrementTrackViews` to a button or
other frontend interaction.


title: Additive_identity
last_modified: 2024-11-04 15:19:09
body:
# Property of Additive Identity

**Let $a$ represent any member of $\mathbb{W}$ or $\mathbb{Z}$ then:**

$$ a + 0 = a $$


title: Binary_units_of_measurement
last_modified: 2024-10-18 18:55:56
body:
# Binary units of measurement

A single place or symbol in a decimal number is called a **digit**. For example
the number 343 is a number containing three digits. A digit can be any one of
the ten numerals through 0-9.

The equivalent entity in the
[binary number system](Binary_number_system.md)
is the **bit**. For example the binary number 110 has three bits. A bit can only
have one of two values in contrast to a digit which can have one of ten values.
These values are 0 and 1.

## Sequences of bits

### Informational complexity

The informational complexity of a single digit is much larger than a single bit:
it can represent one of 10 states whereas a bit can only represent one of two
states.

We can think of how much data can be stored in a number in terms of the total
number of unique arrangements of bits or digits. With this in mind, compare a
two digit digital number to a two bit binary number. For the decimal number each
digit can represent one of ten states, hence the total number of unique states
is equal to $2^{10} (1024)$:

With the binary number we have $2^{2} (4)$, giving us far fewer possible unique
states. They are so few we can easily list them:

```
00
01
10
11
```

### Bytes

In order to express larger binary numbers and greater complexity we work with
sequences of bits.

The standard **base sequence** of bits is called a **byte**. This is a binary
number comprising **eight bits**. For example the number `11001110` is a byte
equivalent to 206 in decimal.

A byte allows for a complexity of up to 256 possible states: $2^{8} = 256$

## Metric units: kilobytes, megabytes etc

Having established that the core quantity of information is the byte, the
convention is to apply the
[standard metric prefixes](Prefixes_for_units_of_electrical_measurement.md)
to the byte to establish units:

| Prefix | Symbol | Expression as base ten exponent | 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     |

Hence 2MB is two million bytes, 4kb is four thousand bytes etc.

Whilst the metric prefixes are conventionally used to refer to aggregates of
bytes, they are only approximate values due to the fact that metric is base ten
whereas bytes are base two. Hence 1MB is not 1,000,000 bytes it is 1,048,576
bytes.

This is usually fine as a shorthand but an accurate representation of byte
values is as follows:

| Prefix | Symbol | Expression as base two exponent | Value             | English word |
| ------ | ------ | ------------------------------- | ----------------- | ------------ |
| Terra- | T / Ti | $2^{40}$                        | 1,099,511,627,776 | trillion     |
| Giga-  | G / Gi | $2^{30}$                        | 1,073,741,824     | billion      |
| Mega-  | M / Mi | $2^{20}$                        | 1,048,576         | million      |
| Kilo-  | k / Ki | $2^{10}$                        | 1,024             | thousand     |

> A special IEEE standard was introduced to distinguish base two within the
> metric nomenclature: kibi, mebi, gibi etc but this was not widely adopted. The
> symbols for this are included above as they are sometimes used by Linux
> programs (Gi, Mi, Ki etc)


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: Data_types_in_Bash
last_modified: 2024-10-18 18:55:56
body:
# Data types in Bash

> There is no typing in Bash

- Bash variables do not have types thus bash is neither loosely or strictly
  typed. Anything you apply the identity operator against becomes a character
  string variable.
- Bash is however able to distinguish numerical strings which is why arithmetic
  operations and comparisons work.
- Consequently there is no `null` type either. The closest thing is an empty
  string, i.e. `APPROX_NULL=""` .

## Declarations

You can achieve a sort of typing through the `declare` keyword, although bear in
mind this is not enforced and you do not have to use it.

### `-r` : readonly

```bash
declare -r var1="I'm read only"
```

Roughly equivalent to a `const` : if you attempt to change the value of `var1`
it will fail with an error message.

### `i` : integer

```bash
declare -i var2="43"
```

The script will treat all subsequent occurrences of `var2` as an integer

### `a` : array

```bash
declare -a anArray
```


title: Child_to_parent_data_flow_in_React
last_modified: 2024-10-18 18:55:56
body:
# Child to parent data flow

Despite React having a uni-directional, downwards data-flow, it is sometimes
necessary to pass events and data from a child back up to the parent. This need
can arise when you want to modularize functionality within a child component
rather than have everything happening in the parent, and you don't want to use a
common shared higher context.

For the example we will use the standard click incrementer.

We have a counter on the parent that is updated in response to a click event in
the child component.

Our parent component is `App` and the child component is `Child`

```jsx
export default function App() {
  const [count, setCount] = useState(0);
  return (
    <>
      <p>{count}</p>
      <Child count={count} setCount={setCount} />
    </>
  );
}

const Child = ({ count, setCount }) => {
  return <button onClick={() => setCount(count + 1)}>Increment</button>;
};
```

So the overall state and updater function exists on the parent. The child is
able to access this state by virtue of receiving `{count, setCount}` as props.
It can therefore manipulate this state and have it be reflected in the parent.

It is not necessary to pass the state variable every time. You could just pass
the updater. We have passed the `count` variable here because the child needs
access to the current value, in order to be able to increment it.


title: Class_inheritance_in_Python
last_modified: 2024-10-18 18:55:56
body:
# Class inheritance in Python

We distinguish the **parent/source/superclass** class and the **subclass** that
**extends** it.

A subclass can have more than one parent class that it extends, but this is
atypical.

> The subclass will inherit all attributes and methods of the superclass unless
> it overrides them.

In addition to overriding the methods of the parent we can extend them in the
child. This is a bit like using an abstract.

## Example of class inheritance

```py

class Person:
    """ Our superclass
    """
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def birthday(self):
        print('Happy birthday you were', self.age)
        self.age += 1
        print('You are now', self.age)

    def __str__(self):
        return self.name + ' is ' + str(self.age)


class Employee(Person):
    """ Our subclass that extends `Person`
    """
    def __init__(self, name, age, id):
        super().__init__(name, age)
        self.id = id

    def calculate_pay(self, hours_worked):
        rate_of_pay = 7.50
        if self.age >= 21:
            rate_of_pay += 2.50
        return hours_worked * rate_of_pay

    def __str__(self):
        return super().__str__() + ' - id(' + str(self.id) + ')'
```

### Key points

- We pass the name of the parent class to the child class as a parameter
- We must include all of the parent class's attributes in the constructor of the
  subclass
- We must use a special `super().__init__(..,...)` method to enact the extension
  of the parent. This method references the `__init__` class in the parent.

## Protected/private methods and attributes

> Protected methods and attributes are private to the class but can be accessed
> from any subclass

> Private methods cannot be accessed from anywhere outside of the class

We declare private methods and attributes with a double underscore (`__`). We
declare protected methods and attributes with a single underscore (`_`).

This is **convention**, there are not actually private and protected attributes
or properties.


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: Python_interpreter
last_modified: 2024-10-18 18:55:56
body:
# Python interpreter

## What is an implementation of a programming language?

An implementation of Python:

> means a program or environment which provides support for the execution of
> programs written in the Python language

As Python is interpreted, any implementation will perforce involve an
interpreting language.

There are three main implementations:

- CPython
  - The default implementation, written in C
- Jython
  - Python implemented by Java and running on the JVM
- PyPy
  - A subset of Python which is faster due to machine code compilation

## CPython

In the standard implementation, the language which interprets Python code is C.

When Python runs in this implementation, code written in C converts it to
bytecode (so-called because each instruction is
[8-bits long](Binary_units_of_measurement.md)). This is a
lower-level transliteration of Python is not meant to be understood by the CPU
(since it is not binary) but rather to be run in the Python virtual machine
which is equipped to understand bytecode. The Python Virtual Machine is
therefore also known as "bytecode interpreter" or the CPython VM. The VM then
converts bytecode into machine-executable code.

The fact that Python does not compile to binary means that it can run on a
greater variety of hardwares however it is slower than a language that compiles
to machine code. PyPy is a solution for this because it is both interpreted and
compiled to optimised machine code at run time.

Where CPython is the interpreter, Python extensions can be written in C.

## PyPy

PyPy is an alternative implementation to CPython. The main difference is that it
uses a JIT-compiler and doesn't convert Python to the intermediary byte code. It
is also written in Python itself, specifically RPython ('restricted Python').
This is capable of converting Python directly to machine code without the
CPython virtual machine.

## Jython

Jython is an implementation of Python written in Java. Like CPython it converts
Python to bytecode but this runs on the JVM. As it is written in Java, a Jython
program can import and use any Java class.

Where Jython is the interpreter, Python extension can be written in Java.


title: Managing_environments_in_NodeJS
last_modified: 2024-10-18 18:55:56
body:
# Managing environments

With a full-scale Node application you will typically run three environments:

- Development
- Testing
- Production

## Accessing the current environment

To determine the current environment we can use the variable
**`process.env.NODE_ENV`** from the
[global process object](Process_object.md).
This works universally regardless of the kind of Node app we are building.

If you have not manually set up your environments, **`process.env.NODE_ENV`**
will return `undefined`.

### Setting the Node environment

#### For the session

`NODE_ENV` is a bash
[environment variable](Environmental_and_shell_variables.md)
like any other. So we can set it in the normal way:

```bash
export NODE_ENV=production
```

### In Express

In Express, there is a built in method for retrieving the current envrionment:
`app.get('env')`. Express will default to the development environment.

<p style="color:red">! How to keep Express and Node environment in sync?</p>

## Configuring environments

We use the third-party [Config](node-config)
package to manage different configurations based on the environment.

Once installed we set up a dedicated config directory with a structure as
follows:

```
config/
    default.json
    development.json
    production.json
```

For example:

```json
// default.json
{
  "name": "My Express app"
}
```

Then to utilise config variables:

```js
const config = require("config");
console.log("Application name:" + config.get("name"));
```

If we toggled the different environments, we would see different outputs from
the above code (assuming we had different config files in `/config` with
different names).

### Sensitive config data

We will need to store passwords, API keys and other kinds of authentication data
for our application. We obviously shouldn't store this data openly in our config
files since it would be made public.

We can do so securely by utilising
[environmental variables](Environmental_and_shell_variables.md)
alongside the config pacakage.

We create a file called `custom-environment-variables` (must be called this) and
map a property to an environmental environment we have already set.

Let's create an environmental variable for a password:

```bash
export APP_PASSWORD='mypassword123'
```

Then in our custom variable file:

```json
{
  "password": "APP_PASSWORD"
}
```

We can then safely reference this value in the course of our normal code:

```js
console.log(config.get("password"));
```

<p style="color:red">! But how would this be achieved in a production server?</p>

<p style="color:red">! And how could we do this programmatically at the start of a local development session without manually setting each environment variable in the terminal?</p>


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

LED' stands for **Light Emitting Diode**, a [circuit]() component that emits
light. The symbol for an LED is displayed below:

![](/img/diode-led.png)

A **diode** is a special kind of component that only permits current to flow
through it in one direction. To achieve this it has very low resistance in one
direction to allow current flow and high resistance in the other direction to
impede current flow. This feature of diodes is clearly represented in the
generic diode circuit symbol:

![](/img/diode.png)

An LED diode lights up when the right amount of current flows through it. A
standard LED has a maximum current of 20mA. An appropriate
[resistor](Resistance.md#resistors)
must therefore be added to the circuit to ensure the current doesn't exceed this
amount.


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: Expansions_and_substitutions
last_modified: 2024-10-18 18:55:56
body:
# Expansions and substitutions

Bash is weird in that parentheses, braces and brackets are used not just as
markers for different code blocks but as the designators of commands in their
own right. The type of bracket you use effects how your input is interpreted.

Below are the main forms of expansion and substitution:

| Representation | Name                 |
| -------------- | -------------------- |
| `~` .          | Tilde expansion      |
| `{..}`         | Brace expansion      |
| `${...}`       | Parameter expansion  |
| `$(...)`       | Command substitution |
| `$((...))`     | Arithmetic expansion |

## Brace expansion: `{..}`

Brace expansion is for changing a smaller part of a greater whole
programmatically. This is best understood by looking at examples:

```
echo c{a,o,u}t
cat cot cut
```

```
echo /tmp/{1..3}/file.txt
/tmp/1/file.txt  /tmp/2/file.txt /tmp/3/file.txt
```

```bash
echo {1..5}
1 2 3 4 5

echo {5..1}
5 4 3 2 1

echo {a...c}
a b c
```

We can also set sequences. If we wanted to count to twenty in intervals of two

```bash
echo {1..20..2}
1 3 5 7 9 11 13 15 17 19
```

> Note that we type _two_ dots **not** an elipsis

### Example use case

We might use brace expansion to generate sequential file names using a
pre-defined naming scheme, eg.

```
touch file_{01..12}{a..d}
ls

file_01a
file_01b
file_01c
file_01d
file_02a
file_02b
...
file_12d
```

The syntax here basically means: for each of the elements in the first list, run
the second list against them.

## Parameter expansion: `${...}`

We use most frequently for returning the value of stored
[variables](Variables_and_data_types_in_Bash.md).
Techically we do not have to use the braces, we can retrieve with just `$var`
however it's better to use them to minimise interpretation fuck-ups which happen
a lot.

When the braces are used, this allows us to transform the values before they are
returned such as only returning from the 6th character: `${var:6}`.

## Command substition: `$(...)`

Command substitution (circle-brackets) allows us to put the output of one
command inside another. Bash runs the bracketed command in a
[sub-shell](Shell_sessions.md) and then returns it
to the main user shell.

For example:

```bash
echo "The current directory is $(pwd)."
```

## Arithemtic expansion: `$((...))`

We use arithmetic expansion when we want to calculate numerical values

See
[Working with numbers in Bash](Working_with_numbers_in_Bash.md)
for more.


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

## Shells

Electrons orbit in concentric circuits around the nucleus of the atom. Each
orbit is called a **shell**.

Each shell can accommodate a maximum number of electrons. The shells are
designated by letters and filled in sequence moving out from the shell nearest
to the nucleus.

_The diagram below demonstrates shell naming conventions and the maximum number
of electrons per shell._

![](/img/valence-shell.svg)

## Valence

The outer shell called the **valence shell** and the number of electrons it
contains, the **valence**. This part of the atom is the most important from the
point of view of electricity because it is from here that electrons can escape
the atom and where electrons from other atoms may join.

> The farther the valence shell is from the nucleus, the less attraction the
> nucleus has on each valence electron. Thus the potential for the atom to gain
> or lose elections increases if the valence shell is not full and is located
> far enough away from the nucleus.

## Conductivity and insularity

The **conductivity** of a material is an expression of its capacity to channel
electrical charge. Where electrical charge is the _flow of free electrons from
one atom to another_. The **insularity** of a material is the opposite: its
_capacity to resist the flow of electrical charge_.

Electrons in the valence shell can gain energy from external forces. If these
electrons gain enough energy, they can leave the atom and become **free
electrons**, moving randomly from atom to atom.

We call materials that contain a large number of free electrons **conductors**.
In contrast **insulators** are atoms that stabilize themselves by absorbing
valence electrons from other atoms to fill their valence shells, eliminating the
free electrons.

> The propensity for the electrons within a conductor to move about and jump
> between atoms, swapping charge, is not something that is only activated when a
> voltage is applied. In fact, the electrons are always doing this, even in a
> piece of inert copper. The difference is they are doing it randomly and in all
> kinds of directions. It is only when a voltage is applied that the motion and
> exchange of electrons is forced in one consistent direction.

### Semiconductors

Semiconductive materials are midway between conductors and insulators: they are
neither good conductors or insulators but can be altered to function in the
manner of either.

## Ionization

Ionization is the process of atoms gaining and losing electrons.

When an atom has an equal number of protons and electrons it is said to be
**electrically balanced**.

A balanced atom that receives one or more extra electrons gives way to an
overall negative charge. An atom in this state is a **negative ion**. Conversely
if a balanced atom loses one or more electrons it becomes positively charged and
is thus called a **positive ion**.

> The process of ionization is a constitutive part of the flow of current but
> not the only part. We also must factor the force that triggers ionization and
> the resistance that impedes it.


title: Retrieve_data_from_SQL_table
last_modified: 2024-10-18 18:55:56
body:
# Retrieve data from table with SQL `SELECT` statement

> Select data from the database. The data returned is stored in a result table
> called the **result-set**.

_A table named `models`:_

| modelId | name                 | cores | releaseDate |
| ------- | -------------------- | ----- | ----------- |
| 2       | Raspberry Pi Model B | 2     | 2022-11-19  |
| 3       | Raspberry Pi Model C | 4     | 2022-11-25  |
| 4       | MacBook Pro M2       | 9     | 2022-03-18  |

## Basic usage

```sql
SELECT name FROM models
```

We can pass in a comma-separated **list of fields**:

```sql
SELECT name, releaseDate FROM models
```

## With parameters

### `WHERE`

The `WHERE` clause specifies a search criterion. The `WHERE` clause should
always be last in the syntax. The clauses are always written in this order:
`FROM` followed by `WHERE`

```sql
SELECT name FROM models WHERE cores > 3
```

### Compound statements

Compound statements allow you to apply more filters to your clauses within an
SQL statement. SQL allows you to build complex, combinatorial `WHERE` clauses by
using Boolean and mathematical operators (i.e `AND` , `OR` , `>` , `<` , `!=` ,
`<=` ...)

Multiple clauses:

```sql
SELECT name, ram, release_date
FROM model
WHERE release_date > '2018-01-01' AND ram > 512;
```

More complex logic achieve with parentheses:

```sql
SELECT name, cores, release_date
FROM model
WHERE (manufacturer_id = 1 OR manufacturer_id = 2) AND cores >= 2;
```

## Transform returned data

We can apply **ordering** to the return data

```sql
SELECT name FROM models ORDER BY name, cores
SELECT name FROM models ORDER BY name, cores DESC
```

> `ORDER BY` always comes last, after the selection and any filtering clauses
> but _before_ a `WHERE` clause

## Paginate

We can break our returned data into blocks with `LIMIT`

```sql
LIMIT 5 -- Return first five items
LIMIT 5,5 -- Return first five items from the sixth row
```


title: Working_with_numbers_in_Bash
last_modified: 2024-10-18 18:55:56
body:
# Working with numbers in Bash

We distinguish:

- **arithmetic expansion** `$(( ))`
  - returns the result of literal numbers that can then be stored in a variable
- **artihmetic evaluation** `(( ))`
  - perform calculations on _existing_ variables

An example of expansion:

```bash
a=3
((a += 3))
echo $a
# 6
((a++))
# 7
```

> Note: we do not use a dollar-sign when referring to variables within
> arithmetic evaluation, there is no need. If we do, we get an error. This is
> because we are using an
> [expansion](Expansions_and_substitutions.md),
> therefore the variables are already being interpreted as variables.

## Declaring variables as integers

It is good practice to safeguard against Bash treating numbers as strings to
declare them as integers in addition to using arithmetic evaluation, e.g:

```bash
declare -i b=3
```

Whilst this isn't a strict type, it means we can do this:

```bash
b=$b+4
echo b
# 7
```

Without getting `3+4` in return

## No decimals in bash

Bash does not support decimal calculations natively. This is what you'd get for
example:

```bash
echo $(( 1/3 ))
# 0 (not 0.33)
```

So work with decimals you should use `awk` or `bc` ("basic calculations").

Example of using `bc`:

```bash
declare -i c=1
declare -i d=3
e=$(echo "scale=3; $c/$d" | bc)
# 0.333
```

## Random numbers

Generate a pseudo-random number between 1 and 20:

```bash
echo $(( 1 + RANDOM % 10))
# 18
```


title: Views_in_relational_databases
last_modified: 2024-10-18 18:55:56
body:
# Views in relational databases

A view is a virtual table that is based on the result-set of a query. Although
it doesn't actually exist in the same manner as an actual table it has exactly
the same properties and functionality. Rather than adding the raw data manually,
you derive it from logic applied to existing actual tables.

This is a good way of dynamically generating data and is often employed as a way
of creating mutations without changing the underlying data structure. In SQL,
views can be created by combining `PRIMARY` and `FOREIGN` keys.


title: Additive_inverse_property
last_modified: 2024-10-18 19:10:47
body:
# Additive inverse property

**Let $a$ represent any member of $\mathbb{Z}$. Then there is a unique member of
$\mathbb{Z}$ $-a$ such that:**

$$ a + (-a) = 0 $$

The sum of a number and it's negative (called **the additive inverse**) is
always zero.


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: Passing_arguments_to_Python_scripts
last_modified: 2024-10-18 18:55:56
body:
# Passing arguments to Python scripts

`sys.argv` is a list that contains the command-line arguments passed to a Python
scripts.

- `sys.argv[0]` = the name of script
- `sys.argv[1]` = the first argument
- `sys.argv[2]` = the second argument, and so on

Example invocation:

```sh
python3 ./my_script.py argument_one argument_two

```

```python
import sys

print(sys.argv[0])
print(sys.arg)
print(sys.argv[1])

# my_script.py
# ['my_script.py', 'argyment_one', 'argument_two']
# argument_one

```


title: Singleton_pattern
last_modified: 2024-10-18 18:55:56
body:
# Singleton pattern

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

## Use-cases

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

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

## Implementation

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

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

### Using an object

We ensure singularity by:

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

```js
const _data = [];

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

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

### Using a class

The same process can be created using classes:

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

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

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

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

export default instance;
```

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

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

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

    return UserStore.instance;
  }

  //rest is the same code as preceding example
}

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

export default instance;
```


title: Props_in_React
last_modified: 2024-10-18 18:55:56
body:
# Props

## Types or interfaces?

They are mostly interchangeable but there are some specific differences

### Interfaces

- Classes and other interfaces can extend an interface but not a type

  ```ts
  interface Animal {
    name: string;
  }

  interface Bear extends Animal {
    honey: boolean;
  }
  ```

- You can add properties to an interface without generating a double declaration
  error:

  ```ts
  interface Window {
      }

  interface Window {
    ts: TypeScriptAPI;
  }
  ```

### Types

You can create unions with types but not interfaces.

> The consensus seems to be that interfaces should be used over types unless
> there are ocassions when you do not want a type to be extendable /
> redeclarable. Or when you want to use unions.

## How we type props

The custom is to type props as an interface. This way we can easily type
function prop types.

```tsx
interface IProps {
  people: {
    name: string;
    age: number;
    note?: string;
  }[];
}

const List: React.FC<IProps> = ({people}: IProps) => {
    return()
}

// Note we say that the props into the func component are of type IProps
// And we destructure the people key

```

Then in the parent:

```tsx

const [people, setPeople] = useState<IState['people']>({});

<List props={people}>

```

## Common types for props

### Object with any number of properties

```tsx
interface IProps {
  obj: {};
}
```

### Array of objects

```tsx
interface IProps {
  val1: string;
  val2: boolean;
}
[];
```

### Functions

```tsx

// Function that doesn't receive or return anything
interface IProps {
    onClick: () => void;
}[]

// Function with named parameter
interface IProps {
    onClick: (id: number) => void;
}[]

// Function that takes an event
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
```

### Other React components

```js
interface IProps {
  children: React.ReactNode;
}

interface IProps {
  children: JSX.Element;
}
```

`React.ReactNode` covers everything that React can render. This should be
definitely used when the prop is a child component, otherwise `JSX.element` is
ok.

<p style="color: red;">Should I use type or interface? What is consensus?</p>


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: Example_scenario_internet_data_transfer
last_modified: 2024-10-18 18:55:56
body:
# Example scenario of data transfer accross the internet

![Path of a network request accross the internet](../img/network-request-layers.png)

![All layers of the IP](../img/all-layers-of-IP.png)

- A client device is connected to a wireless WiFi network.
- This network is connected to the internet via a router.
- Elsewhere on the internet is a server also connected to the internet via
  router.
- A user of the client device opens a web browser and requests a web page hosted
  on the server (we will assume it already knows the IP address)
- The web browser knows HTTP - a protocol of the
  [Application Layer](Application_Layer_of_Internet_Protocol.md) - and forms an
  HTTP request intended for the destination server.
- The browser hands off the HTTP request to the TCP/IP software stack of the
  device's [operating system](Basic_model_of_the_operating_system.md), asking
  that the data be delivered to the server on its IP address and at port 80.
- The TCP/IP software stack on the client OS encapsulates the HTTP payload in a
  TCP segment (at the
  [Transport Layer](Transport_Layer_of_Internet_Protocol.md)), setting the
  destination port to 80 in the segment header.
- The [Internet Layer](Internet_Layer_of_Internet_Protocol.md) software on the
  client then wraps the TCP segment in an IP packet, which includes the
  destination IP address of the server in the packet header.
- At the [Link Layer](Link_Layer_of_Internet_Protocol.md) of the client device,
  the IP packet is encapsulated in a frame with the
  [MAC_address](MAC_addresses.md) of the local router in its header. The frame
  is wirelessly transmitted by the client device's WiFi hardware.
- The wireless access point receives the frame and passes to the router. The
  router examines the internet layer packet to determine the destination IP
  address.
- To reach the server, the request needs to travel through multiple routers on
  the internet and the process is replicated (looking up MAC address of the
  device and determining IP target). Eventually, the request reaches the router
  on the network where the server is connected.
- The last router puts the packet in a frame suitable for the server's local
  network and in the frame's header is the MAC address of the server.
- The server receives the frame and the packet is passed to the TCP/IP software
  stack. This passes the HTTP data to the process listening on TCP port 80.
- Web server software, listening on port 80 handles the request and replies to
  the client, start ing the process again in reverse order.


title: Delete_a_branch
last_modified: 2024-10-18 18:55:56
body:
# Delete a branch

### Locally

```bash
git branch -d <local_branch_name>
```

### Remote

```bash
git push origin --delete <remote_branch_name>
```


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: Stacks
last_modified: 2024-10-18 18:55:56
body:
_A stack visualised vertically_ ![stack2.svg](stack2.svg)

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

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

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

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

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

## We can build a stack from an array

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

## Demonstration

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

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

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

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

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

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

## Run through

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

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

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

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

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

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

## Practical applications

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


title: File_descriptors
last_modified: 2024-10-18 18:55:56
body:
# File descriptors

File descriptors are shorthand for `stdin`, `stdout` and `stderr`:

| File descriptor | Name            | Common abbreviation |
| --------------- | --------------- | ------------------- |
| 0               | Standard input  | `stdin`             |
| 1               | Standard output | `stdout`            |
| 2               | Standard error  | `stderr`            |

They are typically used when you want to
[redirect](Redirection_in_Bash.md) the output of the
standard/input /output/error somewhere, e.g a file or as input to another
program. A classic case is `[some_command] > /dev/null 2>&1`

They are all technically "files" which are open and which append themselves to
every process that can run in the shell. For any command or program that you
run, you will be able to access `0`, `1` and `2` for them. In this way they are
similar to variables like
[`$0`](Passing_arguments_and_options_to_Bash_scripts.md#passing-arguments)
and
[`$@`](Passing_arguments_and_options_to_Bash_scripts.md#passing-arguments).
They have a universal meaning within the context of the shell runtime.


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: Disjunction_Introduction
last_modified: 2024-10-18 18:55:56
body:
# Disjunction Introduction

This rule can seem a little odd: like we are randomly introducing an additional
proposition without giving any justification. However this is just a consequence
of the fact if $P$ is true, so is $P \lor Q$ since disjunction is not the same
as conjunction: only one disjunct needs to be true for the compound disjunction
to be true. This is represented in the context of
[truth-trees](Truth-trees.md#disjunction-decomposition) by the fact that truth
can pass up via either branch of a disjunction pattern. This rule is sometimes
also referred to (confusingly) as _Addition_.

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


title: Memory_Management_Unit
last_modified: 2024-10-18 18:55:56
body:
# Memory Management Unit (MMU)

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

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

## Pages

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

## Shared pages

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

## Page faults

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

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

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


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: Linux_disk_partitions
last_modified: 2024-10-18 18:55:56
body:
# Linux disk partitions

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

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

## Viewing current partitions

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

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

```bash
$ lsblk

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

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

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

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

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

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

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

## Standard Linux partitions

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

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

### Boot partition

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

### Root partition (`/`)

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

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

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

### Home partition

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

## Types of partition table

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

#### Primary, extended and logical partitions

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

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

In our example above:

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

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

## Related notes

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


title: Components_props_classes
last_modified: 2024-10-18 18:55:56
body:
# Components and props with class-based components

A **component** is a class or a function that manages the content that is output
on the page (the rendering of **elements**) and the data (**props**) and state
that is applied to the elements that are output.

**Components can be created using ES6 classes or using the newer hook
methodology.**

A class-based component is a mixture of standard Javascript, JSX Javascript and
HTML wrapped within an extension of the `React.Component` **class** that you
import into your custom JS when installing the React library:

```js
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
```

The JSX content of the component lives within the `render()` method that belongs
to the `React.Component` class.

Once we have defined a class component that contains a JSX element we can then
use JSX tags to place the resulting React element in our page. This is a
placeholder for the HTML that is rendered by the class component. For example:

```html
<body>
  <Welcome name="Thomas" />
</body>
```

> A class component must include `render()`, and the return can only return one
> parent element. Also there _must be_ a parent component. Where this isn't
> possible, the convention is to use empty opening and closing tags, viz:
> `<>...</>` . These are called **React Fragments** and this is actually a short
> hand for `<React.Fragment>...</React.Fragment>`

## Class components with state

Most class components will also possess state. State is managed through the
constructor method on the class component, for example:

```jsx
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = { date: new Date() };
  }
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
```

We use the `super()` method to inherit the parent constructor (because custom
components extend the React Component parent class) and we pass state as a
special kind of prop.

## Class components and simple components

Within the class-based paradigm of React a distinction is made between simple
and class components.

A class component is obviously a component that is created as a class and thus
which can inherit fields and methods via `super` from the `React.Component`
class. This is necessary for handling props and configuring state.

A simple component, in contrast, is a function, not a class. These are used for
what we can call static or 'dumb' components, i.e they just output content and
data, they don't have reactive state. This is why they are also sometimes called
**functional stateless components**.

Simple components can however take props. In fact props are just like arguments
that you pass to a function. When you write a simple component, your parameters
are props.

Here's an example of a simple component that doesn't take any props

```jsx
function PageHeader() {
  return (
    <header>
      <h1>Site Title</h1>
    </header>
  );
}
```

We can safely use arrow functions with simple components. So the same component
could be written:

```jsx
const PageHeader = () => {
  return (
    <header>
      <h1>Site Title</h1>
    </header>
  );
};
```

Now adding a prop:

```jsx
const PageHeader = (props) => {
  return (
    <header>
      <h1>{props.sitename}</h1>
    </header>
  );
};
```

Then, in JSX we would apply the component with:

```jsx
<PageHeader sitename="The name of the site" />
```

> When we are using simple (functional) components we don't need to use
> `this.props` . `this` is only required with classes. As a simple component is
> a function we just say `props` .

It's worth noting in passing that you don't **have** to use functions for simple
components. You can achieve the same outcome with a class and it won't generate
an error. But it requires more code and complexity than is actually needed for
the job:

```jsx
class PageHeader extends React.Component {
  render() {
    return (
      <header>
        <h1> {this.props.siteName} </h1>
      </header>
    );
  }
}

export default PageHeader;
```


title: Docker_images
last_modified: 2024-10-18 18:55:56
body:
# Docker images

Docker needs an image to run a container. Images are stored in the local
registry. If it can't find it, it will try and pull it from Docker Hub.

## View your local images

```
docker images
```

```sh
REPOSITORY                     TAG       IMAGE ID       CREATED         SIZE
debian                         latest    e6d9c9c3e100   12 days ago     118MB
ubuntu                         latest    bab8ce5c00ca   6 weeks ago     69.2MB
cda/mysql-5.7                  latest    312ce2a6cea5   3 months ago    495MB
cda/node-16                    latest    b26e54c8fa11   3 months ago    1.44GB
bitnami/minio                  latest    ef6d14df2158   3 months ago    229MB
opensearchproject/opensearch   1.3.7     0f8ef57c3629   4 months ago    831MB
hello-world                    latest    46331d942d63   13 months ago   9.14kB
```

## Repository, registry, tag

When you specify the name of an image, you are referring to the image's
repository. The registry is the list of repositories available on the Docker
Hub. Each repository has a tag, indicating the current production branch.

For example:

```
docker run -it ubuntu:18:04 /bin/bash
```

- `ubuntu` is the image repository
- `18.04` is the image tag

> The Image ID distinguishes the image globally and is not local to you.

To get detailed info on an image:

```
docker image inspect debian
```

## "Dangling" images

A "dangling" image is an unused image - one that is not associated with a
container. Re move dangling images with

```
docker image prune
```

## Anatomy of a Docker image: layering

The process of constructing a Docker image involves creating a series of
intermediate layers, each representing a change or addition to the filesystem.
These layers are stacked on top of one another, forming the final image. This
approach is called layering.

Layering in Docker images offers several benefits:

1. Reusability: Common layers can be shared between multiple images, reducing
   disk space usage and build time. For example, if multiple images use the same
   base operating system, they can all share the same base layer.

2. Caching: During the build process, Docker caches layers that have already
   been built. If a layer hasn't changed, Docker will reuse it in subsequent
   builds, speeding up the build process.

3. Incremental updates: When updating an image, only the layers that have
   changed need to be updated and redistributed. This reduces the size of
   updates and makes the process more efficient.

Each instruction in the Dockerfile creates a new layer in the image. Some common
instructions include:

- `FROM`: Specifies the base image to start with (e.g., an operating system or
  an existing image).

- `RUN`: Executes a command, often used for installing packages or configuring
  the system.

- `COPY`: Copies files or directories from the host machine to the image.

- `ADD`: Similar to COPY but can also handle URLs and automatic extraction of
  archives.

- `ENV`: Sets environment variables that will be available inside the container.

- `CMD`: Specifies the default command to run when a container is started from
  the image.

- `ENTRYPOINT` : Similar to CMD, but allows the container to be run as an
  executable, with additional arguments.

When building an image, Docker starts with the base image specified in the FROM
instruction and applies each subsequent instruction, creating a new layer for
each step. The final result is a layered image that contains the application and
its dependencies, ready to be run as a container.

### The read-write layer

The final layer at the "top" of the image is a thin read-write layer. In
contrast to the intermediate layers it can execute read-write functionality and
is not read-only.

Files in the intermediate layer cannot be modified, only the read-write layer
can be modified. Files are copied over from intermediate layers when requested
by the read-write layer, rather being modified themselves.

Tip for writing build files:

Things that change less should be higher up (earlier). Things that change more
should be twoardds the end. This allows for better layer caching. Particularly
bear this in mind with the commands: RUN, COPY, ADD.


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: Lifecycle_methods
last_modified: 2024-10-18 18:55:56
body:
# Lifecycle methods

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

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

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

## Specific lifecycle phases

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

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

The two methods above only occur once in a lifecycle.

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

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

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

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

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

6. `componentWillUnmount()`

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

## Side-effects: why lifecycle phases matter

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

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

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

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

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


title: Tags_in_Git
last_modified: 2024-10-18 18:55:56
body:
# Tags in Git

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

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

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

## Creating a tag

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

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

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

## Deleting a tag

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

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

## Listing tags

```
git tag --list
```

## Examples of use

```
git show v1.1

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

## Pushing tags to a remote

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

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

Push all tags:

```
git push origin --tags

# Delete remote tag

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

## Getting tags from the remote

```
git fetch
```

This automatically fetches tags too.


title: VirtualMemory
last_modified: 2024-10-19 10:18:48
body:
# Virtual memory

Virtual memory is an abstracted and idealised representation of the physical
memory capacity of a machine that is presented to user space for its memory
operations.

When an OS implements virtual memory, [processes](./Processes.md) in
[user space](./User_Space.md) cannot directly read or write to the physical
memory. Instead they execute memory operations against virtual memory and the
[kernel](./The_kernel.md) translates these into the actual operations against
the memory hardware.

The main benefits:

- User mode processes do not have to be concerned with the physical memory
  management
- There is a buffer between user mode processes and physical memory, meaning
  that memory cannot be accidentally corrupted by other processes in user space.

Because the physical memory is abstracted, it can be the case that the physical
[memory addresses](./Memory_addresses.md) are non-contiguous or even distributed
accross different hardware components (such as the
[cache](./Register_and_cache_memory.md) and [swap](./Swap_space.md)). Despite
this, the memory addresses will appear contiguous in virtual memory. Each user
space process is presented with the same range of available memory addresses and
the same total capacity.

It is also possible for the kernel to present user space with an available
virtual memory capcacity that exceeds the current physical capacity of the
machine:

> _It's possible for the kernel and all running processes to request more
> byte<!--  -->s of virtual memory than the total size of RAM. In that
> situation, the OS can move move bytes of memory to secondary storage to make
> room in RAM for newly requested memory._

_How Computers Really Work_ (2021) p.206

## Virtual memory and the kernel

The kernel itself utilises virtual memory.

During the [boot_process](Boot_process.md), it runs in physical memory mode
however it rapidly creates a 1:1 mapping of physical to virtual memory for
itself. This direct mapping allows the kernel to easily access any physical
memory location as needed, whilst retaining the benefits of virtual memory.

With its own virtual memory established, it can then build additional virtual
memory structures for itself and user processes.

While the kernel uses virtual memory it can also access physical memory at any
time wherease user space processes can only ever access virtual memory.

The kernel virtual memory has a different range of virtual addresses to work
with than user space virtual memory.

Unlike user space virtual memory, the kernel has access to everything running in
kernel address space whereas processes in user address space are partitioned
from each other with separate address spaces that cannot interact.

In addition to being able to access its own virtual memory (and physical memory,
as required) the kernel can also access any user processes' virtual address.

This said, the virtual memory space of the kernel and the virtual memory space
of the user processes are distinct. It is not the case that the kernel is
superset of all available virtual memory. It can access user space virtual
memory because it sets up the tables and locations, not because it is a subset
of its own virtual memory.

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


title: http_in_Node
last_modified: 2024-10-18 18:55:56
body:
# `http` module

The HTTP Module allows us to create a web server that listens for HTTP requests
on a given port. It is therefore perfect for creating backends for client-side
JavaScript.

## Creating a server

An HTTP server is another instance of an
[event emitter](events.md)). It
therefore has all the same methods as the `EventEmitter` class: `on`, `emit`,
`addListener` etc. This demonstrates again how much of Node's core functionality
is based on event emitters.

_Creating a server_

```js
const http = require("http");

const server = http.createServer(); // Create server as emitter

// Register functions to run when listener is triggered
server.on("connection", (socket) => {
  console.log("new connection...");
});

server.listen(3000);
console.log("Listening on port 3000");
```

This server is functionally equivalent to a generic event emitter:

```js
// Raise an event
const emitter = new EventEmitter("messageLogged");

// Register a listener
emitter.on("messagedLogged", function () {
  console.log("The listener was called.");
});
```

Whenever a request is made to this server, it raises an event. We can therefore
target it with the `on` method and make it execute a function when requests are
made.

If we were to start the server by running the file and we then used a browser to
navigate to the port, we would see `new connection` logged every time we refresh
the page.

### Sockets and `req, res`

A socket is a generic protocol for client-server communication. Crucially it
**allows simultaneous communication both ways**. The client can contact the
server but the server can also contact the client. Our listener function above
uses a socket as the callback function but in most cases this is quite
low-level, not distinguishing responses from requests. It is more likely that
you would initiate a `request, resource` architecture in place of a socket:

```js
const server = http.createServer((req, res) => {
  if (req.url === "/") {
    res.write("hello");
    res.end();
  }
});
```

#### Return JSON

Below is an example of using this architecture to return JSON to the client:

```js
const server = http.createServer((req, res) => {
  if (req.url === "/products") {
    res.write(JSON.stringify(["shoes", "lipstick", "cups"]));
    res.end();
  }
});
```

### Express

In reality you would rarely use the `http` module directly to create a server.
This is because it is quite low level and each response must be written in a
linear fashion as with the two URLs in the previous example. Instead we use
Express which is a framework for creating servers and routing that is an
abstraction on top of the core HTTP module.


title: Bell_System
last_modified: 2024-10-30 13:34:21
body:
# Bell System

Denotes the family of companies run by the Bell Telephone Company during its 100
year outright monopoly on the telecommunications industry and infrastructure in
the US. Inclusive of the American Telephone and Telegraph Company (AT&T), AT&T
Long Lines (line network), the Western Electric Company (manufacturing
telephonic equipment), and Bell Labs (the R&D section). Each major city/ region
had a subsidiary, e.g. Michigan Bell, Northwestern Bell, the New York Telephone
Company etc.

Colloquially known as "Ma Bell" ("mother Bell").

The monopoly was broken up in 1983. Today, after the breakup the remaining (now
separate) companies are AT&T (phone network, mobile network), Verizon (mobile
network), and Lumen (cloud computing, networking services).


title: Virtual_private_cloud
last_modified: 2024-10-18 18:55:56
body:
# Virtual Private Cloud

AWS VPC is used to create a virtual network. This is typically used in concert
with [[zk/Elastic_Compute_Cloud]] to manage connections to a virtual server both
privately and accross the internet.

You define a network address range and then create subnets for managing
different connections and functionality. You use a public subnet for resources
that must be connected to the internet and a private subnet for resources that
are to remain isolated from the internet.

The diagram below details a basic VPC configuration:

![](../img/aws-vpc-example.png)

- Within a given AWS region we have created a VPC network.
- This comprises public and private subnets
- Both subnets host an EC2 instance
- The public subnet has a bridge to the internet through the Internet Gateway
- Both subnets have a routing table to manage requests and access


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: 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: AWS_architecture_hierarchy
last_modified: 2024-10-18 18:55:56
body:
# AWS architecture hierarchy

From bottom up:

```mermaid
flowchart TD
    Data centres --> Availability Zones--> Regions
```

| Entity             | Description                     |
| ------------------ | ------------------------------- |
| Data center        | Warehouse full of servers       |
| Availability Zones | A cluster of data centers       |
| Region             | A cluster of Availability Zones |


title: React_errors
last_modified: 2024-10-18 18:55:56
body:
# Errors

## State update on unmounted component

> Can't perform a React state update on an unmounted component. This is a no-op,
> but it indicates a memory leak in your application. To fix, cancel all
> subscriptions and asynchronous tasks in a useEffect cleanup function.

This typically happens when you have an API request running in the background.
Once the data is returned, React tries to update the state but the component is
unmounted.

As the warning suggests, this can be resolved using the cleanup parameter within
[React_useEffect](React_useEffect.md#cleanup-functions).


title: Loops_in_Python
last_modified: 2024-10-18 18:55:56
body:
# Loops in Python

## For

There are three main types of `for` loop in Python and they all use the
`for...in` clause:

1. Traditional `for` loop with range:

   ```py
   for i in range(0, 5):
       print(i)
   ```

   This is equivalent to the following in JavaScript:

   ```js
   for (let i = 0; i <= 5: i++) {
       console.log(i)
   }
   ```

2. `for` loop with iterable:

   ```py
   fruits = ["apple", "banana", "cherry"]
   for fruit in fruits:
       print(fruit)
   ```

3. `for` loop with `enumerate()` function: Iterate over an iterable whilst also
   keeping track of the index of the current element:
   ```py
   for index, fruit in enumerate(fruits):
       print(f"Index {index}: {fruit}")
   ```

### Loop in increments greater than 1

The following loop increments by 2 on each iteration

```py
for i in range(0, 10, 2):
    print(i, ' ', end='')
print()

# 0  2  4  6  8
```

### Break and continue

#### Break

```py
num = int(input('Enter a number from 1-6 to check for: '))
for i in range(0, 6):
    if i == num:
        break
    print(i, ' ')
```

#### Continue

```py
for i in range(0, 10):
    print(i, ' ', end='')
    if i % 2 == 1:
        continue
    print('hey its an even number')
    print('we love even numbers')
print('Done')

"""
0  hey its an even number
we love even numbers
1  2  hey its an even number
we love even numbers
3  4  hey its an even number
we love even numbers
5  6  hey its an even number
we love even numbers
7  8  hey its an even number
we love even numbers
9  Done
"""
```

## While

```python
count = 0
print('Starting')
while count < 10:
    print(count, '', end='')
    count += 1

print()  # not part of the while loop
print('Done')

"""
Starting
0 1 2 3 4 5 6 7 8 9
Done
"""
```

> There are no `do while` loops in Python


title: Network_protocols
last_modified: 2024-10-18 18:55:56
body:
# Network protocols

Protocols are agreed standards specifying and governing communication between
nodes on a [network](Network_fundamentals.md).

Network protocols are typically _layered_. The implmentation of a network layer
is a _stack_.

Stacks enshrine encapsulation: not every layer has to be directly concerned with
every other layer. Only adjacent layers that interface with each other need to
be in contact.

A prime example of a layered network stack is the
[internet_protocol_suite](Internet_fundamentals.md).


title: Four_bit_adder
last_modified: 2024-10-18 18:55:56
body:
# Four-bit adder

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

We want to be able to calculate the following sum:

```
0010
0011
____
0101
```

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

Let's walk through the process:

>

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

## Ripple carry adder

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

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

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


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)


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: Logical_consistency
last_modified: 2024-10-18 18:55:56
body:
# Logical consistency

## Informal definition

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

### Demonstration

The following set of propositions form an inconsistent set:

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

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

## Formal definition

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

### Informal expression

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

### Formal expression

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

### Truth table

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

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

## Derivation

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

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

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

![](/img/derivation_from_contradiction.png)

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

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


title: Remote_tracking_branches
last_modified: 2024-10-18 18:55:56
body:
# Remote tracking branches

A remote tracking branch is a **local** snapshot or reference to the state of a
branch on a remote repository.

When you clone a remote repository, Git automatically creates remote tracking
branches for all the branches on the remote repository.

The remote tracking branches have the prefix `origin/` followed by the name of
the remote branch. For example, if you clone a repository with a branch named
`develop`, Git creates a remote tracking branch named `origin/develop`.

When you use commands like `git fetch` and `git pull`, you are interacting with
the remote repository and having it reflected in the remote tracking branches.

For instance, `git fetch` makes the remote tracking branch reflect the remote
changes so that you have a local copy.

The remote tracking branch is not the same as a local branch. A local branch is
a branch that may or may not exist on the remote. Once it has been pushed to the
remote repository, a remote tracking branch will be created for its remote
version. When you make local changes, the snapshot of the remote tracking branch
will no longer be up to date: your local branch will be "ahead" of it. Once you
push, the remote tracking branch snapshot is update and reflects the local state
of the branch and the two are in sync again.

The same is true of remote branches. When you checkout a remote branch, your
local version of it reflects a remote tracking version of it. This goes out of
sync with your local version when changes are made on the remote.


title: Appending_to_files_in_Python
last_modified: 2024-10-25 09:28:57
body:
# Appending to files in Python

```py
file = open("example.txt", "a")
file.write('Add something to the end of the file')
file.close()
```

```py
with open("example.txt", "a"):
    file.write("Add something to the end of the file")
file.close()
```


title: Exponents
last_modified: 2024-10-18 18:55:56
body:
## Equivalent equations

> Two equations are equivalent if they have the same solution set.

We know from the distributive property of multiplication that the equation
$a \cdot (b + c )$ is equivalent to $a \cdot b + a \cdot c$. If we assign values
to the variables such that $b$ is equal to $5$ and $c$ is equal to $2$ we can
demonstrate the equivalence that obtains in the case of the distributive
property by showing that both $a \cdot (b + c )$ and $a \cdot b + a \cdot c$
have the same solution:

$$ 2 \cdot (5 + 2) = 14 $$

$$ 2 \cdot 5 + 2 \cdot 2 =14 $$

When we substitute $a$ with $2$ (the solution) we arrive at a true statement
(the assertion that arrangement of values results in $14$). Since both
expressions have the same solution they are equivalent.

## Creating equivalent equations

Adding or subtracting the same quantity from both sides (either side of the $=$
) of the equation results in an equivalent equation.

### Demonstration with addition

$$ x - 4 = 3 \\ x -4 (+ 4) = 3 (+ 4) $$

Here we have added $4$ to each side of the equation. If $x = 7$ then:

$$ 7 - 4 (+ 4) = 7 $$

and:

$$ 3 + 4 = 7 $$

### Demonstration with subtraction

$$ x + 4 = 9 \\ x + 4 (-4) = 9 (-4) $$

Here we have subtracted $4$ from each side of the equation. If $x = 5$ then:

$$ 5 + 4 (-4) = 5 $$

and

$$ 9 - 4 = 5 $$


title: Sets_in_Python
last_modified: 2024-10-18 18:55:56
body:
# Sets in Python

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

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

## Basic usage

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

## Looping through sets

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

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

## Check for membership

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

## Remove items from set

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

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

basket.clear()
print(basket)
#set

```

## Add items to a set

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

## Start with empty set

To declare an empty set you cannot just do:

```py
my_set = {}
```

You have to use a constructor:

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

## Apply unions and intersections

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

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


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

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

```


title: Multiplicative_property_of_negative_one
last_modified: 2024-10-18 18:55:56
body:
# The Multiplicative Property of Negative One

> **Let $a$ represent any member of $\mathbb{Z}$, then:**

$$ (-1) \cdot a = -a $$


title: Stale_branches_and_pruning
last_modified: 2024-10-18 18:55:56
body:
# Stale branches and pruning

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

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

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

```sh
git remote prune origin

# or run a preview of the action

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


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: Difference_between_remote_origin_and_head
last_modified: 2024-10-18 18:55:56
body:
# Difference between _remote_, _origin_, _HEAD_

<dl>
<dt> remote
</dt>
<dd>
A remote is a git repository that is hosted on a server which you can interact with over a network. You can have more than one remote for a local repository.
</dd>

<dt>
origin
</dt>
<dd>
The default name given to the remote repository that was cloned. When you clone a repository, git automatically creates a default remote called `origin`. This is essentially an alias for the URL of this repository.
</dd>

<dt>HEAD</dt>
<dd>A reference to the most recent commit on the currently checked-out branch. It points to the tip of the branch which is the commit that was most recently added. If you have changes in your working directory that have yet to be committed, the HEAD will be behind them. Once you commit them, the HEAD will update to reflect the new commit.</dd>
</dl>


title: GPU_versus_CPU
last_modified: 2024-10-18 18:55:56
body:
# GPU versus CPU

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

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

- Harnessing the GPU requires specialised programming approaches.

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

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


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

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

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

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

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

The symbol for ground in circuit diagrams:

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


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: 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: What_is_memory
last_modified: 2024-10-18 18:55:56
body:
# What is memory ?

> A CPU is just an operator on memory. It reads its instructions and data from
> the memory and writes back out to the memory. (Ward 2021)

When a [CPU](CPU_architecture.md) executes a program, it needs a place to store
the program's **instructions** and **related data**. This is the role of memory.

The data that comprises a program is a series of bits. The basic unit of memory
storage is a **memory cell**: a circuit that can store a single bit.

[RAM](./DRAM_and_SRAM_memory.md) memory is _volatile_ : the memory is only
retained whilst the computer has a power supply and is wiped when the computer
is rebooted. This contrasts with the memory of the harddisk which is
non-volatile and is retained after a reboot.

Programs that are executing are loaded into memory because the chips that
comprise memory can read and store data much faster than the harddisk. It would
be possible to run a program from the harddisk but it would be 500 - 1000 times
slower than memory.


title: Database_indices
last_modified: 2024-10-18 18:55:56
body:
# database indices

A database index is best understood by analogy with physical data. Imagine
needing to find information about a specific topic in a very long book. Without
being able to turn to the back and consult the index we would need to scan every
page of the book looking for information on the topic we are seeking.

Similarly with a database if we are looking for a specific value in a very long
table it can be computationally costly and slow to iterate through every row
looking for a specific value (this is known as a "full table scan").

For this reason we use indexes. In a database an index is like a shortcut or
quick reference guide. However instead of page numbers as in the book analogy,
the database index has pointers to the actual location of the rows in the table.
If an index is applied to a given column, when an element in that column is
requested, the database will not scan every row, instead it will go straight to
the element using the index.

Say we are looking for the surname "Bishop" in a table that consists of
`lastName` and `phoneNumber` . If we apply an index to the `lastName` column,
the database will have a direct reference to the row that "Bishop" is in and
thus can return the `phoneNumber` value more quickly.

Although the programmer can explicitly create indexes, the way the database
utilises it is largely abstracted from view. Most RDMSs will automatically
decide which index to use for query optimisation however the programmer can also
use hints to force the use of a specific index.

The main thing is to think about data points that would benefit from an index,
for instance for the most heavily used queries or those that run against the
longest tables.

While indices make lookups more performant, they should be used judiciously as
they increase the storage space the database requires and can incresae the
execution time of queries that insert or update records. (This makes sense, as
any update to a row, requires a reindexation of the table.)

The best use case for an index is for data retrieval that utilises `WHERE`
clauses.

## SQL syntax

```sql
CREATE INDEX index_name ON table_name(column_name)
```


title: MongoDB_connection
last_modified: 2024-11-04 14:26:19
body:
# MongoDB connection

```js
const mongoose = require("mongoose");

mongoose..connect("mongodb://127.0.0.1/[databse_name]");

const courseSchema = new mongoose.Schema({
    name: String,
    author: String,
    tags: [String],
    data: Date,
    isPublished: Boolean,
    price: Number
})

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

async function getCourses(){
    return await Course
        .find({isPublished: true, tags: "backend"})
        .sort({name: 1})
        .select({name: 1, author: 1});
}
```


title: Python_package_management
last_modified: 2024-10-18 18:55:56
body:
# Package management

- You can use `conda` (the package manager that comes with `anaconda`). This
  makes it easier to work with conflicting package libraries (a bit like a
  package lock).

- The alternative is the native `pip` but you have to create virtual
  environments (`venv`) to manage packages at different versions.

To make use of virtual environments in `pip` you have to create the virtual
environment before installing anything:

```
python3 -m venv venv
```

This will create a `venv` directory in your project that will manage the
handling of modules.

> This is especially important in ArchLinux since without a virtual environment
> it will ask you to install packages with `pacman` which is annoying for local
> packages.

You then activate the environment with:

```
source venv3/bin/activate
```

Now you can install packages:

```
pip [library_name]
```

## requirements.txt

The `requirements.txt` file is similar to the `package.json` in Node projects.

Each line of the `requirements.txt`` file specifies a package that your project
depends on and, optionally, the desired version of that package. When you share
your project with others or move it between different environments (e.g., from
development to production), this file makes it easy to set up your environment
with the right dependencies.

### Example

```
Flask==1.1.2
requests>=2.24.0
numpy~=1.19.2
pandas
```

### Generate requirements file

```
pip freeze > requirements.txt

```

## Pypy

- pypi.org is package registry like NPM


title: Function_overloads
last_modified: 2024-10-19 09:57:01
body:
# Function overloads

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

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

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

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

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

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

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

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

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


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: 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: Docker_compose_and_Dockerfile_difference
last_modified: 2024-10-18 18:55:56
body:
# Docker compose and Dockerfile difference

### Key Differences

- **Scope**: A `Dockerfile` is for building a single Docker image, while
  `docker-compose.yml` is for orchestrating multiple containers (that might be
  built from `Dockerfiles`) to work together as a unified application.
- **Use Case**: A `Dockerfile` is necessary when you need to create a custom
  image. In contrast, `docker-compose.yml` is used when you want to deploy and
  manage an application that consists of multiple containers.
- **Command**: You build an image from a `Dockerfile` using `docker build`. You
  start the application defined by `docker-compose.yml` using
  `docker-compose up`.

### Dockerfile

- **Purpose**: A `Dockerfile` is a text document that contains all the commands
  a user could call on the command line to assemble an image. It automates the
  process of creating Docker images by specifying a sequence of steps and
  configurations such as setting environment variables, installing software, and
  copying files from the local directory into the image.
- **Functionality**: It is essentially a blueprint for building Docker images.
  You define the base image, software packages, scripts, files, environment
  variables, and other components you need in your Docker image.
- **Usage**: You use a `Dockerfile` when you need to create a custom Docker
  image based on your application's specific dependencies and runtime
  environment. After defining your `Dockerfile`, you use the `docker build`
  command to create an image.

  Example structure of a Dockerfile:

  ```Dockerfile
  FROM python:3.8
  WORKDIR /app
  COPY . /app
  RUN pip install -r requirements.txt
  CMD ["python", "./my_script.py"]
  ```

### docker-compose.yml

- **Purpose**: A `docker-compose.yml` file is used to define and run
  multi-container Docker applications. With a single command, you can configure
  all of your application's services, networks, and volumes in this YAML file
  and bring everything up or down.
- **Functionality**: It simplifies the deployment and management of application
  components that are meant to run in separate containers while still needing to
  communicate or work together. For example, a web application might consist of
  a web server, a database, and a caching system, each running in its container.
- **Usage**: `docker-compose` is used for defining and running complex
  applications with Docker. Instead of using lengthy `docker run` commands with
  many flags, you write a `docker-compose.yml` file and use `docker-compose up`
  to start the application.

  Example structure of a docker-compose.yml file:

  ```yaml
  version: "3"
  services:
    web:
      build: .
      ports:
        - "5000:5000"
    db:
      image: postgres
      volumes:
        - ./data:/var/lib/postgresql/data
  ```


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: Creating_a_RESTful_API_Introduction
last_modified: 2024-10-18 18:55:56
body:
# Creating a RESTful API: Introduction

We are going to use Express to create a
[RESTful API](RESTful_APIs.md) in Node.js.

## Request types

Express provides us with methods corresponding to each of the
[HTTP request types](HTTP_request_types.md):

- `.get()`
- `.post()`
- `.put()`
- `.delete()`

## Our data set

> Typically when you create a RESTful API you are going to be returning data
> from a database. For simplicity we are just going simulate this with a simple
> data array so that we can focus on the Express syntax rather than database
> handling. Later we will integrate this with a
> [MongoDB database](05_%20Integrating_the_database.md).

We will mainly work with the following array of objects:

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

## Creating an Express instance

We first create an instance of Express within `index.js`. This will be the main
coordinating file and we will aim to minimise the amount of business logic we
have in this file. It should really just be for initialization and managing
[middleware](Middleware_in_NodeJS.md).

```js
const express = require("express");
const app = express();
```

## Routing

We are going to receive all our HTTP requests at the path `/api/courses` and we
will place all code related to this route in a dedicated Node module
(`routes/courses.js`) rather than within `index.js` which should remain as
uncluttered as possible.

First we need to declare this in `index.js`:

```js
const courses = require("./routes/courses");
app.use("/api/courses", courses);
```

Then within `courses.js` we instantiate an express router that `app.js` will
route to:

```js
const router = express.Router();
```

Our REST requests will all follow the following pattern:

```js
router.get("/", (req, res) => {
  res.send(courses);
});
```

We target the `Router` instance we created and all paths will be expressed as
`/` since the app will route by default to `/courses/api` from `index.js`

## Create listener

With the routing established and the Express instance created we can now listen
for requests:

```js
app.listen(3000, () => console.log("Listening on port 30000..."));
```

## REST endpoints

We can now proceed to set up our RESTful endpoints:

[GET requests](1_GET.md)

[POST requests](2_POST.md)

[PUT requests](3_PUT.md)

[DELETE requests](4_DELETE.md)


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

We use React's Context API to enable us to easily share stateful data between
all levels of an application without having to use repetitious 'prop-drilling'
at a component level.

This works best with data that is global by definition, such as theme parameters
or access authorisation. Context should not be used when standard and proximate
compositional methods and prop-passing are sufficient.

## Providers and consumers

The Context API preceded the introduction of hooks in React 16.8 and it still
underpins context management in hooks, although the syntax is simplified.

The process is as follows:

- Initiate a context
- Create a parent `Provider` component that owns the data that will be passed
  down
- Create child `Consumer` components that receive the data of the parent.

We will demonstrate using a set of styles as the context that we want to pass
around our app.

```jsx
const style = {
  border: "2px solid dodgerblue",
  background: "lightblue",
  color: "dodgerblue",
};
```

Le's initiate a context:

```jsx
const StyleContext = React.createContext();
```

Now that we have our data and have initialized a context, we can apply it to our
app components:

```jsx
<StyleContext.Provider value={style}>...</StyleContext.Provider>
```

This is the parent element of the context environment. This stores the
contextual data as a prop. Next we need to make a component that acts as the
consumer of this data. Let's create this component and call it `Child` for
simplicity:

```jsx
const Child = () => {
  return (
    <StyleContext.Consumer>
      {(value) => (
        <div style={value}>
          <p>
            I'm a <b>child</b>. I <b>consume</b> of the data my parent provides.
          </p>
        </div>
      )}
    </StyleContext.Consumer>
  );
};
```

The contents of this component are wrapped in tags that reference the specific
context (`StyleContext`) and the role that the component plays: `Consumer`.

We are taking the `value` prop owned by `StyleContext.Provider` and passing it
to the wrapping component of `Child` as a `style` tag. This means that `Child`
will display these styles.

The final step is just to insert our `<Child />` components beneath the parent
component, e.g.:

```jsx
<StyleContext.Provider value={style}>
  <Child />
  <Child />
</StyleContext.Provider>
```

## `useContext` hook

The hook removes the need to explicitly declare `Provider` and `Consumer`
components; their role becomes more implicit.

To demonstrate let's use a different example. We are going to have a single
integer as the data context, `42`.

First invoke and intialize the Context API, just as before:

```jsx
const NumberContext = React.createContext(42);
```

Next, we just need to create our provider component. We don't have to worry
about a consumer since this is handled implicitly by the invocation of the hook

```jsx
const Context = () => {
  const data = useContext(NumberContext);
  return <Typography>{data}</Typography>;
};
```

Then, in our code we just insert the `Context` component:

```jsx
<Context>...</Context>
```

## Updating context state

In the examples above we have only been consuming state that is owned by the
provider however in most scenarios you will also want to update the state from a
consumer. This is best achieved by combining `useContext` with a reducer and is
detailed in [Application state management](Application_state_management_with_React_hooks.md).


title: React_Components
last_modified: 2024-10-18 18:55:56
body:
# Components

We write functional components in the manner of a normal
[function](Functions.md) that take
a props argument and return a JSX element.

```jsx
interface IProps = {
  message: string;
};
const App = ({message}: IProps): JSX.Element => <div>{message}</div>;
```

## `React.FunctionComponent` / `React.FC`

It used to be the case that you would type a function component like so:

```jsx
const App: React.FunctionComponent<{ message: string }> = ({ message }) => (
  <div>{message}</div>
);
```

This is now discouraged as it means the component can always accept children,
even when you do not want this to be the case. There are several other minor
drawerbacks but it's generally best to leave out the declaration.


title: Tuples_in_Python
last_modified: 2024-10-18 18:55:56
body:
# Tuples in Python

TODO: Exapand tuple notes - give more use cases

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

Tuples have the following properties:

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

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

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

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

## Basic usage

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

# 5
"""
```

## Slicing

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

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

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

## Looping

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

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

## Useful methods and predicates

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

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

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

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

# orange is in the Tuple
```

## Nest tuples

```python

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

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

```

// TODO: How to flatten a tuple?


title: List_comprehension_in_Python
last_modified: 2024-10-18 18:55:56
body:
# List comprehension

List comprehension is a syntactic shorthand for applying a function to each
element in a list without explicitly using loop syntax.

Since its introduction to the language, the same functionality has become
achievable by using functional methods like
[`map` and `filter`](Map_and_filter_in_Python.md),
utilising [lambdas](Lambdas_in_Python.md)
however list comprehension is often more straightforward and easier to read.

## Syntax

Here is a basic example which applies `+ 1` to each integer in a list:

```python
values = [1, 2, 4, 6, 8, 9]

new_values = [i + 1 for i in values]
print('new_values', new_values)
# new_values [2, 3, 5, 7, 9, 10]
```

The basic syntax is as follows:

```
new_list  = [expression for each member in an iterable]
```

- The _expression_ can be the member itself, a call to a method, or any other
  valid expression that returns a value. In the example above, the expression
  `i + i` adds one to each member value.

- The _member_ is the object or value in the list or iterable. In the example
  above, the member value is _i_.

- The _iterable_ is a list, set, dictionary or any other object that can return
  its elements one at a time. In the example above, the iterable is each value
  in `values`.

This is a much more condensed way of achieving the same outcome with a
traditional loop:

```py
values = [1, 2, 4, 6, 8, 9]
new_list = []
for i in values:
  values.append(i+1)
```

### Another example

In the following example, we apply list comprehension with a `in range` loop
structure:

```py
new_list = [i * i for i in range(10) ]
print(new_list)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

```

### Adding a condition

We can apply a conditional to a comprehension:

```py
new_list = [item + 1 for item in values if item % 2 == 0]

print('new_list:', new_list)
# new_list: [3, 5, 7, 9]
```

### Filters

By applying a condition (and no execution to each element) we effectively create
a filter:

```py
numbers = [1, 2, 3, 4, 4, 4]
numbers_filtered = [i for i in numbers if i > 2]
print(numbers_filtered)
# [3, 4, 5]
```

For comparison, the same outcome could be achieved with a filter and lambda:

```py
with_filter = list(filter(lambda x: x > 2, numbers))
print(with_filter)
# [3, 4, 5]
```

## Set comprehension

We can also apply comprehension to sets. The syntax is practically identical but
the difference is the resultant data structure will not contain duplicates.

```py
numbers = [1, 2, 3, 4, 4, 4]
unique =  {i for i in numbers}
print(unique)
# {1,2,3,4}
```

## Dictionary comprehension

```py
squares = {i: i * i for i in range(5)}
print(squares)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
```


title: Case_statements_in_Bash
last_modified: 2024-10-18 18:55:56
body:
# Case statements in Bash

Usage against a variable

```sh
animal="dog"
case $animal in
    bird ) echo "Avian";;
    dog|puppy ) echo "Canine";;
    * ) echo "No match";;
esac
```

Usage as part of a function:

```bash
function convertCharToInt {
    case $1 in
        A|X )
            echo 1 ;;
        B|Y )
            echo 2 ;;
        C|Z )
            echo 3 ;;
        *)
            echo ;;
    esac
}
```

Usage:

```bash
declare -i intValue = $(convertCharToInt B)
```


title: Python_modules_and_imports
last_modified: 2024-10-18 18:55:56
body:
# Python modules

## Distinguishing _modules_ from _packages_

Modules and packages in Python are very similar to modules in Node.js: a
combination of several pieces of functionality (often written in a mix of
different syntactic styles) bundled together to be used as a unit.

- A **module** is basically just a script - a single Python file that you can
  import using the `import` statement
- A **package** is a _collection of modules_

A package is a whole directory which has an `__init__.py` at its root which
designates it as a package (this can be empty, but it is necessary to have this
file for the interpreter to understand that the directory is a package).

## Creating modules

### Naming modules

The name of a module is the name of its file, e.g. `utils.py` defines the module
`utils`. When we import a module we do not need to specify the extension.

### Example module

Here is an example of a module.

```py
"""utils module"""

print('Hello I am the utils module')


def printer(some_object):
    print('printer')
    print(some_object)
    print('done')


class Shape:

    def __init__(self, id):
        self.id = id

    def __str__(self):
        return 'Shape - ' + self.id


default_shape = Shape('square')


def _special_function():
    print('Special function')

```

## Importing modules

> Whether we are importing our own local module, a third-party module or a
> built-in module native to Python, the syntax is the same.

- If the module is in the same directory as the file you wish to import it into,
  you can just directly import with:
  ```py
  import utils
  utils.specialFunction
  shape = utils.Shape('circle')
  ```
- If the module you want to import is in a subdirectory, you will need to make
  that subdirectory a package in order to be able to import from it. This means
  adding an `__init__.py`. Then you can use the following import syntax:
  ```py
  from subdir import utils
  ...
  ```

### Different import syntax

- If you don't want to have to prepend the imported module's name when using it
  (e.g. `utils.specialFunction`). You can import like so:

  ```py
  from utils import *
  specialFunction()
  ```

- By default an import makes everything in the module available, however you can
  choose to import only a subset. E.g.:
  ```py
  from utils import Shape
  #  or with an alias:
  from utils import Shape as lorem
  ```

## `__main__`

The `__main__` attribute is important in Python.

### Usage in scripts/modules

When you run a script (module) Python assigns the string `__main__` to the
`__name__` attribute to the script that is being executed.

If you run the script as an import into another script, the `__name__` attribute
of the imported module is set to the module name, not `__main__`. This is why,
when we import we say `import [module_name]` and not `import __main__`.

When used with a module, `__main__` indicates code that should be run straight
away when the given script file is run. You won't always want to execute
everything in the module file on execution so you can put this stuff outside of
`__main__`

Typically you will only want immediate execution to happen when **the module is
the entrypoint** to the program.

We indicate this with `__main__` as follows:

```py
if __name__ == "__main__":
    print("This runs only if the module is the main module")
```

### Usage in packages

The only real difference with packages is that you can also define a
`__main__.py` file in the root of the package directory.

This functions in the same manner as `__main__` in an individual module however
this time it is not the entry point for a single script but the entrypoint of
the package as a whole.

> When the package is run directly as a script, the code in `__main__.py` is
> executed, just like it would be if it were a standalone script. This allows a
> package to be run as a script, as well as letting it contain modules that can
> be imported separately.

You can of course still have individual entrypoints for the modules that
comprise the package.

### Running the package as a package

If you want to run the whole package at once, rather than individually importing
the modules that belong to it. You can run the following from the command-line:

```
python -m package_name
```

With this, Python will try to locate the `__main__.py` file and execute it as
though it were a standalone script.

### Using a `main()` function

To ensure that certain code runs on initialisation we can define a special
`main` function or class method that runs when the module or package is
loaded.We would put the main set-up logic in `main` and coordinate the execution
of the module or package through it.

```py
def main():
    printer(default_shape)
    shape = Shape("circle")
    printer(shape)

if __name__ == "__main__":
    main()
```

### Example

```py
'''palindrome module'''

def is_palindrome(inp):
    inp_list = list(inp)
    inp_list_len = len(inp_list)
    for i in range(inp_list_len):
        if inp_list[i] != inp_list[inp_list_len - 1 - i]:
            return False
    return True


def main():
    palindrome_one = "soros"
    palindrome_two = "torot"
    not_palindrome = "chair"

    print(is_palindrome(palindrome_one))
    print(is_palindrome(palindrome_two))
    print(is_palindrome(not_palindrome))


if __name__ == '__main__':
    main()
```


title: Williams_Tube_memory
last_modified: 2024-10-18 18:55:56
body:
# Williams Tube memory

- One of the early attempts to create RAM in computers based on the EDVAC
  stored-program architecture.

- Worked by displaying a grid of dots on a cathode-ray tube ("screen")

- Due to the way CRTs work, this creates a small charge of static electricity
  over each dot. The charge at the location of each of the dots is read by a
  thin metal sheet just in front of the display.

- Each dot position could be written to and read from and the pattern was
  constantly refreshed as the dots would fade over time.

![A Williams CRT tube](../img/williams-tube.jpg)

![Memory dot pattern from a Williams Tube](../img/williams-tube-dots.jpg)


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: Joins_in_SQL
last_modified: 2024-10-18 18:55:56
body:
# Joins in SQL

<!-- Once a relationship has been created using primary and foreign keys (as detailed in the previous section), you are able to combine and integrate data from the different tables. This is known as performing **joins.** -->

We perform joins in SQL when we combine and integrate data from multiple tables.
In order for a join to be possible, **the tables you want to join must have rows
that share the same fields**.

The output will be represented as a table but it is virtual, not an actual
table. (If you wish to actually create a table or a view off the back of a join
operation you should combine the join with the
[`CREATE TABLE`](Create_an_SQL_table.md) command etc.)

## Inner joins

> An inner join combines rows that have matching values in two or more tables.
> This join is based on the existence of a common field between the tables and
> is used to retrieve data that appears in both tables.

The following is a good scenario for creating an inner join:

> We want to create a list of the name of all computers that have been sold and
> when they were sold.

We will be working from the following tables.

_The `sales` table_:

| saleId | modelId | saleDate   | employeeId | price   |
| ------ | ------- | ---------- | ---------- | ------- |
| 1      | 44      | 2020-07-27 | tbishop    | 40.99   |
| 2      | 22      | 2021-02-05 | tbishop    | 986.99  |
| 3      | 14      | 2022-11-16 | tgnomay    | 1248.99 |

_The `model` table_:

| modelId | modelName            |
| ------- | -------------------- |
| 44      | Raspberry Pi Model A |
| 22      | Apple MacBook Air    |
| 14      | Lenovo ThinkPad P15  |

The data points we need are not provided in a single table. `sales` gives us the
`saleDate` but not the `modelName`. In order to meet our requirements we must
perform a join that combines `modelName` from the `model` table and `saleDate`
from the `sale` table to give us a table like the following:

| modelName            | sale_date  |
| -------------------- | ---------- |
| Raspberry Pi Model A | 2015-02-01 |
| Apple Macbook Air    | 2018-11-01 |
| Lenovo ThinkPad P15  | 2018-11-01 |

We can do this by utilising the field they have in common: `modelId`.

### Implementation

The general syntax for creating an inner join from two tables that share a
common field is as follows:

```sql
SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.columnName = table2.columnName
```

So basically, look at `table1` and `table2` return the rows where the fields for
`table1` and `table2` match, where these fields are the ones specified in the
`SELECT`.

Applied to our example:

```sql
SELECT model.modelName, sales.saleDate -- Name the fields
FROM model -- Specify the first table
INNER JOIN sales -- Specify the second table
ON model.model_id = sales.model_id; -- Specify the match criteria
```

> It is important to note that the match must _be_ a match. Both tables must
> have a row with the same value for `modelId` for the join to be applied. If
> there is a `modelId` of X in `sales` but no corresponding X for `modelId` in
> `model`, nothing will be returned for that row.

We can represent the logical relationship that obtains between the `sales` and
`model` tables as follows:

![](/img/sql-inner-join.png)

## Outer joins

Outer joins are joins that return matched values **and unmatched** values from
either or both tables.

To explain outer joins we need to expand our data set:

_The `sales` table_:

| saleId | modelId | saleDate   | employeeId | price   |
| ------ | ------- | ---------- | ---------- | ------- |
| 1      | 44      | 2020-07-27 | tbishop    | 40.99   |
| 2      | 22      | 2021-02-05 | tbishop    | 986.99  |
| 3      | 14      | 2022-11-16 | tgnomay    | 1248.99 |
| 4      |         | 2020-03-01 | msmelk     | 699.99  |

_The `model` table_:

| modelId | modelName            |
| ------- | -------------------- |
| 44      | Raspberry Pi Model A |
| 22      | Apple MacBook Air    |
| 14      | Lenovo ThinkPad P15  |
| 66      | Google Chromebook    |

> We will assume that `sales` is our left table and `model` our right.

### Left outer join

A left outer join returns all records from the left or first table and the
matching records from the right or second table. If there are not matches on the
right then only the left data is returned.

In the context of our dataset, in the _sales_ database we have a row where there
is a sale with no corresponding `modelId`. If we performed a left outer join
this would give us the following table in return:

| modelName            | sale_date  |
| -------------------- | ---------- |
| Raspberry Pi Model A | 2015-02-01 |
| Apple Macbook Air    | 2018-11-01 |
| Lenovo ThinkPad P15  | 2018-11-01 |
|                      | 2020-03-01 |

The logical relationship sustained between `sales` and `model` by a left inner
join is represented in the following diagram:

![](/img/sql-left-outer-join.png)

#### Implementation

The schematic form:

```sql
SELECT column_name(s)
FROM table1
LEFT JOIN table2
ON table1.column_name = table2.column_name;
```

Applied to our data set:

```sql
SELECT model.modelName, sales.saleDate
FROM sales.saleDate
LEFT JOIN model
ON sales.modelId = model.modelId
```

### Right outer join

This is the inverse of the right outer join. A right outer join returns all
records from the right or second table and the matching records from the left or
first table. If there are not matches on the left then only the right data is
returned.

In the context of our dataset, in the _model_ database we have a row where there
is a `modelId` without a corresponding entry in the `sales` table. If we
performed a right outer join this would give us the following table in return:

| modelName            | sale_date  |
| -------------------- | ---------- |
| Raspberry Pi Model A | 2015-02-01 |
| Apple Macbook Air    | 2018-11-01 |
| Lenovo ThinkPad P15  | 2018-11-01 |
| Google Chromebook    |            |

The logical relationship sustained between `sales` and `model` by a right inner
join is represented in the following diagram:

![](/img/sql-right-outer-join.png)

#### Implementation

The schematic form:

```sql
SELECT column_name(s)
FROM table1
RIGHT JOIN table2
ON table1.column_name = table2.column_name;
```

Applied to our data set:

```sql
SELECT model.modelName, sales.saleDate
FROM sales.saleDate
RIGHT JOIN model
ON sales.modelId = model.modelId
```

### Full outer join

The full outer join returns all rows when there is a match in either the left or
the right table. So, for table A and table B the full join returns all rows in A
and all rows in B including those where there is a match between A and B. This
is the broadest type of join and can often result in large data sets.

In the context of our dataset it would result in the following table being
generated:

| modelName            | sale_date  |
| -------------------- | ---------- |
| Raspberry Pi Model A | 2015-02-01 |
| Apple Macbook Air    | 2018-11-01 |
| Lenovo ThinkPad P15  | 2018-11-01 |
|                      | 2020-03-01 |
| Google Chromebook    |            |

Represented by the following diagram:

![](/img/sql-full-outer-join.png)

#### Implementation

The schematic form:

```sql
SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2
ON table1.column_name = table2.column_name;
```

Applied to our data set:

```sql
SELECT model.modelName, sales.saleDate
FROM sales.saleDate
FULL OUTER JOIN model
ON sales.modelId = model.modelId
```

This type of join is used when you want to discern when there is _not_ a match
between two fields across tables. For example: imagine that you wanted a list of
computers that had never been sold. In this case, you would be interested in
rows where there is a `model_id` without a corresponding `sales_id` .

In SQL this would be achieved with:

```sql
SELECT model.name, sales.sale_date
FROM model
LEFT JOIN sales on model.model_id = sales.model_id;
```

Note that this would return all the model names but where there isn't a sale
data, `NULL` would be returned. This is an **important distinction :** the outer
join method doesn't just return the rows with a `NULL` value for `sale_date` as
we might expect. It returns all models along with those that have not been sold.
This is because it is oriented to the "left" table, equivalent to the table in
the SQL that we cited first with the `on` keyword.

> A left outer join returns all the records from the left (model) table and
> those that match in the right (sales) table. Where there are no matching
> records in the right (sales) table, a `NULL` value is returned.

A **right outer join**, often referred to as a right join, is the opposite of a
left outer; it returns all the records from the right table and those that match
in the left table. In our scenario this would be all the models that had a
`sale_date` including models that didn't have a `sale_date` , i.e which returned
`NULL`

Finally, a **full outer join** returns all the records from both tables, and
where a record cannot be matched, a NULL value is returned. So this would mean
there could be `NULL`s in both fields of the returned rows.

We can combine multiple types of join in the same SQL query:

```sql
SELECT model.name, sales.sale_date, manufacturer.url
FROM model
LEFT JOIN sales on model.model_id = sales.model_id
INNER JOIN manufacturer on model.manufacturer_id = manufacturer.manufacturer_id;
```


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: Killing_processes
last_modified: 2024-10-18 19:16:00
body:
# Killing processes

## Kill a process running on a port

For example a local server.

```bash
sudo lsof -t -i:8000
# List files and proces ID (-t) and internet connections (-i) on port number

$ 7890
# Gives you process id

sudo kill -9 7890
# Kill the process that is running there
```

## Alterntive

```sh
ss -ltnp | grep ':8000'
```

Returns:

```
LISTEN 0      511            [::1]:8000          [::]:*    users:(("node",pid=10493,fd=34))
```

Kill with:

```
kill 10493
```


title: Text_manipulation_in_Bash
last_modified: 2024-10-18 18:55:56
body:
# Text manipulation

## Sorting strings: `sort`

If you have a `.txt` file containing text strings, each on a new line you can
use the sort function to quickly put them in alphabetical order:

```bash
sort file.txt
```

Note that this will not save the sort, it only presents it as a standard output.
To save the sort you need to direct the sort to a file in the standard way:

```bash
sort file.txt > output.txt
```

### Options

- `-r`
  - reverse sort
- `c`
  - check if file is already sorted. If not, it will highlight the strings which
    are not sorted

## Find and replace: `sed`

The `sed` programme can be used to implement find and replace procedures. In
`sed`, find and replace are covered by the substitution option: `/s` :

```bash
sed ‘s/word/replacement word/’ file.txt
```

This however will only change the first instance of word to be replaced, in
order to apply to every instance you need to add the global option: `-g` .

As sed is a stream editor, any changes you make using it, will only occur within
the standard output , they will not be saved to file. In order to save to file
you need to specify a new file output (using `> output.txt`) in addition to the
original file. This hasthe benefit of leaving the original file untouched whilst
ensuring the desired outcome is stored permanently.

Alternatively, you can use the `-i` option which will make the changes take
place in the source file as well as in standard input.

Note that this will overwrite the original version of the file and it cannot be
regained. If this is an issue then it is recommended to include a backup command
in the overall argument like so:

```bash
sed -i.bak ‘s/word/replacement word/’ file.txt
```

This will create the file `file.txt.bak` in the directory you are working within
which is the original file before the replacement was carried out.

### Remove duplicates

We can use the `sort -u` command can be used to remove duplicates:

```bash
sort -u file.txt
```

It is important to sort before attempting to remove duplicates since the `-u`
flag works on the basis of the strings being adjacent.

## Split a large file into multiple smaller files: `split`

Suppose you have a file containing 1000 lines. You want to break the file up
into five separate files, each containing two hundred lines. You can use `split`
to accomplish this, like so:

```bash
split -l 200 big-file.txt new-files
```

`split` will categorise the resulting five files as follows:

- new-file-aa,
- new-file-ab
- new-file-ac,
- newfile-ad,
- new-file-ae.

If you would rather have numeric suffixes, use the option `-d` . You can also
split a file by its number of bytes, using the option `-b` and specifying a
constituent file size.

## Merge multiple files into one with `cat`

We can use `cat` read multiple files at once and then append a redirect to save
them to a file:

```bash
cat file_a.txt file_b.txt file_c.txt > merged-file.txt
```

## Count lines, words, etc: `wc`

To count words:

```bash
wc file.txt
```

When we use the command three numbers are outputted, in order: lines, words,
bytes.

You can use modifiers to get just one of the numbers: `-l`, `-w` , `-b` .


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

`any` is a TS-specific type that we can think of it as a higher level parent to
all the other types that exist in TypeScript.

It means in effect that either no type declaration has been asserted or that the
TS compiler cannot infer the type that you mean. Because `any` does not have a
data type it is equivalent to all the individual scalar and reference types
combined. In TS this kind of type is called a **supertype**, and specific types
that actually correspond to a scalar or reference type are known as
**subtypes**. `any`is the supertype of all types and `string` for example is a
subtype of `any`.

> Every value of `string` can be assigned to its supertype`any` but not every
> value of `any` can be assigned to its subtype `string`

You can declare `any` as a type if you wish however it is discouraged because it
effectively undermines the whole purpose of TS. Doing so is basically the same
thing as declaring a value in normal JS - there is no designation at left hand
assignation of which type the data belongs to.

> `any` reflects JavaScript's overarching flexibility; you can see it as a
> backdoor to a world where you want neither tooling nor type safety.

`any` means you can escape errors during development. If you are using custom
types/interfaces and you keep getting an annoying error saying that property X
doesn't exist on type,, `any` will allow you to overcome it until you go back
later and refine.


title: API_Gateway
last_modified: 2024-10-18 18:55:56
body:
# AWS API Gateway

We can use Gateway as the front-door to our application. It will create an
[HTTP](HTTP_request_types.md) endpoint that you can call from a
client. In response to a client request you can then call a
[lambda function](Lambda_handler_function.md) that
executes a backend process.

![](/img/gateway-services.png)

See
[using API Gateway as Lambda trigger](Practical_walkthrough_Lambda_creation_within_AWS.md)
for a basic example of usage.


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

## Remove property

Remove property from a JSON array of objects:

```json
{
  "member_inputs": [
    {
      "name": "Thomas",
      "input_type": "select"
    },
    {
      "name": "Martha",
      "input_type": "text"
    }
  ]
}
```

```sh
jq '.member_details |= map(del(.input_type))' memberDetails.json
```

> Add '-i' to modify the source file directly (in-place)

> Multiple properties can be deleted simply by chaining,
> e.g.`(del(.input_type, .another_property))`


title: Mapped_types_in_TS
last_modified: 2024-10-18 18:55:56
body:
# Mapped types in TypeScript

A mapped type is a way to create new types based on existing ones by
transforming their properties

As it works on a single type and typically narrows or limits the properties it
works in a way that is opposite to a [union]() or [intersection]() type.

> Mapped types are an example of when `type` is preferable to `interface`, since
> you cannot generate mapped types as interfaces, although you may generate a
> mapped type from an interface.

For the demonstrations we will use the following type as the example that we
will map from:

```ts
type Person = {
  name: string;
  age: number;
  city: string;
  country: string;
};
```

## Main varieties of mapped types

### Read only

Creates a type with all properties of the given type set to `readonly`:

```ts
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type ReadonlyPerson = Readonly<Person>;
```

This is equivalent to:

```ts
type ReadonlyPerson = {
  readonly name: string;
  readonly age: number;
  readonly city: string;
  readonly country: string;
};
```

### Partial

Creates a type with all properties of the given type set to optional.

```ts
type Partial<T> = {
  [P in keyof T]?: T[P];
};

type PartialPerson = Partial<Person>;
```

This is equivalent to:

```ts
type PartialPerson = {
  name?: string;
  age?: number;
  city?: string;
  country?: string;
};
```

### Pick

This is useful when you want to create a new type based on a subset of
properties from an existing type.

```ts
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

type PersonNameAndAge = Pick<Person, "name" | "age">;
```

This is equivalent to:

```ts
type PersonNameAndAge = {
  name: string;
  age: number;
};
```

### Record

Creates a type with keys of the given type and values of the specified type. It
is a way of shoehorning keys from an existing type with new values.

Basic syntax:

```ts
Record<Keys, ValueType>;
```

Example:

```ts
type UserRole = "admin" | "user" | "guest";

// Create a Record type to store user role descriptions
type UserRoleDescriptions = Record<UserRole, string>;

// Create an object that implements the UserRoleDescriptions type
const roleDescriptions: UserRoleDescriptions = {
  admin: "Has full access to the system",
  user: "Can access limited resources",
  guest: "Can view public resources only",
};
```

Here is a simpler example

```ts
type Person = {
  name: string;
  nationality: string;
};

type PersonInstance = Record<string, Person>;
const thomasInstance: PersonInstance = {
  thomas: {
    name: "Thomas",
    nationality: "British",
  },
};
```

### Exclude

Creates a type by excluding specific properties from the given type.

```ts
type Exclude<T, U> = T extends U ? never : T;

type KeysWithoutAge = Exclude<keyof Person, "age">;
```

This is equivalent to:

```ts
type KeysWithoutAge = "name" | "city" | "country";
```

### Extract

Creates a type by extracting specific properties from the given type. Basically
the opposite operation to Exclude.

```ts
type Extract<T, U> = T extends U ? T : never;

type NameKey = Extract<keyof Person, "name">;
```

This is equivalent to:

```ts
type NameKey = "name";
```

### Non-nullable

Creates a type by removing null and undefined from the given type.

```ts
type NonNullable<T> = T extends null | undefined ? never : T;

type NullablePerson = {
  name: string | null;
  age: number | null;
  city: string | null;
  country: string | null;
};

type NonNullablePerson = NonNullable<NullablePerson>;
```

Equivalent to:

```ts
type NonNullablePerson = {
  name: string;
  age: number;
  city: string;
  country: string;
};
```


title: Using_SQLite_with_Python
last_modified: 2024-11-13 18:12:58
body:
# Using SQLite with Python

SQLite is part of the core library, you do not require a separate package

```py
import sqlite3
```

## Connect to database

Connect to an existing SQLite `.db` file or create a new database with the same
command:

```py
connection = sqlite3.connect("my_database.db")
```

## Disconnect from database

```python
connection.close()
```

## Querying

`connect` exposes a `cursor` entity. This is used for executing changes against
the database file.

### Example: creating a table

```python
CREATE_TAGS_TABLE = """
CREATE TABLE IF NOT EXISTS tags (
    name TEXT PRIMARY KEY
)
"""

cursor = connection.cursor()
cursor.execute(CREATE_TAGS_TABLE)
cursor.commit()

# Confirm changes

cursor.execute("SELECT name from sqlite_master WHERE type='table'")
print(cursor.fetchall())
# (tags)
```

## Prepared statements and parameterised queries

### Parameterised queries

```py
username = "thomas"
password = "123"
cursor.execute("SELECT * FROM users WHERE username = ? AND password = ?",
            (username, password))
```

Alternatively, pass the [tuple](Tuples_in_Python.md) directly:

```python
login_data = ("thomas", "123")
cursor.execute("SELECT * FROM users WHERE username = ? AND password = ?",
               login_data)

```

### Prepared statements

> With prepared statements you have to pass in the parameters

```python
user_data = {
    "username": "thomas",
    "email": "thomas@email.com"
}

cursor.execute("INSERT INTO users (username, email) VALUES (:username, :email)")
```


title: Corresponding_material_and_biconditional
last_modified: 2024-10-18 18:55:56
body:
# Corresponding material conditional to show validity

To demonstrate _truth-functional validity_ we have to construct a truth-table
which contains each of the premises and the conclusion and then review each row
to see if there is an assignment where both the premises and the conclusion are
true.

A simpler way to get the same result is to invoke the corresponding material
conditional. Here we concatenate the premises using conjunction and then join
them to the conclusion using the material conditional, which then becomes the
main connective. We then populate the truth table for this compound proposition.
If it is logically true, the argument is valid.

### Demonstration

We will demonstrate with the following set:

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

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

| $P$ | $Q$ | $(P \leftrightarrow Q) \land (P \lor Q)) \leftrightarrow (P \land Q)$ |
| --- | --- | --------------------------------------------------------------------- |
| T   | T   | T                                                                     |
| T   | F   | T                                                                     |
| F   | T   | T                                                                     |
| F   | F   | T                                                                     |

We see above that the main connective, the material conditional returns true for
every truth-functional assignment. In other words it is logically true.
Consequently the argument is valid

## Corresponding material biconditional

We can use the corresponding material biconditional as a shorthand for
demonstrating logical equivalence between two propositions.

For two putatively equivalent propositions $P$ and $Q$, $P$ and $Q$ are
logically equivalent if the compound proposition $P \leftrightarrow Q$ is
logically true.


title: Python_data_types
last_modified: 2024-10-18 18:55:56
body:
# Python data-types

- Python is dynamically typed rather than untyped. It updates the types on the
  fly as you are writing your code.
- Type-hints in the editor like `-> str` mean "at the moment it is a string". It
  doesn't mean you can't redefine the value as something else.
- Each data type in Python inherits off of a built-in class, similar to
  prototypes in JS

The core data-types are as follows:

- [str](Strings_in_Python.md)
- bool
- float
- double

We can identify types using the built-in `type()` function:

```python
# Integer number
my_variable = 422
print(my_variable)
print(type(my_variable))
# <class 'int'>

# String type
my_variable = 'Natalia'
print(my_variable)
print(type(my_variable))
# <class 'str'>

# Boolean type
my_variable = True
print(my_variable)
print(type(my_variable))
# <class 'bool'>
```

## Converting data-types

For every data-type there is a corresponding converter method, e.g:

```python
a_string = '32'
print(f'a_string {a_string} is {type(a_string)}')
an_int = int(a_string)

print(f'an_int {a_string} is {type(an_int)}')
a_float = float(a_string)

print(f'a_float {a_string} is {type(a_float)}')
another_string = str(42)

print(f'another_string {a_string} is {type(another_string)}')
```


title: Natural_numbers
last_modified: 2024-10-18 19:17:38
body:
# Natural numbers

## The set of natural numbers

$$ \mathbb{N} = {1, 2, 3, ...} $$

Natural numbers are most simply expressed as **the set of numbers we use for
counting**. The set of natural numbers start at one and continuity to infinity.


title: Negative_exponents
last_modified: 2024-10-18 19:17:49
body:
# Negative exponents

When calculating the exponents of a negative number the answer will always will
be positive:

$$
-5^2 = 25
$$

This confused me but it was because I was thinking of it in terms of
$-5 \cdot 5$ when in fact it is $-5 \cdot -5$ and when two negative numbers are
multiplied the product is always negative.


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: 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: Logical_indeterminacy
last_modified: 2024-10-18 18:55:56
body:
# Logical indeterminacy

The vast majority of propositions in natural and formal logical languages are
**neither
[logically true](Logical_truth_and_falsity.md#logical-truth)
or
[logically false](Logical_truth_and_falsity.md#logical-falsity)**.
This makes sense because propositions of this form are all either tautologies or
contradictions and as such do not express information about the state of events
in the world. We call propositions that are neither logically true or logically
false, **logically indeterminate** propositions.

## Informal definition

A proposition is logically indeterminate if it is neither logically true or
logically false. This is to say: it can be both [consistently](Consistency.md)
asserted and consistently denied.

For example the proposition:

```
It is raining.
```

May be true or false thus it can it both be asserted and denied quite
consistently. It is true if it actually is raining and false if it actually is
not raining. There is no logical contradiction implied by saying it is raining
when it isn't raining, this assertion is simply false. There is a contradiction
in saying that both states obtain. Thus the proposition:

```
It is raining and it is not raining.
```

Cannot be consistently asserted as there is no possibility of the proposition
being true. It is either raining or it isn't raining. Given the law for
conjunction, both conjuncts must be true for the proposition as a whole to be
true. But in the case of this proposition if one conjunct is true, the other
must be false and vice versa, hence it is not possible for the proposition to be
true at all. It can _only_ be false.

Contrariwise the proposition:

```
It is raining or it is not raining.
```

Cannot be consistently denied as there is no possibility of it being false. It
is either raining or not raining. Given the law for disjunction, either disjunct
can be true to make the proposition as a whole true. Given that it is either
raining or not raining in either scenario, the proposition as a whole will be
true. Therefore there is no possibility of it being false, it can _only_ be
true.

## Formal definition

> A proposition P is truth-functionally indeterminate if and only if it is
> neither truth-functionally true or truth-functionally false. should be avoided
> in arguments, they 'prove' everything whi

```
P
```

### Truth-table

| $P$ | $P$ |
| --- | --- |
| T   | T   |
| F   | F   |


title: Law_of_Non_Contradiction
last_modified: 2024-10-18 18:55:56
body:
# Law of Non-Contradiction

> A proposition cannot be true and false at the same time.

$$
\lnot (P \land  \lnot P)
$$


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: Creating_a_RESTful_API_PUT
last_modified: 2024-10-18 18:55:56
body:
# Creating a RESTful API: `PUT` requests

To demonstrate the handling of PUT requests, we will create a handler that
updates an element in the course array, based on its `id` and return the updated
entry:

```js
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));

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

Our request:

```js
const updateCourse = async (courseChange) => {
  try {
    const resp = await axios.put("http://localhost:3000/api/courses/1", {
      name: courseChange.name,
    });
    console.log(resp.data);
  } catch (err) {
    console.error(err);
  }
};

updateCourse({ name: "A new course" });
```

Returns:

```js
{ id: 1, name: 'A new course' }
```


title: Local_AWS_development_with_SAM
last_modified: 2024-10-18 18:55:56
body:
# Local AWS development with SAM

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

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

## Basic set up

Enter your project directory.

First build your SAM application:

```sh
sam build
```

We then run:

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

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

This will be indicated by:

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

If we want to invoke the function directly we use:

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

## Using environment variables

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

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

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

## Create secret

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

## Create local ENV file

> These must be in JSON to work with SAM:

### Local env

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

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

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

### Update `template.yaml`

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

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

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

### Pass in the environment variable at invocation:

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

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

### Create handler within the Lambda itself

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

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

const secretsManager = new AWS.SecretsManager();

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


title: Data_types_in_MySQL
last_modified: 2024-10-18 18:55:56
body:
# Data types in MySQL

An understanding of the differences between data types is important because it
obviously affects the amount of memory the database uses.

## Numeric

### Integer (`INT`)

- No fractional part, fixed number of digits
- `TINYINT`, `BIGINT` etc are all instances of `INT`, the difference is the size
  of the number stored.

### Fixed point (`DECIMAL`)

Contains decimal point. Use when accuracy is more important that representing
very large or very small values. Hence you would use this for monetary amounts.

### Floating point (`FLOAT`)

Contains decimal point. Use when the ability to represent very large and very
small values is more important than precision. Hence you would not use this for
monetary amounts.

## String

### Fixed-length character strings ()

Spaces are stripped in storage but represented with a character set.

An example would be `CHAR(10)` or `CHAR(3)`. Here we set the upper limit but it
must be the case that no string exceeds it. For either of these, if you add a
value that is, e.g, two characters in length, it will add spaces to pad it out
and make up the upper limit.

### Variable-length character strings

As above but allowing for variable-length strings.

A common example is `VARCHAR(255)`. The 255 refers to the maximal character
length, not the byte length. It is the largest number of characters that can be
accommodated by an 8-bit number (byte). We must put `255` as the parameter even
if our character lengths will be below this but where we don't know the minimum
and maximum length.

## Large object storage

### Blob

For storing images, audio and other binary media. Variants: `TINYBLOB`, `BLOB`,
`MEDIUMBLOB` `LONGBLOB`

### Text

Like blob but has character set Variants: `TINYTEXT`, `TEXT`, `MEDIUMTEXT`,
`LONGTEXT`

## Date and time

- `DATE`
- `DATETIME`
- `TIMESTAMP`

## Speciality

### Enumerations

- `ENUM`: a single value from a list
- `SET`: several values from a list

## Storage limits

Each row can have a maximum of 65,535 bytes.


title: Strategies_for_constructing_proofs
last_modified: 2024-10-18 18:55:56
body:
# Strategies for constructing proofs in propositional logic

## General strategy

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

## Goal analysis

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

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

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

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

### Demonstration

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

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

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

So far we have:

![](/img/step1.png)

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

![](/img/step2.png)

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

![](/img/step3.png)

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

![](/img/step4.png)

### A further example

We will seek to prove the following:

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

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

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

![proof.png](proof.png)

## Proving theorems

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

### Demonstration

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

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

![](/img/theoremproof.png)

## A complex theorem proof

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

![](/img/dsfdsfsdfwe.png)

### Walkthrough

**Lines 1-12**

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

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

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

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

**Lines 13-26**

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


title: RESTful_APIs
last_modified: 2024-10-18 18:55:56
body:
# RESTful APIs

## Definition of an API

An application programming interface (API) is a set of definitions and protocols
for building and integrating application software. It can be thought of as a
contract between an information provider and an informational consumer. The API
is a mediator between the clients and the resources they wish to acquire from a
server or database.

## REST

REST stands for **Representational State Transfer**. It is a set of
_architectural constraints_ on the structure of an API rather than a fixed
protocol. It is a particular way of implementing client-server interaction over
HTTP.

When a request is made from a client to resources via RESTful API, the API
transfers a representation of of the state of the resource to the requester or
endpoint. The information is delivered via HTTP. The format can be of several
types (HTML, XML, plaintext, Python, PHP etc) but is generally JSON because of
its broad compatibility with multiple programming languages.

### Key constraints

In order to qualify as RESTful, an API must meet the following constraints:

1. **Uniform interface**: Possess a client-server architecture with request
   manage through HTTP
1. **Client-server decoupling** : The client and server applications must be
   completely independent of one another. The _only_ information the client
   should know about the server is the URI it uses to request the resource, it
   can't interact with the server in any other way. Likewise, the server
   shouldn't modify the client application in any way (contrast for example SSR)
   other than passing the requested data via HTTP.
1. **Statelessness** Server applications should not be able to store any data
   related to a client request. The request alone should contain all the
   information necessary for processing it, without recourse to any specifics of
   the client application. For example, a specification of POST with a certain
   JSON body and header authentication will be all that is provided to the
   server.
1. **Cacheability** Where possible, resources should be cacheable on the client
   or server side. Server responses must contain information about whether
   caching is allowed for the delivered resource (you see this in the headers in
   the DevTools console). The goal here is to improve performance on the client
   side whilst increasing scaleability on the server side.
1. **Layered system architecture** It may be the case that the data flow between
   the client and the server is not direct. For instance the request may be
   funneled through middleware or another program before it is received by the
   server. Similarly there may be several steps before the client receives the
   requested data. Whilst one should not assume a direct correspondence, REST
   APIs need to be designed so that neither the client nor the server can tell
   whether it communicates with the end application or an intermediary.

## Example

A basic example of a REST API would be a series of methods corresponding to the
main [HTTP request types](HTTP_request_types.md).

| HTTP request type | URI                 | Action                      | Body ?                   |
| ----------------- | ------------------- | --------------------------- | ------------------------ |
| GET               | /api/customers      | Retrieve customers as array | No                       |
| GET               | /api/customers/guid | Get a specific customer     | No, data comes from GUID |
| PUT               | /api/customers/guid | Update an existing customer | Yes                      |
| DELETE            | /api/customers/1    | Delete a customer           | No, data comes from GUID |
| POST              | /api/customers      | Create a new customer       | Yes                      |

// Add: PATCH and explain differenct from PUT


title: Call_stack
last_modified: 2024-11-04 14:24:25
body:
# Call-stack

A [stack](Stacks.md) data structure that holds the information of the functions
called within a program that allows transfer of the application control from
these functions to the main process after code inside the functions has been
executed.

## Example

```js
function greet(who) {
  console.log("Hello " + who);
}

greet("Harry");

console.log("Bye");
```

### Breakdown

1. Interpreter receives call to `greet()`
2. Goes to the definition of this function (`function greet(who)...`)
3. Executes the `console.log` within this function
4. Returns to the location that called it (`greet("Harry")`)
5. Finds that there is nothing else to do in this function so moves to next
   function: the `console.log("bye")`
6. Executes
7. Returns to line that called it. Finds nothing else to do. Exits program.

## Related notes

![Stack memory](Stack_memory.md)


title: React_useReducer
last_modified: 2024-10-18 18:55:56
body:
# `useReducer`

The `useReducer` hook is best used in scenarios where you are manipulating state
in a way that is too complex for the trivial [React_useState](React_useState.md) use case.
`useState` is best employed when you are updating a single value or toggling a
boolean. If you are updating the state of an object or more complex data
structure, it is often more efficient to employ `useReducer`.

This makes the code more manageable and also helps with separating state
management from rendering.

## Syntax

```jsx
const [state, dispatch] = useReducer(reducer, initialState);
```

- `initialState`
  - The starting state, typically an object
- `reducer`
  - A pure function that accepts two parameters:
    - The current state
    - An action object
  - The reducer function must update the current state (immutably) and return
    the new state
  - We can think of the reducer as working in the same manner as
    `state`/`setState` in the `useState` hook. The functional role is the same,
    it is just that the reducer offers more than one type of update.

### Example reducer

```js
function reducer(state, action) {
  let newState;
  switch (action.type) {
    case "increase":
      newState = { counter: state.counter + 1 };
      break;
    case "descrease":
      newState = { counter: state.counter - 1 };
      break;
    default:
      throw new Error();
  }
  return newState;
}
```

In this example we are updating an object with the following shape:

```js
{
    counter: 0,
}
```

This would be the `initialState` that we pass to the `useReducer` hook along
with a reference to `reducer` above.

To update the state we would invoke the `dispatch` function which applies one of
the actions defined in the reducer. For example the following dispatch
increments the counter by one:

```js
dispatch({ type: "increase" });
```

To view the updated value:

```js
console.log(state.counter);
```

### Refining the syntax

Because React doesn't mutate state, the reducer doesn't directly modify the
current state in the `state` variable, it creates a new instance of the state
object on each update.

In the reducer example above this is achieved by declaring a variable `newState`
that is updated by each `action` type and then returned. There is a more elegant
way of doing this using spread syntax:

```js
function reducer(state, action) {
  switch (action.type) {
    case "increase":
      return { ...state, counter: state.counter + 1 };
      break;
    case "decrease":
      return { ...state, counter: state.counter - 1 };
      break;
    default:
      throw new Error();
  }
}
```

### Including payloads

In the examples so far, we have updated the the state directly via the action
type however it is also possible to pass data along with the `action.type` as
`action.payload`.

For example:

```js
dispatch(
  {
    type: 'increase_by_payload'
    payload: 3,
  });
```

Then we would update our reducer to handle this case:

```js
function reducer(state, action) {
  switch (action.type) {
    ...
    case 'increase_by_payload':
    return {...state, counter: state.counter + action.payload}
    default:
      throw new Error();
  }
}
```


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: File_permissions_and_execution_in_Bash
last_modified: 2024-10-18 18:55:56
body:
# File permissions and executables

Every Unix file has a set of permissions that determine whether you can read,
write or run (execute) the file.

## Viewing file permissions

In order to see file permissions within the terminal, use the `-l` or `-rfl`
with the `ls` command. Remember this command can be applied at both the
directory and single-file level. For example:

```bash
drwxr-xr-x  7 thomas thomas 4096 Oct  2 19:22 angular-learning-lab
drwxr-xr-x  5 thomas thomas 4096 Oct 17 18:05 code-exercises
drwxr-xr-x  5 thomas thomas 4096 Sep  4 16:15 js-kata
drwxr-xr-x  9 thomas thomas 4096 Sep 26 18:10 sinequanon
drwxr-xr-x 12 thomas thomas 4096 Sep 19 17:41 thomas-bishop
drwxr-xr-x  5 thomas thomas 4096 Sep  4 19:24 ts-kata
```

### What the output means

The first column of the permissions output is known as the file's _mode_. The
sequence from left to right is as follows:

```
-       - - -                 - - -                 - - -
type    user permissions      group permissions     other permissions
```

<dl>
  <dt>type</dt>
  <dd>The file type. A dash just means an ordinary file. `d` means directory </dd>
 
  <dt>user permissions</dt>
  <dd>read, write or execute. A dash means 'nothing': the permissions for that slot in the set have not be assigned</dd>

  <dt>group and other</dt>
  <dd>group is obviously what anyone belonging to the current file's user group can do. Everyone else (outside of the user and the group) is covered by the other permissions, sometimes known as 'world' permissions</dd>
</dl>

## Modifying permissions: `chmod`

We use `chmod` for transferring ownership and file permissions quickly from the
command-line.

### Octal notation

`chmod` uses octal notation. Each numeral refers to a permission set. There are
three numerals. The placement denotes the user group. From left to right this
is:

- user
- group
- everyone else.

If you are working solo and not with group access to files, you can disregard
assigning the other numerals, by putting zeros in as placeholders.

[Permission codes](685254916b2642f189e6316b876e09c9)

### Example

```bash
$ chmod -v 700 dummy.txt
$ ls -l dummy.txt
$ -rwx------  1 thomasbishop  staff  27 13 May 15:42 dummy.txtExample
```

### Useful options

`-v` → verbose: tell the user what `chmod` is doing

`-r` → work recursively, i.e apply the action to directories as well as files

`-f` →silent: suppress most error messages

## Running bash files

In most cases, especially when you are working alone, the most frequent codes
you are going to need are 700 and 600. When shell scripting, you need to make
your scripts executable for them to work, therefore you should always
`chmod 700` when creating a `.sh` file.

Then to invoke the script from the shell you simply enter:

```bash
./your-bash-script.sh
```


title: Recursion
last_modified: 2024-10-18 18:55:56
body:
> A recursive function is a function that calls itself in its definition.

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

## Schema

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

## Why use recursive functions?

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

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

## Base condition

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

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

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

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

recurse();
```

## Demonstrations

### Countdown

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

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

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

countDown(4);
```

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

Each stage in the process is noted below:

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

### Finding factorials

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

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

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

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

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

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

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

let num = 3;

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

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


title: Querying_a_Mongo_collection
last_modified: 2024-10-18 18:55:56
body:
# Query a Mongo collection with Mongoose

We now have the following entries in our `courses` collection:

```js
{
  name: 'Python Course',
  author: 'Terry Ogleton',
  tags: [ 'python', 'backend' ],
  isPublished: true,
  _id: new ObjectId("62f4e2527ac4aa2c30d41d23"),
  date: 2022-08-11T11:04:50.750Z,
  __v: 0
}

{
  name: 'Javascript Course',
  author: 'Tosh Gnomay',
  tags: [ 'js', 'frontend' ],
  isPublished: true,
  _id: new ObjectId("62f4e2527ac4aa2c30d41d24"),
  date: 2022-08-11T11:04:50.750Z,
  __v: 0
}

```

Now we will query the collection. This capability is provided via the Mongoose
schema class we used to create the `Course`
[model](Create_collections_and_documents_with_Mongoose.md#models).
We have the following methods available to use from the schema:

- `find`
- `findById`
- `findByIdAndRemove`
- `findByIdAndUpdate`
- `findOne`
- `findOneAndUpdate`
- `findOneAndRemove`
- ...

The various `find` methods return a value that is promisified.

## Return values with `find`

```js
async function getCourses() {
  const courses = await Course.find();
  console.log(courses);
}
```

## Filter values returned

This will return all the published courses where Tosh Gnomay is the author:

```js
async function getCourses() {
  const courses = await Course.find({
    author: "Tosh Gnomay",
    isPublished: true,
  });
  console.log(courses);
}
```

This time we will filter by the same author but we will add additional
parameters to distinguish:

- only the first ten entries (using `.limit(10)`)
- sort ascending by name (using `.sort({name: 1}))` , to descend we would use
  `-1`)
- only return the properties `name` and `tags` for the item in the collection
  (using `.select({name: 1, tags: 1})`)

```js
async function getCourses() {
  const courses = await Course.find({
    author: "Tosh Gnomay",
    isPublished: true,
  })
    .limit(10)
    .sort({ name: 1 })
    .select({ name: 1, tags: 1 });
  console.log(courses);
}
```

This returns:

```js
[
  {
    _id: new ObjectId("62f4f07a875cff48827b8731"),
    name: "Java Course",
    tags: ["java", "backend"],
  },
  {
    _id: new ObjectId("62f4e2527ac4aa2c30d41d24"),
    name: "Javascript Course",
    tags: ["js", "frontend"],
  },
];
```

> Note that the UUID is always returned, whether we specify it or not.

## Querying with operators

So far when filtering we have been doing so with reference to properties that
exist on the document's model (`author`, `isPublished` etc) and we have applied
tranformations on the data returned (sorting, limiting the number or matches
etc.). However we can also apply **operators** within our queries. Operators
allow us to perform computations on the data, for example: for a given numerical
property on an object, return the objects for which this value is within a
certain range.

When we apply operators we use the `$` symbol before the operator and pass the
operator function as an object.

The schema:

```
Model.find( { property: { $operator: conditions } } )
```

### Comparison operators

The following comparison operators are available in MongoDB:

| Operator | Function                  |
| -------- | ------------------------- |
| `eq`     | Equal to                  |
| `ne`     | Not equal to              |
| `gt`     | Greater than              |
| `gte`    | Greater than or less than |
| `lt`     | Less than                 |
| `lte`    | Less than or equal to     |
| `in`     | In                        |
| `nin`    | Not in                    |

We can employ these comparators within a `.find` filter. For example let's
imagine that our `courses` instances have a property of `price`.

To filter course prices that are greater than or equal to 10 and less than or
equal to 29:

```js
Course.find({ price: { $gte: 10, $lte: 20 } });
```

To filter course prices that are either 10, 15 or 20:

```js
Course.find({ price: { $in: [10, 15, 20] } });
```

### Logical operators

When we apply logical operators, we do not apply the query within the main
`find` method. We use a dedicated method that corresponds to the logical
predicate.

For example to query by logical
[OR](Truth-functional_connectives.md#disjunction):

```js
async function getCourses() {
  const courses = await Course.find().or([
    { author: "Tosh Gnomay" },
    { isPublished: true },
  ]);
  console.log(courses);
}
```

We write each disjunct as an object representing the conditions we are filtering
for within an array that is passed to the `.or()` method.

The same syntax applies for conjunction.

### Regular expressions

When filtering by strings we can use Regex for greater power.

Previously we filtered by the author name:

```js
.find({author: "Tosh Gnomay"})
```

To demonstrate regex we could filter by names beginning with `T`:

```js
.find({author: /^T*/})
```

```js
async function getCourses() {
  const courses = await Course.find()
    .or([{ author: "Tosh Gnomay" }, { isPublished: true }])
    .count();
  console.log(courses);
}
```

This will return a number.

### Pagination

We previously used the `limit()` method to control how many matches we return
from a query. We can extend this functionality by creating pagination. This
allows us to meter the return values into set chunks. This is used frequently
when interacting with a database through a mediating RESTful API. For example to
return values from endpoints such as:

```
/api/courses?pageNumber=2&pageSize=10
```

To do this you pass two values as query parameters

```js
const pageNumber = 2;
const pageSize = 10;

async function getCourses() {
  const courses = await Course.find()
    .skip(pageNumber - 1 * pageSize)
    .limit(pageSize);
  console.log(courses);
}
```


title: Docker_containers
last_modified: 2024-10-18 18:55:56
body:
# Docker containers

## Launch a container

We launch a container by running, e.g

```sh
docker run hello-world
```

Docker looks for the `hello-world` image in order to start the container. If it
cannot find it, it will fetch the image from DockerHub.

(`docker run` assumes you are saying "run image in container" but you can
specify other Docker objects such as networks.)

The `docker run` command is actually a composite of the following two commands:

```sh
docker create --name [my_container_name] hello-world
docker start [my_container]
```

Here is an applied example:

```
docker run --name my-container debian /bin/echo "Hello, world"
```

This creates and runs a container from the Debian image and executes `/bin/echo`
inside of it. Once you have run the above line, if the process is successful it
will exit. The container will stop running but it will remain in memory. Nothing
will be output (no "Hello, world"), because when you run it, you are _outside_
of the container.

## Launch a container as a daemon

You can also run a container as a [daemon](). In this mode, the container will
run in the background and detach from the console. For example:

```sh
docker run -d debian /bin/sh -c /bin/sh -c "while true; do echo 'Hello!'; sleep 1; done"
```

Inside the container, this will echo "Hello!" every second in an infinite loop
whilst the daemon is active.

The above command will not actually output anything. Instead it wil ouput the
container ID, e.g:

```
2749d796cbd64e9cf57307329e792587c39d8244f2377e62d78f3f3f77eecdb4
```

You can use this to access the log for the container. When you do so, you will
then see the output:

```sh
docker log 2749
hello
hello
hello
...
```

(We could also use the container name to reference the container, if we launched
it with the `--name` param.)

## Stopping a container

```
docker stop 2749
```

There will be a delay because it shuts down gracefully. It sends a SIGINT to the
process in the container with PID 1 (i.e the root or parent process for the
container).

`stop` will keep the container in memory. This means you can still refer bak to
the logs and that it can be restarted.

Instead of `stop`, if you were to use:

```
docker rm 2749
```

The container will be stopped and deleted. The logs are deleted and the
container cannot be recovered.

We can also tell Docker to immediately remove a container after it exits:

```
docker run --rm [image]
```

## Interacting with containers

In the examples so far the container is a closed box. You don't have a terminal
through which you can interact with the container as you would with an OS. You
can only start the container and view its activities by accessing the logs.

For images that have an OS we can use `-i -t` to launch a terminal in
interactive mode.

```sh
docker run -i -t debian /bin/bash
root@0022da12f2f2:/# ls
bin  boot  dev	etc  home  lib	media  mnt  opt  proc  root  run  sbin	srv  sys  tmp  usr  var
root@0022da12f2f2:/# whoami
root
root@0022da12f2f2:/# ls usr/
bin  games  include  lib  libexec  local  sbin	share  src
```

Note that it defaults to the root user.

Containers are lightweight and should only contain the minium environment needed
to run an application. For this reason OSs that are included in an image are
often very stripped-back and many programs and processes will not be there by
default. (Although you could install a package manager and install the commands
you need.)

The previous command launches the container and enters into it with a terminal.
If a container is already running, you can enter into it in terminal mode with
`exec`:

```
docker exec -i -t my_container bash
```

If a container is running in detached mode we can attach to the main process
with `docker attach`

```
docker attach my_container
```

## Container lifecycle

![](/img/container-lifecycle.png)

All containers have a lifecycle represented by five distinct states. Each state
has an associated command:

- created
  - `docker create`, `docker run`
- running
- paused
  - `docker pause`, `docker unpause`
- stopped
  - `docker stop`, `docker kill`
- removed
  - `docker rm`

## Main container commands

```sh
docker create # create container from image
docker run # create and start container from image
docker run --name # add custom name for container
docker ps # list currently active containers
docker ps -a # list all containers (inactive and active)
docker logs # display a container log
docker stop # stop a running container
docker start # starts a stopped (exited) container
docker rm # remove a stopped (exited) container
```

## Container troubleshooting

Lots of diagnostic output:

```
docker inspect container_name
```

List processes (from outside):

```
docker top container_name
```

View logs

```
docker logs container_name
```


title: Twos_complement
last_modified: 2024-10-18 18:55:56
body:
# Two's complement

## Summary

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

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

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

## Detail

### Procedural steps

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

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

The procedure for deriving the complement is as follows.

To derive the complement of an unsigned number:

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

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

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

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

### Formal expression

$$
    2^n - x
$$

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

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

$$
    16 - 5 = 11
$$

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

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

$$

  1011 + 0101 = 0000
$$

### Advantages

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

### Limitations

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

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

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

## Related notes

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


title: React_application_structure
last_modified: 2024-10-18 18:55:56
body:
# React application structure

When you use `create-react-app` it creates a default directory structure that is
a sensible one to follow:

```
build/
public/
	index.html
src/
	index.js
	App.js
```

## `public/`

- `public` constains the `index.html` file via which React connects to the
  traditional DCOM. Within `index.html` you have `<div id="root">...</div>` as
  the entry point where your application is injected into the standard DOM.

- Everything in this folder is as per standard web development practice.

- You shouldn't include React code here but you can paste necessary links and
  metadata to the `head` of `index.html`

## `src/`

- It automatically contains `index.js` . This imports `React` and `ReactDOM` and
  contains the `ReactDOM.render()` method:
  ```js
  ReactDOM.render(
    <App />
  document.getElementById('root')
  );
  ```
- The `App` component is your parent component and the container for your app.
  It is where you will import your sub-components.
- If you are using React Router `App` becomes the index for the different page
  components. See [Routing](Routing.md) for more detail.

## `build`

`build` houses the compiled version of your application, ready to be deployed to
a server. All contents are minified, compressed and optimised using Webpack.

Once you have executed `npm run build` you can test the output by using `serve`
via the console:

```bash
serve -s build
```

`build` will comprise a minified version of `index.html` and a `static/`
directory which holds your compiled CSS and JS that `index.html` links to, as
per a standard static website.


title: Multiple_pointers
last_modified: 2024-10-18 18:55:56
body:
# Multiple pointers

The multiple pointers pattern can be useful for moving through an array and
comparing elements against each other in a time-efficient manner and without
costly multiple loops.

## Example: moving pairs

In this example we shuffle through an array creating a pair from each element
encountered. Thus for an array such as `[4, 3, 2, 1, 3, 6, 7]` we would return
`[ [ 4, 3 ], [ 3, 2 ], [ 2, 1 ], [ 1, 3 ], [ 3, 6 ], [ 6, 7 ] ]`.

```js
function movingPairs(arr) {
  const store = [];
  let left = 0;
  let right = 1;
  for (left; left < arr.length - right; left++) {
    store.push([arr[left], arr[right + left]]);
  }
  return store;
}
```

## Example: identify unique values

An example application of the above pattern would be identifying the number of
unique values in an array that contains duplicates. One way to do this in
JavaScript would be to use a Set however this is a more generic approach that
only requires a single loop:

```js
function uniqueValues(arr) {
  if (!arr.length) {
    return 0;
  } else {
    let count = 1;
    let left = 0;
    let right = 1;
    for (right; right < arr.length; right++) {
      if (arr[left] !== arr[right]) {
        count++;
      }
      left++;
    }

    return count;
  }
}
```

This works by having two pointers at either end of the array; one moving from
the right and one moving from the left. Whilst the right pointer is less than
the array length, the left pointer is incremented as the right moves closer to
it. At each incrementation, the values of pointers (used as indexes) are
compared for uniqueness, and a count is kept.

## Example: identify duplicates

This is very similar to the above but this time we are looking for duplicates
rather than unique values.

```js
function areThereDuplicatesPointer(...input) {
  let start = 0;
  let next = 1;
  while (next < input.length) {
    let same = input[start] === input[next];
    if (same) {
      return true;
    }
    start++;
    next++;
  }

  return false;
}
console.log(areThereDuplicatesPointer(1, 2, 3)); // false
console.log(areThereDuplicatesPointer(1, 2, 2)); // true
```


title: Role_of_unique_key_in_relational_databases
last_modified: 2024-10-18 18:55:56
body:
# Unique key

> Every table in a relational database should have a **unique key**. A unique
> key is one **field that uniquely identifies each record**.

This is essential for carrying out operations across database tables and for
creating and deleting database entires in accordance with the
[ACID principle](ACID_principle.md). It is also a safeguard: it means
you can always identify a record by itself and don't have to rely on generic
queries to identify it.

Sometimes you will have a dedicated field such as `UNIQUE_ID` for the unique
key. Other times you can use an existing field to fulfil that function. When an
existing, visible column is used as the unique key it is called the **primary
key**. Other times the unique key can be hidden from the table.

In both cases the following constraints **must be met:**

1. No two records can have the **same** primary key data
1. The primary key value should **never be reused**. Thus, if a record is
   deleted from the table, it should not be re-allocated to a new record.
1. A primary key value **must not be modified** once it has been created
1. A primary key **must have a value**; it cannot be `null`


title: Negation_Introduction
last_modified: 2024-10-18 18:55:56
body:
# Negation Introduction

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

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


title: Dividing_fractions
last_modified: 2024-10-18 18:55:56
body:
# Dividing fractions

Suppose you have the following shape:

![draw.io-Page-9.drawio 1.png](draw.io-Page-9.drawio.png)

One part is shaded. This represents one-eighth of the original shape.

![one-eighth-a.png](one-eighth-a.png)

Now imagine there are four instances of the shape and one-eighth remains shaded.
How man one-eighths are there in four?

![draw.io-Page-9.drawio 2.png](draw.io-Page-9.drawio.png)

The shaded proportion represents $\frac{1}{8}$ of the shape. Imagine four of
these shapes, how many eighths are there?

This is a division statement: to find how many one-eighths there are we would
calculate:

$$
4 \div \frac{1}{8}
$$

But actually it makes more sense to think of this as a multiplication. There are
four shapes of eight parts meaning there are $4 \cdot 8$ parts in total, 32. One
of these parts is shaded making it equal to $\frac{1}{32}$.

From this we realise that when we divide fractions by an amount, we can express
the calculation in terms of multiplication and arrive at the correct answer:

$$
4 \div \frac{1}{8} = 4 \cdot 8 = 32
$$

Note that we omit the numerator but that technically the answer would be
$\frac{1}{32}$.

### Formal specification of how to divide fractions

We combine the foregoing (that it is easier to divide by fractional amounts
using multiplication) with the concept of a [reciprocol](Reciprocals.md) to
arrive at a definitive method for dividing two fractions. It boils down to:
_invert and multiply_:

If $\frac{a}{b}$ and $\frac{c}{d}$ are fractions then:
$$\frac{a}{b} \div \frac{c}{d} = \frac{a}{b} \cdot \frac{d}{c}$$

We invert the divisor (the second factor) and change the operator from division
to multiplication.

#### Demonstration

Divide $\frac{1}{2}$ by $\frac{3}{5}$

$$
\begin{split}
\frac{1}{2} \div \frac{3}{5}  = \frac{1}{2} \cdot \frac{5}{3}  \\
= \frac{5}{5}
\end{split}
$$

Divide $\frac{-6}{x}$ by $\frac{-12}{x^2}$

$$
\begin{split}
\frac{-6}{x} \div \frac{12}{x^2} = \frac{-6}{x} \cdot \frac{x^2}{-12} \\ =
\frac{(\cancel{3} \cdot \cancel{2} )}{\cancel{x}} \cdot \frac{(\cancel{x} \cdot \cancel{x} )}{\cancel{3} \cdot \cancel{2} \cdot 2} \\ =
\frac{x}{2}

\end{split}


$$


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
```


