# notes

path: API_Gateway.md
sortable_path: API_Gateway.md
title: AWS API Gateway
checksum: 1de860415cea2a7f6badd3fcc6cc63fc451eb28cdb282b8bb801f952e5f416f8
created: 2024-10-22 16:52:56.353214389+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["AWS","APIs"]}
lead:
  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.

body:
  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.

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

  # 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.


path: AWS_CLI.md
sortable_path: AWS_CLI.md
title: AWS CLI frequent commands
lead: ### Retrieve current user
checksum: ad59e9169e092479228f6887e2a59a8d5526ca3d53595d3cf7e1f33348920730
created: 2024-10-22 16:52:56.353948805+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["AWS","procedural"]}
body:
  ### Retrieve current user

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

  ### List users

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

  ### View profile data

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

  ## CloudFormation

  ### List stacks

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

  ### Delete a stack

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

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

  ## Billing ("Cost explorer")

  ### Usage for month

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

  ### Retrieve current user

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

  ### List users

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

  ### View profile data

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

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

  # AWS CLI frequent commands

  ### Retrieve current user

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

  ### List users

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

  ### View profile data

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

  ## CloudFormation

  ### List stacks

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

  ### Delete a stack

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

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

  ## Billing ("Cost explorer")

  ### Usage for month

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

  ### Retrieve current user

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

  ### List users

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

  ### View profile data

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


path: AWS_SAM_and_Docker.md
sortable_path: AWS_SAM_and_Docker.md
title: AWS SAM and Docker
checksum: aeb83b931b5d2f07079c2c95151689d42d21ef402afe0bcb9030471ac22c8318
created: 2024-10-22 16:52:56.354331339+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["docker","AWS"]}
lead:
  _How would you use docker containers in AWS. How would this integrate with SAM.
  What are the benefits over just deploying from local via SAM_

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

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

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

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

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

  - Consistency:

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

  - Custom runtimes:

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

  - Easier dependency management:

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

  - Reuse of existing Docker workflows:

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

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

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

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

  # AWS SAM and Docker

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

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

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

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

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

  - Consistency:

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

  - Custom runtimes:

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

  - Easier dependency management:

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

  - Reuse of existing Docker workflows:

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

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

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


path: AWS_architecture_hierarchy.md
sortable_path: AWS_architecture_hierarchy.md
title: AWS architecture hierarchy
lead: From bottom up:
checksum: 58902108d5a31e3a1849fa67612b97a513803753be6d00337fbbfef4508812a7
created: 2024-10-22 16:52:56.354674219+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
body:
  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 |

raw_content:
  ---
  id: oyubuqx2
  tags: [AWS]
  created: Monday, February 19, 2024 | 19:17
  since: just now
  last_modified: Monday, February 19, 2024 | 19:17
  ---

  # 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 |

metadata:
  {"created":"Monday, February 19, 2024 | 19:17","id":"oyubuqx2","last_modified":"Monday, February 19, 2024 | 19:17","since":"just now","tags":["AWS"]}


path: Accessing_metadata_about_SQL_database.md
sortable_path: Accessing_metadata_about_SQL_database.md
title: Accessing metadata about SQL database
lead:
  ```sql
  SHOW DATABASES

checksum: 8e411b866ca90837f4b6050ae4cb06d38ddc149e0a3f9aa7003f123c872e2b94
created: 2024-10-22 16:52:56.354924538+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["SQL","databases"]}
body:
  ```sql
  SHOW DATABASES

  SHOW TABLES

  -- View column names and data types
  DESCRIBE [table_name]

  -- View disk usage per column
  SHOW TABLE STATUS

  ```

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

  # Accessing metadata about SQL database

  ```sql
  SHOW DATABASES

  SHOW TABLES

  -- View column names and data types
  DESCRIBE [table_name]

  -- View disk usage per column
  SHOW TABLE STATUS

  ```


path: Accessing_secrets_from_a_Lambda.md
sortable_path: Accessing_secrets_from_a_Lambda.md
title: Accessing secrets from a Lambda
checksum: e7eb2bac1399a7fbbb77f98529ea2a83bd27f6a4ebac2c9e1e44bfde4ac6d2ad
created: 2024-10-22 16:52:56.355184435+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["AWS","aws-lambda"]}
lead:
  If a Lambda connects to a database or calls an API, it is going need access to
  authentication tokens/passwords.

body:
  If a Lambda connects to a database or calls an API, it is going need access to
  authentication tokens/passwords.

  You obviously should not store these in your code. Instead you should store them
  in AWS Secrets Manager and access them via the `aws-sdk` in your Lambda
  function.

  In order for your function to be able to access the secrets however, it will
  need to be given permission. While the Lambda is not itself a "user" it does
  have an identity in the form of its IAM role. This is disclosed by its ARN.

  To allow the Lambda to access the secret you should add a resource permission on
  the secret that designates the Lambda.

  > When you create a Lambda function, you specify an IAM role that AWS Lambda can
  > assume to execute the function on your behalf. This role is referred to as the
  > execution role. The execution role grants the function the necessary
  > permissions to call other AWS services, access resources, and perform other
  > operations.

  Here is an example of a resource permission giving access to a Lambda:

  ```json
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::885135949562:role/pocket-api-lambda-QueryPocketFunctionRole-GY5ZN3RW31KE"
        },
        "Action": "secretsmanager:GetSecretValue",
        "Resource": "arn:aws:secretsmanager:eu-west-2:885135949562:secret:pocket-api-credentials-wEvQMI"
      }
    ]
  }
  ```

  See
  [Fetch from Secrets Manager](Fetch_from_Secrets_Manager.md)
  for a code example of retrieving a value from Secrets Manager.

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

  # Accessing secrets from a Lambda

  If a Lambda connects to a database or calls an API, it is going need access to
  authentication tokens/passwords.

  You obviously should not store these in your code. Instead you should store them
  in AWS Secrets Manager and access them via the `aws-sdk` in your Lambda
  function.

  In order for your function to be able to access the secrets however, it will
  need to be given permission. While the Lambda is not itself a "user" it does
  have an identity in the form of its IAM role. This is disclosed by its ARN.

  To allow the Lambda to access the secret you should add a resource permission on
  the secret that designates the Lambda.

  > When you create a Lambda function, you specify an IAM role that AWS Lambda can
  > assume to execute the function on your behalf. This role is referred to as the
  > execution role. The execution role grants the function the necessary
  > permissions to call other AWS services, access resources, and perform other
  > operations.

  Here is an example of a resource permission giving access to a Lambda:

  ```json
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::885135949562:role/pocket-api-lambda-QueryPocketFunctionRole-GY5ZN3RW31KE"
        },
        "Action": "secretsmanager:GetSecretValue",
        "Resource": "arn:aws:secretsmanager:eu-west-2:885135949562:secret:pocket-api-credentials-wEvQMI"
      }
    ]
  }
  ```

  See
  [Fetch from Secrets Manager](Fetch_from_Secrets_Manager.md)
  for a code example of retrieving a value from Secrets Manager.


path: Add_Subtract_Fractions.md
sortable_path: Add_Subtract_Fractions.md
title: Adding and subtracting fractions
lead: ## Adding/ subracting fractions with common denominators
checksum: 7c9b05446ce108cb54523c5c6519598d6803f44b72b21f84e789cac008bb30fd
created: 2024-10-22 16:52:56.35577727+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["prealgebra","fractions"]}
body:
  ## 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}
  $$

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

  # 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}
  $$


path: Adding_documents_to_a_Mongo_collection.md
sortable_path: Adding_documents_to_a_Mongo_collection.md
title: Adding documents to a collection
checksum: c8d62e948fd02c6e17f06f9f86160fc06534336683a11c46439528ba0a6a6a41
created: 2024-10-22 16:52:56.356311322+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["mongo-db","node-js","mongoose","databases"]}
lead:
  We have our database (`playground`) and collection (`courses`) established. We
  now need to add documents to our collection. We will do this via a function
  since this will be an asynchronous process:

body:
  We have our database (`playground`) and collection (`courses`) established. We
  now need to add documents to our collection. We will do this via a function
  since this will be an asynchronous process:

  ```js
  const pythonCourse = new Course({
    name: "Python Course",
    author: "Terry Ogleton",
    tags: ["python", "backend"],
    isPublished: true,
  });

  async function addCourseDocToDb(courseDocument) {
    try {
      const result = await courseDocument.save();
      console.log(result);
    } catch (err) {
      console.error(err.message);
    }
  }

  addCourseDocToDb(nodeCourse);
  ```

  When we run this, we call the `save` method on the Mongoose schema. We will then
  have the Mongo document outputted to the console:

  ```
  {
    name: 'Python Course',
    author: 'Terry Ogleton',
    tags: [ 'python' ],
    isPublished: true,
    _id: new ObjectId("62f4ac989d2fec2f01596b9b"),
    date: 2022-08-11T07:15:36.978Z,
    __v: 0
  }
  ```

  This will also be reflected in Compass:

  ![](/img/mongo-doc-added.png)

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

  # Adding documents to a collection

  We have our database (`playground`) and collection (`courses`) established. We
  now need to add documents to our collection. We will do this via a function
  since this will be an asynchronous process:

  ```js
  const pythonCourse = new Course({
    name: "Python Course",
    author: "Terry Ogleton",
    tags: ["python", "backend"],
    isPublished: true,
  });

  async function addCourseDocToDb(courseDocument) {
    try {
      const result = await courseDocument.save();
      console.log(result);
    } catch (err) {
      console.error(err.message);
    }
  }

  addCourseDocToDb(nodeCourse);
  ```

  When we run this, we call the `save` method on the Mongoose schema. We will then
  have the Mongo document outputted to the console:

  ```
  {
    name: 'Python Course',
    author: 'Terry Ogleton',
    tags: [ 'python' ],
    isPublished: true,
    _id: new ObjectId("62f4ac989d2fec2f01596b9b"),
    date: 2022-08-11T07:15:36.978Z,
    __v: 0
  }
  ```

  This will also be reflected in Compass:

  ![](/img/mongo-doc-added.png)


path: Additive_identity.md
sortable_path: Additive_identity.md
title: Property of Additive Identity
lead: **Let $a$ represent any member of $\mathbb{W}$ or $\mathbb{Z}$ then:**
body:
  **Let $a$ represent any member of $\mathbb{W}$ or $\mathbb{Z}$ then:**

  $$ a + 0 = a $$

checksum: f81c1d68bed1ed5c4e862696951339b54834434057b22aaf3f4ab07f299d736c
created: 2024-10-22 16:52:56.35662732+00:00
modified: 2024-11-04 15:19:09.466031559+00:00
metadata: {"tags":["theorems"]}
raw_content:
  ---
  tags:
    - theorems
  ---

  # Property of Additive Identity

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

  $$ a + 0 = a $$


path: Additive_inverse_property.md
sortable_path: Additive_inverse_property.md
title: Additive inverse property
checksum: 227324525c8d868ca344db4cc4ae34e6fb4f05a42c69c6bf71015db3895352dd
created: 2024-10-22 16:52:56.356885207+00:00
modified: 2024-10-18 18:10:47.136138492+00:00
metadata: {"tags":["theorems"]}
lead:
  **Let $a$ represent any member of $\mathbb{Z}$. Then there is a unique member of
  $\mathbb{Z}$ $-a$ such that:**

body:
  **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.

raw_content:
  ---
  tags:
    - theorems
  ---

  # 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.


path: Aggregate_functions_in_SQL.md
sortable_path: Aggregate_functions_in_SQL.md
title: Aggregate functions in SQL
checksum: 58685a3a55cae5dc4503f7088bd8ebc3145b0458584311238f718b292bea90cf
created: 2024-10-22 16:52:56.357117091+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["SQL","databases"]}
lead:
  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.

body:
  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
  ```

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

  # 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
  ```


path: Algebra_key_terms.md
sortable_path: Algebra_key_terms.md
title: Algebra key terms
checksum: ed6943753cbd8befe96a537af9b1fdc6fb95615d1b45687f98eec92c88c5524b
created: 2024-10-22 16:52:56.357390376+00:00
modified: 2024-10-18 18:11:02.043088003+00:00
metadata: {"tags":["algebra"]}
lead:
  - **Variable**
    - A symbol that stands for a value which may vary
  - **Equation**
    - A mathematical statement that equates two mathematical expressions (states
      that they are the same/ establishes an identity relation)
  - **Solution** ^678811
    - A numerical value that **satisfies** an equation. When the variable in the
      equation is replaced by the solution, a true statement results

body:
  - **Variable**
    - A symbol that stands for a value which may vary
  - **Equation**
    - A mathematical statement that equates two mathematical expressions (states
      that they are the same/ establishes an identity relation)
  - **Solution** ^678811
    - A numerical value that **satisfies** an equation. When the variable in the
      equation is replaced by the solution, a true statement results

  ### Example

  $$ 4 = y - 11 $$

  The example above is an **equation**. $y$ is the variable. This can be replaced
  by $15$ which is the **solution** to the equation:

  $$ 4 = 15 -11 $$

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

  # Algebra key terms

  - **Variable**
    - A symbol that stands for a value which may vary
  - **Equation**
    - A mathematical statement that equates two mathematical expressions (states
      that they are the same/ establishes an identity relation)
  - **Solution** ^678811
    - A numerical value that **satisfies** an equation. When the variable in the
      equation is replaced by the solution, a true statement results

  ### Example

  $$ 4 = y - 11 $$

  The example above is an **equation**. $y$ is the variable. This can be replaced
  by $15$ which is the **solution** to the equation:

  $$ 4 = 15 -11 $$


path: Algorithmic_complexity.md
sortable_path: Algorithmic_complexity.md
title: Algorithmic complexity
checksum: 41074cb4a43cd5e053de6ed9c7d135e41c9965e7ab7ad2f9f4cbe20dcb97cc21
created: 2024-10-22 16:52:56.358562479+00:00
modified: 2024-10-19 08:56:34.670283263+00:00
metadata: {"tags":["algorithms"]}
lead:
  ![Screenshot_2021-05-11_at_18.55.23.png](Screenshot_2021-05-11_at_18.55.23.png)
  _Summary of the main classes of algorithmic complexity_

body:
  ![Screenshot_2021-05-11_at_18.55.23.png](Screenshot_2021-05-11_at_18.55.23.png)
  _Summary of the main classes of algorithmic complexity_

  ## Distinguish algorithms from programs

  Test commit Algorithms are general sets of instructions that take data in one
  state, follow a prescribed series of steps and return data in another state.
  Programs are a specific application of one or more algorithms to achieve an
  outcome in a specific context. With algorithms, the actual detail of the steps
  is mostly abstracted and it is irrelevant to what end the algorithm is being
  put. For instance you may create a program that returns addresses from a
  database using a postcode. It is irrelevant to the efficiency or value of the
  algorithm whether or not you are looking up postcodes or some other form of
  alphanumeric string.

  ## Algorithmic efficiency

  Algorithms can be classified based on their efficiency. Efficiency is function
  of the runtime speed. However this doesn't always mean that the fastest
  algorithms are best.

  If we are landing the Curiosity Rover on Mars we may choose an algorithm that is
  slower on average for a guarantee that it will never take longer than we find
  acceptable. In other cases for example a video game, we may choose an algorithm
  that keeps the average time down, even if this occasionally leads to processes
  that need to be aborted because they take too long.

  We need a generalised measure of efficiency to compare algorithms, across
  variant hardware. We can't simply use the number of steps, since some steps will
  be quicker to complete than others in the course of the overall algorithm and
  may take longer on different machines. Moreover the same algorithm could run at
  different speeds on the same machine, depending on its internal state at the
  given time that it ran. So we use the following: **the number of steps required
  relative to the input.**

  > Two given computers may differ in how quickly they can run an algorithm
  > depending on clock speed, available memory and so forth. They will however
  > tend to require approximately the same number of instructions and we can
  > measure the rate at which the number of instructions increases with the
  > problem size.

  This is what **asymptotic runtime** means: the rate at which the runtime of an
  algorithm grows compared to the size of its input. For precision and accuracy we
  use the worst case scenario as the benchmark.

  So: the efficiency of algorithm _A_ can be judged relative to the efficiency of
  algorithm _B_ based on the rate at which the runtime of _A_ grows compared to
  its input, compared to the same property in _B_, assuming the worst possible
  performance.

  From now on we will use the word 'input' to denote the data that the algorithm
  receives (in most cases we will envision this as an array containing a certain
  data type) and 'execution' to denote the computation that is applied by the
  algorithm to each item of the data input. Rephrasing the above with these terms
  we can say that 'algorithmic efficiency' is a measure that describes the rate at
  which the execution time of an algorithm increases relative to the size of its
  input.

  We will find that for the runtime of some algorithms, the size of the input does
  not change the execution time. In these cases, the runtime is proportional to
  the input quantity. In this case, regardless of whether the input is an array of
  one hundred elements or an array of ten elements, the amount of work that is
  executed on each element is the same.

  For other cases, this will not hold true. We will find that there is a
  relationship between input size and execution time such that the length of the
  input affects the amount of work that needs to be performed on each item at
  execution.

  ## Linear time

  Let's start with linear time, which is the easiest runtime to grasp.

  We need an example to make this tangible and show how an algorithm's runtime
  changes compared to the size of its input. Let's take a simple function that
  takes a sequence of integers and returns their sum:

  ```js
  function findSum(arr){
  	let total = 0;
  	for (let i = 0; i < arr.length; i++){
  		total = total += arr[i];
  	)
  	return total
  }
  ```

  The input of this function is an array of integers. It returns their sum as the
  output. Let's say that it takes 1ms for the function to sum an array of two
  integers.

  If we passed in an array of four integers, how would this change the runtime?
  The answer is that, providing that the time it takes to sum two integers doesn't
  change, it would take twice as long.

  As the time it takes to execute `findSum` doesn't change, we can say confidently
  that the runtime is as long as the number of integers we pass in.

  A more general way to say this is that the runtime is equal to size of the
  input. For algorithms of the class of which `findSum` is a member: **the total
  runtime is proportional to the number of items to be processed**.

  ## Introducing asymptotic notation

  If we say that it takes 1ms for two integers to be summed, this gives us the
  following data set:

  | Length of input | Runtime |
  | :-------------- | ------: |
  | 2               |       2 |
  | 3               |       3 |
  | 4               |       4 |
  | 5               |       5 |

  If we plotted this as a graph it is clear that this is equivalent to a linear
  distribution:![lin.svg](lin.svg)

  Algorithms which display this distribution are therefore called **linear
  algorithms**.

  The crucial point is that the amount of time it takes to sum the integers does
  not increase as the algorithm proceeds and the input size grows. This time
  remains the same. If it did increase, we would have a fluctuating curve on the
  graph. This aspect remains constant, only the instructions increase. This is why
  we have a nice steadily-advancing distribution in the graph.

  We can now introduce notation to formalise the algorithmic properties we have
  been discussing.

  ## Big O notation

  To express linear time algorithms formally, we say that:

  > it takes some constant amount of time ($C$) to sum one integer and n times as
  > long to sum n integers

  Here the constant is the time for each execution to run and n is the length of
  the input. Thus the complexity is equal to that time multiplied by the input.

  The algebraic expression of this is $cn$ : the constant multiplied by the length
  of the input. In algorithmic notation, the reference to the constant is always
  removed. Instead we just use n and combine it with a 'big O' which stands for
  'order of complexity'. Likewise, if we have an array of four integers being
  passed to `findSum` we could technically express it as O(4n), but we don't
  because we are interested in the general case not the specific details of the
  runtime. So a linear algorithm is expressed algebraically as $O(n)$ which is
  read as "oh of n" and means

  > $O(n)$ = with an order of complexity equal to (some constant) multiplied by n

  Applied, this means an input of length 6 ($n$) where runtime is constant ($c$)
  at 1ms has a total runtime of 6 x 1 = 6ms in total. Exactly the same as our
  table and graph. O n is just a mathematical way of saying _the runtime grows on
  the order of the size of the input._

  > It's really important to remember that when we talk about the execution
  > runtime being constant at 1ms, this is just an arbitrary placeholder. We are
  > not really bothered about whether it's 1ms or 100ms: 'constant' in the
  > mathematical sense doesn't mean a unit of time, it means 'unchanging'. We are
  > using 1ms to get traction on this concept but the fundamental point being
  > expressed is that the size of the input doesn't affect the execution time
  > across the length of the execution time.

  ## Constant time

  Constant time is another one of the main classes of algorithmic complexity. It
  is expressed as O(1). Here, we do away with n because with constant time we are
  only ever dealing with a single execution so we don't need a variable to express
  nth in a series or 'more than one'. Constant time covers all singular processes,
  without iteration.

  An example in practice would be printing `array[0]` . Regardless of the size of
  the array, it is only ever going to take one step, or constant times one. On a
  graph this is equivalent to a flat line along the time axis. Since it only
  happens for one instant, it doesn't persist over time or have multiple
  iterations.

  ### Relation to linear time

  If you think about it, there is a clear logical relationship between constant
  and linear time: because the execution time of a linear algorithm is constant,
  regardless of the size of n, each execution of O(n) is equal to O(1). Thus O(n)
  is simply O(1) writ large or iterated. At any given execution of an O(n)
  algorithm n is going to be equal to 1.

  ## Quadratic time

  With the examples of constant and linear time, the total number of instructions
  doesn't change the amount of work that needs to be performed for each item, but
  this only covers one subset of algorithms. In cases other than O(1) and O(n),
  the length of the input **can** affect the amount of work that needs to be
  performed at execution. The most common example of this scenario is known as
  quadratic time, represented as $O(n^2)$.

  Let's start with an example.

  ```js
  const letters = ['A', 'B', 'C'];

  function quadratic(arr) {
    for (let i = 0; i < arr.length; i++) {
      for (let j = 0; j < arr.length j++) {
        console.log(arr[i]);
      }
    }
  }

  quadratic(letters);
  ```

  This function takes an array . The outer loop runs once for each element of the
  array that is passed to the function. For each iteration of the outer loop, the
  inner loop also runs once for each element of the array.

  In the example this means that the following is output:

  ```
  A A A B B B C C C (length: 9)
  ```

  Mathematically this means that n (the size of the input) grows at a rate of n2
  or the input multiplied by itself. Our outer loop (`i`) is performing n
  iterations (just like in linear time) but our inner loop (`j`) is also
  performing n iterations, three `j`s for every one `i` . It is performing n
  iterations for every nth iteration of the outer loop. So runtime here is
  directly proportional to the squared size of the input data set. As the input
  array has a length of 3, and the inner array runs once for every element in the
  array, this is equal to 3 x 3 or 3 squared (9).

  If the input had length 4, the runtime would be 16 or 4x4. For every execution
  of linear time (the outer loop) the inner loop runs as many times as is equal to
  the length of the input.

  This is not a linear algorithm because as n grows the runtime increases as a
  factor of it. Therefore the runtime is not growing proportional to the size of
  the input, it is growing proportional to the size of the input squared.

  Graphically this is represented with a curving lines as follows:
  ![square.svg](square.svg)

  We can clearly see that as n grows, the runtime gets steeper and more
  pronounced,

  ## Logarithmic time (log n)

  A logarithm is best understood as the inverse of exponentiation:

  $$ \log \_{2}8 = 3 \leftrightarrow 2^3 = 8 $$

  When we use log in the context of algorithms we are always using the binary
  number system so we omit the 2, we just say log.

  > With base two logarithms, the logarithm of a number roughly measures the
  > number of times you can divide that number by 2 before you get a value that is
  > less than or equal to 1

  So applying this to the example of $\log 8$ , it is borne out as follows:

  - 8 / 2 = 4 — count: 1
  - 4 / 2 = 2 — count: 2
  - 2 / 2 = 1 — count: 3

  As we are now at 1, we can't divide any more, so $\log 8$ is equal to 3.

  Obviously this doesn't work so neatly with odd numbers, so we approximate.

  For example, with $\log 25$:

  - 25 / 2 = 12.5 — count: 1

  - 12.5 / 2 = 6.25 — count: 2

  - 6.25 / 2 = 3.125 — count: 3

  - 3.125 / 2 = 1.5625 — count: 4

  - 1.5625 / 2 = 0.78125

    Now we are lower than 1 so we have to stop. We can only say that the answer to
    $\log 25$ is somewhere between 4 and 5.

    The exact answer is $\log 25 \approx 4.64$

  Back to algorithms: $O(\log n)$ is a really good complexity to have. It is close
  to O(1) and in between O(1) and O(n). Represented graphically, it starts of with
  a slight increase in runtime but then quickly levels off:

  ![Screenshot_2021-05-11_at_18.51.02.png](Screenshot_2021-05-11_at_18.51.02.png)

  Many sorting algorithms run in log n time, as does recursion.

  ## Reducing O complexity to the general case

  When we talk about big O we are looking for the most general case, slight
  deviations, additions or diminutions in n are not as important as the big
  picture. We are looking for the underlying logic and patterns that are
  summarised by the classes of O(1), O(n), O(n2) and others.

  For example, with the following function:

  ```js
  function sumAndAddTwo(arr) {
    let total = 0;
    for (let i = 0; i < arr.length; i++) {
      total += arr[i];
    }
    total = total += 2;
  }
  ```

  The formal representation of the above complexity would be O(n) + O(1). But it's
  easier just to say O(n), since the O(1) that comes from adding two to the result
  of the loop, makes a marginal difference overall.

  Similarly, with the following function:

  ```js
  function processSomeIntegers(integers){
  	let sum, product = 0;

  	integers.forEach(function(int){
  		return sum += int;
  	}

  	integers.forEach(function(int){
  		return product *= int;
  	}

  	console.log(`The sum is ${sum} and the product is ${product}`);
  }
  ```

  It might appear to be more complex than the earlier summing function but it
  isn't really. We have one array (`integers` ) and two loops. Each loop is of
  O(n) complexity and does a constant amount of work. If we add O(n) and O(n) we
  still have O(n), not O(2n). The constant isn't changed in any way by the fact
  that we are looping twice through the array in separate processes, it just
  doubles the length of n. So rather than formalising this as O(n) + O(n), we just
  reduce it to O(n).

  When seeking to simplify algorithms to their most general level of complexity,
  we should keep in mind the following shorthands:

  - Arithmetic operations always take constant time
  - Variable assignment always takes constant time
  - Accessing an element in an array by index or an object value by key is always
    constant
  - in a loop the complexity is the length of the loop times the complexity of
    whatever happens inside of the loop

  With this in mind we can break down the `findSum` function like so:

  ![breakdown.svg](breakdown.svg)

  This gives us:

  $$ O(1) + O(1) + O(n) $$

  Which, as noted above can just be reduced to O(n).

  ## Space complexity

  So far we have talked about time complexity only: how the runtime changes
  relative to the size of the input. With space complexity, we are interested in
  how much memory (conceived as an abstract spatial quantity corresponding to the
  machine's hardware) is required by the algorithm. We can use Big O notation for
  space complexity as well as time complexity.

  Space complexity in this sense is called 'auxiliary space complexity'. This
  means the space that the algorithm itself takes up, independent of the the size
  of the inputs. We are not focusing on the space that each input item takes up,
  only the overall space of the algorithm.

  Again there are some rules of thumb:

  - Booleans, `undefined`, and `null` take up constant space
  - Strings require O(n) space, where n is the sting length
  - Reference types take up O(n): an array of length 4 takes up twice as much
    space as an array of length 2

  So with space complexity we are not really interested in how many times the
  function executes, if it is a loop. We are looking to where data is stored: how
  many variables are initialised, how many items there are in the array.

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

  # Algorithmic complexity

  ![Screenshot_2021-05-11_at_18.55.23.png](Screenshot_2021-05-11_at_18.55.23.png)
  _Summary of the main classes of algorithmic complexity_

  ## Distinguish algorithms from programs

  Test commit Algorithms are general sets of instructions that take data in one
  state, follow a prescribed series of steps and return data in another state.
  Programs are a specific application of one or more algorithms to achieve an
  outcome in a specific context. With algorithms, the actual detail of the steps
  is mostly abstracted and it is irrelevant to what end the algorithm is being
  put. For instance you may create a program that returns addresses from a
  database using a postcode. It is irrelevant to the efficiency or value of the
  algorithm whether or not you are looking up postcodes or some other form of
  alphanumeric string.

  ## Algorithmic efficiency

  Algorithms can be classified based on their efficiency. Efficiency is function
  of the runtime speed. However this doesn't always mean that the fastest
  algorithms are best.

  If we are landing the Curiosity Rover on Mars we may choose an algorithm that is
  slower on average for a guarantee that it will never take longer than we find
  acceptable. In other cases for example a video game, we may choose an algorithm
  that keeps the average time down, even if this occasionally leads to processes
  that need to be aborted because they take too long.

  We need a generalised measure of efficiency to compare algorithms, across
  variant hardware. We can't simply use the number of steps, since some steps will
  be quicker to complete than others in the course of the overall algorithm and
  may take longer on different machines. Moreover the same algorithm could run at
  different speeds on the same machine, depending on its internal state at the
  given time that it ran. So we use the following: **the number of steps required
  relative to the input.**

  > Two given computers may differ in how quickly they can run an algorithm
  > depending on clock speed, available memory and so forth. They will however
  > tend to require approximately the same number of instructions and we can
  > measure the rate at which the number of instructions increases with the
  > problem size.

  This is what **asymptotic runtime** means: the rate at which the runtime of an
  algorithm grows compared to the size of its input. For precision and accuracy we
  use the worst case scenario as the benchmark.

  So: the efficiency of algorithm _A_ can be judged relative to the efficiency of
  algorithm _B_ based on the rate at which the runtime of _A_ grows compared to
  its input, compared to the same property in _B_, assuming the worst possible
  performance.

  From now on we will use the word 'input' to denote the data that the algorithm
  receives (in most cases we will envision this as an array containing a certain
  data type) and 'execution' to denote the computation that is applied by the
  algorithm to each item of the data input. Rephrasing the above with these terms
  we can say that 'algorithmic efficiency' is a measure that describes the rate at
  which the execution time of an algorithm increases relative to the size of its
  input.

  We will find that for the runtime of some algorithms, the size of the input does
  not change the execution time. In these cases, the runtime is proportional to
  the input quantity. In this case, regardless of whether the input is an array of
  one hundred elements or an array of ten elements, the amount of work that is
  executed on each element is the same.

  For other cases, this will not hold true. We will find that there is a
  relationship between input size and execution time such that the length of the
  input affects the amount of work that needs to be performed on each item at
  execution.

  ## Linear time

  Let's start with linear time, which is the easiest runtime to grasp.

  We need an example to make this tangible and show how an algorithm's runtime
  changes compared to the size of its input. Let's take a simple function that
  takes a sequence of integers and returns their sum:

  ```js
  function findSum(arr){
  	let total = 0;
  	for (let i = 0; i < arr.length; i++){
  		total = total += arr[i];
  	)
  	return total
  }
  ```

  The input of this function is an array of integers. It returns their sum as the
  output. Let's say that it takes 1ms for the function to sum an array of two
  integers.

  If we passed in an array of four integers, how would this change the runtime?
  The answer is that, providing that the time it takes to sum two integers doesn't
  change, it would take twice as long.

  As the time it takes to execute `findSum` doesn't change, we can say confidently
  that the runtime is as long as the number of integers we pass in.

  A more general way to say this is that the runtime is equal to size of the
  input. For algorithms of the class of which `findSum` is a member: **the total
  runtime is proportional to the number of items to be processed**.

  ## Introducing asymptotic notation

  If we say that it takes 1ms for two integers to be summed, this gives us the
  following data set:

  | Length of input | Runtime |
  | :-------------- | ------: |
  | 2               |       2 |
  | 3               |       3 |
  | 4               |       4 |
  | 5               |       5 |

  If we plotted this as a graph it is clear that this is equivalent to a linear
  distribution:![lin.svg](lin.svg)

  Algorithms which display this distribution are therefore called **linear
  algorithms**.

  The crucial point is that the amount of time it takes to sum the integers does
  not increase as the algorithm proceeds and the input size grows. This time
  remains the same. If it did increase, we would have a fluctuating curve on the
  graph. This aspect remains constant, only the instructions increase. This is why
  we have a nice steadily-advancing distribution in the graph.

  We can now introduce notation to formalise the algorithmic properties we have
  been discussing.

  ## Big O notation

  To express linear time algorithms formally, we say that:

  > it takes some constant amount of time ($C$) to sum one integer and n times as
  > long to sum n integers

  Here the constant is the time for each execution to run and n is the length of
  the input. Thus the complexity is equal to that time multiplied by the input.

  The algebraic expression of this is $cn$ : the constant multiplied by the length
  of the input. In algorithmic notation, the reference to the constant is always
  removed. Instead we just use n and combine it with a 'big O' which stands for
  'order of complexity'. Likewise, if we have an array of four integers being
  passed to `findSum` we could technically express it as O(4n), but we don't
  because we are interested in the general case not the specific details of the
  runtime. So a linear algorithm is expressed algebraically as $O(n)$ which is
  read as "oh of n" and means

  > $O(n)$ = with an order of complexity equal to (some constant) multiplied by n

  Applied, this means an input of length 6 ($n$) where runtime is constant ($c$)
  at 1ms has a total runtime of 6 x 1 = 6ms in total. Exactly the same as our
  table and graph. O n is just a mathematical way of saying _the runtime grows on
  the order of the size of the input._

  > It's really important to remember that when we talk about the execution
  > runtime being constant at 1ms, this is just an arbitrary placeholder. We are
  > not really bothered about whether it's 1ms or 100ms: 'constant' in the
  > mathematical sense doesn't mean a unit of time, it means 'unchanging'. We are
  > using 1ms to get traction on this concept but the fundamental point being
  > expressed is that the size of the input doesn't affect the execution time
  > across the length of the execution time.

  ## Constant time

  Constant time is another one of the main classes of algorithmic complexity. It
  is expressed as O(1). Here, we do away with n because with constant time we are
  only ever dealing with a single execution so we don't need a variable to express
  nth in a series or 'more than one'. Constant time covers all singular processes,
  without iteration.

  An example in practice would be printing `array[0]` . Regardless of the size of
  the array, it is only ever going to take one step, or constant times one. On a
  graph this is equivalent to a flat line along the time axis. Since it only
  happens for one instant, it doesn't persist over time or have multiple
  iterations.

  ### Relation to linear time

  If you think about it, there is a clear logical relationship between constant
  and linear time: because the execution time of a linear algorithm is constant,
  regardless of the size of n, each execution of O(n) is equal to O(1). Thus O(n)
  is simply O(1) writ large or iterated. At any given execution of an O(n)
  algorithm n is going to be equal to 1.

  ## Quadratic time

  With the examples of constant and linear time, the total number of instructions
  doesn't change the amount of work that needs to be performed for each item, but
  this only covers one subset of algorithms. In cases other than O(1) and O(n),
  the length of the input **can** affect the amount of work that needs to be
  performed at execution. The most common example of this scenario is known as
  quadratic time, represented as $O(n^2)$.

  Let's start with an example.

  ```js
  const letters = ['A', 'B', 'C'];

  function quadratic(arr) {
    for (let i = 0; i < arr.length; i++) {
      for (let j = 0; j < arr.length j++) {
        console.log(arr[i]);
      }
    }
  }

  quadratic(letters);
  ```

  This function takes an array . The outer loop runs once for each element of the
  array that is passed to the function. For each iteration of the outer loop, the
  inner loop also runs once for each element of the array.

  In the example this means that the following is output:

  ```
  A A A B B B C C C (length: 9)
  ```

  Mathematically this means that n (the size of the input) grows at a rate of n2
  or the input multiplied by itself. Our outer loop (`i`) is performing n
  iterations (just like in linear time) but our inner loop (`j`) is also
  performing n iterations, three `j`s for every one `i` . It is performing n
  iterations for every nth iteration of the outer loop. So runtime here is
  directly proportional to the squared size of the input data set. As the input
  array has a length of 3, and the inner array runs once for every element in the
  array, this is equal to 3 x 3 or 3 squared (9).

  If the input had length 4, the runtime would be 16 or 4x4. For every execution
  of linear time (the outer loop) the inner loop runs as many times as is equal to
  the length of the input.

  This is not a linear algorithm because as n grows the runtime increases as a
  factor of it. Therefore the runtime is not growing proportional to the size of
  the input, it is growing proportional to the size of the input squared.

  Graphically this is represented with a curving lines as follows:
  ![square.svg](square.svg)

  We can clearly see that as n grows, the runtime gets steeper and more
  pronounced,

  ## Logarithmic time (log n)

  A logarithm is best understood as the inverse of exponentiation:

  $$ \log \_{2}8 = 3 \leftrightarrow 2^3 = 8 $$

  When we use log in the context of algorithms we are always using the binary
  number system so we omit the 2, we just say log.

  > With base two logarithms, the logarithm of a number roughly measures the
  > number of times you can divide that number by 2 before you get a value that is
  > less than or equal to 1

  So applying this to the example of $\log 8$ , it is borne out as follows:

  - 8 / 2 = 4 — count: 1
  - 4 / 2 = 2 — count: 2
  - 2 / 2 = 1 — count: 3

  As we are now at 1, we can't divide any more, so $\log 8$ is equal to 3.

  Obviously this doesn't work so neatly with odd numbers, so we approximate.

  For example, with $\log 25$:

  - 25 / 2 = 12.5 — count: 1

  - 12.5 / 2 = 6.25 — count: 2

  - 6.25 / 2 = 3.125 — count: 3

  - 3.125 / 2 = 1.5625 — count: 4

  - 1.5625 / 2 = 0.78125

    Now we are lower than 1 so we have to stop. We can only say that the answer to
    $\log 25$ is somewhere between 4 and 5.

    The exact answer is $\log 25 \approx 4.64$

  Back to algorithms: $O(\log n)$ is a really good complexity to have. It is close
  to O(1) and in between O(1) and O(n). Represented graphically, it starts of with
  a slight increase in runtime but then quickly levels off:

  ![Screenshot_2021-05-11_at_18.51.02.png](Screenshot_2021-05-11_at_18.51.02.png)

  Many sorting algorithms run in log n time, as does recursion.

  ## Reducing O complexity to the general case

  When we talk about big O we are looking for the most general case, slight
  deviations, additions or diminutions in n are not as important as the big
  picture. We are looking for the underlying logic and patterns that are
  summarised by the classes of O(1), O(n), O(n2) and others.

  For example, with the following function:

  ```js
  function sumAndAddTwo(arr) {
    let total = 0;
    for (let i = 0; i < arr.length; i++) {
      total += arr[i];
    }
    total = total += 2;
  }
  ```

  The formal representation of the above complexity would be O(n) + O(1). But it's
  easier just to say O(n), since the O(1) that comes from adding two to the result
  of the loop, makes a marginal difference overall.

  Similarly, with the following function:

  ```js
  function processSomeIntegers(integers){
  	let sum, product = 0;

  	integers.forEach(function(int){
  		return sum += int;
  	}

  	integers.forEach(function(int){
  		return product *= int;
  	}

  	console.log(`The sum is ${sum} and the product is ${product}`);
  }
  ```

  It might appear to be more complex than the earlier summing function but it
  isn't really. We have one array (`integers` ) and two loops. Each loop is of
  O(n) complexity and does a constant amount of work. If we add O(n) and O(n) we
  still have O(n), not O(2n). The constant isn't changed in any way by the fact
  that we are looping twice through the array in separate processes, it just
  doubles the length of n. So rather than formalising this as O(n) + O(n), we just
  reduce it to O(n).

  When seeking to simplify algorithms to their most general level of complexity,
  we should keep in mind the following shorthands:

  - Arithmetic operations always take constant time
  - Variable assignment always takes constant time
  - Accessing an element in an array by index or an object value by key is always
    constant
  - in a loop the complexity is the length of the loop times the complexity of
    whatever happens inside of the loop

  With this in mind we can break down the `findSum` function like so:

  ![breakdown.svg](breakdown.svg)

  This gives us:

  $$ O(1) + O(1) + O(n) $$

  Which, as noted above can just be reduced to O(n).

  ## Space complexity

  So far we have talked about time complexity only: how the runtime changes
  relative to the size of the input. With space complexity, we are interested in
  how much memory (conceived as an abstract spatial quantity corresponding to the
  machine's hardware) is required by the algorithm. We can use Big O notation for
  space complexity as well as time complexity.

  Space complexity in this sense is called 'auxiliary space complexity'. This
  means the space that the algorithm itself takes up, independent of the the size
  of the inputs. We are not focusing on the space that each input item takes up,
  only the overall space of the algorithm.

  Again there are some rules of thumb:

  - Booleans, `undefined`, and `null` take up constant space
  - Strings require O(n) space, where n is the sting length
  - Reference types take up O(n): an array of length 4 takes up twice as much
    space as an array of length 2

  So with space complexity we are not really interested in how many times the
  function executes, if it is a loop. We are looking to where data is stored: how
  many variables are initialised, how many items there are in the array.


path: Analogue_and_digital.md
sortable_path: Analogue_and_digital.md
title: Analogue and digital
checksum: ff7dd2387d6104befc4c1b0f9048c394886d1fb6de445716a5f023161c3eca5e
created: 2024-10-22 16:52:56.359304077+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["analogue"]}
lead:
  Analogue and digital are paradigms for recording information, specifically
  information about an object or state that obtains in the world.

body:
  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.

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

  # 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.


path: Any.md
sortable_path: Any.md
title: Any
checksum: b2801ace6c428c85cdc564984cf34e3b5f28317ac0b82a6740e3cfac1d6bb012
created: 2024-10-22 16:52:56.360187367+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["typescript"]}
lead:
  `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.

body:
  `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.

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

  # 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.


path: Apollo_Client.md
sortable_path: Apollo_Client.md
title: Apollo Client
checksum: 280ecfac0884055a0b287088c2e2b2fb2463e605fbdc039a9e18e57ae41d2c98
created: 2024-10-22 16:52:56.361123406+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["graphql"]}
lead:
  Apollo Client is the client-side counterpart to
  [Apollo Server](Apollo_Server.md). We use it for
  managing queries and mutations from the frontend to our Apollo GraphQL server.
  It is specifically designed to work with React.

body:
  Apollo Client is the client-side counterpart to
  [Apollo Server](Apollo_Server.md). We use it for
  managing queries and mutations from the frontend to our Apollo GraphQL server.
  It is specifically designed to work with React.

  > We will be working with the
  > [schema](Apollo_Server.md#example-schema) we defined
  > when working on the server

  ## Initializing the client

  We initialise the client and set-up in memory caching to reduce network
  requests:

  ```js
  const client = new ApolloClient({
    uri: "http://localhost:4000",
    cache: new InMemoryCache(),
  });
  ```

  > The `uri` property must match the location of our Apollo server.

  ## Utilising the provider

  Apollo provides a top level application context that we can wrap our React app
  in. This will provide access to the client object from anywhere within the app,
  eg:

  ```jsx
  ReactDOM.render(
    <ApolloProvider client={client}>
      <GlobalStyles />
      <Pages />
    </ApolloProvider>,
    document.getElementById("root")
  );
  ```

  ## Running a query

  ### Queries as entry points

  From the client point of view, the queries in the schema are _entry points_.
  Although the queries exist in the schema, this alone is not sufficient for them
  to be entry points. Remember a schema is just a specification or contract
  between the frontend and the backend, it is not itself executable code.

  Therefore, for each query in the schema we must write a frontend implementation.
  We do this with **query constants**. The frontend implementation has a backend
  analogue: the
  [resolver](Apollo_Server.md#implementing-resolvers)
  that is invoked when the frontend issues a query. The schema standardises this
  relationship so that every query on the client must have a corresponding
  resolver on the backend.

  ### Query constants

  To run a query against our server we must define a query contant first. We use a
  `gql` literal again:

  ```js
  import { gql } from "@apollo/client";
  const TRACKS = gql`
    query GetTracks {
      tracksForHome {
        id
        title
        thumbnail
        length
        modulesCount
        author {
          name
          photo
        }
      }
    }
  `;
  ```

  The convention is to name the query constant in `ALL_CAPS`.

  > Note that the name of the query on the client doesn't have to match the query
  > type defined in the schema (there is no `GetTracks` in the schema), this is
  > just a client-side designator. However it should reference the schema on the
  > second line (`tracksForHome`).

  ### `useQuery` hook

  The `useQuery` hook provides a straightforward wrapper for sending queries and
  receiving data back from the server.

  When a component renders, `useQuery` returns an object from the Apollo Client
  that contains loading, error, and data properties.

  ```jsx
  const { loading, error, data } = useQuery(TRACKS);

  const Tracks = () => {
    const { loading, error, data } = useQuery(TRACKS);

    if (loading) return "Loading...";

    if (error) return `Error! ${error.message}`;

    return <Layout grid>{JSON.stringify(data)}</Layout>;
  };
  ```

  - We destructure the `loading, error, data` variables that are returned from the
    hook
  - We pass in our query constant as an argument.
  - In the example we just render the serialized data but we could of course pass
    the data as a prop and map through it in an embedded child component.

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

  # Apollo Client

  Apollo Client is the client-side counterpart to
  [Apollo Server](Apollo_Server.md). We use it for
  managing queries and mutations from the frontend to our Apollo GraphQL server.
  It is specifically designed to work with React.

  > We will be working with the
  > [schema](Apollo_Server.md#example-schema) we defined
  > when working on the server

  ## Initializing the client

  We initialise the client and set-up in memory caching to reduce network
  requests:

  ```js
  const client = new ApolloClient({
    uri: "http://localhost:4000",
    cache: new InMemoryCache(),
  });
  ```

  > The `uri` property must match the location of our Apollo server.

  ## Utilising the provider

  Apollo provides a top level application context that we can wrap our React app
  in. This will provide access to the client object from anywhere within the app,
  eg:

  ```jsx
  ReactDOM.render(
    <ApolloProvider client={client}>
      <GlobalStyles />
      <Pages />
    </ApolloProvider>,
    document.getElementById("root")
  );
  ```

  ## Running a query

  ### Queries as entry points

  From the client point of view, the queries in the schema are _entry points_.
  Although the queries exist in the schema, this alone is not sufficient for them
  to be entry points. Remember a schema is just a specification or contract
  between the frontend and the backend, it is not itself executable code.

  Therefore, for each query in the schema we must write a frontend implementation.
  We do this with **query constants**. The frontend implementation has a backend
  analogue: the
  [resolver](Apollo_Server.md#implementing-resolvers)
  that is invoked when the frontend issues a query. The schema standardises this
  relationship so that every query on the client must have a corresponding
  resolver on the backend.

  ### Query constants

  To run a query against our server we must define a query contant first. We use a
  `gql` literal again:

  ```js
  import { gql } from "@apollo/client";
  const TRACKS = gql`
    query GetTracks {
      tracksForHome {
        id
        title
        thumbnail
        length
        modulesCount
        author {
          name
          photo
        }
      }
    }
  `;
  ```

  The convention is to name the query constant in `ALL_CAPS`.

  > Note that the name of the query on the client doesn't have to match the query
  > type defined in the schema (there is no `GetTracks` in the schema), this is
  > just a client-side designator. However it should reference the schema on the
  > second line (`tracksForHome`).

  ### `useQuery` hook

  The `useQuery` hook provides a straightforward wrapper for sending queries and
  receiving data back from the server.

  When a component renders, `useQuery` returns an object from the Apollo Client
  that contains loading, error, and data properties.

  ```jsx
  const { loading, error, data } = useQuery(TRACKS);

  const Tracks = () => {
    const { loading, error, data } = useQuery(TRACKS);

    if (loading) return "Loading...";

    if (error) return `Error! ${error.message}`;

    return <Layout grid>{JSON.stringify(data)}</Layout>;
  };
  ```

  - We destructure the `loading, error, data` variables that are returned from the
    hook
  - We pass in our query constant as an argument.
  - In the example we just render the serialized data but we could of course pass
    the data as a prop and map through it in an embedded child component.


path: Apollo_Server.md
sortable_path: Apollo_Server.md
title: Apollo Server
checksum: 50995c32734e2cc9fbe3b68e5c58924fa885d669585b922b2fb1c94deb1c9154
created: 2024-10-22 16:52:56.362985696+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["graphql","REST","APIs"]}
lead:
  > Apollo Server is the part of the Apollo suite that we use to create the
  > backend of a GraphQL project: a GraphQL server.

body:
  > 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)

raw_content:
  ---
  tags: [graphql, REST, APIs]
  ---

  # 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)


path: Application_Layer_of_Internet_Protocol.md
sortable_path: Application_Layer_of_Internet_Protocol.md
title: Application Layer of the Internet Protocol
lead: The final, topmost layer of the Internet Protocol suite.
checksum: b1dff8e8911ef498988c406e96ff83d64f86f048499cc3c958ca5d4b29b5edfa
created: 2024-10-22 16:52:56.367320956+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"created":"Friday, September 06, 2024","tags":["internet","networks"]}
body:
  The final, topmost layer of the Internet Protocol suite.

  ![Data at the Application Layer](../img/application-layer-data.png)

  The Application Layer is where we get to the protocols that describe the
  behaviour of applications. All the preceding lower levels are effectively
  **plumbing that enables applications to do the things they want accross the
  internet**.

  Whereas the lower levels facilitate communication accross the internet, the
  Application Layer is concerned with achieving a specific task once the ability
  to communicate is assured.

  Application Layer protocols include HTTP, the Simple Mail Transfer Protocol
  (SMTP), and FTP.

raw_content:
  ---
  tags: [internet, networks]
  created: Friday, September 06, 2024
  ---

  # Application Layer of the Internet Protocol

  The final, topmost layer of the Internet Protocol suite.

  ![Data at the Application Layer](../img/application-layer-data.png)

  The Application Layer is where we get to the protocols that describe the
  behaviour of applications. All the preceding lower levels are effectively
  **plumbing that enables applications to do the things they want accross the
  internet**.

  Whereas the lower levels facilitate communication accross the internet, the
  Application Layer is concerned with achieving a specific task once the ability
  to communicate is assured.

  Application Layer protocols include HTTP, the Simple Mail Transfer Protocol
  (SMTP), and FTP.


path: Application_state_management_with_React_hooks.md
sortable_path: Application_state_management_with_React_hooks.md
title: Application state management
checksum: da892726957c790ec1bce967b56554aea92feeb3b52aed639a7ae0dae3d3a10e
created: 2024-10-22 16:52:56.368282027+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["javascript","react"]}
lead:
  Although [React_useReducer](React_useReducer.md) and [React_useContext](React_useContext.md) have
  many sound use cases by themselves, when they are combined they offer a way to
  acheive a system of global state management, without utilising third-party
  libraries like Redux.

body:
  Although [React_useReducer](React_useReducer.md) and [React_useContext](React_useContext.md) have
  many sound use cases by themselves, when they are combined they offer a way to
  acheive a system of global state management, without utilising third-party
  libraries like Redux.

  ## Requirements

  The Context API combined with a reducer addresses the following needs:

  - be able to consume global state from anywhere in the component hierarchy
    without prop-drilling
  - update global state from within child components from anywhere in the
    component hierarchy
  - access, maintain and update complex structured data (arrays/objects) not just
    single values, as global state

  ## Implementation

  In essence the approach is as follows:

  - Create a context provider that houses the global state object
  - Attach this state to a reducer function that operates on the state object
    through dispatch methods

  First off, our initial state and overall state object:

  ```js
  const initialState = {
    count: 0,
  };
  ```

  Next, we create our context. This is what we will invoke when we want to consume
  our state.

  ```js
  export const CounterContext = React.createContext({});
  ```

  Now we need a reducer that will handle the state updates. We will just use the
  same setup as we used in the example of
  [React_useReducer](React_useReducer.md#refining-the-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;
      case "increase_by_payload":
        return { ...state, counter: state.counter + action.payload };
      default:
        throw new Error();
    }
    return newState;
  }
  ```

  Finally, we need to make a provider as a top-level component that receives the
  reducer's `state` and `dispatch` methods as props:

  ```js
  export const CounterProvider = ({children}) => {
      // We pass our reducer function and initial state to useReducer as params
      const [state, dispatch] = React.useReducer(reducer, initialState)

      // We create our provider and pass the reducer state and update method as props. This is the provider to the CounterContext consumer
      <CounterContext.Provider value={{state, dispatch}}>
          {children}
      </CounterContext.Provider>
  }
  ```

  Now whenever we want to consume or update our global state, we can invoke the
  context within a lower-level component, for example:

  ```js
  const {state, dispatch} = useContext(CounterContext);

  dispatch({
      type 'increase_by_payload',
      payload: 4
  })

  console.log(state.counter) // 4

  ```

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

  # Application state management

  Although [React_useReducer](React_useReducer.md) and [React_useContext](React_useContext.md) have
  many sound use cases by themselves, when they are combined they offer a way to
  acheive a system of global state management, without utilising third-party
  libraries like Redux.

  ## Requirements

  The Context API combined with a reducer addresses the following needs:

  - be able to consume global state from anywhere in the component hierarchy
    without prop-drilling
  - update global state from within child components from anywhere in the
    component hierarchy
  - access, maintain and update complex structured data (arrays/objects) not just
    single values, as global state

  ## Implementation

  In essence the approach is as follows:

  - Create a context provider that houses the global state object
  - Attach this state to a reducer function that operates on the state object
    through dispatch methods

  First off, our initial state and overall state object:

  ```js
  const initialState = {
    count: 0,
  };
  ```

  Next, we create our context. This is what we will invoke when we want to consume
  our state.

  ```js
  export const CounterContext = React.createContext({});
  ```

  Now we need a reducer that will handle the state updates. We will just use the
  same setup as we used in the example of
  [React_useReducer](React_useReducer.md#refining-the-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;
      case "increase_by_payload":
        return { ...state, counter: state.counter + action.payload };
      default:
        throw new Error();
    }
    return newState;
  }
  ```

  Finally, we need to make a provider as a top-level component that receives the
  reducer's `state` and `dispatch` methods as props:

  ```js
  export const CounterProvider = ({children}) => {
      // We pass our reducer function and initial state to useReducer as params
      const [state, dispatch] = React.useReducer(reducer, initialState)

      // We create our provider and pass the reducer state and update method as props. This is the provider to the CounterContext consumer
      <CounterContext.Provider value={{state, dispatch}}>
          {children}
      </CounterContext.Provider>
  }
  ```

  Now whenever we want to consume or update our global state, we can invoke the
  context within a lower-level component, for example:

  ```js
  const {state, dispatch} = useContext(CounterContext);

  dispatch({
      type 'increase_by_payload',
      payload: 4
  })

  console.log(state.counter) // 4

  ```


path: Arithmetic_Logic_Unit.md
sortable_path: Arithmetic_Logic_Unit.md
title: Arithmetic Logic Unit (ALU)
checksum: 94c70c85b6b233fc4ec4ad9272a6a3d33141310dbfdfe648547443d7304c4077
created: 2024-10-22 16:52:56.371278035+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["CPU"]}
lead:
  The ALU is the centerpiece or core of the
  [CPU](CPU_architecture.md) architecture, where the
  binary calculations occur. All the other components on the CPU chip are
  appendanges to the execution that occurs within the ALU.

body:
  The ALU is the centerpiece or core of the
  [CPU](CPU_architecture.md) architecture, where the
  binary calculations occur. All the other components on the CPU chip are
  appendanges to the execution that occurs within the ALU.

  The ALU comprises
  [logic gates](Logic_gates.md) that
  execute the instructions passed from memory and where the data stored by the
  registers is acted upon. A processor's ALU is just a complex combinatorial logic
  circuit.

  It executes arithmetic and logical operations on binary numbers and where you
  will find operations conducted by
  [full-adders and half adders](Half_adder_and_full_adder.md)
  etc.

  More specifically, the ALU is responsible for the _execute_ phase of the
  [fetch, decode, execute cycle](Fetch_decode_execute.md).

  ## ALU execution lifecycle

  1. **Inputs**: The ALU receives two operands and a control signal as inputs. The
     operands are the data on which the operations will be performed. The control
     signal tells the ALU which operation it needs to perform.
  2. **Perform operation**: The ALU carries out the requested operation. For
     instance, if the control signal indicates an addition operation, the ALU sums
     the two operands.
  3. **Output**: The ALU then sends the operation to another part of the CPU for
     further processing or storage. The ALU also outputs a status bit that can be
     used by other parts of the CPU to make decisions. For instance if the additon
     results in a value that is too large to be stored, the ALU will set an
     overflow flag.

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

  # Arithmetic Logic Unit (ALU)

  The ALU is the centerpiece or core of the
  [CPU](CPU_architecture.md) architecture, where the
  binary calculations occur. All the other components on the CPU chip are
  appendanges to the execution that occurs within the ALU.

  The ALU comprises
  [logic gates](Logic_gates.md) that
  execute the instructions passed from memory and where the data stored by the
  registers is acted upon. A processor's ALU is just a complex combinatorial logic
  circuit.

  It executes arithmetic and logical operations on binary numbers and where you
  will find operations conducted by
  [full-adders and half adders](Half_adder_and_full_adder.md)
  etc.

  More specifically, the ALU is responsible for the _execute_ phase of the
  [fetch, decode, execute cycle](Fetch_decode_execute.md).

  ## ALU execution lifecycle

  1. **Inputs**: The ALU receives two operands and a control signal as inputs. The
     operands are the data on which the operations will be performed. The control
     signal tells the ALU which operation it needs to perform.
  2. **Perform operation**: The ALU carries out the requested operation. For
     instance, if the control signal indicates an addition operation, the ALU sums
     the two operands.
  3. **Output**: The ALU then sends the operation to another part of the CPU for
     further processing or storage. The ALU also outputs a status bit that can be
     used by other parts of the CPU to make decisions. For instance if the additon
     results in a value that is too large to be stored, the ALU will set an
     overflow flag.


path: Arrays.md
sortable_path: Arrays.md
title: Arrays
checksum: ed6c28fdf4d51abda193b6c6488e006f1bbbce6ed275a00bfde113d65e1e520c
created: 2024-10-22 16:52:56.374613148+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["data-structures"]}
lead:
  > Arrays are used both on their own and to implement many other data structures
  > that place additional restrictions on how the data is manipulated.

body:
  > Arrays are used both on their own and to implement many other data structures
  > that place additional restrictions on how the data is manipulated.

  ## Algorithmic complexity

  In terms of data retrieval arrays have a good runtime since retrieving an
  element or storing an element takes constant time. Also an entire array takes up
  O(n) space.

  This only applies in the case of strict arrays that exist in strictly typed and
  more advanced languages such as Java and C++. In JavaScript an array is really
  closer to a list in other languages. That is to say, it's size (and of course
  the types it holds) does not need to be known in advance or specified at all. In
  stricter languages, you would cast the type for the array and specify its
  length, for example:

  ```cpp
  int anArray[10] = { 1, 2, 3, 4,  5, 6, 7, 8, 9, 10 }
  ```

  Whilst storage and retrieval take place in constant time, deleting and inserting
  elements through methods like `pop` and `shift` are more time consuming, since
  the index of every item has to be updated in response.

  ## Random access

  As we can access any element of the array at any time through index notation,
  arrays offer random and non-sequential access. This is in contrast to stacks and
  queues (where only the top/bottom can be accessed) and linked lists.

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

  # Arrays

  > Arrays are used both on their own and to implement many other data structures
  > that place additional restrictions on how the data is manipulated.

  ## Algorithmic complexity

  In terms of data retrieval arrays have a good runtime since retrieving an
  element or storing an element takes constant time. Also an entire array takes up
  O(n) space.

  This only applies in the case of strict arrays that exist in strictly typed and
  more advanced languages such as Java and C++. In JavaScript an array is really
  closer to a list in other languages. That is to say, it's size (and of course
  the types it holds) does not need to be known in advance or specified at all. In
  stricter languages, you would cast the type for the array and specify its
  length, for example:

  ```cpp
  int anArray[10] = { 1, 2, 3, 4,  5, 6, 7, 8, 9, 10 }
  ```

  Whilst storage and retrieval take place in constant time, deleting and inserting
  elements through methods like `pop` and `shift` are more time consuming, since
  the index of every item has to be updated in response.

  ## Random access

  As we can access any element of the array at any time through index notation,
  arrays offer random and non-sequential access. This is in contrast to stacks and
  queues (where only the top/bottom can be accessed) and linked lists.


path: Assembly.md
sortable_path: Assembly.md
title: Assembly
lead:
  - Assembly is one level up from machine code and provides a more human-friendly
  abstraction layer

checksum: 72b7486baf4304139997fdc06d66fc465dd7226f470814b14b095b1a086bf7b1
created: 2024-10-22 16:52:56.375300524+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"created":"Tuesday, March 12, 2024","id":"2v5c","tags":["CPU"]}
body:
  - Assembly is one level up from machine code and provides a more human-friendly
    abstraction layer

  - An assembly instruction is a _mneumonic_ that comprises an "op code" plus
    operands

  - It is translated back to machine code by an assembler for the computer to
    execute.

  ## Example instruction

  We can translate the machine instruction `e3a07004` into the assembly
  instruction `mov r7, #4`. This translates to: _move the value number 4 to the
  register r7_.

  ## Assemblers

  Although assembly is useful for humans, computers still work at the level of
  machine code. Therefore you need an **assembler** to translate the assembly code
  to machine code. An assembly language text file is fed into an assembler and a
  binary object file containing machine code is returned.

  A disassembler does the opposite: translate machine code into assembly.

  ## Relation to instruction set architectures

  - the ISA defines the hardware capabilities and the instructions that can be run
    on the hardware
  - machine code is a binary representation of these instructions and can be
    directly executed by the CPU
  - humans use an assembly language version of the machine code which is then
    translated back to machine code for the computer to execute.

  ## Related notes

  [[Hexadecimal_number_system]], [[Instruction_set_architectures]],
  [[CPU_architecture]]

raw_content:
  ---
  id: 2v5c
  tags: [CPU]
  created: Tuesday, March 12, 2024
  ---

  # Assembly

  - Assembly is one level up from machine code and provides a more human-friendly
    abstraction layer

  - An assembly instruction is a _mneumonic_ that comprises an "op code" plus
    operands

  - It is translated back to machine code by an assembler for the computer to
    execute.

  ## Example instruction

  We can translate the machine instruction `e3a07004` into the assembly
  instruction `mov r7, #4`. This translates to: _move the value number 4 to the
  register r7_.

  ## Assemblers

  Although assembly is useful for humans, computers still work at the level of
  machine code. Therefore you need an **assembler** to translate the assembly code
  to machine code. An assembly language text file is fed into an assembler and a
  binary object file containing machine code is returned.

  A disassembler does the opposite: translate machine code into assembly.

  ## Relation to instruction set architectures

  - the ISA defines the hardware capabilities and the instructions that can be run
    on the hardware
  - machine code is a binary representation of these instructions and can be
    directly executed by the CPU
  - humans use an assembly language version of the machine code which is then
    translated back to machine code for the computer to execute.

  ## Related notes

  [[Hexadecimal_number_system]], [[Instruction_set_architectures]],
  [[CPU_architecture]]


path: Atomic_and_molecular_propositions.md
sortable_path: Atomic_and_molecular_propositions.md
title: Atomic and molecular propositions
lead: Propositions are expressions **that have truth values**, either true or false.
checksum: 0f1737bf0899ba64ecff683beeb9fb8ca98568e09ae3b9c6d2fb7557fa194425
created: 2024-10-22 16:52:56.380720987+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  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.

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

  # 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.


path: Awk.md
sortable_path: Awk.md
title: Awk
checksum: 556b41b3727769b88974bcba5fa87bd2ed6666c38a121b4a9b040f0d13bcd34f
created: 2024-10-22 16:52:56.381561875+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["shell","awk"]}
lead:
  > Awk is a programming language designed for text processing and data
  > extraction. It was created in the 1970s and remains widely used today for
  > tasks such as filtering and transforming text data, generating reports, and
  > performing basic calculations. Awk is known for its simplicity and
  > versatility, making it a popular tool for Unix system administrators and data
  > analysts.

body:
  > Awk is a programming language designed for text processing and data
  > extraction. It was created in the 1970s and remains widely used today for
  > tasks such as filtering and transforming text data, generating reports, and
  > performing basic calculations. Awk is known for its simplicity and
  > versatility, making it a popular tool for Unix system administrators and data
  > analysts.

  ## Invocation

  We can use `awk` directly in `stdin` or we can reference `.awk` files for more
  elaborate scripts

  ```bash
  # CLI
  awk [program] file1, file2, file3

  # Script file
  awk -f [ref_to_script_file] file1, file2, file3
  ```

  We can also pipe to it. This piped command receives output from the `echo`
  command and prints the value in the last field for each record:

  ```bash
  echo -e "1 2 3 5\n2 2 3 8" | awk '{print $(NF)}'
  ```

  ## Syntactic structure

  `awk` is a line-oriented language.

  An `awk` program consists in a sequence of **pattern: action** statements and
  optional functional definitions.

  For most of the examples we will use this list as the input:

  ```
  cloud
  existence
  ministerial
  falcon
  town
  sky
  top
  bookworm
  bookcase
  war
  Peter 89
  Lucia 95
  Thomas 76
  Marta 67
  Joe 92
  Alex 78
  Sophia 90
  Alfred 65
  Kate 46
  ```

  > `awk` particularly lends itself to inputs that are structured by whitespace or
  > in columns, like what you get from commands like `ls` and `grep`

  ### Patterns and actions

  The basic structure of an `awk` script is as follows:

  ```
  pattern {action}
  ```

  A **pattern** is what you want to match against. It can be a literal string or a
  regex. The **action** is what process you want to execute against the lines in
  the input that match the pattern.

  The following script prints the line that matches `Joe`:

  ```bash
  awk '/Joe/ {print}' list.txt
  ```

  `/Joe/` is the patttern and `{print}` is the action.

  ### Lines, records, fields

  ![](/img/awk-outline.png)

  When `awk` receives a file it divides the lines into **records**.

  Each line `awk` receives is broken up into a sequence of **fields**.

  The fields are accessed by special variables:

  - `$1` reads the first field, `$2` reads the second field and so on.

  - The variable `$0` refers to the whole record

  So, in the picture `cloud existence ministerial` corresponse to `$1` `$2` `$3`

  ## Basic examples

  **_Match a pattern_**

  ```bash
  awk '/book/ { print }' list.txt
  # bookworm
  # bookcase
  ```

  **_Print all words that are longer that five characters_**

  ```bash
  awk 'length($1) > 5 { print $0 }' list.txt
  ```

  For the first field of every line (we only have one field per line), if it is
  greater than 5 characters print it. The "every line" part is provided for via
  the all fields variable - `$0`.

  We actually don't need to include the `{ print $0 }` action, as this is the
  default behaviour. We could have just put `length($1) > 5 list.txt`

  **_Print all words that do not have three characters_**

  ```bash
  awk '!(length($1) == 3)' list.txt
  ```

  Here we negate by prepending the pattern with `!` and wrapping it in
  parentheses.

  **_Return words that are either three characters or four characters in length_**

  ```
  awk '(length($1) == 3) || (length($1) == 4)' list.txt
  ```

  Here we use the logical OR to match against more than one pattern. Notice that
  whenever we use a Boolean operator such as NOT or OR, we wrap our pattern in
  parentheses.

  **_Match and string-interpolate the output_**

  ```bash
  awk 'length($1) > 0  {print $1, "has", length($1), "chars"}' list.txt

  # storeroom has 9 chars
  # tree has 4 chars
  # cup has 3 chars
  ```

  **_Match against a numerical property_**

  ```bash
  awk '$2 >= 90 { print $0 }' scores.txt

  # Lucia 95
  # Joe 92
  # Sophia 90
  ```

  This returns the records where there is a secondary numerical field that is
  greater than 90.

  **_Match a field against a regular expression_**

  ```bash
  awk '$1 ~ /^[b,c]/ {print $1}' words.txt
  ```

  This matches all the fields in the `$1` place that begin with 'b' or 'c'.

  The tilde is the regex match operator. You must be passing a regex to use it,
  otherwise use `==`.

  ## Syntactic shorthands

  - For a statement like `awk 'length($1) > 5 { print $0 }' list.txt`. We actually
    don't need to include the `{ print $0 }` action, as this is the default
    behaviour and it is implied. We could have just put `length($1) > 5 list.txt`.

  https://zetcode.com/lang/awk/

  ## Built-in variables

  ### `NF`

  The value of `NF` is the **number** of **fields** in the current record. `Awk`
  automatically updates the value of `NF` every time it reads a record.

  No matter how many fields there are, the last value in a record can always be
  represented by `$NF`.

  ### `NR`

  `NR` represents the **number** of **records**. It is set at the point at which
  the file is read.

  ### `FS`

  `FS` represents the **field separator**. The default field separator is a space.
  We can specify a different separator with the `-F` flag. E.g to separate by
  comma:

  ```bash
  awk -F, '{print $1 }' list.txt
  ```

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

  # Awk

  > Awk is a programming language designed for text processing and data
  > extraction. It was created in the 1970s and remains widely used today for
  > tasks such as filtering and transforming text data, generating reports, and
  > performing basic calculations. Awk is known for its simplicity and
  > versatility, making it a popular tool for Unix system administrators and data
  > analysts.

  ## Invocation

  We can use `awk` directly in `stdin` or we can reference `.awk` files for more
  elaborate scripts

  ```bash
  # CLI
  awk [program] file1, file2, file3

  # Script file
  awk -f [ref_to_script_file] file1, file2, file3
  ```

  We can also pipe to it. This piped command receives output from the `echo`
  command and prints the value in the last field for each record:

  ```bash
  echo -e "1 2 3 5\n2 2 3 8" | awk '{print $(NF)}'
  ```

  ## Syntactic structure

  `awk` is a line-oriented language.

  An `awk` program consists in a sequence of **pattern: action** statements and
  optional functional definitions.

  For most of the examples we will use this list as the input:

  ```
  cloud
  existence
  ministerial
  falcon
  town
  sky
  top
  bookworm
  bookcase
  war
  Peter 89
  Lucia 95
  Thomas 76
  Marta 67
  Joe 92
  Alex 78
  Sophia 90
  Alfred 65
  Kate 46
  ```

  > `awk` particularly lends itself to inputs that are structured by whitespace or
  > in columns, like what you get from commands like `ls` and `grep`

  ### Patterns and actions

  The basic structure of an `awk` script is as follows:

  ```
  pattern {action}
  ```

  A **pattern** is what you want to match against. It can be a literal string or a
  regex. The **action** is what process you want to execute against the lines in
  the input that match the pattern.

  The following script prints the line that matches `Joe`:

  ```bash
  awk '/Joe/ {print}' list.txt
  ```

  `/Joe/` is the patttern and `{print}` is the action.

  ### Lines, records, fields

  ![](/img/awk-outline.png)

  When `awk` receives a file it divides the lines into **records**.

  Each line `awk` receives is broken up into a sequence of **fields**.

  The fields are accessed by special variables:

  - `$1` reads the first field, `$2` reads the second field and so on.

  - The variable `$0` refers to the whole record

  So, in the picture `cloud existence ministerial` corresponse to `$1` `$2` `$3`

  ## Basic examples

  **_Match a pattern_**

  ```bash
  awk '/book/ { print }' list.txt
  # bookworm
  # bookcase
  ```

  **_Print all words that are longer that five characters_**

  ```bash
  awk 'length($1) > 5 { print $0 }' list.txt
  ```

  For the first field of every line (we only have one field per line), if it is
  greater than 5 characters print it. The "every line" part is provided for via
  the all fields variable - `$0`.

  We actually don't need to include the `{ print $0 }` action, as this is the
  default behaviour. We could have just put `length($1) > 5 list.txt`

  **_Print all words that do not have three characters_**

  ```bash
  awk '!(length($1) == 3)' list.txt
  ```

  Here we negate by prepending the pattern with `!` and wrapping it in
  parentheses.

  **_Return words that are either three characters or four characters in length_**

  ```
  awk '(length($1) == 3) || (length($1) == 4)' list.txt
  ```

  Here we use the logical OR to match against more than one pattern. Notice that
  whenever we use a Boolean operator such as NOT or OR, we wrap our pattern in
  parentheses.

  **_Match and string-interpolate the output_**

  ```bash
  awk 'length($1) > 0  {print $1, "has", length($1), "chars"}' list.txt

  # storeroom has 9 chars
  # tree has 4 chars
  # cup has 3 chars
  ```

  **_Match against a numerical property_**

  ```bash
  awk '$2 >= 90 { print $0 }' scores.txt

  # Lucia 95
  # Joe 92
  # Sophia 90
  ```

  This returns the records where there is a secondary numerical field that is
  greater than 90.

  **_Match a field against a regular expression_**

  ```bash
  awk '$1 ~ /^[b,c]/ {print $1}' words.txt
  ```

  This matches all the fields in the `$1` place that begin with 'b' or 'c'.

  The tilde is the regex match operator. You must be passing a regex to use it,
  otherwise use `==`.

  ## Syntactic shorthands

  - For a statement like `awk 'length($1) > 5 { print $0 }' list.txt`. We actually
    don't need to include the `{ print $0 }` action, as this is the default
    behaviour and it is implied. We could have just put `length($1) > 5 list.txt`.

  https://zetcode.com/lang/awk/

  ## Built-in variables

  ### `NF`

  The value of `NF` is the **number** of **fields** in the current record. `Awk`
  automatically updates the value of `NF` every time it reads a record.

  No matter how many fields there are, the last value in a record can always be
  represented by `$NF`.

  ### `NR`

  `NR` represents the **number** of **records**. It is set at the point at which
  the file is read.

  ### `FS`

  `FS` represents the **field separator**. The default field separator is a space.
  We can specify a different separator with the `-F` flag. E.g to separate by
  comma:

  ```bash
  awk -F, '{print $1 }' list.txt
  ```


path: Axioms_of_set_theory.md
sortable_path: Axioms_of_set_theory.md
title: Axioms of set theory
checksum: 2421995f5e5a2902a6d12d29e642bb846a568768a22cf93fa384272df50a4e6e
created: 2024-10-22 16:52:56.382341161+00:00
modified: 2024-10-18 18:10:26.925754646+00:00
metadata: {"tags":["set-theory","theorems"]}
lead:
  The basic notions of set theory are defined in
  [Basic properties of sets](Basic%20properties%20of%20sets.md). There we
  introduced a formal syntax that will be utilised to define the axioms. For easy
  reference:

body:
  The basic notions of set theory are defined in
  [Basic properties of sets](Basic%20properties%20of%20sets.md). There we
  introduced a formal syntax that will be utilised to define the axioms. For easy
  reference:

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

  ## Axiom of Extensionality

  Sets which contain the same members are the same set. If sets A and B contain
  the same elements then A = B.
  $$\forall a \forall b [\forall x (x \in a \longleftrightarrow x \in b) \rightarrow a =b]$$

  [link to test](beta.md)

raw_content:
  ---
  tags:
    - set-theory
    - theorems
  ---

  # Axioms of set theory

  The basic notions of set theory are defined in
  [Basic properties of sets](Basic%20properties%20of%20sets.md). There we
  introduced a formal syntax that will be utilised to define the axioms. For easy
  reference:

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

  ## Axiom of Extensionality

  Sets which contain the same members are the same set. If sets A and B contain
  the same elements then A = B.
  $$\forall a \forall b [\forall x (x \in a \longleftrightarrow x \in b) \rightarrow a =b]$$

  [link to test](beta.md)


path: Basic_model_of_the_operating_system.md
sortable_path: Basic_model_of_the_operating_system.md
title: Basic model of a nix operating system
lead:
  We can abstract the Linux OS into three operational levels or tiers, from the
  bottom up:

checksum: adb2cb2eb5321b12cb4fe19d75c059b192eb7b8440a10b6661014617a2c99742
created: 2024-10-22 16:52:56.384664098+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["operating-systems","Linux"]}
body:
  We can abstract the Linux OS into three operational levels or tiers, from the
  bottom up:

  <dl>
  <dt>User processes: user space</dt>
  <dd>The running programs that the kernel manages. Also known as the user space which is the memory that the kernal assigns for user processes. Comprising:
  <ul>
      <li>Graphical user interface</li>
      <li>Servers</li> 
      <li>Shell</li>    
    </ul>
  <dt>Kernel: kernel space</dt>
  </dd>
  <dd>The core of the operating system. Software residing in memory that tells the CPU where to look for its next task. Acts as a mediator and primary interface between the hardware and the user processes. Known as kernel space: the memory that the kernel allocates for itself. Comprising:
    <ul>
      <li>System calls</li>
      <li>Process management</li> 
      <li>Memory management</li>    
      <li>Device drivers</li>    
    </ul>
  </dd>
  <dt>Hardware</dt>
  <dd>The base: one or more CPUs and RAM memory performing computations and writing to memory. Comprising:
    <ul>
      <li>Processor (CPU)</li>
      <li>Main memory (RAM)</li> 
      <li>Disks</li>    
      <li>Network ports</li>    
    </ul>
  </dl>

  !! Add info on kernel mode and user mode
  https://www.geeksforgeeks.org/user-mode-and-kernel-mode-switching/

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

  # Basic model of a nix operating system

  We can abstract the Linux OS into three operational levels or tiers, from the
  bottom up:

  <dl>
  <dt>User processes: user space</dt>
  <dd>The running programs that the kernel manages. Also known as the user space which is the memory that the kernal assigns for user processes. Comprising:
  <ul>
      <li>Graphical user interface</li>
      <li>Servers</li> 
      <li>Shell</li>    
    </ul>
  <dt>Kernel: kernel space</dt>
  </dd>
  <dd>The core of the operating system. Software residing in memory that tells the CPU where to look for its next task. Acts as a mediator and primary interface between the hardware and the user processes. Known as kernel space: the memory that the kernel allocates for itself. Comprising:
    <ul>
      <li>System calls</li>
      <li>Process management</li> 
      <li>Memory management</li>    
      <li>Device drivers</li>    
    </ul>
  </dd>
  <dt>Hardware</dt>
  <dd>The base: one or more CPUs and RAM memory performing computations and writing to memory. Comprising:
    <ul>
      <li>Processor (CPU)</li>
      <li>Main memory (RAM)</li> 
      <li>Disks</li>    
      <li>Network ports</li>    
    </ul>
  </dl>

  !! Add info on kernel mode and user mode
  https://www.geeksforgeeks.org/user-mode-and-kernel-mode-switching/


path: Basic_properties_of_sets.md
sortable_path: Basic_properties_of_sets.md
title: Basic properties of sets
lead: ## Set theory
checksum: 9b55c6a03f81d5a81ac8fb971cdb41402ae99b96ba131001672265865fb76aa0
created: 2024-10-22 16:52:56.386168486+00:00
modified: 2024-10-18 18:11:22.740147009+00:00
metadata: {"tags":["set-theory"]}
body:
  ## 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)

raw_content:
  ---
  tags:
    - set-theory
  ---

  # 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)


path: Basics_of_web_components.md
sortable_path: Basics_of_web_components.md
title: Basics of Web Components
checksum: f6d7375777d8de96f5a26c61061288c88ebdce6034cb2bd30c88d98d36316858
created: 2024-10-22 16:52:56.387617594+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":[]}
lead:
  Web Components are custom HTML elements like those we create with frameworks
  like React and Angular but which are native to HTML and the DOM and therefore do
  not require any special compilation or libraries.

body:
  Web Components are custom HTML elements like those we create with frameworks
  like React and Angular but which are native to HTML and the DOM and therefore do
  not require any special compilation or libraries.

  - Designed to be interoperable with normal HTML
  - Designed to be interoperable with component frameworks like React
  - Designed to be highly modular and reusable
  - Utilise the shadow DOM which is like the virtual DOM but native to the browser
    and invocable through plain JavaScript. A bit like an iFrame within the
    "light" DOM and helps greatly with style-scoping.

  ## Example of composing a web component:

  ```js
  // MyComponent.js
  class MyComponent extends HTMLElement {
    connectedCallback() {
      this.innerHTML = `<h1>Hello world</h1>`;
    }
  }

  customElements.define("my-component", MyComponent);
  ``;
  ```

  ```html
  <!-- index.html -->
  <my-component></my-component>
  ```

  ## Resources

  [A complete introdution to Web Components]()

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

  # Basics of Web Components

  Web Components are custom HTML elements like those we create with frameworks
  like React and Angular but which are native to HTML and the DOM and therefore do
  not require any special compilation or libraries.

  - Designed to be interoperable with normal HTML
  - Designed to be interoperable with component frameworks like React
  - Designed to be highly modular and reusable
  - Utilise the shadow DOM which is like the virtual DOM but native to the browser
    and invocable through plain JavaScript. A bit like an iFrame within the
    "light" DOM and helps greatly with style-scoping.

  ## Example of composing a web component:

  ```js
  // MyComponent.js
  class MyComponent extends HTMLElement {
    connectedCallback() {
      this.innerHTML = `<h1>Hello world</h1>`;
    }
  }

  customElements.define("my-component", MyComponent);
  ``;
  ```

  ```html
  <!-- index.html -->
  <my-component></my-component>
  ```

  ## Resources

  [A complete introdution to Web Components]()


path: Biconditional_Elimination.md
sortable_path: Biconditional_Elimination.md
title: Biconditional Elimination
checksum: 6e260f7e54fa14885b24a2638256a678b1d7d42ebe7daf660e09e03aff427b37
created: 2024-10-22 16:52:56.388664322+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["logic"]}
lead:
  Give that the biconditional means that if $P$ is the case, $Q$ must be the case
  and if $Q$ is the case, $P$ must be the case, if we have $P \leftrightarrow Q$
  and $P$, we can derive $Q$ and vice versa.

body:
  Give that the biconditional means that if $P$ is the case, $Q$ must be the case
  and if $Q$ is the case, $P$ must be the case, if we have $P \leftrightarrow Q$
  and $P$, we can derive $Q$ and vice versa.

  ![](/img/biconditional-elim.png)

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

  # Biconditional Elimination

  Give that the biconditional means that if $P$ is the case, $Q$ must be the case
  and if $Q$ is the case, $P$ must be the case, if we have $P \leftrightarrow Q$
  and $P$, we can derive $Q$ and vice versa.

  ![](/img/biconditional-elim.png)


path: Biconditional_Introduction.md
sortable_path: Biconditional_Introduction.md
title: Biconditional introduction
checksum: e4b45b3947cd932f0f761ab6e4867ae3289f7508fe324cf9f9986b1088dbb92d
created: 2024-10-22 16:52:56.389555572+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["logic"]}
lead:
  The biconditional means if $P$ is the case, $Q$ must be the case and if $Q$ is
  the case, $P$ must be the case. Thus to introduce this operator we must
  demonstrate both that $Q$ follows from $P$ and that $P$ follows from $Q$. We do
  this via two sub-proofs.

body:
  The biconditional means if $P$ is the case, $Q$ must be the case and if $Q$ is
  the case, $P$ must be the case. Thus to introduce this operator we must
  demonstrate both that $Q$ follows from $P$ and that $P$ follows from $Q$. We do
  this via two sub-proofs.

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

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

  # Biconditional introduction

  The biconditional means if $P$ is the case, $Q$ must be the case and if $Q$ is
  the case, $P$ must be the case. Thus to introduce this operator we must
  demonstrate both that $Q$ follows from $P$ and that $P$ follows from $Q$. We do
  this via two sub-proofs.

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


path: Binary_addition.md
sortable_path: Binary_addition.md
title: Binary addition
checksum: 5baa5a9b388d270c208c6e9a81daa2fad52eeefe810514ab1abfdba1d341312a
created: 2024-10-22 16:52:56.390286328+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["binary"]}
lead:
  - We add binary values in columns just like we would with denary addition.
  - Each column is classified on the basis of place-value. In denary this is 10,
    in binary it is 2.
  - When we conduct a binary addition, we add the binary values as if they were
    normal integers but we represent the sums as binary.
    - For example: $1 + 1 = 2$ for the calculation but the sum is $10$

body:
  - We add binary values in columns just like we would with denary addition.
  - Each column is classified on the basis of place-value. In denary this is 10,
    in binary it is 2.
  - When we conduct a binary addition, we add the binary values as if they were
    normal integers but we represent the sums as binary.
    - For example: $1 + 1 = 2$ for the calculation but the sum is $10$

  ## Examples

  ### Example one

  $$
  1010 + 0111 = 10001
  $$

  $$
  10 + 7 = 17
  $$

  In the first column: $1 + 0 = 1$, so we simply put the binary value for $1$:

  ```
  1010
  0111
  ____
     1
  ```

  In the second column: $1 + 1 = 2$. In binary this is represented as $10$. So we
  put $0$ beneath the bar and carry the $1$:

  ```
   1
  1010
  0111
  ____
    01
  ```

  In the third column, we repeat the previous process. We are presented with
  $1 + 0 + 1$ which is $2$. As $2$ is $10$ in binary, we put the zero beneath the
  line and carry the $1$:

  ```
  11
  1010
  0111
  ____
   001
  ```

  In the final column, we again have $1+1$ giving us $2$ or $10$ but at this point
  we cannot carry any more because we have reached the final column. So instead of
  carrying the $1$ we just put both digits beneath the line $10$.

  ```
   11
   1010
   0111
  _____
  10001
  ```

  ### Example two

  $$
  1001 + 0111 = 10000
  $$

  $$
  9 + 7 = 16
  $$

  ```
   111
   1001
   0111
  _____
  10000
  ```

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

  # Binary addition

  - We add binary values in columns just like we would with denary addition.
  - Each column is classified on the basis of place-value. In denary this is 10,
    in binary it is 2.
  - When we conduct a binary addition, we add the binary values as if they were
    normal integers but we represent the sums as binary.
    - For example: $1 + 1 = 2$ for the calculation but the sum is $10$

  ## Examples

  ### Example one

  $$
  1010 + 0111 = 10001
  $$

  $$
  10 + 7 = 17
  $$

  In the first column: $1 + 0 = 1$, so we simply put the binary value for $1$:

  ```
  1010
  0111
  ____
     1
  ```

  In the second column: $1 + 1 = 2$. In binary this is represented as $10$. So we
  put $0$ beneath the bar and carry the $1$:

  ```
   1
  1010
  0111
  ____
    01
  ```

  In the third column, we repeat the previous process. We are presented with
  $1 + 0 + 1$ which is $2$. As $2$ is $10$ in binary, we put the zero beneath the
  line and carry the $1$:

  ```
  11
  1010
  0111
  ____
   001
  ```

  In the final column, we again have $1+1$ giving us $2$ or $10$ but at this point
  we cannot carry any more because we have reached the final column. So instead of
  carrying the $1$ we just put both digits beneath the line $10$.

  ```
   11
   1010
   0111
  _____
  10001
  ```

  ### Example two

  $$
  1001 + 0111 = 10000
  $$

  $$
  9 + 7 = 16
  $$

  ```
   111
   1001
   0111
  _____
  10000
  ```


path: Binary_colour_encoding.md
sortable_path: Binary_colour_encoding.md
title: Binary encoding of colours
checksum: f7d32918877f637734c3fbf1e59750eb947e08049492017e842e58b6f267431b
created: 2024-10-22 16:52:56.391547518+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["binary"]}
lead:
  The approach to encoding binary representations of colour is very similar to the
  approach we explored when looking at the encoding of
  [alphanumeric values](Text_encoding.md).

body:
  The approach to encoding binary representations of colour is very similar to the
  approach we explored when looking at the encoding of
  [alphanumeric values](Text_encoding.md).

  We begin by determining the total number of colours and colour shades we want to
  represent. With this value established we then decide on the bit-length required
  that will accomodate this number of variations. Finally, we assign a binary
  number to each representation.

  ### Greyscale

  We can start with a limited palette: greyscale. Here there is black and white
  and number of interim shades of grey. The convention is to use an 8-bit number
  which, given $2^8 = 256$ provides 256 shade variations.

  In decimal, 0 is equal to black (zero light intensity) and 255 is equal to white
  (full light intensity). Some examples of this (including binary and hex
  representations are below):

  ![](/img/greyscale-encoding.svg)

  ### Colour encoding

  A single 8-bit number is sufficient for 256 greyscale combinations but with
  colour, the convention is to use three 8-bit numbers where each byte corresponds
  to a value for red, green and blue (RGB). Thus the overall bit-length is 24
  ($2^3 + 2^3 + 2^3$). Each value corresponds to a light intensity for the given
  colour. Combined they are capable of representing all colours.

  Some examples below

  #### Red

  Red is represented in RGB with all 8 red bits to set to 1 and the remaining 16
  bits for the other two colours set to 0.

  ![](/img/red-encoding.svg)

  #### Yellow

  Yellow is represented in RGB with both red and blue set to 1 and the remaining 8
  green bits set to ):

  ![](/img/yellow-encoding.svg)

  ## Binary encoding of images

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

  # Binary encoding of colours

  The approach to encoding binary representations of colour is very similar to the
  approach we explored when looking at the encoding of
  [alphanumeric values](Text_encoding.md).

  We begin by determining the total number of colours and colour shades we want to
  represent. With this value established we then decide on the bit-length required
  that will accomodate this number of variations. Finally, we assign a binary
  number to each representation.

  ### Greyscale

  We can start with a limited palette: greyscale. Here there is black and white
  and number of interim shades of grey. The convention is to use an 8-bit number
  which, given $2^8 = 256$ provides 256 shade variations.

  In decimal, 0 is equal to black (zero light intensity) and 255 is equal to white
  (full light intensity). Some examples of this (including binary and hex
  representations are below):

  ![](/img/greyscale-encoding.svg)

  ### Colour encoding

  A single 8-bit number is sufficient for 256 greyscale combinations but with
  colour, the convention is to use three 8-bit numbers where each byte corresponds
  to a value for red, green and blue (RGB). Thus the overall bit-length is 24
  ($2^3 + 2^3 + 2^3$). Each value corresponds to a light intensity for the given
  colour. Combined they are capable of representing all colours.

  Some examples below

  #### Red

  Red is represented in RGB with all 8 red bits to set to 1 and the remaining 16
  bits for the other two colours set to 0.

  ![](/img/red-encoding.svg)

  #### Yellow

  Yellow is represented in RGB with both red and blue set to 1 and the remaining 8
  green bits set to ):

  ![](/img/yellow-encoding.svg)

  ## Binary encoding of images


path: Binary_encoding.md
sortable_path: Binary_encoding.md
title: Binary encoding
checksum: 99eb0e8fb99059e41258ea3bbbd02bcf37d792ed80a43c6eec4c3982af0caa69
created: 2024-10-22 16:52:56.392871252+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["binary"]}
lead:
  We know that everything going on in a computer is the manipulation of binary
  digits. Thus all data must ultimately reduce to binary numbers manipulated
  through logic circuits.

body:
  We know that everything going on in a computer is the manipulation of binary
  digits. Thus all data must ultimately reduce to binary numbers manipulated
  through logic circuits.

  _Encoding_ is the process of establishing a correspondence between sets of
  binary numbers and sets of symbols that are representative of a given type of
  data. For certain fundamental data types, for example alphanumeric characters
  and colours, there are agreed standards of encoding such that, for example, that
  `111111` (binary) and `3F` (hex) always corresponds to the character `?`. The
  reverse is obviously _decoding_: deriving the data/ symbol from the binary
  format.

  > An encoding system maps each symbol to a unique sequence of bits. A computer
  > then interprets that sequence of bits and displays the apppropriate symbol to
  > the user.

  The length of the binary number (sometimes called the _word length_)
  corresponding to 8-bit, 16-bit, 32-bit etc. that is used to represent a given
  data set is determined by the number of variations that you require to capture
  the entire range of the dataset:

  > An $n$-bit binary system can encode $2^n$ different things

  For example, say we know that there are 18 levels to a computer game. To encode
  a reference for each level we would need a binary number that is capable of at
  least 18 total variations. In this instance a 32-bit ($2^{5}$ ) number would be
  best because the next smallest (16-bit) would not be sufficient. Our levels
  would have representations as follows:

  ```
  00001 (1)
  00010 (2)
  00011 (3)
  00100 (4)
  ...
  ```

  ## Related points

  Think about when you open a file format in a text editor that cannot decode it.
  For example trying to open a Word document in VSCode. The mangled letters it
  displays is the encoded binary data. When you open the file in Word, the
  decoding is applied and it resembles what you would expect.

  When we save a file, the different file extensions denote different formats and
  these are encoding formats. For example if you save an image file as `.png`
  rather than `.jpg`, you are applying a different encoding algorithm to the data
  that compresses the raw binary data in a different way.

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

  # Binary encoding

  We know that everything going on in a computer is the manipulation of binary
  digits. Thus all data must ultimately reduce to binary numbers manipulated
  through logic circuits.

  _Encoding_ is the process of establishing a correspondence between sets of
  binary numbers and sets of symbols that are representative of a given type of
  data. For certain fundamental data types, for example alphanumeric characters
  and colours, there are agreed standards of encoding such that, for example, that
  `111111` (binary) and `3F` (hex) always corresponds to the character `?`. The
  reverse is obviously _decoding_: deriving the data/ symbol from the binary
  format.

  > An encoding system maps each symbol to a unique sequence of bits. A computer
  > then interprets that sequence of bits and displays the apppropriate symbol to
  > the user.

  The length of the binary number (sometimes called the _word length_)
  corresponding to 8-bit, 16-bit, 32-bit etc. that is used to represent a given
  data set is determined by the number of variations that you require to capture
  the entire range of the dataset:

  > An $n$-bit binary system can encode $2^n$ different things

  For example, say we know that there are 18 levels to a computer game. To encode
  a reference for each level we would need a binary number that is capable of at
  least 18 total variations. In this instance a 32-bit ($2^{5}$ ) number would be
  best because the next smallest (16-bit) would not be sufficient. Our levels
  would have representations as follows:

  ```
  00001 (1)
  00010 (2)
  00011 (3)
  00100 (4)
  ...
  ```

  ## Related points

  Think about when you open a file format in a text editor that cannot decode it.
  For example trying to open a Word document in VSCode. The mangled letters it
  displays is the encoded binary data. When you open the file in Word, the
  decoding is applied and it resembles what you would expect.

  When we save a file, the different file extensions denote different formats and
  these are encoding formats. For example if you save an image file as `.png`
  rather than `.jpg`, you are applying a different encoding algorithm to the data
  that compresses the raw binary data in a different way.


path: Binary_encoding_of_text.md
sortable_path: Binary_encoding_of_text.md
title: Binary encoding of text
lead: Text encoding is an applied instance of [binary encoding](Binary_encoding.md).
checksum: 683ac93d8098ce5f04f4333dc8f52066eb99a030964addd9c9bc2032a9ce82e0
created: 2024-10-22 16:52:56.393644145+00:00
modified: 2024-11-04 14:22:11.461026806+00:00
metadata: {"tags":["binary"]}
body:
  Text encoding is an applied instance of [binary encoding](Binary_encoding.md).

  ## ASCII

  There are around 100 characters in total required to render A-Z, a-z, 0-9 and
  the special characters of Lating text. The ASCII (American Standard Code for
  Information Interchange) system achieves this with 8-bit code. Thus, each
  character symbol corresponds to a byte. As $2^8 = 256$, this allows for a total
  of 256 characters (where only 7-bits are sufficient, a leading `0` is added).

  Below are some examples of the ASCII correspondences:

  | Binary    | Hex | Character |
  | --------- | --- | --------- |
  | 00100000  | 20  | [space]   |
  | 00100001  | 21  | !         |
  | 001010112 | 2B  | +         |

  However there are only 128 characters represented in ASCII, thus using 256-bits
  is somewhat excessive. This lead people to try and use the remaining, free 128
  bits to accommodate characters from non-English languages. This was quickly
  found to be insufficient, necessitating the development of a new encoding
  standard, Unicode...

  ## Unicode and UTF-8

  Whereas ASCII only encodes 128 English alphanumeric characters, the scope of
  Unicode is much broader, as such it is a superset of ASCII (every character in
  ASCII is in Unicode but not the converse). Unicode is a universal character
  encoding that defines every character in every spoken language of the world. The
  Unicode standard is maintained by the Unicode Consortium and defines more than
  1,40,000 characters from more than 150 modern and historic scripts along with
  emoji.

  In contrast to ASCII, it doesn't achieve this by mapping every character to a
  bit. Instead it provides an abstract representation of every character which is
  then encoded using a designated encoding protocol, such as UTF-8, UTF-16, UTF-32
  etc. These abstract representations are called "code-points" and are represented
  as hexadecimal numbers between 0xO - 0x10FFFF (1114111), prepended by `U+`, for
  example ` U+00F7` which is the sign for division.

  As the encoding names suggest they encode to different bit sizes:

  - UTF-8 and UTF-16 are variable length encodings.
  - In UTF-8, a character may occupy a minimum of 8 bits.
  - In UTF-16, a character length starts with 16 bits.
  - UTF-32 is a fixed length encoding of 32 bits.

  > Unicode can be stored using several different encodings, which translate the
  > character codes into sequences of bytes. So, crucially Unicode is not itself
  > an encoding.

  UTF-8 uses the ASCII set for the first 128 characters. That's handy because it
  means ASCII text is also valid in UTF-8.

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

  # Binary encoding of text

  Text encoding is an applied instance of [binary encoding](Binary_encoding.md).

  ## ASCII

  There are around 100 characters in total required to render A-Z, a-z, 0-9 and
  the special characters of Lating text. The ASCII (American Standard Code for
  Information Interchange) system achieves this with 8-bit code. Thus, each
  character symbol corresponds to a byte. As $2^8 = 256$, this allows for a total
  of 256 characters (where only 7-bits are sufficient, a leading `0` is added).

  Below are some examples of the ASCII correspondences:

  | Binary    | Hex | Character |
  | --------- | --- | --------- |
  | 00100000  | 20  | [space]   |
  | 00100001  | 21  | !         |
  | 001010112 | 2B  | +         |

  However there are only 128 characters represented in ASCII, thus using 256-bits
  is somewhat excessive. This lead people to try and use the remaining, free 128
  bits to accommodate characters from non-English languages. This was quickly
  found to be insufficient, necessitating the development of a new encoding
  standard, Unicode...

  ## Unicode and UTF-8

  Whereas ASCII only encodes 128 English alphanumeric characters, the scope of
  Unicode is much broader, as such it is a superset of ASCII (every character in
  ASCII is in Unicode but not the converse). Unicode is a universal character
  encoding that defines every character in every spoken language of the world. The
  Unicode standard is maintained by the Unicode Consortium and defines more than
  1,40,000 characters from more than 150 modern and historic scripts along with
  emoji.

  In contrast to ASCII, it doesn't achieve this by mapping every character to a
  bit. Instead it provides an abstract representation of every character which is
  then encoded using a designated encoding protocol, such as UTF-8, UTF-16, UTF-32
  etc. These abstract representations are called "code-points" and are represented
  as hexadecimal numbers between 0xO - 0x10FFFF (1114111), prepended by `U+`, for
  example ` U+00F7` which is the sign for division.

  As the encoding names suggest they encode to different bit sizes:

  - UTF-8 and UTF-16 are variable length encodings.
  - In UTF-8, a character may occupy a minimum of 8 bits.
  - In UTF-16, a character length starts with 16 bits.
  - UTF-32 is a fixed length encoding of 32 bits.

  > Unicode can be stored using several different encodings, which translate the
  > character codes into sequences of bytes. So, crucially Unicode is not itself
  > an encoding.

  UTF-8 uses the ASCII set for the first 128 characters. That's handy because it
  means ASCII text is also valid in UTF-8.


path: Binary_number_system.md
sortable_path: Binary_number_system.md
title: Binary number system
lead: ## Decimal (denary) number system
checksum: de795b619205de28ea495df901a6e4ac370659672bbe831d2a51cb880307571c
created: 2024-10-22 16:52:56.395774294+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["binary","number-systems"]}
body:
  ## Decimal (denary) number system

  Binary is a **positional number system**, just like the decimal number system.
  This means that the value of an individual digit is conferred by its position
  relative to other digits. Another way of expressing this is to say that number
  systems work on the basis of **place value**.

  In the decimal system the columns increase by **powers of 10**. This is because
  there are ten total integers in the system:

  $1, 2, 3, 4, 5, 6, 7, 8, 9$

  When we have completed all the possible intervals between $0$ and $9$, we start
  again in a new column.

  The principle of counting in decimal:

  ```
  0009
  0010
  0011
  0012
  0013
  ...
  0019
  0020
  0021
  ```

  Thus each column is ten times larger than the previous column:

  - Ten ($10^1$) is ten times larger than one ($10^0$)
  - A hundred ($10^2$) is ten times larger than ten ($10^1$)

  We use this knowledge of the exponents of the base of 10 to read integers that
  contain multiple digits (i.e. any number greater than 9).

  Thus 264 is the sum of

  $$[4 \cdot (10^0)] + [6 \cdot (10^1)] + [2 \cdot 10^2]  $$

  ## Binary number system

  In the binary number system, the columns increase by powers of two. This is
  because there are only two integers: 0 and 1. As a result, you are required to
  begin a new column every time you complete an interval from 0 to 1.

  So instead of:

  $$ 10^0, 10^1, 10^2, 10^3 ... (1, 10, 100, 1000) $$

  You have:

  $$ 2^0, 2^1, 2^2, 2^3, 2^4... (0, 2, 4, 8, 16) $$

  When counting in binary, we put zeros as placeholders in the columns we have not
  yet filled. This helps to indicate when we need to begin a new column. Thus the
  counting sequence:

  $$ 1, 2, 3, 4 $$

  is equal to:

  $$ 0001, 0010, 0011, 0100 $$

  Counting in binary:

  ```
  000000
  000001
  000010
  000011
  000100
  000101
  000111
  ```

  ## Binary prefix

  To distinguish numbers in binary from decimal or
  [hexadecimal](Hexadecimal_number_system.md)
  numbers, it is common to use the prefix `0b`. Thus, e.g, `0b110` for decimal
  `6`.

  ## Converting decimal to binary

  Let's convert 6 into binary:

  If we have before us the binary place values ($1, 2, 4, 8$). We know that 6 is
  equal to: **1 in the two column and 1 in the 4 column → 110**

  More clearly:

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

  And for comparison:

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

  Or we can express the binary as:

  $$ (1 _ 2) + (1 _ 4) $$

  Or more concisely as:

  $$ 2^1 + 2^2 $$

  ### Another example

  Let's convert 23 into binary:

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

raw_content:
  ---
  tags: [binary, number-systems]
  ---

  # Binary number system

  ## Decimal (denary) number system

  Binary is a **positional number system**, just like the decimal number system.
  This means that the value of an individual digit is conferred by its position
  relative to other digits. Another way of expressing this is to say that number
  systems work on the basis of **place value**.

  In the decimal system the columns increase by **powers of 10**. This is because
  there are ten total integers in the system:

  $1, 2, 3, 4, 5, 6, 7, 8, 9$

  When we have completed all the possible intervals between $0$ and $9$, we start
  again in a new column.

  The principle of counting in decimal:

  ```
  0009
  0010
  0011
  0012
  0013
  ...
  0019
  0020
  0021
  ```

  Thus each column is ten times larger than the previous column:

  - Ten ($10^1$) is ten times larger than one ($10^0$)
  - A hundred ($10^2$) is ten times larger than ten ($10^1$)

  We use this knowledge of the exponents of the base of 10 to read integers that
  contain multiple digits (i.e. any number greater than 9).

  Thus 264 is the sum of

  $$[4 \cdot (10^0)] + [6 \cdot (10^1)] + [2 \cdot 10^2]  $$

  ## Binary number system

  In the binary number system, the columns increase by powers of two. This is
  because there are only two integers: 0 and 1. As a result, you are required to
  begin a new column every time you complete an interval from 0 to 1.

  So instead of:

  $$ 10^0, 10^1, 10^2, 10^3 ... (1, 10, 100, 1000) $$

  You have:

  $$ 2^0, 2^1, 2^2, 2^3, 2^4... (0, 2, 4, 8, 16) $$

  When counting in binary, we put zeros as placeholders in the columns we have not
  yet filled. This helps to indicate when we need to begin a new column. Thus the
  counting sequence:

  $$ 1, 2, 3, 4 $$

  is equal to:

  $$ 0001, 0010, 0011, 0100 $$

  Counting in binary:

  ```
  000000
  000001
  000010
  000011
  000100
  000101
  000111
  ```

  ## Binary prefix

  To distinguish numbers in binary from decimal or
  [hexadecimal](Hexadecimal_number_system.md)
  numbers, it is common to use the prefix `0b`. Thus, e.g, `0b110` for decimal
  `6`.

  ## Converting decimal to binary

  Let's convert 6 into binary:

  If we have before us the binary place values ($1, 2, 4, 8$). We know that 6 is
  equal to: **1 in the two column and 1 in the 4 column → 110**

  More clearly:

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

  And for comparison:

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

  Or we can express the binary as:

  $$ (1 _ 2) + (1 _ 4) $$

  Or more concisely as:

  $$ 2^1 + 2^2 $$

  ### Another example

  Let's convert 23 into binary:

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


path: Binary_units_of_measurement.md
sortable_path: Binary_units_of_measurement.md
title: Binary units of measurement
checksum: e1f5207016c7e704ae907b445b9e14048d2ac454739316a2367cc136d6172294
created: 2024-10-22 16:52:56.397861829+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["binary"]}
lead:
  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.

body:
  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)

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

  # 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)


path: Bitwise_operators.md
sortable_path: Bitwise_operators.md
title: Bitwise operators
checksum: f2f23e111cec77af2f800265ad1d4a282ceec10d81746e324281f564878521f0
created: 2024-10-22 16:52:56.400816254+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"created":"Saturday, April 20, 2024","id":"t127","tags":["binary"]}
lead:
  In addition to mathematical, logical and comparison operators, there are
  _bitwise operators_. These operators execute conditions based on the actual bits
  of a value rather than the values that the bits are encoded to represent.

body:
  In addition to mathematical, logical and comparison operators, there are
  _bitwise operators_. These operators execute conditions based on the actual bits
  of a value rather than the values that the bits are encoded to represent.

  Bitwise operators are typically represented with single characters of existing
  operators, e.g. `&` instead of `&&`:

  | Bitwise operation | Operator      |
  | ----------------- | ------------- |
  | AND               | `&`           |
  | OR                | (single pipe) |
  | NOT               | `~`           |

  An example of using the `&` operator:

  ```py
  x = 5
  y = 3
  a = x & y
  b = x | y
  ```

  The value of `a` will be 1. The reason is we are looking at the bit values of
  `x` and `y` and then applying Boolean AND to each bit:

  ```
  x = 5 = 0101
  y = 3 = 0011
  a = 1 = 0001
  ```

  Working from right to left for each column:

  - true and true = true
  - false and true = false
  - true and false = false
  - false and false = false

  This leaves us with 0001 which is equal to 1 in binary and denary.

  For the case of bitwise OR we get 7 as the result of `x | y`:

  ```
  x = 5 = 0101
  y = 3 = 0011
  b = 7 = 0111
  ```

  - true or true = true
  - false or true = true
  - true or false = true
  - false or false = true

  This leaves us with 0111 which is equal to 7 in denary.

  ## Why use them?

  Bitwise operations are more of a hallmark of lower-level programming. As bit
  operations are the fastest and lowest level of computation, being able to
  directly access and operate on bits within a programming language can be
  beneficial when efficiency and speed of execution is a factor or when memory is
  constrained.

  ## Related notes

raw_content:
  ---
  id: t127
  tags: [binary]
  created: Saturday, April 20, 2024
  ---

  # Bitwise operators

  In addition to mathematical, logical and comparison operators, there are
  _bitwise operators_. These operators execute conditions based on the actual bits
  of a value rather than the values that the bits are encoded to represent.

  Bitwise operators are typically represented with single characters of existing
  operators, e.g. `&` instead of `&&`:

  | Bitwise operation | Operator      |
  | ----------------- | ------------- |
  | AND               | `&`           |
  | OR                | (single pipe) |
  | NOT               | `~`           |

  An example of using the `&` operator:

  ```py
  x = 5
  y = 3
  a = x & y
  b = x | y
  ```

  The value of `a` will be 1. The reason is we are looking at the bit values of
  `x` and `y` and then applying Boolean AND to each bit:

  ```
  x = 5 = 0101
  y = 3 = 0011
  a = 1 = 0001
  ```

  Working from right to left for each column:

  - true and true = true
  - false and true = false
  - true and false = false
  - false and false = false

  This leaves us with 0001 which is equal to 1 in binary and denary.

  For the case of bitwise OR we get 7 as the result of `x | y`:

  ```
  x = 5 = 0101
  y = 3 = 0011
  b = 7 = 0111
  ```

  - true or true = true
  - false or true = true
  - true or false = true
  - false or false = true

  This leaves us with 0111 which is equal to 7 in denary.

  ## Why use them?

  Bitwise operations are more of a hallmark of lower-level programming. As bit
  operations are the fastest and lowest level of computation, being able to
  directly access and operate on bits within a programming language can be
  beneficial when efficiency and speed of execution is a factor or when memory is
  constrained.

  ## Related notes


path: Bluetooth.md
sortable_path: Bluetooth.md
title: Bluetooth
lead: ## Connect to devices with `bluetoothctl`
checksum: dd188c808847640393d9a61d09970ec5a296d30aaa30fc9d99c3dbea666dcd5d
created: 2024-10-22 16:52:56.401545837+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["networks","procedural","Linux"]}
body:
  ## Connect to devices with `bluetoothctl`

  ```bash
  # Start bluetooth first
  bluetoothctl power on
  ```

  Then:

  ```bash
  # Launch bluetoothctl
  bluetoothctl
  ```

  This initiates `[bluetooth] #` prompt. Then follow this sequence:

  ```bash
  [bluetooth]# agent on
  [bluetooth]# default-agent
  [bluetooth]# scan on
  pair [device_code]
  connect [device_code]
  ```

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

  # Bluetooth

  ## Connect to devices with `bluetoothctl`

  ```bash
  # Start bluetooth first
  bluetoothctl power on
  ```

  Then:

  ```bash
  # Launch bluetoothctl
  bluetoothctl
  ```

  This initiates `[bluetooth] #` prompt. Then follow this sequence:

  ```bash
  [bluetooth]# agent on
  [bluetooth]# default-agent
  [bluetooth]# scan on
  pair [device_code]
  connect [device_code]
  ```


path: Boolean_algebra.md
sortable_path: Boolean_algebra.md
title: Boolean algebra
lead: ## Algebraic laws
checksum: 2736963999ce80c193d14df114f37bcd52b109f6abc0495a7fb98c9317ce692f
created: 2024-10-22 16:52:56.40241631+00:00
modified: 2024-10-18 17:55:56.289060649+00:00
metadata: {"tags":["propositional-logic","algebra","nand-to-tetris","logic"]}
body:
  ## Algebraic laws

  Many of the laws that obtain in the mathematical realm of algebra also obtain
  for Boolean expressions.

  ### The Commutative Law

  $$
      x \land y = y \land x \\
  $$

  $$

      x \lor y = y \lor x
  $$

  Compare the
  [Commutative Law](Whole_numbers.md#the-commutative-property)
  in the context of arithmetic.

  ### The Associative Law

  $$
      x   \land (y \land z) = (x \land y) \land z
  $$

  $$
      x   \lor (y \lor z) = (x \lor y) \lor z
  $$

  Compare the
  [Associative Law](Whole_numbers.md#the-associative-property)
  in the context of arithmetic.

  ### The Distributive Law

  $$
      x \land (y \lor z) = (x \land y) \lor (x \land z)
  $$

  $$
      x \lor (y \land z) = (x \lor y) \land (x \lor z)
  $$

  Compare how the
  [Distributive Law applies in the case of algebra based on arithmetic](Distributivity.md):

  $$
      a \cdot (b + c) = a \cdot b + a \cdot c
  $$

  ### Double Negation Elimination

  $$
      \lnot \lnot x = x
  $$

  ### Idempotent Law

  $$
      x \land x = x
  $$

  > Combining a quantity with itself either by logical addition or logical
  > multiplication will result in a logical sum or product that is the equivalent
  > of the quantity

  ### DeMorgan's Laws

  In addition we have
  [DeMorgan's Laws](DeMorgan's_Laws.md) which express
  the relationship that obtains between the negations of conjunctive and
  disjunctive expressions:

  $$
  \lnot(x \land y)  = \lnot x \lor \lnot y
  $$

  $$
      \lnot (x \lor y) = \lnot x \land \lnot y
  $$

  ## Applying the laws to simplify complex Boolean expressions

  Say we have the following expression:

  $$
      \lnot(\lnot(x) \land \lnot (x \lor y))
  $$

  We can employ DeMorgan's Laws to convert the second conjunct to a different
  form:

  $$
      \lnot (x \lor y) = \lnot x \land \lnot y
  $$

  So now we have:

  $$
      \lnot(\lnot(x) \land (\lnot x \land \lnot y ))
  $$

  As we have now have an expression of the form _P and (Q and R)_ we can apply the
  Distributive Law to simplify the brackets (_P and Q and R_):

  $$
      \lnot( \lnot(x) \land \lnot(x) \land \lnot(y))
  $$

  Notice that we are repeating ourselves in this reformulation. We have
  $\lnot(x) \land \lnot(x)$ but this is just the same $\lnot(x)$ by the principle
  of **idempotence**. So we can reduce to:

  $$
      \lnot(\lnot(x) \land \lnot(y))
  $$

  This gives our expression the form of the first DeMorgan Law
  ($\lnot (P \land Q)$), thus we can apply the law ($\lnot P \lor \lnot Q$) to
  get:

  $$
  \lnot(\lnot(x)) \lor \lnot(\lnot(y))
  $$

  Of course now we have two double negatives. We can apply the double negation law
  to get:

  $$
      x \lor y
  $$

  ### Truth table

  Whenever we simplify an algebraic expression the value of the resulting
  expression should match that of the complex expression. We can demonstrate this
  with a truth table:

  | $x$ | $y$ | $\lnot(\lnot(x) \land \lnot (x \lor y))$ | $x \lor y$ |
  | --- | --- | ---------------------------------------- | ---------- |
  | 0   | 0   | 0                                        | 0          |
  | 0   | 1   | 1                                        | 1          |
  | 1   | 0   | 1                                        | 1          |
  | 1   | 1   | 1                                        | 1          |

  ### Significance for computer architecture

  The fact that we can take a complex Boolean function and reduce it to a simpler
  formulation has great significance for the development of computer
  architectures, specifically
  [logic gates](Logic_gates.md). It
  would be rather resource intensive and inefficient to create a gate that is
  representative of the complex function. Whereas the simplified version only
  requires a single
  [OR gate](Logic_gates.md#or-gate).

raw_content:
  ---
  tags:
    - propositional-logic
    - algebra
    - nand-to-tetris
    - logic
  ---

  # Boolean algebra

  ## Algebraic laws

  Many of the laws that obtain in the mathematical realm of algebra also obtain
  for Boolean expressions.

  ### The Commutative Law

  $$
      x \land y = y \land x \\
  $$

  $$

      x \lor y = y \lor x
  $$

  Compare the
  [Commutative Law](Whole_numbers.md#the-commutative-property)
  in the context of arithmetic.

  ### The Associative Law

  $$
      x   \land (y \land z) = (x \land y) \land z
  $$

  $$
      x   \lor (y \lor z) = (x \lor y) \lor z
  $$

  Compare the
  [Associative Law](Whole_numbers.md#the-associative-property)
  in the context of arithmetic.

  ### The Distributive Law

  $$
      x \land (y \lor z) = (x \land y) \lor (x \land z)
  $$

  $$
      x \lor (y \land z) = (x \lor y) \land (x \lor z)
  $$

  Compare how the
  [Distributive Law applies in the case of algebra based on arithmetic](Distributivity.md):

  $$
      a \cdot (b + c) = a \cdot b + a \cdot c
  $$

  ### Double Negation Elimination

  $$
      \lnot \lnot x = x
  $$

  ### Idempotent Law

  $$
      x \land x = x
  $$

  > Combining a quantity with itself either by logical addition or logical
  > multiplication will result in a logical sum or product that is the equivalent
  > of the quantity

  ### DeMorgan's Laws

  In addition we have
  [DeMorgan's Laws](DeMorgan's_Laws.md) which express
  the relationship that obtains between the negations of conjunctive and
  disjunctive expressions:

  $$
  \lnot(x \land y)  = \lnot x \lor \lnot y
  $$

  $$
      \lnot (x \lor y) = \lnot x \land \lnot y
  $$

  ## Applying the laws to simplify complex Boolean expressions

  Say we have the following expression:

  $$
      \lnot(\lnot(x) \land \lnot (x \lor y))
  $$

  We can employ DeMorgan's Laws to convert the second conjunct to a different
  form:

  $$
      \lnot (x \lor y) = \lnot x \land \lnot y
  $$

  So now we have:

  $$
      \lnot(\lnot(x) \land (\lnot x \land \lnot y ))
  $$

  As we have now have an expression of the form _P and (Q and R)_ we can apply the
  Distributive Law to simplify the brackets (_P and Q and R_):

  $$
      \lnot( \lnot(x) \land \lnot(x) \land \lnot(y))
  $$

  Notice that we are repeating ourselves in this reformulation. We have
  $\lnot(x) \land \lnot(x)$ but this is just the same $\lnot(x)$ by the principle
  of **idempotence**. So we can reduce to:

  $$
      \lnot(\lnot(x) \land \lnot(y))
  $$

  This gives our expression the form of the first DeMorgan Law
  ($\lnot (P \land Q)$), thus we can apply the law ($\lnot P \lor \lnot Q$) to
  get:

  $$
  \lnot(\lnot(x)) \lor \lnot(\lnot(y))
  $$

  Of course now we have two double negatives. We can apply the double negation law
  to get:

  $$
      x \lor y
  $$

  ### Truth table

  Whenever we simplify an algebraic expression the value of the resulting
  expression should match that of the complex expression. We can demonstrate this
  with a truth table:

  | $x$ | $y$ | $\lnot(\lnot(x) \land \lnot (x \lor y))$ | $x \lor y$ |
  | --- | --- | ---------------------------------------- | ---------- |
  | 0   | 0   | 0                                        | 0          |
  | 0   | 1   | 1                                        | 1          |
  | 1   | 0   | 1                                        | 1          |
  | 1   | 1   | 1                                        | 1          |

  ### Significance for computer architecture

  The fact that we can take a complex Boolean function and reduce it to a simpler
  formulation has great significance for the development of computer
  architectures, specifically
  [logic gates](Logic_gates.md). It
  would be rather resource intensive and inefficient to create a gate that is
  representative of the complex function. Whereas the simplified version only
  requires a single
  [OR gate](Logic_gates.md#or-gate).


path: Boolean_function_synthesis.md
sortable_path: Boolean_function_synthesis.md
title: Boolean function synthesis
checksum: 1433ac8313e9d2229274099b122f96b4b6d1569c64e2f870714d8696123fd3ce
created: 2024-10-22 16:52:56.409259842+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["propositional-logic","nand-to-tetris","logic"]}
lead:
  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.

body:
  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)
  $$

raw_content:
  ---
  tags:
    - propositional-logic
    - nand-to-tetris
    - logic
  ---

  # 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)
  $$


path: Boolean_functions.md
sortable_path: Boolean_functions.md
title: Boolean functions
lead: An example of a Boolean function:
checksum: b964dae9e3c6ee9c233fbaf9c8543731f59730b3d7b4459f7e1705055b13c174
created: 2024-10-22 16:52:56.414886726+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["propositional-logic","nand-to-tetris","logic"]}
body:
  An example of a Boolean function:

  $$
  f(x,y,z) = (x \land y) \lor (\lnot(x) \land z )
  $$

  Here is a work through where $f(1, 0, 1)$:

  - The first disjunction : $\lnot(x) \land z$ is false because $x$ is 1 and $z$
    is 0
  - The second disjunction: $x \land y$ is false because $x$ is 1 and $y$ is 1
  - The overall function returns false because the main connective is disjunction
    and both of its disjuncts are false

  We can compute all possible outputs of the function by constructing a
  [trkjuth table](Truth-tables.md) with each possible
  variable as the truth conditions and the output of the function as the truth
  value:

  | $x$ | $y$ | $z$ | $f(x,y,z) = (x \land y) \lor (\lnot(x) \land z )$ |
  | --- | --- | --- | ------------------------------------------------- |
  | 0   | 0   | 0   | 0                                                 |
  | 0   | 0   | 1   | 1                                                 |
  | 0   | 1   | 0   | 0                                                 |
  | 0   | 1   | 1   | 1                                                 |
  | 1   | 0   | 0   | 0                                                 |
  | 1   | 0   | 1   | 0                                                 |
  | 1   | 1   | 0   | 1                                                 |
  | 1   | 1   | 1   | 1                                                 |

raw_content:
  ---
  tags:
    - propositional-logic
    - nand-to-tetris
    - logic
  ---

  # Boolean functions

  An example of a Boolean function:

  $$
  f(x,y,z) = (x \land y) \lor (\lnot(x) \land z )
  $$

  Here is a work through where $f(1, 0, 1)$:

  - The first disjunction : $\lnot(x) \land z$ is false because $x$ is 1 and $z$
    is 0
  - The second disjunction: $x \land y$ is false because $x$ is 1 and $y$ is 1
  - The overall function returns false because the main connective is disjunction
    and both of its disjuncts are false

  We can compute all possible outputs of the function by constructing a
  [trkjuth table](Truth-tables.md) with each possible
  variable as the truth conditions and the output of the function as the truth
  value:

  | $x$ | $y$ | $z$ | $f(x,y,z) = (x \land y) \lor (\lnot(x) \land z )$ |
  | --- | --- | --- | ------------------------------------------------- |
  | 0   | 0   | 0   | 0                                                 |
  | 0   | 0   | 1   | 1                                                 |
  | 0   | 1   | 0   | 0                                                 |
  | 0   | 1   | 1   | 1                                                 |
  | 1   | 0   | 0   | 0                                                 |
  | 1   | 0   | 1   | 0                                                 |
  | 1   | 1   | 0   | 1                                                 |
  | 1   | 1   | 1   | 1                                                 |


path: Boot_process.md
sortable_path: Boot_process.md
title: Boot process
checksum: 5a5f3511f050204e815570b7123063294918488e28e1ad5dce63c783b11575e4
created: 2024-10-22 16:52:56.417190061+00:00
modified: 2024-11-04 14:22:36.548127601+00:00
metadata: {"tags":["disks","operating-systems"]}
lead:
  The primary or boot
  [partition](Disks.md#primary-extended-and-logical-partitions) of a harddisk
  contains a bootloader. It is the job of the bootloader to locate the
  [kernel](The_kernel.md) on the harddrive and inject it into memory so that they
  operating system can start. This is the boot process.

body:
  The primary or boot
  [partition](Disks.md#primary-extended-and-logical-partitions) of a harddisk
  contains a bootloader. It is the job of the bootloader to locate the
  [kernel](The_kernel.md) on the harddrive and inject it into memory so that they
  operating system can start. This is the boot process.

  ## Boot loaders

  The boot loader loads the kernel into memory from the disk and then starts the
  kernel with a set of kernel parameters.

  Like the kernel itself, the boot loader requires a driver in order to access the
  disk but it can't use the same one as the kernel since at this point, the kernel
  is not yet loaded into memory. So it has its own special driver: this is either
  the BIOS or the UEFI firmware.

  A boot loader's core functionality includes the ability to do the following:

  - select from multiple kernels
  - switch between sets of kernel parameters
  - provide support for booting other operating systems

  ### BIOS and UEFI

  BIOS and UEFI are both firmware that is installed directly on the motherboard of
  the computer. They are firmware because they are software that is permanent and
  programmed into read-only memory.

  In the context of disks, their most crucial role is locating the operating
  system on the harddisk and loading it into memory so that the bootstrapping
  process can begin. However they are also responsible for the computer clock and
  the management of peripherals.

  As we can see from the `fdisk` readout, the boot partition uses EFI, the storage
  partition associated with UEFI.

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

  Whilst UEFI is installed on the hardware, most of its configuration is stored in
  the EFI partition on the disk, whereas with BIOS, everything is on the chip.
  This makes booting faster with UEFI.

  Even though most modern computers use UEFI, it may still be referred to as BIOS
  for user-continuity (for instance on WIndows). With Linux you have to explicitly
  create your boot process so the two are clearly distinguishable.

  ### GRUB

  The de facto standard boot loader for Linux is GRUB: Grand Unified Boot Loader.

  ![](/img/grub.jpg)

  You see the GRUB default menu when you first start a Linux machine. It will
  offer you various options for loading your installed OS or other OSs. GRUB is a
  [filesystem](./Filesystems.md) like the main disk. If you press `e` in this
  screen you can view and edit specific boot parameters. Pressing `c` gives you
  access to the GRUB command line interface. This allows you to interact with GRUB
  in the same way as you would with any other filesystem, allowing for advanced
  configuration.

  ## The boot sequence

  1. The machine's BIOS or boot firmware (in the case of UEFI) loads and runs a
     boot loader. It finds the bootloader by looking for the part of the disk that
     contains either the MBR or GPT sector.
  2. The boot loader finds the kernel image on disk, loads it into memory and
     starts it.
  3. The kernel initializes the devices and its drivers.
  4. The kernel mounts the root filesystem.
  5. The kernel starts a program called **init**. It has a
     [process id](Processes.md#processes-ps) of 1. This is the point at which
     [user space](User_Space.md) starts.
  6. Init sets the rest of the system processes in motion.
  7. At the end of the boot process, init starts a process allowing you to log in.

  ## Boot diagnostics

  Linux keeps a log of the boot process as part of the [journal](./journald.md)
  log of everything that happens on disk. We can use the command `journalct -k` to
  view the diagnostics generated during the boot. Here is a selection:

  ```
  Jul 07 07:12:58 archbish kernel: Linux version 5.18.5-arch1-1 (linux@archlinux) (gcc (GCC) 12.1.0, GNU ld (GNU Binutils) 2.38) #1 SMP PREEMPT_DYNAMIC Thu, 16 Jun 2022 20:40:45 +0000
  Jul 07 07:12:58 archbish kernel: Command line: BOOT_IMAGE=/boot/vmlinuz-linux root=UUID=2ee6b834-0857-49dc-b8ba-a24d46d228ae rw loglevel=3 quiet
  Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
  Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
  Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x020: 'AVX-512 opmask'
  Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x040: 'AVX-512 Hi256'
  Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[2]:  576, xstate_sizes[2]:  256
  Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[5]:  832, xstate_sizes[5]:   64
  Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[6]:  896, xstate_sizes[6]:  512
  Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[7]: 1408, xstate_sizes[7]: 1024
  Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[9]: 2432, xstate_sizes[9]:    8
  Jul 07 07:12:58 archbish kernel: x86/fpu: Enabled xstate features 0x2e7, context size is 2440 bytes, using 'compacted' format.
  Jul 07 07:12:58 archbish kernel: signal: max sigframe size: 3632
  Jul 07 07:12:58 archbish kernel: BIOS-provided physical RAM map:
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000000000000-0x000000000009efff] usable
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x000000000009f000-0x00000000000fffff] reserved
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000000100000-0x000000008ef6efff] usable
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x000000008ef6f000-0x000000008f03efff] type 20
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x000000008f03f000-0x0000000093f2efff] reserved
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000093f2f000-0x0000000094b2efff] ACPI NVS
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000094b2f000-0x0000000094bfefff] ACPI data
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000094bff000-0x0000000094bfffff] usable
  Jul 07 07:12:58 archbish kernel: NX (Execute Disable) protection: active
  Jul 07 07:12:58 archbish kernel: efi: EFI v2.70 by Lenovo
  Jul 07 07:12:58 archbish kernel: efi: ACPI=0x94bfe000 ACPI 2.0=0x94bfe014 SMBIOS=0x90cbd000 SMBIOS 3.0=0x90cb0000 TPMFinalLog=0x9498f000 MEMATTR=0x8aa30018 ESRT=0x8ab9c000
  Jul 07 07:12:58 archbish kernel: SMBIOS 3.2.0 present.
  Jul 07 07:12:58 archbish kernel: DMI: LENOVO 20W4000NUK/20W4000NUK, BIOS N34ET40W (1.40 ) 06/25/2021
  Jul 07 07:12:58 archbish kernel: tsc: Detected 2400.000 MHz processor
  Jul 07 07:12:58 archbish kernel: tsc: Detected 2419.200 MHz TSC
  Jul 07 07:12:58 archbish kernel: e820: update [mem 0x00000000-0x00000fff] usable ==> reserved
  Jul 07 07:12:58 archbish kernel: e820: remove [mem 0x000a0000-0x000fffff] usable
  Jul 07 07:12:58 archbish kernel: last_pfn = 0x45f800 max_arch_pfn = 0x400000000
  Jul 07 07:12:58 archbish kernel: x86/PAT: Configuration [0-7]: WB  WC  UC- UC  WB  WP  UC- WT
  Jul 07 07:12:58 archbish kernel: last_pfn = 0x94c00 max_arch_pfn = 0x400000000
  Jul 07 07:12:58 archbish kernel: esrt: Reserving ESRT space from 0x000000008ab9c000 to 0x000000008ab9c150.
  Jul 07 07:12:58 archbish kernel: e820: update [mem 0x8ab9c000-0x8ab9cfff] usable ==> reserved
  ```

  We can see that the first thing that is registered is the OS image that contains
  the kernel. From this point on, the kernel is active and doing its work.

  ## Initial kernel actions

  The diagnostics above are a running commentary provided by the kernel itself
  about its actions. The details will be specific to the machine in question but
  the general sequence is as follows for all operating systems:

  1. CPU inspection
  2. Memory inspection
  3. Device bus discovery
  4. Device discovery
  5. Auxiliary kernel subsystem setup (networks etc)
  6. Mounting of the root filesystem
  7. Starting of user space

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

  # Boot process

  The primary or boot
  [partition](Disks.md#primary-extended-and-logical-partitions) of a harddisk
  contains a bootloader. It is the job of the bootloader to locate the
  [kernel](The_kernel.md) on the harddrive and inject it into memory so that they
  operating system can start. This is the boot process.

  ## Boot loaders

  The boot loader loads the kernel into memory from the disk and then starts the
  kernel with a set of kernel parameters.

  Like the kernel itself, the boot loader requires a driver in order to access the
  disk but it can't use the same one as the kernel since at this point, the kernel
  is not yet loaded into memory. So it has its own special driver: this is either
  the BIOS or the UEFI firmware.

  A boot loader's core functionality includes the ability to do the following:

  - select from multiple kernels
  - switch between sets of kernel parameters
  - provide support for booting other operating systems

  ### BIOS and UEFI

  BIOS and UEFI are both firmware that is installed directly on the motherboard of
  the computer. They are firmware because they are software that is permanent and
  programmed into read-only memory.

  In the context of disks, their most crucial role is locating the operating
  system on the harddisk and loading it into memory so that the bootstrapping
  process can begin. However they are also responsible for the computer clock and
  the management of peripherals.

  As we can see from the `fdisk` readout, the boot partition uses EFI, the storage
  partition associated with UEFI.

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

  Whilst UEFI is installed on the hardware, most of its configuration is stored in
  the EFI partition on the disk, whereas with BIOS, everything is on the chip.
  This makes booting faster with UEFI.

  Even though most modern computers use UEFI, it may still be referred to as BIOS
  for user-continuity (for instance on WIndows). With Linux you have to explicitly
  create your boot process so the two are clearly distinguishable.

  ### GRUB

  The de facto standard boot loader for Linux is GRUB: Grand Unified Boot Loader.

  ![](/img/grub.jpg)

  You see the GRUB default menu when you first start a Linux machine. It will
  offer you various options for loading your installed OS or other OSs. GRUB is a
  [filesystem](./Filesystems.md) like the main disk. If you press `e` in this
  screen you can view and edit specific boot parameters. Pressing `c` gives you
  access to the GRUB command line interface. This allows you to interact with GRUB
  in the same way as you would with any other filesystem, allowing for advanced
  configuration.

  ## The boot sequence

  1. The machine's BIOS or boot firmware (in the case of UEFI) loads and runs a
     boot loader. It finds the bootloader by looking for the part of the disk that
     contains either the MBR or GPT sector.
  2. The boot loader finds the kernel image on disk, loads it into memory and
     starts it.
  3. The kernel initializes the devices and its drivers.
  4. The kernel mounts the root filesystem.
  5. The kernel starts a program called **init**. It has a
     [process id](Processes.md#processes-ps) of 1. This is the point at which
     [user space](User_Space.md) starts.
  6. Init sets the rest of the system processes in motion.
  7. At the end of the boot process, init starts a process allowing you to log in.

  ## Boot diagnostics

  Linux keeps a log of the boot process as part of the [journal](./journald.md)
  log of everything that happens on disk. We can use the command `journalct -k` to
  view the diagnostics generated during the boot. Here is a selection:

  ```
  Jul 07 07:12:58 archbish kernel: Linux version 5.18.5-arch1-1 (linux@archlinux) (gcc (GCC) 12.1.0, GNU ld (GNU Binutils) 2.38) #1 SMP PREEMPT_DYNAMIC Thu, 16 Jun 2022 20:40:45 +0000
  Jul 07 07:12:58 archbish kernel: Command line: BOOT_IMAGE=/boot/vmlinuz-linux root=UUID=2ee6b834-0857-49dc-b8ba-a24d46d228ae rw loglevel=3 quiet
  Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
  Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
  Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x020: 'AVX-512 opmask'
  Jul 07 07:12:58 archbish kernel: x86/fpu: Supporting XSAVE feature 0x040: 'AVX-512 Hi256'
  Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[2]:  576, xstate_sizes[2]:  256
  Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[5]:  832, xstate_sizes[5]:   64
  Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[6]:  896, xstate_sizes[6]:  512
  Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[7]: 1408, xstate_sizes[7]: 1024
  Jul 07 07:12:58 archbish kernel: x86/fpu: xstate_offset[9]: 2432, xstate_sizes[9]:    8
  Jul 07 07:12:58 archbish kernel: x86/fpu: Enabled xstate features 0x2e7, context size is 2440 bytes, using 'compacted' format.
  Jul 07 07:12:58 archbish kernel: signal: max sigframe size: 3632
  Jul 07 07:12:58 archbish kernel: BIOS-provided physical RAM map:
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000000000000-0x000000000009efff] usable
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x000000000009f000-0x00000000000fffff] reserved
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000000100000-0x000000008ef6efff] usable
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x000000008ef6f000-0x000000008f03efff] type 20
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x000000008f03f000-0x0000000093f2efff] reserved
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000093f2f000-0x0000000094b2efff] ACPI NVS
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000094b2f000-0x0000000094bfefff] ACPI data
  Jul 07 07:12:58 archbish kernel: BIOS-e820: [mem 0x0000000094bff000-0x0000000094bfffff] usable
  Jul 07 07:12:58 archbish kernel: NX (Execute Disable) protection: active
  Jul 07 07:12:58 archbish kernel: efi: EFI v2.70 by Lenovo
  Jul 07 07:12:58 archbish kernel: efi: ACPI=0x94bfe000 ACPI 2.0=0x94bfe014 SMBIOS=0x90cbd000 SMBIOS 3.0=0x90cb0000 TPMFinalLog=0x9498f000 MEMATTR=0x8aa30018 ESRT=0x8ab9c000
  Jul 07 07:12:58 archbish kernel: SMBIOS 3.2.0 present.
  Jul 07 07:12:58 archbish kernel: DMI: LENOVO 20W4000NUK/20W4000NUK, BIOS N34ET40W (1.40 ) 06/25/2021
  Jul 07 07:12:58 archbish kernel: tsc: Detected 2400.000 MHz processor
  Jul 07 07:12:58 archbish kernel: tsc: Detected 2419.200 MHz TSC
  Jul 07 07:12:58 archbish kernel: e820: update [mem 0x00000000-0x00000fff] usable ==> reserved
  Jul 07 07:12:58 archbish kernel: e820: remove [mem 0x000a0000-0x000fffff] usable
  Jul 07 07:12:58 archbish kernel: last_pfn = 0x45f800 max_arch_pfn = 0x400000000
  Jul 07 07:12:58 archbish kernel: x86/PAT: Configuration [0-7]: WB  WC  UC- UC  WB  WP  UC- WT
  Jul 07 07:12:58 archbish kernel: last_pfn = 0x94c00 max_arch_pfn = 0x400000000
  Jul 07 07:12:58 archbish kernel: esrt: Reserving ESRT space from 0x000000008ab9c000 to 0x000000008ab9c150.
  Jul 07 07:12:58 archbish kernel: e820: update [mem 0x8ab9c000-0x8ab9cfff] usable ==> reserved
  ```

  We can see that the first thing that is registered is the OS image that contains
  the kernel. From this point on, the kernel is active and doing its work.

  ## Initial kernel actions

  The diagnostics above are a running commentary provided by the kernel itself
  about its actions. The details will be specific to the machine in question but
  the general sequence is as follows for all operating systems:

  1. CPU inspection
  2. Memory inspection
  3. Device bus discovery
  4. Device discovery
  5. Auxiliary kernel subsystem setup (networks etc)
  6. Mounting of the root filesystem
  7. Starting of user space


path: Breadboards.md
sortable_path: Breadboards.md
title: Breadboards
lead: ![](../img/breadboard-diagram.jpg)
checksum: c30e28a837b489ecbebb55a6b34153401f1007ceafd0a4318766d0bb0246307e
created: 2024-10-22 16:52:56.423586116+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"created":"Sunday, June 23, 2024","id":"jbf1","tags":["electronics"]}
body:
  ![](../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.

raw_content:
  ---
  id: jbf1
  tags: [electronics]
  created: Sunday, June 23, 2024
  ---

  # 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.


path: Bus.md
sortable_path: Bus.md
title: Bus
checksum: feafa49cc6af71d9de0d74887d090c395dac12a1db60956577d0a933ffc74f03
created: 2024-10-22 16:52:56.426035015+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["bus","computer-architecture"]}
lead:
  A bus is a communication system that transfers data between components inside a
  computer, or between computers.

body:
  A bus is a communication system that transfers data between components inside a
  computer, or between computers.

  **A bus can be thought of as a kind of pipeline that allows different components
  of the computer to communicate with each other.**

  Note that the use of the word "bus" varies somewhat in points of emphasis. A bus
  can mean:

  - The physical wires that make up the bus
  - The communication channel that is established over those wires

  ## Main buses

  | Bus type      | Description                                                                                                                                                                                                        |
  | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
  | System bus    | The primary pathway between the CPU and [memory](Memory.md). It comprises the **data bus** that transfers data from the memory to the CPU and the **address bus** which transmits requests from the CPU to memory. |
  | Internal bus  | Connects local devices for example the harddisk to the CPU.                                                                                                                                                        |
  | External bus  | Connects peripheral devices such as disks and printers to the [motherboard](Motherboard.md)                                                                                                                        |
  | Expansion bus | Allows expansion boards to access the CPU and memory.                                                                                                                                                              |
  | Frontside bus | Main computer bus that determines data transfer rate speed and is the primary data transfer path between the CPU, RAM and other [motherboard](Motherboard.md) devices.                                             |
  | Backside bus  | Transfers secondary cache (L2 cache) data at faster speeds, allowing more efficient CPU operations                                                                                                                 |

  ## Bus standards

  ### Parallel and serial transmission types

  There is a distinction between buses that use **serial** and buses that use
  **parallel transmission**. They are distinguished by the way in which the bits
  are transferred from one point to another, along the bus.

  Serial Transmission is the type of transmission in which a single communication
  link is used to transfer the data from an end to another. On other hand Parallel
  Transmission is the transmission in which multiple parallel links are used that
  transmit each bit of data simultaneously.

  <img src="../img/serial-transmission.jpg" width="800px"/>
  <img src="../img/parallel-transmission.jpg" width="800px"/>

  - Serial buses are cheaper to implement than parallel buses
  - Serial buses operate at greater [latency](Bus.md#latency) than parallel buses

  ## Latency

  Latency means _delay_: the delay from the time the data is requested until the
  time it arrives. In the context of a microprocesser and buses a key locus of
  latency would be between the time a request takes to travel accross the system
  bus from the CPU to the memory.

  ### Bandwidth

  In general, bandwidth is a measure of the capacity for a communications channel
  to transmit data. A bus is a channel between two components thus we can talk
  about the bandwidth of buses. The speed and efficiency of a bus is a function of
  its bandwidth, although other factors such as its length and proximity to the
  source and receiver are also important.

  Bandwidth can be calculated in terms of how many bits can be transferred per
  second. The wider the bus, the more bits that can be transferred. If we have a
  bus that can run at 66MHz and transfer 64 bits, 64 bits is 8Mb thus the
  bandwidth is 8 x 66 = 528Mb.

  ## Standards

  The mode of transmission is used to distinguish the different bus standards.

  SCSI (Small Computer System Interface) and ATA (Advanced Technology Attachment)
  are used for hard drive devices (i.e. internal buses). USB (Univeral Serial Bus)
  and SATA (Serial Advanced Technology Attachment) are used for peripheral
  devices, keyboards and modem devices (i.e external buses).

raw_content:
  ---
  tags:
    - bus
    - computer-architecture
  ---

  # Bus

  A bus is a communication system that transfers data between components inside a
  computer, or between computers.

  **A bus can be thought of as a kind of pipeline that allows different components
  of the computer to communicate with each other.**

  Note that the use of the word "bus" varies somewhat in points of emphasis. A bus
  can mean:

  - The physical wires that make up the bus
  - The communication channel that is established over those wires

  ## Main buses

  | Bus type      | Description                                                                                                                                                                                                        |
  | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
  | System bus    | The primary pathway between the CPU and [memory](Memory.md). It comprises the **data bus** that transfers data from the memory to the CPU and the **address bus** which transmits requests from the CPU to memory. |
  | Internal bus  | Connects local devices for example the harddisk to the CPU.                                                                                                                                                        |
  | External bus  | Connects peripheral devices such as disks and printers to the [motherboard](Motherboard.md)                                                                                                                        |
  | Expansion bus | Allows expansion boards to access the CPU and memory.                                                                                                                                                              |
  | Frontside bus | Main computer bus that determines data transfer rate speed and is the primary data transfer path between the CPU, RAM and other [motherboard](Motherboard.md) devices.                                             |
  | Backside bus  | Transfers secondary cache (L2 cache) data at faster speeds, allowing more efficient CPU operations                                                                                                                 |

  ## Bus standards

  ### Parallel and serial transmission types

  There is a distinction between buses that use **serial** and buses that use
  **parallel transmission**. They are distinguished by the way in which the bits
  are transferred from one point to another, along the bus.

  Serial Transmission is the type of transmission in which a single communication
  link is used to transfer the data from an end to another. On other hand Parallel
  Transmission is the transmission in which multiple parallel links are used that
  transmit each bit of data simultaneously.

  <img src="../img/serial-transmission.jpg" width="800px"/>
  <img src="../img/parallel-transmission.jpg" width="800px"/>

  - Serial buses are cheaper to implement than parallel buses
  - Serial buses operate at greater [latency](Bus.md#latency) than parallel buses

  ## Latency

  Latency means _delay_: the delay from the time the data is requested until the
  time it arrives. In the context of a microprocesser and buses a key locus of
  latency would be between the time a request takes to travel accross the system
  bus from the CPU to the memory.

  ### Bandwidth

  In general, bandwidth is a measure of the capacity for a communications channel
  to transmit data. A bus is a channel between two components thus we can talk
  about the bandwidth of buses. The speed and efficiency of a bus is a function of
  its bandwidth, although other factors such as its length and proximity to the
  source and receiver are also important.

  Bandwidth can be calculated in terms of how many bits can be transferred per
  second. The wider the bus, the more bits that can be transferred. If we have a
  bus that can run at 66MHz and transfer 64 bits, 64 bits is 8Mb thus the
  bandwidth is 8 x 66 = 528Mb.

  ## Standards

  The mode of transmission is used to distinguish the different bus standards.

  SCSI (Small Computer System Interface) and ATA (Advanced Technology Attachment)
  are used for hard drive devices (i.e. internal buses). USB (Univeral Serial Bus)
  and SATA (Serial Advanced Technology Attachment) are used for peripheral
  devices, keyboards and modem devices (i.e external buses).


path: CPU_architecture.md
sortable_path: CPU_architecture.md
title: CPU architecture
checksum: 6691fc7c99d830a001d9898cffd3f0d3ce1aaf02d006de17bdedd228ed8ebd00
created: 2024-10-22 16:52:56.431044599+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["CPU","computer-architecture"]}
lead:
  At the core of a computer sits the Central Processing Unit. This is the assembly
  of chips that execute all computation. Instructions are passed to the CPU along
  the data bus part of the [system bus](Bus.md) from the
  memory. The [kernel](The_kernel.md), also residing in memory,
  sequences and schedules the sending of data to the CPU and manages requests from
  the CPU for data in memory.

body:
  At the core of a computer sits the Central Processing Unit. This is the assembly
  of chips that execute all computation. Instructions are passed to the CPU along
  the data bus part of the [system bus](Bus.md) from the
  memory. The [kernel](The_kernel.md), also residing in memory,
  sequences and schedules the sending of data to the CPU and manages requests from
  the CPU for data in memory.

  The CPU comprises three core components:

  - Registers (a type of memory positioned on the same chip as the CPU )
  - the Arithmetic Logic Unit (ALU)
  - the Control Unit (CU)

  ![](/img/von_neumann_architecture.jpeg)

  > This method of putting together a computer is known as the **Von Neumann
  > Architecture**. It was devised by John von Neumann in about 1945, well before
  > any of the components that would be needed to produce it had actually been
  > invented.

  ## Registers

  This is the part of the CPU that stores data. The memory cells that comprise it
  do not have [capacitors](Memory.md) (unlike RAM)
  so they cannot store very much data but they work faster, which is what is
  important. Because their memory capacity is so small, we measure the size of
  registers in bits rather than bytes.

  In terms of speed, registers sit at the top part of the overall
  [memory hierarchy](Memory.md#the-memory-hierarchy).

  There are five main types of register in the CPU:

  | Register type           | What it stores                                              |
  | ----------------------- | ----------------------------------------------------------- |
  | Accumulator             | The results of calculations                                 |
  | Instruction Register    | The DRAM address of the **instruction** to be processed     |
  | Memory Address Register | The DRAM address of the **data** to be processed            |
  | Memory Data Register    | The store of the data that is currently being processed     |
  | Program Counter         | The RAM address of the **next instruction** to be processed |

  ## Arithmetic Logic Unit

  See [Arithmetic Logic Unit](Arithmetic_Logic_Unit.md)

  ## Control Unit

  The CPU's [controller](Chipset_and_controllers.md). It
  takes the instructions in binary form from RAM memory (separate from the CPU,
  but connected) and then signals to the to ALU and memory registers what it is
  supposed to do to execute the instructions. Think of it as the overseer that
  gets the ALU and registers to work together to run program instructions.

  ## The system clock

  Whilst modern CPUs and multithreading make it appear as though the CPU is
  capable of running multiple processes at once, access to the CPU is in fact
  sequential. The illusion of simultaneous computation is due to the fact the
  processor is so fast that we do not detect the sequential changes. For this to
  happen, the CPU needs to have a means of scheduling and sequencing processes.
  This is made possible through the system clock, hence when talking about the
  speed of the CPU we do so with reference to _clock speeds_ and the _clock
  cycle_.

  The clock's circuitry is based on a quartz crystal system like that used in
  watches. At precisely timed intervals, the clock sends out pulses of electricity
  that cause bits to move from place to place within
  [logic gates](Logic_gates.md) or
  between logic gates and
  [registers](CPU_architecture.md#registers). This is
  covered in greater detail in the discussion of
  [clock signals in digital circuits](Clock_signals.md).

  Simple instructions such as add can often be executed in just one clock cycle,
  whilst complex operations such as divide will require a number of smaller steps,
  each using one cycle.

  We measure the speed of a chip process within the CPU in **Hertz (Hz)**. One
  Hertz is equivalent to _1 cycle per second_ where a "cycle" is equivalent to a
  single clock **tick**. Thus a tick covers a period of 1 second.

  A speed of 2GHz for example means two billion cycles per second. This would mean
  that the clock was completing two billion cycles at each tick. It is clock speed
  that is being referred to when computers are marketed in terms of the number of
  Hz a processor possesses.

  ## Processing cycles

  Each "cycle" is the execution of a process that commences once the
  [kernel](The_kernel.md) hands control to the CPU. Each cycle
  follows a sequence of events known as
  [fetch, decode, and execute](Fetch_decode_execute.md).

  ## Electromagnetism: broader scientific context

  Hertz was the scientist who detected
  [electromagentic waves](Electromagnetism.md).
  We use Hertz as a measure of the frequency of electromatic wave cycles in a
  signal.

  ![](/img/hertz_wave_freq.gif)

  As the diagram above shows, a cycle is equal to one ascending and one descending
  crest. The more cycles per time unit, the greater the Hertz. We see the Hz
  increase as the number of cycles increases over time.

  ## Processor cores

  // TODO: add to dedicated file

  // Add diagram The vast majority of general purpose computers are multi-core.
  This means that the CPU contains more than one processing unit. They are best
  thought of as mini-CPUs within the main CPU since they each have the same
  overall Von Neumann architecture.

  With Intel processors the two main consumer processors are the i5 and i7. The
  latter has more cores than the former. Consequently it has faster clock speeds
  and greater concurrency due to increased threads.

  ### Cache access

  // See pp.129

  ### History

  The development of multi-core processors emerged because practical limits began
  to be reached when trying to increase the clock speeds of processors. For a
  period, increased transistor density meant that there were steady increases in
  clock speeds. This meant that more instructions could be executed per second.

  When the 3GHz level was reached it became clear that pushing a CPU beyond this
  led to excessive heat production and the logic gates not being able to keep up
  at those speeds. Instead of maxing out the clock rate, attention turned to
  running multiple instructions in parallel in order to increase performance.

raw_content:
  ---
  tags:
    - CPU
    - computer-architecture
  ---

  # CPU architecture

  At the core of a computer sits the Central Processing Unit. This is the assembly
  of chips that execute all computation. Instructions are passed to the CPU along
  the data bus part of the [system bus](Bus.md) from the
  memory. The [kernel](The_kernel.md), also residing in memory,
  sequences and schedules the sending of data to the CPU and manages requests from
  the CPU for data in memory.

  The CPU comprises three core components:

  - Registers (a type of memory positioned on the same chip as the CPU )
  - the Arithmetic Logic Unit (ALU)
  - the Control Unit (CU)

  ![](/img/von_neumann_architecture.jpeg)

  > This method of putting together a computer is known as the **Von Neumann
  > Architecture**. It was devised by John von Neumann in about 1945, well before
  > any of the components that would be needed to produce it had actually been
  > invented.

  ## Registers

  This is the part of the CPU that stores data. The memory cells that comprise it
  do not have [capacitors](Memory.md) (unlike RAM)
  so they cannot store very much data but they work faster, which is what is
  important. Because their memory capacity is so small, we measure the size of
  registers in bits rather than bytes.

  In terms of speed, registers sit at the top part of the overall
  [memory hierarchy](Memory.md#the-memory-hierarchy).

  There are five main types of register in the CPU:

  | Register type           | What it stores                                              |
  | ----------------------- | ----------------------------------------------------------- |
  | Accumulator             | The results of calculations                                 |
  | Instruction Register    | The DRAM address of the **instruction** to be processed     |
  | Memory Address Register | The DRAM address of the **data** to be processed            |
  | Memory Data Register    | The store of the data that is currently being processed     |
  | Program Counter         | The RAM address of the **next instruction** to be processed |

  ## Arithmetic Logic Unit

  See [Arithmetic Logic Unit](Arithmetic_Logic_Unit.md)

  ## Control Unit

  The CPU's [controller](Chipset_and_controllers.md). It
  takes the instructions in binary form from RAM memory (separate from the CPU,
  but connected) and then signals to the to ALU and memory registers what it is
  supposed to do to execute the instructions. Think of it as the overseer that
  gets the ALU and registers to work together to run program instructions.

  ## The system clock

  Whilst modern CPUs and multithreading make it appear as though the CPU is
  capable of running multiple processes at once, access to the CPU is in fact
  sequential. The illusion of simultaneous computation is due to the fact the
  processor is so fast that we do not detect the sequential changes. For this to
  happen, the CPU needs to have a means of scheduling and sequencing processes.
  This is made possible through the system clock, hence when talking about the
  speed of the CPU we do so with reference to _clock speeds_ and the _clock
  cycle_.

  The clock's circuitry is based on a quartz crystal system like that used in
  watches. At precisely timed intervals, the clock sends out pulses of electricity
  that cause bits to move from place to place within
  [logic gates](Logic_gates.md) or
  between logic gates and
  [registers](CPU_architecture.md#registers). This is
  covered in greater detail in the discussion of
  [clock signals in digital circuits](Clock_signals.md).

  Simple instructions such as add can often be executed in just one clock cycle,
  whilst complex operations such as divide will require a number of smaller steps,
  each using one cycle.

  We measure the speed of a chip process within the CPU in **Hertz (Hz)**. One
  Hertz is equivalent to _1 cycle per second_ where a "cycle" is equivalent to a
  single clock **tick**. Thus a tick covers a period of 1 second.

  A speed of 2GHz for example means two billion cycles per second. This would mean
  that the clock was completing two billion cycles at each tick. It is clock speed
  that is being referred to when computers are marketed in terms of the number of
  Hz a processor possesses.

  ## Processing cycles

  Each "cycle" is the execution of a process that commences once the
  [kernel](The_kernel.md) hands control to the CPU. Each cycle
  follows a sequence of events known as
  [fetch, decode, and execute](Fetch_decode_execute.md).

  ## Electromagnetism: broader scientific context

  Hertz was the scientist who detected
  [electromagentic waves](Electromagnetism.md).
  We use Hertz as a measure of the frequency of electromatic wave cycles in a
  signal.

  ![](/img/hertz_wave_freq.gif)

  As the diagram above shows, a cycle is equal to one ascending and one descending
  crest. The more cycles per time unit, the greater the Hertz. We see the Hz
  increase as the number of cycles increases over time.

  ## Processor cores

  // TODO: add to dedicated file

  // Add diagram The vast majority of general purpose computers are multi-core.
  This means that the CPU contains more than one processing unit. They are best
  thought of as mini-CPUs within the main CPU since they each have the same
  overall Von Neumann architecture.

  With Intel processors the two main consumer processors are the i5 and i7. The
  latter has more cores than the former. Consequently it has faster clock speeds
  and greater concurrency due to increased threads.

  ### Cache access

  // See pp.129

  ### History

  The development of multi-core processors emerged because practical limits began
  to be reached when trying to increase the clock speeds of processors. For a
  period, increased transistor density meant that there were steady increases in
  clock speeds. This meant that more instructions could be executed per second.

  When the 3GHz level was reached it became clear that pushing a CPU beyond this
  led to excessive heat production and the logic gates not being able to keep up
  at those speeds. Instead of maxing out the clock rate, attention turned to
  running multiple instructions in parallel in order to increase performance.


path: Call_stack.md
sortable_path: Call_stack.md
title: Call-stack
checksum: aff77aa1324668b27489401411bee67763b5a9f1cea56ef1cf45cba5ac16ebdb
created: 2024-10-22 16:52:56.443433044+00:00
modified: 2024-11-04 14:24:25.113342576+00:00
metadata: {"tags":["memory","javascript"]}
lead:
  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.

body:
  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)

raw_content:
  ---
  tags: [memory, javascript]
  ---

  # 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)


path: Capturing_user_input_in_Bash.md
sortable_path: Capturing_user_input_in_Bash.md
title: Capturing user input in Bash
lead: We use [read](Read_command%20_in_Bash.md) to gather user input.
checksum: 355ed7bb1a923b303e8fd84b686b9fa6d64bc574fcd25b60dce3f30a5bc07318
created: 2024-10-22 16:52:56.446018146+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["shell"]}
body:
  We use [read](Read_command%20_in_Bash.md) to gather user input.

  ## Capturing raw inputs

  ```bash
  # Wait for the input
  echo "What is your name?"
  read name

  # Silently gather the input and proceed (the input won't show up in the terminal)
  echo "What is your password?"
  read -s pass

  # Combine in single line
  read  -p "What's your favourite animal? " animal

  echo name: $name, pass $pass, animal: $animal
  ```

  ## Offering a selection

  We can use `select` to get the user to choose from a series of options.

  ```sh
  select animal in "bird" "dog" "cow"
  do
      echo "You selected $animal"
      break
  done
  ```

  This will present each option as number that can be inputted:

  ```
  1) bird
  2) dog
  3) cow
  #? 1
  You selected bird
  ```

  We could make this more robust by using a case statement:

  ```sh
  select animal in "bird" "dog" "cow"
  do
      case $animal in
          bird) echo "Birds like to tweet";;
          dog) echo "Dogs like to woof";;
          cow) echo "Cows like to moo";;
          quit) break;;
          *) "I'm not sure what that means"
      esac
  done
  ```

  ## Ensuring you capture a valid response from the user

  ### Set a default response value with `-i`

  If the user doesn't enter anything, the value will be set to the default.

  ```sh
  read -ep "Favourite colour? " -i "blue" favecolour
  echo "$favecolour"
  # blue
  ```

  Alternative formulation:

  ```sh

  read -p "Favourite colour? [blue]" fave

  # If response empty...
  if [[ -z $fave ]]; then
      fave="blue"
  fi
  echo "$fave was selected"
  ```

  ### Check right number of arguments supplied

  Here we test that the right number of
  [arguments](Passing_arguments_and_options_to_Bash_scripts.md)
  have been passed to the script:

  ```sh
  if (( $#<3 )); then
      echo "This command requires three arguments:"a
      echo "username, userId, and favouriteNumber"
  else
      echo "username: $1"
      echo "userid: $2"
      echo "favourite number: $3"
  fi
  ```

  ## Check the response is valid

  The following example demonstrates checking an input for a pattern (a data-link
  integer sequence):

  ```sh
  read -p "Which year? [nnnn] " year
  until [[ $year =~ [0-9{4} ]];
      read -p "A four-digit year, please! [nnnn] " year
  done
  echo "Selected year: $year"

  ```

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

  # Capturing user input in Bash

  We use [read](Read_command%20_in_Bash.md) to gather user input.

  ## Capturing raw inputs

  ```bash
  # Wait for the input
  echo "What is your name?"
  read name

  # Silently gather the input and proceed (the input won't show up in the terminal)
  echo "What is your password?"
  read -s pass

  # Combine in single line
  read  -p "What's your favourite animal? " animal

  echo name: $name, pass $pass, animal: $animal
  ```

  ## Offering a selection

  We can use `select` to get the user to choose from a series of options.

  ```sh
  select animal in "bird" "dog" "cow"
  do
      echo "You selected $animal"
      break
  done
  ```

  This will present each option as number that can be inputted:

  ```
  1) bird
  2) dog
  3) cow
  #? 1
  You selected bird
  ```

  We could make this more robust by using a case statement:

  ```sh
  select animal in "bird" "dog" "cow"
  do
      case $animal in
          bird) echo "Birds like to tweet";;
          dog) echo "Dogs like to woof";;
          cow) echo "Cows like to moo";;
          quit) break;;
          *) "I'm not sure what that means"
      esac
  done
  ```

  ## Ensuring you capture a valid response from the user

  ### Set a default response value with `-i`

  If the user doesn't enter anything, the value will be set to the default.

  ```sh
  read -ep "Favourite colour? " -i "blue" favecolour
  echo "$favecolour"
  # blue
  ```

  Alternative formulation:

  ```sh

  read -p "Favourite colour? [blue]" fave

  # If response empty...
  if [[ -z $fave ]]; then
      fave="blue"
  fi
  echo "$fave was selected"
  ```

  ### Check right number of arguments supplied

  Here we test that the right number of
  [arguments](Passing_arguments_and_options_to_Bash_scripts.md)
  have been passed to the script:

  ```sh
  if (( $#<3 )); then
      echo "This command requires three arguments:"a
      echo "username, userId, and favouriteNumber"
  else
      echo "username: $1"
      echo "userid: $2"
      echo "favourite number: $3"
  fi
  ```

  ## Check the response is valid

  The following example demonstrates checking an input for a pattern (a data-link
  integer sequence):

  ```sh
  read -p "Which year? [nnnn] " year
  until [[ $year =~ [0-9{4} ]];
      read -p "A four-digit year, please! [nnnn] " year
  done
  echo "Selected year: $year"

  ```


path: Case_statements_in_Bash.md
sortable_path: Case_statements_in_Bash.md
title: Case statements in Bash
lead: Usage against a variable
checksum: d6635837d53adf5453b66d8e1f2a394674eb39138e08056e770721c645c15158
created: 2024-10-22 16:52:56.450012525+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["shell"]}
body:
  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)
  ```

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

  # 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)
  ```


path: Cells_and_batteries.md
sortable_path: Cells_and_batteries.md
title: Cells and batteries
checksum: ac49c2c179325a89919368029b550520f81d6ba3a0db62807f3df76b169637d5
created: 2024-10-22 16:52:56.451172072+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["physics","electricity"]}
lead:
  Cells are a
  [voltage source](Voltage.md) that
  generate a difference of potential via a positive and negative electrode
  separated by an electrolytic solution. The electrolytes pull free electrons from
  one of the materials which creates a positive charge. The other material gains
  the free electrons creating a negative charge.

body:
  Cells are a
  [voltage source](Voltage.md) that
  generate a difference of potential via a positive and negative electrode
  separated by an electrolytic solution. The electrolytes pull free electrons from
  one of the materials which creates a positive charge. The other material gains
  the free electrons creating a negative charge.

  > A battery is a combination of two or more cells.

  > Cells which cannot be recharged are called **primary cells**. Cells which can
  > be recharged are called **secondary cells**.

  ## Connecting batteries

  Cells and batteries can be connected to each other in electrical ciruits to
  increase the overall voltage and current that is produced. There are three main
  connection types:

  - series
  - parallel
  - series-parallel

  > The key thing to remember: **cells configured in series increases the overall
  > voltage available** and **cells configured in parallel increases the overall
  > current available**

  The table below summarises the relative differences:

  ![](/img/cell-comparison.svg)

  ### Series connections

  With series connections we distinguish **series aiding** and **series opposing**
  configurations.

  In the case of **series aiding**, cells are connected one in front of another
  with the positive terminal connecting to the negative terminal of the other in a
  line.

  In this configuration the same current flows through all the cells; it is not
  cumulative. We represent this as follow> However the voltage is cumulative: it
  is the _sum_ of the individual cell voltages, represented below as
  [electrical field](Voltage.md#distinguishing-voltage-from-electric-field):

  $$
  E_{T} = E_{1} + E_{2} + E_{3} \\
  $$

  Thus series connections increase voltage but keep current constant.

  _Series battery connection:_

  ![](/img/series-battery-diagram.svg)

  _Can be represented in a circuit diagram in one of the following two ways: as a
  series of cells or as a single battery:_

  ![](/img/series-battcircuit.svg)

  In the case of **series opposing**, negative terminals are connected to each
  other and positive terminals are connected to each other in a series. This
  doesn't have many applications.

  ### Parallel connections

  In parallel connections all positive terminals are connected to each other and
  all negative terminals are connected to each other.

  This time the voltage is the same as each individual cell but the current is the
  sum of the individual cell currents. So the voltage is constant but the current
  is cumulative:

  $$
  E_{T} = E_{1} = E_{2} = E_{3} \\
  $$

  $$
  I_{T} = I_{1} + I_{2} + I_{3}
  $$

  _Parallel battery connection:_

  ![](/img/parallel-battery-diagram.svg)

  _Parallel battery circuit diagram:_

  ![](/img/circ-batt-final.svg)

  ### Series-parrallel

  If we want both a higher voltage and a higher current we can use series-parallel
  configurations. Connecting cells in series increases the voltage and connecting
  cells in parellel increases the current so doint both boosts the amount of both
  quantities.

raw_content:
  ---
  tags: [physics, electricity]
  ---

  # Cells and batteries

  Cells are a
  [voltage source](Voltage.md) that
  generate a difference of potential via a positive and negative electrode
  separated by an electrolytic solution. The electrolytes pull free electrons from
  one of the materials which creates a positive charge. The other material gains
  the free electrons creating a negative charge.

  > A battery is a combination of two or more cells.

  > Cells which cannot be recharged are called **primary cells**. Cells which can
  > be recharged are called **secondary cells**.

  ## Connecting batteries

  Cells and batteries can be connected to each other in electrical ciruits to
  increase the overall voltage and current that is produced. There are three main
  connection types:

  - series
  - parallel
  - series-parallel

  > The key thing to remember: **cells configured in series increases the overall
  > voltage available** and **cells configured in parallel increases the overall
  > current available**

  The table below summarises the relative differences:

  ![](/img/cell-comparison.svg)

  ### Series connections

  With series connections we distinguish **series aiding** and **series opposing**
  configurations.

  In the case of **series aiding**, cells are connected one in front of another
  with the positive terminal connecting to the negative terminal of the other in a
  line.

  In this configuration the same current flows through all the cells; it is not
  cumulative. We represent this as follow> However the voltage is cumulative: it
  is the _sum_ of the individual cell voltages, represented below as
  [electrical field](Voltage.md#distinguishing-voltage-from-electric-field):

  $$
  E_{T} = E_{1} + E_{2} + E_{3} \\
  $$

  Thus series connections increase voltage but keep current constant.

  _Series battery connection:_

  ![](/img/series-battery-diagram.svg)

  _Can be represented in a circuit diagram in one of the following two ways: as a
  series of cells or as a single battery:_

  ![](/img/series-battcircuit.svg)

  In the case of **series opposing**, negative terminals are connected to each
  other and positive terminals are connected to each other in a series. This
  doesn't have many applications.

  ### Parallel connections

  In parallel connections all positive terminals are connected to each other and
  all negative terminals are connected to each other.

  This time the voltage is the same as each individual cell but the current is the
  sum of the individual cell currents. So the voltage is constant but the current
  is cumulative:

  $$
  E_{T} = E_{1} = E_{2} = E_{3} \\
  $$

  $$
  I_{T} = I_{1} + I_{2} + I_{3}
  $$

  _Parallel battery connection:_

  ![](/img/parallel-battery-diagram.svg)

  _Parallel battery circuit diagram:_

  ![](/img/circ-batt-final.svg)

  ### Series-parrallel

  If we want both a higher voltage and a higher current we can use series-parallel
  configurations. Connecting cells in series increases the voltage and connecting
  cells in parellel increases the current so doint both boosts the amount of both
  quantities.


path: Cherry_picking.md
sortable_path: Cherry_picking.md
title: Cherry-picking a commit
checksum: fe762a7442db93621b3dedb6231838254e600efcf00587cf26a342ae26c75207
created: 2024-10-22 16:52:56.454526133+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["git"]}
lead:
  Cherry-picking is the act of selecting a specific commit or set of commits and
  applying them to a different branch. It allows you to extract a change or a
  series of changes from one branch and apply them to another branch without
  merging the entire branch.

body:
  Cherry-picking is the act of selecting a specific commit or set of commits and
  applying them to a different branch. It allows you to extract a change or a
  series of changes from one branch and apply them to another branch without
  merging the entire branch.

  It can also be used to apply a series of commits that are not sequential in a
  branch to another branch in a specific order.

  Cherry-picking can sometimes result in conflicts, especially if the changes
  you're trying to apply conflict with changes in the destination branch. In such
  cases, you'll need to resolve the conflicts manually before completing the
  cherry-pick process.

  ## Syntax

  Suppose you have two branches: `main` and `feature`. You want to apply the
  changes from the commit with hash `xyx` from the `main` branch to the `feature`
  branch.

  First, switch to the `feature` branch:

  ```
  git checkout feature
  ```

  Next, cherry-pick the commit from the `main` branch:

  ```
  git cherry-pick xyz
  ```

  This will apply the changes from the commit with hash `xyz` from thee `main`
  branch to the `feature` branch. This will create a new SHA on `feature` (pqr)
  but the changes will be identical.

  ![](/img/cherry-pick.svg)

  The benefit is that you only take the select changes you want, you are not
  merging the whole `main` branch into feature.

  Note that you can also cherry-pick multiple commits by specifying their hashes
  separated by spaces:

  ```
  git cherry-pick abcdefg hijklmn opqrst
  ```

  ## Limitations

  - You don't have to just cherry-pick locally, you can also cherry-pick from a
    [remote tracking branch](Remote_tracking_branches.md).
  - You cannot cherry-pick merge commits since these commits do not implement a
    set of changes, they are connecting commits.

  ## Use case

  The time when I have cherry-picked is when a commit has been reverted via
  GitHub. This typically happens on the `main` branch when breaking changes have
  been merged and we want to undo this by reverting back to the previous commit,
  from before the problematic commit was merged in.

  In this scenario, you will want to start from the reverted `main` branch and
  then cherry-pick the breaking commit. You can then fix the bug and keep an
  accurate record of the whole history.

raw_content:
  ---
  tags:
    - git
  ---

  # Cherry-picking a commit

  Cherry-picking is the act of selecting a specific commit or set of commits and
  applying them to a different branch. It allows you to extract a change or a
  series of changes from one branch and apply them to another branch without
  merging the entire branch.

  It can also be used to apply a series of commits that are not sequential in a
  branch to another branch in a specific order.

  Cherry-picking can sometimes result in conflicts, especially if the changes
  you're trying to apply conflict with changes in the destination branch. In such
  cases, you'll need to resolve the conflicts manually before completing the
  cherry-pick process.

  ## Syntax

  Suppose you have two branches: `main` and `feature`. You want to apply the
  changes from the commit with hash `xyx` from the `main` branch to the `feature`
  branch.

  First, switch to the `feature` branch:

  ```
  git checkout feature
  ```

  Next, cherry-pick the commit from the `main` branch:

  ```
  git cherry-pick xyz
  ```

  This will apply the changes from the commit with hash `xyz` from thee `main`
  branch to the `feature` branch. This will create a new SHA on `feature` (pqr)
  but the changes will be identical.

  ![](/img/cherry-pick.svg)

  The benefit is that you only take the select changes you want, you are not
  merging the whole `main` branch into feature.

  Note that you can also cherry-pick multiple commits by specifying their hashes
  separated by spaces:

  ```
  git cherry-pick abcdefg hijklmn opqrst
  ```

  ## Limitations

  - You don't have to just cherry-pick locally, you can also cherry-pick from a
    [remote tracking branch](Remote_tracking_branches.md).
  - You cannot cherry-pick merge commits since these commits do not implement a
    set of changes, they are connecting commits.

  ## Use case

  The time when I have cherry-picked is when a commit has been reverted via
  GitHub. This typically happens on the `main` branch when breaking changes have
  been merged and we want to undo this by reverting back to the previous commit,
  from before the problematic commit was merged in.

  In this scenario, you will want to start from the reverted `main` branch and
  then cherry-pick the breaking commit. You can then fix the bug and keep an
  accurate record of the whole history.


path: Child_to_parent_data_flow_in_React.md
sortable_path: Child_to_parent_data_flow_in_React.md
title: Child to parent data flow
checksum: 63856c6f093fdced5941549b4f278df726d284bb3eea79e182d5c3c8b0af24dd
created: 2024-10-22 16:52:56.458384631+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["javascript","react"]}
lead:
  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.

body:
  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.

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

  # 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.


path: Chipset_and_controllers.md
sortable_path: Chipset_and_controllers.md
title: Chipset and controllers
checksum: ca2c2df3275dfcff2591873d8d2d46a8d31cc1ce799ad942f387f33d1671fe0c
created: 2024-10-22 16:52:56.459552977+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["hardware","computer-architecture"]}
lead:
  A **controller** is simply a circuit that controls a process. The **chipset** is
  a combination of controllers placed on the same piece of silicon.

body:
  A **controller** is simply a circuit that controls a process. The **chipset** is
  a combination of controllers placed on the same piece of silicon.

  The chipset manages the data flow between the different components that comprise
  the [motherboard](Motherboard.md): processor,
  [memory](Memory.md),
  [harddisk](What_are_disks.md) and peripherals.

  Buses run in and out of the chipset into these key motherboard components. The
  main chipset is a kind of junction that sits between the memory and CPU through
  which the [system bus](Bus.md) passes.

  The chipset is sometimes called the "glue" or "traffic controller" of the
  motherboard or _an intelligent intersection of buses_.

raw_content:
  ---
  tags:
    - hardware
    - computer-architecture
  ---

  # Chipset and controllers

  A **controller** is simply a circuit that controls a process. The **chipset** is
  a combination of controllers placed on the same piece of silicon.

  The chipset manages the data flow between the different components that comprise
  the [motherboard](Motherboard.md): processor,
  [memory](Memory.md),
  [harddisk](What_are_disks.md) and peripherals.

  Buses run in and out of the chipset into these key motherboard components. The
  main chipset is a kind of junction that sits between the memory and CPU through
  which the [system bus](Bus.md) passes.

  The chipset is sometimes called the "glue" or "traffic controller" of the
  motherboard or _an intelligent intersection of buses_.


path: Class_inheritance_in_Python.md
sortable_path: Class_inheritance_in_Python.md
title: Class inheritance in Python
lead:
  We distinguish the **parent/source/superclass** class and the **subclass** that
  **extends** it.

checksum: f23b365bae11030668220a054322269ca4b5df59675e5f707cca44b909841361
created: 2024-10-22 16:52:56.468374677+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["python","OOP"]}
body:
  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.

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

  # 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.


path: Classes.md
sortable_path: Classes.md
title: Classes
lead: ## Type declarations for classes
checksum: fc2d7c5ef0ba7520975a393cdd55587756bdb0e0736c1aea39b4f0dacc96f62c
created: 2024-10-22 16:52:56.469759875+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["typescript","OOP"]}
body:
  ## Type declarations for classes

  TypeScript offers full type annotations for classes. It also introduces several
  TypeScript-specific options (control access modifiers, interfaces etc) that do
  not exist in JavaScript but which seek to bring it into closer alignment with
  more strict object-oriented languages like Java and C#.

  A class in JavaScript:

  ```js
  class Age {
    constructor(name, birthYear) {
      this.name = name;
      this.birthYear = birthYear;
    }
    currentYear() {
      return new Date().getFullYear();
    }
    get age() {
      return this.currentYear() - this.birthYear;
    }
    get dataOutput() {
      return `${this.personName} is ${this.age} years old`;
    }
  }
  ```

  The same class in TypeScript:

  ```ts
  class Age {
    personName: string;
    birthYear: number;
    constructor(personName: string, birthYear: number) {
      this.personName = personName;
      this.birthYear = birthYear;
    }
    currentYear(): number {
      return new Date().getFullYear();
    }
    get age(): number {
      return this.currentYear() - this.birthYear;
    }
    get dataOutput(): string {
      return `${this.personName} is ${this.age} years old`;
    }
  }
  ```

  The main points to note are:

  - methods must specify their return type, as with [functions](Functions.md)
  - the constructor function must specify its parameters' types
  - we must declare the types of any properties we intend to use at the start of
    the class.

  ### Instantiating a class

  In order to create an object instance of `Age`, we can use the standard
  constructor function, viz:

  ```js
  const mum = new Age("Mary Jo", 1959);
  console.log(mum);

  /* Age { personName: 'Mary Jo', birthYear: 1959 } */
  ```

  But given that classes define objects, we can also now use `Age` as a new custom
  type and define an object that way

  ```jsx
  const thomas: Age = new Age("Thomas", 1988);
  ```

  ### Without constructor

  If your class does not use a constructor, you still need to define your class
  property types at the top:

  ```tsx
  class Dummy {
    aNum: number = 4;
    get getSquare(): number {
      return this.aNum * this.aNum;
    }
  }
  ```

  ## Interfaces

  In most cases the difference between using the `type` and `interface` keywords
  when defining a custom type is marginal however interfaces are specifically
  designed for classes and OOP style programming in TypeScript. This is obviously
  most apparent in a framework like Angular where interfaces are used heavily.

  When we use an interface with a class we are asserting that the class must have
  certain properties and methods in order to qualify as that type. This is most
  helpful when you are working with several developers and want to ensure
  consistency.

  Let's say we have the following interface:

  ```ts
  interface Person {
    firstName: string;
    secondName: string;
    age: number;
    employed: () => boolean;
  }
  ```

  Now we want to create a class that must share this shape. We go ahead and create
  the class and say that it **implements** `Person` :

  ```ts
  class Programmer implements Person {
  // If the below are not included, TS will generate an error
  	firstName: string,
  	secondName: string,
  	age: number,
  	employed: () => boolean
  }
  ```

  ## Inheritance

  We can extend classes in TypeScript by using sub-classes or abstract classes.

  ### Sub-classes

  In the case of sub-classes, we use the phrase
  `[child_class] extends [parent_class]` in the class declaration to designate the
  inheritance relationship between the base class and the new class that is
  derived from it.

  > A derived class has all the properies and methods of its base class but can
  > also define additional members.

  When you instantiate a child class from a parent class, if the parent class has
  constructor values, you must initialise these in the child. You do this by
  calling the parent constructor via the `super()` syntax. For example:

  ```ts
  class Employee extends Person {
    constructor(firstName: string, lastName: string, private jobTitle: string) {
      // call the constructor of the Person class:
      super(firstName, lastName);
    }
  }
  ```

  To override or alter a method that exists on the parent in the child, you can
  use the syntax `super.[methodName]()`.

  ### Abstract classes

  Classes marked `abstract` are similar to parent in the case of sub-classes. The
  difference is that they are like templates. Several of their methods and
  properties may be directly inherited by classes that derive from them (just like
  sub-classes) but they can include 'blank' methods and properties that are
  placeholders for methods and properties that are defined in the derivation
  class.

  I have found this useful for cases where you want to inherit methods from a
  parent class but implement a specific method differently in each derivation.

  ```ts
  export abstract class IndexHyperlinksProvider
     implements vscode.TreeDataProvider<TreeItem>
  {
     public activeFile: string | undefined
     private outlinks
     private fileSystemUtils: FileSystemUtils

     constructor(
        activeFile: string | undefined,
        workspaceRoot: string | undefined
     ) {
        this.setActiveFile(activeFile)
        this.outlinks = this.generateLinks()
        this.fileSystemUtils = new FileSystemUtils(workspaceRoot)
     }

     abstract generateLinks(): Promise<TreeItem[] | undefined>

     public setActiveFile(activeFile: string | undefined) {
        this.activeFile = activeFile
     }
    ...
  ```

  Above we have a base class with a single abstract method `generateLinks()`. The
  class below extends this base. Note that it passes the parent constructor values
  to `super` and defines an actual method for the designated `generateLinks()`
  template:

  ```ts
  export class IndexOutlinksProvider extends IndexHyperlinksProvider {
    public workspaceFiles: string[];
    public context: vscode.ExtensionContext;
    constructor(
      activeFile: string | undefined,
      workspaceRoot: string | undefined,
      workspaceFiles: string[],
      context: vscode.ExtensionContext
    ) {
      super(activeFile, workspaceRoot);
      this.workspaceFiles = workspaceFiles;
      this.context = context;
    }

    public async generateLinks(): Promise<TreeItem[] | undefined> {
      const indexer = new IndexHyperlinks(this.context, this.workspaceFiles);
      if (typeof this.activeFile === "string") {
        const outlinks = await indexer.indexOutlinks(this.activeFile);
        if (outlinks !== undefined) {
          return this.transformLinksToTreeItem(outlinks);
        }
      }
      return;
    }
  }
  ```

  > Importantly, you cannot instantiate abstract classes. You can only `extend`
  > them and then instantiate their derivation. This is another important
  > difference from sub-classes.

  As with sub-classes, you must initialise the properties of the parent
  constructor with `super`.

  ### `implements` vs `extends`

  You shouldn't confuse `implements` with `extends`. `implements` just checks the
  class as an interface in accordance with the principles of
  [duck typing](Custom_types_in_TypeScript.md#duck-typing):
  i.e the implementing class should have the same properties and methods. It
  doesn't affect anything internal to the methods or properties. So e.g, if you
  typed a method parameter as `string` in the base class, this would still default
  to `any` in the derived class.

raw_content:
  ---

  tags: [typescript, OOP]
  ---

  # Classes

  ## Type declarations for classes

  TypeScript offers full type annotations for classes. It also introduces several
  TypeScript-specific options (control access modifiers, interfaces etc) that do
  not exist in JavaScript but which seek to bring it into closer alignment with
  more strict object-oriented languages like Java and C#.

  A class in JavaScript:

  ```js
  class Age {
    constructor(name, birthYear) {
      this.name = name;
      this.birthYear = birthYear;
    }
    currentYear() {
      return new Date().getFullYear();
    }
    get age() {
      return this.currentYear() - this.birthYear;
    }
    get dataOutput() {
      return `${this.personName} is ${this.age} years old`;
    }
  }
  ```

  The same class in TypeScript:

  ```ts
  class Age {
    personName: string;
    birthYear: number;
    constructor(personName: string, birthYear: number) {
      this.personName = personName;
      this.birthYear = birthYear;
    }
    currentYear(): number {
      return new Date().getFullYear();
    }
    get age(): number {
      return this.currentYear() - this.birthYear;
    }
    get dataOutput(): string {
      return `${this.personName} is ${this.age} years old`;
    }
  }
  ```

  The main points to note are:

  - methods must specify their return type, as with [functions](Functions.md)
  - the constructor function must specify its parameters' types
  - we must declare the types of any properties we intend to use at the start of
    the class.

  ### Instantiating a class

  In order to create an object instance of `Age`, we can use the standard
  constructor function, viz:

  ```js
  const mum = new Age("Mary Jo", 1959);
  console.log(mum);

  /* Age { personName: 'Mary Jo', birthYear: 1959 } */
  ```

  But given that classes define objects, we can also now use `Age` as a new custom
  type and define an object that way

  ```jsx
  const thomas: Age = new Age("Thomas", 1988);
  ```

  ### Without constructor

  If your class does not use a constructor, you still need to define your class
  property types at the top:

  ```tsx
  class Dummy {
    aNum: number = 4;
    get getSquare(): number {
      return this.aNum * this.aNum;
    }
  }
  ```

  ## Interfaces

  In most cases the difference between using the `type` and `interface` keywords
  when defining a custom type is marginal however interfaces are specifically
  designed for classes and OOP style programming in TypeScript. This is obviously
  most apparent in a framework like Angular where interfaces are used heavily.

  When we use an interface with a class we are asserting that the class must have
  certain properties and methods in order to qualify as that type. This is most
  helpful when you are working with several developers and want to ensure
  consistency.

  Let's say we have the following interface:

  ```ts
  interface Person {
    firstName: string;
    secondName: string;
    age: number;
    employed: () => boolean;
  }
  ```

  Now we want to create a class that must share this shape. We go ahead and create
  the class and say that it **implements** `Person` :

  ```ts
  class Programmer implements Person {
  // If the below are not included, TS will generate an error
  	firstName: string,
  	secondName: string,
  	age: number,
  	employed: () => boolean
  }
  ```

  ## Inheritance

  We can extend classes in TypeScript by using sub-classes or abstract classes.

  ### Sub-classes

  In the case of sub-classes, we use the phrase
  `[child_class] extends [parent_class]` in the class declaration to designate the
  inheritance relationship between the base class and the new class that is
  derived from it.

  > A derived class has all the properies and methods of its base class but can
  > also define additional members.

  When you instantiate a child class from a parent class, if the parent class has
  constructor values, you must initialise these in the child. You do this by
  calling the parent constructor via the `super()` syntax. For example:

  ```ts
  class Employee extends Person {
    constructor(firstName: string, lastName: string, private jobTitle: string) {
      // call the constructor of the Person class:
      super(firstName, lastName);
    }
  }
  ```

  To override or alter a method that exists on the parent in the child, you can
  use the syntax `super.[methodName]()`.

  ### Abstract classes

  Classes marked `abstract` are similar to parent in the case of sub-classes. The
  difference is that they are like templates. Several of their methods and
  properties may be directly inherited by classes that derive from them (just like
  sub-classes) but they can include 'blank' methods and properties that are
  placeholders for methods and properties that are defined in the derivation
  class.

  I have found this useful for cases where you want to inherit methods from a
  parent class but implement a specific method differently in each derivation.

  ```ts
  export abstract class IndexHyperlinksProvider
     implements vscode.TreeDataProvider<TreeItem>
  {
     public activeFile: string | undefined
     private outlinks
     private fileSystemUtils: FileSystemUtils

     constructor(
        activeFile: string | undefined,
        workspaceRoot: string | undefined
     ) {
        this.setActiveFile(activeFile)
        this.outlinks = this.generateLinks()
        this.fileSystemUtils = new FileSystemUtils(workspaceRoot)
     }

     abstract generateLinks(): Promise<TreeItem[] | undefined>

     public setActiveFile(activeFile: string | undefined) {
        this.activeFile = activeFile
     }
    ...
  ```

  Above we have a base class with a single abstract method `generateLinks()`. The
  class below extends this base. Note that it passes the parent constructor values
  to `super` and defines an actual method for the designated `generateLinks()`
  template:

  ```ts
  export class IndexOutlinksProvider extends IndexHyperlinksProvider {
    public workspaceFiles: string[];
    public context: vscode.ExtensionContext;
    constructor(
      activeFile: string | undefined,
      workspaceRoot: string | undefined,
      workspaceFiles: string[],
      context: vscode.ExtensionContext
    ) {
      super(activeFile, workspaceRoot);
      this.workspaceFiles = workspaceFiles;
      this.context = context;
    }

    public async generateLinks(): Promise<TreeItem[] | undefined> {
      const indexer = new IndexHyperlinks(this.context, this.workspaceFiles);
      if (typeof this.activeFile === "string") {
        const outlinks = await indexer.indexOutlinks(this.activeFile);
        if (outlinks !== undefined) {
          return this.transformLinksToTreeItem(outlinks);
        }
      }
      return;
    }
  }
  ```

  > Importantly, you cannot instantiate abstract classes. You can only `extend`
  > them and then instantiate their derivation. This is another important
  > difference from sub-classes.

  As with sub-classes, you must initialise the properties of the parent
  constructor with `super`.

  ### `implements` vs `extends`

  You shouldn't confuse `implements` with `extends`. `implements` just checks the
  class as an interface in accordance with the principles of
  [duck typing](Custom_types_in_TypeScript.md#duck-typing):
  i.e the implementing class should have the same properties and methods. It
  doesn't affect anything internal to the methods or properties. So e.g, if you
  typed a method parameter as `string` in the base class, this would still default
  to `any` in the derived class.


path: Classes_in_Python.md
sortable_path: Classes_in_Python.md
title: Classes in Python
lead: ## General points
checksum: 5efed1754200f43316830f6904ffc98a8fe289ed390bd5e15d55ea6503e8d032
created: 2024-10-22 16:52:56.474465617+00:00
modified: 2024-11-16 13:35:40.969436594+00:00
metadata: {"tags":["python","OOP"]}
body:
  ## General points

  - We don't need to use `new` when instantiating an instance of a class.
  - All properties and methods must be defined in the constructor. This is in
    contrast to JavaScript where the properties in the constructor are those we
    want to initialise when the object is created and where there may be other
    properties and methods that are used outside of the instantiation process.
  - In contrast to TypeScript and other OOP languages it is not necessary to
    declare the properties you wish to define in the constructor, outside of the
    constructor. As long as they are defined in the constructor they are
    accessible.

  ## Class properties

  In contrast to other languages, you do not need to declare any properties you
  wish to use in the class at the top before you can assign to them. This is true
  whether you define them in the constructor or not.

  Instead, you can create properties just by invoking `self.some_property_name` in
  the course of your code and Python will create the variable implicitly.

  For continuity with other languages you may however define them at the top of
  the class if you wish.

  ## Basic syntax

  ```py
  class Person:
  	""" An example class to hold a persons name and age"""

  	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)

  p1 = Person('John', 36)
  p2 = Person('Thomas', 34)
  print(p1)
  print(p2)

  print(p1.name)

  # John
  ```

  Key points to note:

  - The `__init__` method is the constructor function and must exist on every
    class to define the properties of the class
  - `self` is a reference to the class itself and the object it will create, akin
    to `this` in other languages
  - You must pass `self` as a parameter to every method (this is a difference from
    JS)
  - As with functions, we can use docstrings to document the class. What you write
    here will show up in Intellisense etc

  ## Private, protected members and methods

  As Python is not strictly typed the property accessors are not actually real and
  won't throw errors if violated. However there is a syntax convention.

  - Private

    - `__self.property`
    - `def __private method(self)...`

  - Protected

  - `_self.property`
  - `def _protected method(self)...`

  ## More complex class

  ```py
  class Person:
  	""" An example class to hold a persons name and age"""

  	def __init__(self, name, age):
  		self.name = name
  		self.age = age

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

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

  	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 is_teenager(self):
  		return self.age < 20
  ```

  ## Object references

  When you log a class you get a reference to its hexadecimal [memory](Memory.md)
  reference.

  ```py
  p1 = Person('John', 36)
  p2 = Person('Thomas', 34)

  print(p1)
  print(p2)

  # <__main__.Person object at 0x102e75510>
  # <__main__.Person object at 0x102e75511>
  ```

  This shows each object is unique. You can also generate a specific ID with the
  `id()` method:

  ```py
  print(id(p1))
  print(id(p2))

  # 4379088272
  # 4379088656
  ```

  ## Copying objects

  The same principle that applies to [copying functions](Functions_in_Python.md)
  applies to copying objects created through classes: redeclaration results in a
  duplicate entity. Thus changes to the duplicate will affect the original.

  This becomes obvious when you use `id()` but otherwise might not be apparent. To
  copy you should therefore declare a new instance of the class.

  ### The `str` method

  The memory reference isn't very helpful for viewing the data contents. To get
  around this we can use the builtin `__string__` method which automatically logs
  whatever properties you put in there:

  ```py
  class Person:
      """ An example class to hold a persons name and age"""

      def __init__(self, name, age):
          self.name = name
          self.age = age

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

  p3 = Person('Thomas', 34)
  print(p3)
  # Thomas is 34
  ```

  ## Deleting objects

  You might want to delete an object reference because:

  - the variable referencing the object goes out of scope
  - the variable is set to `None`

  After the `del` statement is applied to a variable that holds an object, the
  object will no longer be available and any attempt to reference it will result
  in an error.

  ```py
  p1 = Person('J-Man', 76)
  del p1
  ```

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

  # Classes in Python

  ## General points

  - We don't need to use `new` when instantiating an instance of a class.
  - All properties and methods must be defined in the constructor. This is in
    contrast to JavaScript where the properties in the constructor are those we
    want to initialise when the object is created and where there may be other
    properties and methods that are used outside of the instantiation process.
  - In contrast to TypeScript and other OOP languages it is not necessary to
    declare the properties you wish to define in the constructor, outside of the
    constructor. As long as they are defined in the constructor they are
    accessible.

  ## Class properties

  In contrast to other languages, you do not need to declare any properties you
  wish to use in the class at the top before you can assign to them. This is true
  whether you define them in the constructor or not.

  Instead, you can create properties just by invoking `self.some_property_name` in
  the course of your code and Python will create the variable implicitly.

  For continuity with other languages you may however define them at the top of
  the class if you wish.

  ## Basic syntax

  ```py
  class Person:
  	""" An example class to hold a persons name and age"""

  	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)

  p1 = Person('John', 36)
  p2 = Person('Thomas', 34)
  print(p1)
  print(p2)

  print(p1.name)

  # John
  ```

  Key points to note:

  - The `__init__` method is the constructor function and must exist on every
    class to define the properties of the class
  - `self` is a reference to the class itself and the object it will create, akin
    to `this` in other languages
  - You must pass `self` as a parameter to every method (this is a difference from
    JS)
  - As with functions, we can use docstrings to document the class. What you write
    here will show up in Intellisense etc

  ## Private, protected members and methods

  As Python is not strictly typed the property accessors are not actually real and
  won't throw errors if violated. However there is a syntax convention.

  - Private

    - `__self.property`
    - `def __private method(self)...`

  - Protected

  - `_self.property`
  - `def _protected method(self)...`

  ## More complex class

  ```py
  class Person:
  	""" An example class to hold a persons name and age"""

  	def __init__(self, name, age):
  		self.name = name
  		self.age = age

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

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

  	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 is_teenager(self):
  		return self.age < 20
  ```

  ## Object references

  When you log a class you get a reference to its hexadecimal [memory](Memory.md)
  reference.

  ```py
  p1 = Person('John', 36)
  p2 = Person('Thomas', 34)

  print(p1)
  print(p2)

  # <__main__.Person object at 0x102e75510>
  # <__main__.Person object at 0x102e75511>
  ```

  This shows each object is unique. You can also generate a specific ID with the
  `id()` method:

  ```py
  print(id(p1))
  print(id(p2))

  # 4379088272
  # 4379088656
  ```

  ## Copying objects

  The same principle that applies to [copying functions](Functions_in_Python.md)
  applies to copying objects created through classes: redeclaration results in a
  duplicate entity. Thus changes to the duplicate will affect the original.

  This becomes obvious when you use `id()` but otherwise might not be apparent. To
  copy you should therefore declare a new instance of the class.

  ### The `str` method

  The memory reference isn't very helpful for viewing the data contents. To get
  around this we can use the builtin `__string__` method which automatically logs
  whatever properties you put in there:

  ```py
  class Person:
      """ An example class to hold a persons name and age"""

      def __init__(self, name, age):
          self.name = name
          self.age = age

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

  p3 = Person('Thomas', 34)
  print(p3)
  # Thomas is 34
  ```

  ## Deleting objects

  You might want to delete an object reference because:

  - the variable referencing the object goes out of scope
  - the variable is set to `None`

  After the `del` statement is applied to a variable that holds an object, the
  object will no longer be available and any attempt to reference it will result
  in an error.

  ```py
  p1 = Person('J-Man', 76)
  del p1
  ```


path: Clock_signals.md
sortable_path: Clock_signals.md
title: Clock signals
checksum: 236bc7b4f645cbd7e468bd144a7723b56a13d04d0e0ceee52c5a7fdc11ce9a60
created: 2024-10-22 16:52:56.478963433+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["binary","memory","electromagnetism","hardware"]}
lead:
  In the examples of digital circuits so far (i.e
  [adders](Half_adder_and_full_adder.md)
  and [latches](Latches.md)) everything
  happens in a single instant or over several repeated instances. This is because
  of how simple the circuits are. In the case of latches only a single bit is
  updated. And even with rippled adders they are just a series of 1-bit updaters
  in a chain.

body:
  In the examples of digital circuits so far (i.e
  [adders](Half_adder_and_full_adder.md)
  and [latches](Latches.md)) everything
  happens in a single instant or over several repeated instances. This is because
  of how simple the circuits are. In the case of latches only a single bit is
  updated. And even with rippled adders they are just a series of 1-bit updaters
  in a chain.

  With more complex circuits that use multiple memory devices which store a series
  of bits at once, we need a way to ensure that the bits are set at the same time.
  We do this by sequencing the execution with the pulses of the system clock.

  A single iteration of the volatage rising and falling is a **pulse**. A complete
  oscillation from low to high and back to low is a **cycle**. As with all
  [electromagnetic](Electromagnetism.md)
  signals we measure the frequency of the wave in Hertz: cylcles per second. We
  also further distinguish the rising and falling edge of a pulse. Rising
  represents the signal passing from ground to its maximum voltage and falling is
  the reverse (the electrons moving from the voltage source to ground).

  The diagram below shows a pulse cycle of 2Hz.

  ![](/img/clock_pulses.png)

  ## Linking components to the clock

  - All components that need to be synchronised are connected to the clock
  - State changes in the component occur only when a clock pulse occurs
  - Clock-driven components will typically trigger their state changes on either
    the rising edge or the falling edge of the pulse.
  - Components that trigger state changes on the rising pulse are **positive
    edge-triggered**
  - Components that trigger state changes on the falling pulse are **negative
    edge-triggered**

  The role of the clock is essential in the functioning of the
  [CPU](CPU_architecture.md#the-system-clock). It is
  the system clock that gives CPUs their performance rating: how many processes
  can execute within a given clock cycle.

raw_content:
  ---
  tags:
    - binary
    - memory
    - electromagnetism
    - hardware
  ---

  # Clock signals

  In the examples of digital circuits so far (i.e
  [adders](Half_adder_and_full_adder.md)
  and [latches](Latches.md)) everything
  happens in a single instant or over several repeated instances. This is because
  of how simple the circuits are. In the case of latches only a single bit is
  updated. And even with rippled adders they are just a series of 1-bit updaters
  in a chain.

  With more complex circuits that use multiple memory devices which store a series
  of bits at once, we need a way to ensure that the bits are set at the same time.
  We do this by sequencing the execution with the pulses of the system clock.

  A single iteration of the volatage rising and falling is a **pulse**. A complete
  oscillation from low to high and back to low is a **cycle**. As with all
  [electromagnetic](Electromagnetism.md)
  signals we measure the frequency of the wave in Hertz: cylcles per second. We
  also further distinguish the rising and falling edge of a pulse. Rising
  represents the signal passing from ground to its maximum voltage and falling is
  the reverse (the electrons moving from the voltage source to ground).

  The diagram below shows a pulse cycle of 2Hz.

  ![](/img/clock_pulses.png)

  ## Linking components to the clock

  - All components that need to be synchronised are connected to the clock
  - State changes in the component occur only when a clock pulse occurs
  - Clock-driven components will typically trigger their state changes on either
    the rising edge or the falling edge of the pulse.
  - Components that trigger state changes on the rising pulse are **positive
    edge-triggered**
  - Components that trigger state changes on the falling pulse are **negative
    edge-triggered**

  The role of the clock is essential in the functioning of the
  [CPU](CPU_architecture.md#the-system-clock). It is
  the system clock that gives CPUs their performance rating: how many processes
  can execute within a given clock cycle.


path: Command_pattern.md
sortable_path: Command_pattern.md
title: Command pattern
checksum: 2a0f47eb1db5f3a881e75eac0a99ad3c7222475866fbd149b6b185de6a3bbfb6
created: 2024-10-22 16:52:56.486853784+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["design-patterns"]}
lead:
  This is typically used when you want to sequence a fairly large number of
  actions in a series with the option to undo or reverse them. Similarly to the
  mediator it has a one-to-many architecture (there is a central class that
  sequences the commands which are handled by other classes) but in addition to
  sending and routing requests between colleagues, it keeps a central store of
  them and can reverse actions.

body:
  This is typically used when you want to sequence a fairly large number of
  actions in a series with the option to undo or reverse them. Similarly to the
  mediator it has a one-to-many architecture (there is a central class that
  sequences the commands which are handled by other classes) but in addition to
  sending and routing requests between colleagues, it keeps a central store of
  them and can reverse actions.

  The typical example is a calculator. Will will generate this using constructor
  function syntax, rather than ES6 class syntax.

  You have a bunch of arithmetic functions:

  ```jsx
  function add(x, y) {
    return x + y;
  }
  function sub(x, y) {
    return x - y;
  }
  function mul(x, y) {
    return x * y;
  }
  function div(x, y) {
    return x / y;
  }
  ```

  Then a generalised `Command` class that has three parameters: undo, execute, and
  return a value:

  ```jsx
  var Command = function (execute, undo, value) {
    this.execute = execute;
    this.undo = undo;
    this.value = value;
  };
  ```

  We then create the specific commands by combining the functions and the
  `Command` class:

  ```jsx
  var AddCommand = function (value) {
    return new Command(add, sub, value);
  };

  // We would create one of these classes for each of the four operations
  ```

  So now the `add` and `subtract` functions slot into `execute` and `undo` on the
  `Command` class.

  Finally we create the centralised command class that will return values based on
  the individual commands it sequences and issues, store them and remove them:

  ```jsx
  var Calculator = function () {
      var current = 0;
      var commands = [];

      function action(command) {
          var name = command.execute.toString().substr(9, 3);
          return name.charAt(0).toUpperCase() + name.slice(1);
      }

      return {
          execute: function (command) {
              current = command.execute(current, command.value);
              commands.push(command);
              log.add(action(command) + ": " + command.value);
          },

          undo: function () {
              var command = commands.pop();
              current = command.undo(current, command.value);
              log.add("Undo " + action(command) + ": " + command.value);
          },

          getCurrentValue: function () {
              return current;
          }
      }
  ```

  ```jsx
  calculator.execute(new AddCommand(100));
  calculator.execute(new SubCommand(24));
  calculator.execute(new MulCommand(6));
  calculator.execute(new DivCommand(2));
  ```

  Or undo them with:

  ```jsx
  calculator.undo();
  ```

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

  # Command pattern

  This is typically used when you want to sequence a fairly large number of
  actions in a series with the option to undo or reverse them. Similarly to the
  mediator it has a one-to-many architecture (there is a central class that
  sequences the commands which are handled by other classes) but in addition to
  sending and routing requests between colleagues, it keeps a central store of
  them and can reverse actions.

  The typical example is a calculator. Will will generate this using constructor
  function syntax, rather than ES6 class syntax.

  You have a bunch of arithmetic functions:

  ```jsx
  function add(x, y) {
    return x + y;
  }
  function sub(x, y) {
    return x - y;
  }
  function mul(x, y) {
    return x * y;
  }
  function div(x, y) {
    return x / y;
  }
  ```

  Then a generalised `Command` class that has three parameters: undo, execute, and
  return a value:

  ```jsx
  var Command = function (execute, undo, value) {
    this.execute = execute;
    this.undo = undo;
    this.value = value;
  };
  ```

  We then create the specific commands by combining the functions and the
  `Command` class:

  ```jsx
  var AddCommand = function (value) {
    return new Command(add, sub, value);
  };

  // We would create one of these classes for each of the four operations
  ```

  So now the `add` and `subtract` functions slot into `execute` and `undo` on the
  `Command` class.

  Finally we create the centralised command class that will return values based on
  the individual commands it sequences and issues, store them and remove them:

  ```jsx
  var Calculator = function () {
      var current = 0;
      var commands = [];

      function action(command) {
          var name = command.execute.toString().substr(9, 3);
          return name.charAt(0).toUpperCase() + name.slice(1);
      }

      return {
          execute: function (command) {
              current = command.execute(current, command.value);
              commands.push(command);
              log.add(action(command) + ": " + command.value);
          },

          undo: function () {
              var command = commands.pop();
              current = command.undo(current, command.value);
              log.add("Undo " + action(command) + ": " + command.value);
          },

          getCurrentValue: function () {
              return current;
          }
      }
  ```

  ```jsx
  calculator.execute(new AddCommand(100));
  calculator.execute(new SubCommand(24));
  calculator.execute(new MulCommand(6));
  calculator.execute(new DivCommand(2));
  ```

  Or undo them with:

  ```jsx
  calculator.undo();
  ```


path: Comparing_React_classes_to_hooks.md
sortable_path: Comparing_React_classes_to_hooks.md
title: Comparing class components to hook-based components
lead:
  We will look at the same component written with hooks and classes. Both use this
  child component:

checksum: 5ed406f78142c4075d1118c27ed9b0cf5d84269988210b765ffd1386709946c1
created: 2024-10-22 16:52:56.490833911+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["javascript","react"]}
body:
  We will look at the same component written with hooks and classes. Both use this
  child component:

  ```jsx
  const CounterBox = (props) => {
    return <div className="output">{props.counterValue}</div>;
  };
  ```

  ## Class component

  ```jsx
  import CounterBox from "./CounterBox";

  class CounterClass extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        counterValue: 0,
      };
    }

    addOne() {
      this.setState({
        counterValue: this.state.counterValue + 1,
      });
    }

    render() {
      return (
        <React.Fragment>
          <button onClick={() => this.addOne()}>Add One</button>
          <CounterBox counterValue={this.state.counterValue} />
        </React.Fragment>
      );
    }
  }
  ```

  ## Hook version

  ```jsx
  import React, { useState } from "react";
  import CounterBox from "./CounterBox";

  function CounterHook() {
    const [counter, setCounter] = useState(0);
    const handleClick = () => setCounter(counter + 1);
    return (
      <React.Fragment>
        <button onClick={handleClick}>Add One</button>
        <CounterBox counterValue={counter}></CounterBox>
      </React.Fragment>
    );
  }
  ```

  ## Main differences

  - Obviously with hooks we use a function, not a class. We can therefore think of
    them as **simple components** that are able to handle state as well as props.
  - We have written the hook as a declared function but we could just as easily
    write it as a function expression or arrow function.
  - With the class, the state versioning is managed via the following properties
    on the `React.Component` class: `this.state` (start state) and `this.setState`
    (the change).
  - In both cases the function that actually creates the change is handled via an
    arrow function. With the hook we use the customary name `handleClick` .

  - The same process is managed in the hook via the `useState()` method. We
    effectively set 'before' and 'after' destructured variables on this method:

    ```jsx
    const [counter, setCounter] = useState(0);

    // Schematically:
    const [val, setVal] = useState(initialVal);
    ```

  ## Binding with class component state

  In the class component example we use an inline arrow function to execute
  `addOne()` however the official way to do it is to bind the event via the
  constructor method to avoid the problems created by `this` scope in classes.
  Using arrows is mostly fine and is less verbose, but it is important to
  understand the 'official' approach.

  The way to write the state change in this way is:

  ```jsx
  class CounterClass extends React.Component{
    constructor(props){
      super(props);
  		this.state = { counterValue: 0};
      this.handleClick = this.handleClick.bind(this); // bind the click to the class scope
    }

    handleClick(event){
      this.setState({
              counterValue: this.state.counterValue + 1;
      });
    }

    render(){
      return (
        <button type="button"
        onClick={this.handleClick}> // reference the class scope with `this`
        Click Me
        </button>
  			<CounterBox counterValue={this.state.counterValue}></CounterBox>
      );
    }
  }
  ```

  ## How hooks improve on class components

  - State, when managed via hooks can be easily decoupled from a specific
    component and reused elsewhere. This also means it can be tested separately
    from any specific component where it is applied. This is much harder to do
    with classes which are closely entangled with the non-state aspects of a
    component through `this` and binding:

    > With Hooks, you can extract stateful logic from a component so it can be
    > tested independently and reused. Hooks allow you to reuse stateful logic
    > without changing your component hierarchy. This makes it easy to share Hooks
    > among many components or with the community.

  - Hooks simplify lifestyle methods and the use of `componentDidMount` etc
    through the `useEffect` hook
  - Classes and `this` are confusing generally. Functions are easier to grasp and
    read.

  ## Relation to function components

  You can think of hooks as function components (previously known as stateless
  components) plus state. Previously function components were 'dumb' components.
  That is, they didn't allow for state management and generally used for layout.
  They could receive data as props but not state. This changes with hooks: you can
  add state to functions and no longer need to use classes exclusively for state.
  In fact you no longer need to use classes at all, unless you want to.

  > The stateful portion of a function component must be placed before the
  > `return()` statement in the function.

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

  # Comparing class components to hook-based components

  We will look at the same component written with hooks and classes. Both use this
  child component:

  ```jsx
  const CounterBox = (props) => {
    return <div className="output">{props.counterValue}</div>;
  };
  ```

  ## Class component

  ```jsx
  import CounterBox from "./CounterBox";

  class CounterClass extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        counterValue: 0,
      };
    }

    addOne() {
      this.setState({
        counterValue: this.state.counterValue + 1,
      });
    }

    render() {
      return (
        <React.Fragment>
          <button onClick={() => this.addOne()}>Add One</button>
          <CounterBox counterValue={this.state.counterValue} />
        </React.Fragment>
      );
    }
  }
  ```

  ## Hook version

  ```jsx
  import React, { useState } from "react";
  import CounterBox from "./CounterBox";

  function CounterHook() {
    const [counter, setCounter] = useState(0);
    const handleClick = () => setCounter(counter + 1);
    return (
      <React.Fragment>
        <button onClick={handleClick}>Add One</button>
        <CounterBox counterValue={counter}></CounterBox>
      </React.Fragment>
    );
  }
  ```

  ## Main differences

  - Obviously with hooks we use a function, not a class. We can therefore think of
    them as **simple components** that are able to handle state as well as props.
  - We have written the hook as a declared function but we could just as easily
    write it as a function expression or arrow function.
  - With the class, the state versioning is managed via the following properties
    on the `React.Component` class: `this.state` (start state) and `this.setState`
    (the change).
  - In both cases the function that actually creates the change is handled via an
    arrow function. With the hook we use the customary name `handleClick` .

  - The same process is managed in the hook via the `useState()` method. We
    effectively set 'before' and 'after' destructured variables on this method:

    ```jsx
    const [counter, setCounter] = useState(0);

    // Schematically:
    const [val, setVal] = useState(initialVal);
    ```

  ## Binding with class component state

  In the class component example we use an inline arrow function to execute
  `addOne()` however the official way to do it is to bind the event via the
  constructor method to avoid the problems created by `this` scope in classes.
  Using arrows is mostly fine and is less verbose, but it is important to
  understand the 'official' approach.

  The way to write the state change in this way is:

  ```jsx
  class CounterClass extends React.Component{
    constructor(props){
      super(props);
  		this.state = { counterValue: 0};
      this.handleClick = this.handleClick.bind(this); // bind the click to the class scope
    }

    handleClick(event){
      this.setState({
              counterValue: this.state.counterValue + 1;
      });
    }

    render(){
      return (
        <button type="button"
        onClick={this.handleClick}> // reference the class scope with `this`
        Click Me
        </button>
  			<CounterBox counterValue={this.state.counterValue}></CounterBox>
      );
    }
  }
  ```

  ## How hooks improve on class components

  - State, when managed via hooks can be easily decoupled from a specific
    component and reused elsewhere. This also means it can be tested separately
    from any specific component where it is applied. This is much harder to do
    with classes which are closely entangled with the non-state aspects of a
    component through `this` and binding:

    > With Hooks, you can extract stateful logic from a component so it can be
    > tested independently and reused. Hooks allow you to reuse stateful logic
    > without changing your component hierarchy. This makes it easy to share Hooks
    > among many components or with the community.

  - Hooks simplify lifestyle methods and the use of `componentDidMount` etc
    through the `useEffect` hook
  - Classes and `this` are confusing generally. Functions are easier to grasp and
    read.

  ## Relation to function components

  You can think of hooks as function components (previously known as stateless
  components) plus state. Previously function components were 'dumb' components.
  That is, they didn't allow for state management and generally used for layout.
  They could receive data as props but not state. This changes with hooks: you can
  add state to functions and no longer need to use classes exclusively for state.
  In fact you no longer need to use classes at all, unless you want to.

  > The stateful portion of a function component must be placed before the
  > `return()` statement in the function.


path: Compile_Python_app_to_single_executable.md
sortable_path: Compile_Python_app_to_single_executable.md
title: Compile Python app to single executable
lead: Assume you are already using a Pip venv.
checksum: b37e6fed8544630f8bc355e7d3d16baeca84cee7f64713fefac23499634281f2
created: 2024-10-22 16:52:56.493125585+00:00
modified: 2024-10-19 11:56:50.287481603+00:00
metadata: {"created":"Saturday, October 19, 2024","tags":["python","procedural"]}
body:
  Assume you are already using a Pip venv.

  Install `pyinstaller` and/or add it to your `requirements.txt`:

  ```sh
  souce venv/bin/activate
  pip install pyinstaller
  pip install -r requirements.txt
  ```

  Then from root of project:

  ```sh
  pyinstall -F src/app.py
  ```

  The `-F` flag compiles the application to a single file containing all necessary
  external packages and local imports.

  It outpute to `/dist`. Say the app is called `hello`, run the executable thus:

  ```sh
  /my-python-application/dist/app
  ```

raw_content:
  ---
  tags: [python, procedural]
  created: Saturday, October 19, 2024
  ---

  # Compile Python app to single executable

  Assume you are already using a Pip venv.

  Install `pyinstaller` and/or add it to your `requirements.txt`:

  ```sh
  souce venv/bin/activate
  pip install pyinstaller
  pip install -r requirements.txt
  ```

  Then from root of project:

  ```sh
  pyinstall -F src/app.py
  ```

  The `-F` flag compiles the application to a single file containing all necessary
  external packages and local imports.

  It outpute to `/dist`. Say the app is called `hello`, run the executable thus:

  ```sh
  /my-python-application/dist/app
  ```


path: Compile_from_source.md
sortable_path: Compile_from_source.md
title: Compile package from source
lead:
  1. Clone the repo from GitHub
  2. `cd` into the repo
  3. Run `makepkg -c`

checksum: de8c3d8e71405593b3c5f2f87e14f4ee9e431466785abd71552ee440cf2140e8
created: 2024-10-22 16:52:56.495994088+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["Linux","procedural"]}
body:
  1. Clone the repo from GitHub
  2. `cd` into the repo
  3. Run `makepkg -c`

  ## Remove compiled package

  1. If `build` dir still exists for package try running `make uninstall`.
  2. Reinstall the package and run `make uninstall`
  3. Locate the installed package in `/usr/local/bin` and run `sudo rm -rf`

raw_content:
  ---
  tags: [Linux, procedural]
  ---

  # Compile package from source

  1. Clone the repo from GitHub
  2. `cd` into the repo
  3. Run `makepkg -c`

  ## Remove compiled package

  1. If `build` dir still exists for package try running `make uninstall`.
  2. Reinstall the package and run `make uninstall`
  3. Locate the installed package in `/usr/local/bin` and run `sudo rm -rf`


path: Components_props_classes.md
sortable_path: Components_props_classes.md
title: Components and props with class-based components
checksum: 14027cdf0e45e62ff9b9d99481137c168e48d9eed37510a95bba2fa61ae8daa7
created: 2024-10-22 16:52:56.500752876+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["javascript","react"]}
lead:
  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.

body:
  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;
  ```

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

  # 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;
  ```


path: Components_props_hooks_in_React.md
sortable_path: Components_props_hooks_in_React.md
title: Components and props with hooks
lead: As of React 16.8 a class component can be expressed using a function-based hook.
checksum: 15c018061a8e397cfea50f3d2534a8030f03d46576d2586db7e574ba8cff0847
created: 2024-10-22 16:52:56.503386636+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["javascript","react"]}
body:
  As of React 16.8 a class component can be expressed using a function-based hook.

  A class component:

  ```jsx
  class Welcome extends React.Component {
    render() {
      return <h1>Hello, {this.props.name}</h1>;
    }
  }
  ```

  The same component expressed as a hook:

  ```jsx
  function Welcome(props) {
    return <h1>Hello, {props.name}</h1>;
  }
  ```

  To output this component we use the same standard JSX syntax and pass the prop
  as an attribute of the JSX element:

  ```jsx
  <Welcome name="Thomas" />
  ```

  ## How hooks are rendered

  1. We call `ReactDOM.render()` with the `<Welcome name="Thomas" />` element.
     (This would typically be nested within a root `App` component; it wouldn't be
     directly passed to the parent render function.)
  2. React calls the `Welcome` component with `{name: 'Thomas'}` as the props.
  3. Our `Welcome` component returns a `<h1>Hello, Thomas</h1>` element as the
     result.
  4. React DOM efficiently updates the DOM to match `<h1>Hello, Thomas</h1>`.

  ## Function components must be pure functions

  > All React components must act like pure functions with respect to their props

  This means that functions should not attempt to change the inputs they receive
  as parameters. They must always return the same result for the same inputs:

  ```jsx
  function sum(a, b) {
    return a + b;
  }
  ```

  ```jsx
  function withdraw(account, amount) {
    account.total -= amount;
  }
  ```

  The reason for this is that **props are read-only** and we pass props to
  function components.

  We change components by using state, not by mutating props. This is consistent
  with React's
  [principle of the immutability of elements](Elements-992594b9cd2e483c85cddddffeb16f11).

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

  # Components and props with hooks

  As of React 16.8 a class component can be expressed using a function-based hook.

  A class component:

  ```jsx
  class Welcome extends React.Component {
    render() {
      return <h1>Hello, {this.props.name}</h1>;
    }
  }
  ```

  The same component expressed as a hook:

  ```jsx
  function Welcome(props) {
    return <h1>Hello, {props.name}</h1>;
  }
  ```

  To output this component we use the same standard JSX syntax and pass the prop
  as an attribute of the JSX element:

  ```jsx
  <Welcome name="Thomas" />
  ```

  ## How hooks are rendered

  1. We call `ReactDOM.render()` with the `<Welcome name="Thomas" />` element.
     (This would typically be nested within a root `App` component; it wouldn't be
     directly passed to the parent render function.)
  2. React calls the `Welcome` component with `{name: 'Thomas'}` as the props.
  3. Our `Welcome` component returns a `<h1>Hello, Thomas</h1>` element as the
     result.
  4. React DOM efficiently updates the DOM to match `<h1>Hello, Thomas</h1>`.

  ## Function components must be pure functions

  > All React components must act like pure functions with respect to their props

  This means that functions should not attempt to change the inputs they receive
  as parameters. They must always return the same result for the same inputs:

  ```jsx
  function sum(a, b) {
    return a + b;
  }
  ```

  ```jsx
  function withdraw(account, amount) {
    account.total -= amount;
  }
  ```

  The reason for this is that **props are read-only** and we pass props to
  function components.

  We change components by using state, not by mutating props. This is consistent
  with React's
  [principle of the immutability of elements](Elements-992594b9cd2e483c85cddddffeb16f11).


path: Concise_subfield_mapping_JS.md
sortable_path: Concise_subfield_mapping_JS.md
title: Concise mapping of object subfields in JS
lead: ## Scenario
checksum: 5c536647ce5cf86a77cee84492355525c621a5cd124a235b33e201119dfeddf4
created: 2024-10-22 16:52:56.507090536+00:00
modified: 2024-11-04 15:20:00.706970358+00:00
metadata: {"created":"Friday, June 28, 2024","id":"dv3u","tags":[]}
body:
  ## Scenario

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

  ## Implementation

  Standard approach with a map:

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

  // We just want the `name` property

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

  More concise approach with destructuring:

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

  ## Related notes

raw_content:
  ---
  id: dv3u
  tags: []
  created: Friday, June 28, 2024
  ---

  # Concise mapping of object subfields in JS

  ## Scenario

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

  ## Implementation

  Standard approach with a map:

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

  // We just want the `name` property

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

  More concise approach with destructuring:

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

  ## Related notes


path: Conditional_Elimination.md
sortable_path: Conditional_Elimination.md
title: Conditional elimination
checksum: 86df0761a089d28e97421503a40335da94a7feefd373da84c1fcd37ec2119002
created: 2024-10-22 16:52:56.508317356+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  If we have a conditional and we have independently derived its antecedent, we
  may invoke its consequent. This is often referred to as _Modus ponens_
  (affirming the antecedent).

body:
  If we have a conditional and we have independently derived its antecedent, we
  may invoke its consequent. This is often referred to as _Modus ponens_
  (affirming the antecedent).

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

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

  # Conditional elimination

  If we have a conditional and we have independently derived its antecedent, we
  may invoke its consequent. This is often referred to as _Modus ponens_
  (affirming the antecedent).

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


path: Conditional_Introduction.md
sortable_path: Conditional_Introduction.md
title: Conditional Introduction
checksum: 247293c3755879d733dbf3bf78e879cc01a2f9ed5785d51536ecfe87658ba54b
created: 2024-10-22 16:52:56.509491278+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  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_

body:
  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)

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

  # 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)


path: Conditional_statements_in_Python.md
sortable_path: Conditional_statements_in_Python.md
title: Conditional statements in Python
lead: ## Basic example
checksum: eb5adce7a43084442de1be205babccfa9147bb34ad1283270826a26f040a04b4
created: 2024-10-22 16:52:56.511394775+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["python"]}
body:
  ## Basic example

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

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

  # 5
  # The number is accepted

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

  ## Else if

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

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

  ## Nested conditions

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

  ## Ternaries/ shorthand conditionals

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

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

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

  # Conditional statements in Python

  ## Basic example

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

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

  # 5
  # The number is accepted

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

  ## Else if

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

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

  ## Nested conditions

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

  ## Ternaries/ shorthand conditionals

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

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


path: Conditionals_in_Bash.md
sortable_path: Conditionals_in_Bash.md
title: Conditionals in Bash
lead: ## If statements
checksum: fdcef36604a1998318aad2344da29e3528c8ba744128a50ebf65c5b70bdba92e
created: 2024-10-22 16:52:56.513116008+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["shell"]}
body:
  ## 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
  ```

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

  # 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
  ```


path: Conjunction_Elimination.md
sortable_path: Conjunction_Elimination.md
title: Conditional Elimination
checksum: 6b085ea36ded639035f90c2b4f19e77bbe6486ebfa9a74ec528ca791451bf46b
created: 2024-10-22 16:52:56.514415398+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  If a conjunction exists, it means that both conjuncts are the case; therefore we
  can legitimately extract either one of them. Also known as _Simplification_.

body:
  If a conjunction exists, it means that both conjuncts are the case; therefore we
  can legitimately extract either one of them. Also known as _Simplification_.

  ![](/img/conjunc-elim.png)

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

  # Conditional Elimination

  If a conjunction exists, it means that both conjuncts are the case; therefore we
  can legitimately extract either one of them. Also known as _Simplification_.

  ![](/img/conjunc-elim.png)


path: Conjunction_Introduction.md
sortable_path: Conjunction_Introduction.md
title: Conjunction Introduction
checksum: e43fe155b19e30c610cd32626982f2d13ca28e5569c48a08144a0b001dbac9a4
created: 2024-10-22 16:52:56.516094498+00:00
modified: 2024-10-18 18:11:52.620712712+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  If two conjuncts have each been independently derived then they can be
  conjoined. Also known more simply as _Conjunction_

body:
  If two conjuncts have each been independently derived then they can be
  conjoined. Also known more simply as _Conjunction_

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

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

  # Conjunction Introduction

  If two conjuncts have each been independently derived then they can be
  conjoined. Also known more simply as _Conjunction_

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


path: Connect_to_Mongo_database.md
sortable_path: Connect_to_Mongo_database.md
title: Connect to a database with Mongoose
checksum: 7aaf3990da7ec46f0171cfe1671c406ad4f29d136007bff56c9e4ad840e092fa
created: 2024-10-22 16:52:56.517326548+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["mongo-db","node-js","mongoose","databases"]}
lead:
  Now that we have installed and configured MongoDB, we need to connect to it via
  Node.js. Mongoose is a simple API for interacting with a Mongo database via
  Node.

body:
  Now that we have installed and configured MongoDB, we need to connect to it via
  Node.js. Mongoose is a simple API for interacting with a Mongo database via
  Node.

  With Mongoose installed we can connect to a database. We don't have any Mongo
  databases yet beyond the defaults but the following Mongoose connection logic
  will create and connect to a new database called `playground`:

  Providing the Mongo server is running (execture `mongod`), we will see the
  confirmation message in the console.

  ```js
  mongoose
    .connect("mongodb://127.0.0.1/playground")
    .then(() => console.log("Connected to MongoDB"))
    .catch((err) => console.error(err));
  ```

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

  # Connect to a database with Mongoose

  Now that we have installed and configured MongoDB, we need to connect to it via
  Node.js. Mongoose is a simple API for interacting with a Mongo database via
  Node.

  With Mongoose installed we can connect to a database. We don't have any Mongo
  databases yet beyond the defaults but the following Mongoose connection logic
  will create and connect to a new database called `playground`:

  Providing the Mongo server is running (execture `mongod`), we will see the
  confirmation message in the console.

  ```js
  mongoose
    .connect("mongodb://127.0.0.1/playground")
    .then(() => console.log("Connected to MongoDB"))
    .catch((err) => console.error(err));
  ```


path: Connecting_a_frontend_to_a_Docker_backend.md
sortable_path: Connecting_a_frontend_to_a_Docker_backend.md
title: Connecting a frontend to a Docker backend
checksum: 7c96a810eb119b05198949b080689653ab1ace62545d30a8b316620b3220f6fa
created: 2024-10-22 16:52:56.518564765+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["docker","node-js"]}
lead:
  Building on from
  [NodeJS backend with MySQL database](Node_and_MySQL_db.md)
  we can add a frontend by adapting the existing
  [Docker Compose](Docker_Compose.md) files (one for each
  environment) to accept an additional dependency.

body:
  Building on from
  [NodeJS backend with MySQL database](Node_and_MySQL_db.md)
  we can add a frontend by adapting the existing
  [Docker Compose](Docker_Compose.md) files (one for each
  environment) to accept an additional dependency.

  We won't create a container for the frontend as this is not necessary.

  We would add the following to the `services` section of the compose file:

  ```yml
  services:
    ...
    frontend:
      build: ./frontend
      container_name: frontend_app
      ports:
        - "3000:3000"
      environment:
        - REACT_APP_API_URL=http://localhost:3001
      depends_on:
        - app
  ```

  The `REACT_APP_API_URL` environment variable tells the frontend application
  where to find the backend API. This will then be accessbile via `process.env`
  and can be used directly in the frontend, e.g:

  ```js
  fetch(`${process.env.REACT_APP_API_URL}/api/data`)
    .then((response) => response.json())
    .then((data) => {
      // Process the data from the backend API
    });
  ```

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

  # Connecting a frontend to a Docker backend

  Building on from
  [NodeJS backend with MySQL database](Node_and_MySQL_db.md)
  we can add a frontend by adapting the existing
  [Docker Compose](Docker_Compose.md) files (one for each
  environment) to accept an additional dependency.

  We won't create a container for the frontend as this is not necessary.

  We would add the following to the `services` section of the compose file:

  ```yml
  services:
    ...
    frontend:
      build: ./frontend
      container_name: frontend_app
      ports:
        - "3000:3000"
      environment:
        - REACT_APP_API_URL=http://localhost:3001
      depends_on:
        - app
  ```

  The `REACT_APP_API_URL` environment variable tells the frontend application
  where to find the backend API. This will then be accessbile via `process.env`
  and can be used directly in the frontend, e.g:

  ```js
  fetch(`${process.env.REACT_APP_API_URL}/api/data`)
    .then((response) => response.json())
    .then((data) => {
      // Process the data from the backend API
    });
  ```


path: Containerization.md
sortable_path: Containerization.md
title: Containerizaton
checksum: 483b8bdabe5646cffbb0c5210cc92ce48d4527a70013c29106e40275dd25c8b3
created: 2024-10-22 16:52:56.523696522+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["docker","containerization","Linux"]}
lead:
  > In this entry we look at containerization as a general technology that is
  > distinct from its particular implementation by Docker.

body:
  > 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

raw_content:
  ---
  tags: [docker, containerization, Linux]
  ---

  # 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


path: Controlled_components_in_React.md
sortable_path: Controlled_components_in_React.md
title: Controlled components
lead: Controlled components relate to how you use forms in React.
checksum: af81e879e3f02c89c3466db77f21fa436c84f9a15d6ac37d6c6bafade97c566a
created: 2024-10-22 16:52:56.528572545+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["javascript","react"]}
body:
  Controlled components relate to how you use forms in React.

  You can use standard form HTML within a React component but applying React state
  to the form via controlled components gives you greater power over what you do
  with the inputs and how you handle the data that is received.

  Standard HTML forms have a separate state from the rest of the document object
  and this is highly mutable. It can send requests to a server and reload the
  page. This mutability can obviously interfere with the state you maintain in
  your React components; this is why it is best to manage form state along with
  React state.

  We can combine the two [native HTML form state and React state] by making the
  React state be the “single source of truth”. Then the React component that
  renders a form also controls what happens in that form on subsequent user input.
  An input form element whose value is controlled by React in this way is called a
  “controlled component”.

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

  # Controlled components

  Controlled components relate to how you use forms in React.

  You can use standard form HTML within a React component but applying React state
  to the form via controlled components gives you greater power over what you do
  with the inputs and how you handle the data that is received.

  Standard HTML forms have a separate state from the rest of the document object
  and this is highly mutable. It can send requests to a server and reload the
  page. This mutability can obviously interfere with the state you maintain in
  your React components; this is why it is best to manage form state along with
  React state.

  We can combine the two [native HTML form state and React state] by making the
  React state be the “single source of truth”. Then the React component that
  renders a form also controls what happens in that form on subsequent user input.
  An input form element whose value is controlled by React in this way is called a
  “controlled component”.


path: Corresponding_material_and_biconditional.md
sortable_path: Corresponding_material_and_biconditional.md
title: Corresponding material conditional to show validity
checksum: bca590c17019fe18ef5abdf07e90778538f0de75f7537be061a938ec6d791e8e
created: 2024-10-22 16:52:56.530750164+00:00
modified: 2024-10-18 17:55:56.299060832+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  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.

body:
  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.

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

  # 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.


path: Coulombs_Laws.md
sortable_path: Coulombs_Laws.md
title: Coulombs Laws
checksum: a4e92e62f5d8d9b0b4f04ac115e648842cdeab6f742a92c4eb135b1554954a8d
created: 2024-10-22 16:52:56.532130258+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["physics","electricity"]}
lead:
  Coulomb's Laws express the fundamental relationship between electric charge,
  force and attraction. They are also known as the **Electrostatic Laws** and
  define **the electic force**.

body:
  Coulomb's Laws express the fundamental relationship between electric charge,
  force and attraction. They are also known as the **Electrostatic Laws** and
  define **the electic force**.

  ## First Law

  > Like charges of electricity repel each other. Unlike charges attract each
  > other.

  Within the atom we see this at work in the way that negatively charged electrons
  are attracted to the positively charged nucleus. This attractive force is
  balanced by the centrifugal force caused by the electron's rotation around the
  nucleus. This keeps the electrons in orbit. Without it, they would spiral into
  the nucleus.

  Within circuits, the first law dictates the movement of charge from negatively
  charged atoms to positively charged atoms through the conductor.

  ## Second Law

  The attraction or repulsion between charges acts along the line between the two
  charges.

  ## Third Law

  The size of the electric force varies inversely as the square of the distance
  between the two charges. Thus if the distance between the two charges is
  doubled, the attraction or repulsion becomes weaker, decreasing to a quarter of
  the original value.

  ## Fourth Law

  The size of the electric force is proportional to the value of each charge
  measured in coulombs.

raw_content:
  ---
  tags: [physics, electricity]
  ---

  # Coulombs Laws

  Coulomb's Laws express the fundamental relationship between electric charge,
  force and attraction. They are also known as the **Electrostatic Laws** and
  define **the electic force**.

  ## First Law

  > Like charges of electricity repel each other. Unlike charges attract each
  > other.

  Within the atom we see this at work in the way that negatively charged electrons
  are attracted to the positively charged nucleus. This attractive force is
  balanced by the centrifugal force caused by the electron's rotation around the
  nucleus. This keeps the electrons in orbit. Without it, they would spiral into
  the nucleus.

  Within circuits, the first law dictates the movement of charge from negatively
  charged atoms to positively charged atoms through the conductor.

  ## Second Law

  The attraction or repulsion between charges acts along the line between the two
  charges.

  ## Third Law

  The size of the electric force varies inversely as the square of the distance
  between the two charges. Thus if the distance between the two charges is
  doubled, the attraction or repulsion becomes weaker, decreasing to a quarter of
  the original value.

  ## Fourth Law

  The size of the electric force is proportional to the value of each charge
  measured in coulombs.


path: Create_Mongo_database.md
sortable_path: Create_Mongo_database.md
title: Creating a MongoDB database
lead: First create a directory for the database and set permissions:
checksum: a56970bff12ebf7528c38b17562308601f88cd30e75003021a128d7cfcee43ac
created: 2024-10-22 16:52:56.533537173+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["mongo-db","node-js","mongoose","databases"]}
body:
  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)

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

  # 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)


path: Create_an_SQL_table.md
sortable_path: Create_an_SQL_table.md
title: Create an SQL table
lead: We create tables in an SQL database with the `CREATE` command.
checksum: 97305fa1066934cd3ac031d08eef174b5b2ab13cc9ec2bf97898c087fc7fb471
created: 2024-10-22 16:52:56.535202086+00:00
modified: 2024-10-18 17:55:56.292394043+00:00
metadata: {"tags":["SQL","databases"]}
body:
  We create tables in an SQL database with the `CREATE` command.

  Below is an example of this in practice. Each field corresponds to a column. We
  specify the name of the field and its corresponding
  [data type](Data_types_in_MySQL.md). Every table must have a
  **primary key**. In the example, `employee_id` is the primary key.

  ```sql
  CREATE TABLE employee (
      employee_id text PRIMARY KEY,
      first_name text,
      surname text,
      address_number integer,
      address_1 text,
      address_2 text,
      locality text,
      region text,
      postal_code text,
      phone_number text,
      days_per_week real
      );
  ```

  ## Check table metadata

  We can use `DESCRIBE [table_name]` any time we want to list out the data types
  and other metadata about the table.

  We can use `SHOW TABLE STATUS`/

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

  # Create an SQL table

  We create tables in an SQL database with the `CREATE` command.

  Below is an example of this in practice. Each field corresponds to a column. We
  specify the name of the field and its corresponding
  [data type](Data_types_in_MySQL.md). Every table must have a
  **primary key**. In the example, `employee_id` is the primary key.

  ```sql
  CREATE TABLE employee (
      employee_id text PRIMARY KEY,
      first_name text,
      surname text,
      address_number integer,
      address_1 text,
      address_2 text,
      locality text,
      region text,
      postal_code text,
      phone_number text,
      days_per_week real
      );
  ```

  ## Check table metadata

  We can use `DESCRIBE [table_name]` any time we want to list out the data types
  and other metadata about the table.

  We can use `SHOW TABLE STATUS`/


path: Create_timed_systemd_job.md
sortable_path: Create_timed_systemd_job.md
title: Create timed systemd job
checksum: 8889b0dcfcbe2ace8278fa8d60743ab3c08db3e4a357db8006ecc5d57630a89f
created: 2024-10-22 16:52:56.53814541+00:00
modified: 2024-10-18 17:55:56.295727437+00:00
metadata: {"tags":["systems-programming","systemd","Linux","procedural"]}
lead:
  To use systemd to run a bash script at 11am each day, you can create a new
  systemd service file. Here is an example of what the service file might look
  like:

body:
  To use systemd to run a bash script at 11am each day, you can create a new
  systemd service file. Here is an example of what the service file might look
  like:

  ```
  [Unit]
  Description=Run My Script

  [Service]
  Type=simple
  ExecStart=/bin/bash /path/to/my_script.sh

  [Install]
  WantedBy=default.target
  ```

  Save this file to `/etc/systemd/system/my_script.service`.

  Then you can use systemd to schedule the script to run at 11am each day by
  creating a new systemd timer file. Here is an example of what the timer file
  might look like:

  ```
  [Unit]
  Description=Run My Script Daily at 11am

  [Timer]
  OnCalendar=*-*-* 11:00:00

  [Install]
  WantedBy=timers.target
  ```

  Save this file to `/etc/systemd/system/my_script.timer`

  Then you can enable and start the timer with the following commands:

  ```
  sudo systemctl enable my_script.timer
  sudo systemctl start my_script.timer
  ```

  This will run the script at 11am everyday You can check the status of the timer
  with the following command:

  ```
  sudo systemctl status my_script.timer
  ```

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

  # Create timed `systemd` job

  To use systemd to run a bash script at 11am each day, you can create a new
  systemd service file. Here is an example of what the service file might look
  like:

  ```
  [Unit]
  Description=Run My Script

  [Service]
  Type=simple
  ExecStart=/bin/bash /path/to/my_script.sh

  [Install]
  WantedBy=default.target
  ```

  Save this file to `/etc/systemd/system/my_script.service`.

  Then you can use systemd to schedule the script to run at 11am each day by
  creating a new systemd timer file. Here is an example of what the timer file
  might look like:

  ```
  [Unit]
  Description=Run My Script Daily at 11am

  [Timer]
  OnCalendar=*-*-* 11:00:00

  [Install]
  WantedBy=timers.target
  ```

  Save this file to `/etc/systemd/system/my_script.timer`

  Then you can enable and start the timer with the following commands:

  ```
  sudo systemctl enable my_script.timer
  sudo systemctl start my_script.timer
  ```

  This will run the script at 11am everyday You can check the status of the timer
  with the following command:

  ```
  sudo systemctl status my_script.timer
  ```


path: Creating_a_Docker_image.md
sortable_path: Creating_a_Docker_image.md
title: Creating a Docker image
lead: We create a new image with `docker build...`
checksum: 22ef91b18ea66b61649e2293b99ba7b1d771394bc9bf5acdf7c9bdb92aa605d8
created: 2024-10-22 16:52:56.539988431+00:00
modified: 2024-10-18 17:55:56.302394226+00:00
metadata: {"tags":["docker"]}
body:
  We create a new image with `docker build...`

  The key components of a build:

  - A Dockerfile
  - An asset directory (the **build context**) containing the Dockerfile and all
    resources needed for the build.

  ## Syntax

  Here is an example of a Dockerfile that builds a container for running a Python
  app:

  ```Dockerfile
  # Use an official Python runtime as a parent image
  FROM python:3.8-slim

  # Set the working directory to /app
  WORKDIR /app

  # Copy the current directory contents into the container at /app
  COPY . /app

  # Install any needed packages specified in requirements.txt
  RUN pip install --trusted-host pypi.python.org -r requirements.txt

  # Make port 80 available to the world outside this container
  EXPOSE 80

  # Define environment variable
  ENV NAME World

  # Run app.py when the container launches
  CMD ["python", "app.py"]

  ```

  ### Main syntax explained

  | Keyword   | Role                                                                                                                                                                                                                                                                   |
  | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | #         | Lines starting with '#' are comments, providing explanations or context for the Dockerfile.                                                                                                                                                                            |
  | `FROM`    | Specifies the base image used to build the new image. In this case, it's using the official Python 3.8 slim image.                                                                                                                                                     |
  | `WORKDIR` | Sets the working directory for any subsequent instructions in the Dockerfile. Here, it's set to '/app'. Everything after a `WORKDIR` line, all instructions will be executed in this directory. If it doesn't exist, Docker will create it. Can have multiple.         |
  | `COPY`    | Copies files or directories from the source (in this case, the current directory) to the destination (in this case, '/app') within the Docker image. The source is the build context. We are copying from the build context to the container when we use this keyword. |
  | `ADD`     | Same as `COPY` but can be passed a tarball or a URL                                                                                                                                                                                                                    |
  | `RUN`     | Executes a command and commits the result as a new layer in the image. In this example, it installs packages specified in the 'requirements.txt' file using pip.                                                                                                       |
  | `EXPOSE`  | Informs Docker that the container listens on the specified network ports at runtime. In this case, port 80.                                                                                                                                                            |
  | `ENV`     | Sets an environment variable in the image. In this example, the variable 'NAME' is set to 'World'.                                                                                                                                                                     |
  | `CMD`     | Specifies the command to run when the container starts. Here, it runs 'app.py' using Python. Note that there can only be one CMD instruction in a Dockerfile.                                                                                                          |
  | `USER`    | The default user will be `root`. This is not good for security. Have to choose a distribution image that allows you to change user. For example: `USER: nobody:nogroup`. Ubuntu allows for this.                                                                       |
  | `LABEL`   | Metadata that you want to add to the image to be viewable when image is inspected, e.g. `LABEL maintainer=tactonbishop@gmail.com`                                                                                                                                      |

  ### Another example

  The following Dockerfile creates a frontend React application:

  ```Dockerfile
  # Use the official Node.js image as the base image
  FROM node:14-alpine

  # Set the working directory to /app
  WORKDIR /app

  # Copy the package.json and package-lock.json files
  COPY package*.json ./

  # Install dependencies
  RUN npm ci

  # Copy the rest of the application code
  COPY . .

  # Expose the port the app will run on
  EXPOSE 3000

  # Start the React development server
  CMD ["npm", "start"]
  ```

  ### Two modes of `RUN`: shell and exec

  The RUN command in a Dockerfile has two forms: the "exec" form and the "shell"
  form. Both forms are used to execute commands during the build process, but they
  have different syntax and behavior.

  Exec form is written as `RUN ["executable", "param1", "param2", ...]`. This form
  executes the command directly without invoking a shell. As a result, shell
  processing features like environment variable substitution, pipes, and redirects
  are not available. It is the preferred form when you need to run a command
  without relying on shell behavior or when you want to avoid shell-related
  issues, such as variable substitution or command injection.

  Shell form is written as `RUN command param1 param2 ....` This form executes the
  command within a shell, which is /bin/sh -c on Linux and cmd /S /C on Windows.
  It allows for shell processing, enabling environment variable substitution,
  pipes, redirects, and other shell features. This form is preferred when you need
  to use shell features or when you want to chain multiple commands together.

  ## Environmental variables

  > ENV instructions in a Dockerfile are used to define environment variables that
  > can be accessed by the processes running inside the container. These variables
  > can be used to configure the behavior of the application, pass parameters, or
  > store sensitive information like API keys.

  ```Dockerfile
  # Use an official Node.js runtime as a parent image
  FROM node:14-alpine

  # Set the working directory to /app
  WORKDIR /app

  # Copy package.json and package-lock.json into the container
  COPY package*.json ./

  # Install any needed packages specified in package.json
  RUN npm ci

  # Copy the rest of the application code
  COPY . .

  # Set environment variables
  ENV PORT=8000
  ENV API_KEY=my_secret_key

  # Expose the port the app will run on
  EXPOSE 8000

  # Run the app
  CMD ["npm", "start"]
  ```

  In this example, two environment variables are defined: PORT and API_KEY. These
  variables can be accessed in the application code using process.env.PORT and
  process.env.API_KEY in Node.js, for example.

  To override the environment variables defined in the Dockerfile when running the
  container, you can use the -e flag followed by the variable name and its new
  value in the docker run command:

  ```sh
  docker run -e PORT=8080 -e API_KEY=new_secret_key -p 8080:8000 <image_name>
  ```

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

  # Creating a Docker image

  We create a new image with `docker build...`

  The key components of a build:

  - A Dockerfile
  - An asset directory (the **build context**) containing the Dockerfile and all
    resources needed for the build.

  ## Syntax

  Here is an example of a Dockerfile that builds a container for running a Python
  app:

  ```Dockerfile
  # Use an official Python runtime as a parent image
  FROM python:3.8-slim

  # Set the working directory to /app
  WORKDIR /app

  # Copy the current directory contents into the container at /app
  COPY . /app

  # Install any needed packages specified in requirements.txt
  RUN pip install --trusted-host pypi.python.org -r requirements.txt

  # Make port 80 available to the world outside this container
  EXPOSE 80

  # Define environment variable
  ENV NAME World

  # Run app.py when the container launches
  CMD ["python", "app.py"]

  ```

  ### Main syntax explained

  | Keyword   | Role                                                                                                                                                                                                                                                                   |
  | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | #         | Lines starting with '#' are comments, providing explanations or context for the Dockerfile.                                                                                                                                                                            |
  | `FROM`    | Specifies the base image used to build the new image. In this case, it's using the official Python 3.8 slim image.                                                                                                                                                     |
  | `WORKDIR` | Sets the working directory for any subsequent instructions in the Dockerfile. Here, it's set to '/app'. Everything after a `WORKDIR` line, all instructions will be executed in this directory. If it doesn't exist, Docker will create it. Can have multiple.         |
  | `COPY`    | Copies files or directories from the source (in this case, the current directory) to the destination (in this case, '/app') within the Docker image. The source is the build context. We are copying from the build context to the container when we use this keyword. |
  | `ADD`     | Same as `COPY` but can be passed a tarball or a URL                                                                                                                                                                                                                    |
  | `RUN`     | Executes a command and commits the result as a new layer in the image. In this example, it installs packages specified in the 'requirements.txt' file using pip.                                                                                                       |
  | `EXPOSE`  | Informs Docker that the container listens on the specified network ports at runtime. In this case, port 80.                                                                                                                                                            |
  | `ENV`     | Sets an environment variable in the image. In this example, the variable 'NAME' is set to 'World'.                                                                                                                                                                     |
  | `CMD`     | Specifies the command to run when the container starts. Here, it runs 'app.py' using Python. Note that there can only be one CMD instruction in a Dockerfile.                                                                                                          |
  | `USER`    | The default user will be `root`. This is not good for security. Have to choose a distribution image that allows you to change user. For example: `USER: nobody:nogroup`. Ubuntu allows for this.                                                                       |
  | `LABEL`   | Metadata that you want to add to the image to be viewable when image is inspected, e.g. `LABEL maintainer=tactonbishop@gmail.com`                                                                                                                                      |

  ### Another example

  The following Dockerfile creates a frontend React application:

  ```Dockerfile
  # Use the official Node.js image as the base image
  FROM node:14-alpine

  # Set the working directory to /app
  WORKDIR /app

  # Copy the package.json and package-lock.json files
  COPY package*.json ./

  # Install dependencies
  RUN npm ci

  # Copy the rest of the application code
  COPY . .

  # Expose the port the app will run on
  EXPOSE 3000

  # Start the React development server
  CMD ["npm", "start"]
  ```

  ### Two modes of `RUN`: shell and exec

  The RUN command in a Dockerfile has two forms: the "exec" form and the "shell"
  form. Both forms are used to execute commands during the build process, but they
  have different syntax and behavior.

  Exec form is written as `RUN ["executable", "param1", "param2", ...]`. This form
  executes the command directly without invoking a shell. As a result, shell
  processing features like environment variable substitution, pipes, and redirects
  are not available. It is the preferred form when you need to run a command
  without relying on shell behavior or when you want to avoid shell-related
  issues, such as variable substitution or command injection.

  Shell form is written as `RUN command param1 param2 ....` This form executes the
  command within a shell, which is /bin/sh -c on Linux and cmd /S /C on Windows.
  It allows for shell processing, enabling environment variable substitution,
  pipes, redirects, and other shell features. This form is preferred when you need
  to use shell features or when you want to chain multiple commands together.

  ## Environmental variables

  > ENV instructions in a Dockerfile are used to define environment variables that
  > can be accessed by the processes running inside the container. These variables
  > can be used to configure the behavior of the application, pass parameters, or
  > store sensitive information like API keys.

  ```Dockerfile
  # Use an official Node.js runtime as a parent image
  FROM node:14-alpine

  # Set the working directory to /app
  WORKDIR /app

  # Copy package.json and package-lock.json into the container
  COPY package*.json ./

  # Install any needed packages specified in package.json
  RUN npm ci

  # Copy the rest of the application code
  COPY . .

  # Set environment variables
  ENV PORT=8000
  ENV API_KEY=my_secret_key

  # Expose the port the app will run on
  EXPOSE 8000

  # Run the app
  CMD ["npm", "start"]
  ```

  In this example, two environment variables are defined: PORT and API_KEY. These
  variables can be accessed in the application code using process.env.PORT and
  process.env.API_KEY in Node.js, for example.

  To override the environment variables defined in the Dockerfile when running the
  container, you can use the -e flag followed by the variable name and its new
  value in the docker run command:

  ```sh
  docker run -e PORT=8080 -e API_KEY=new_secret_key -p 8080:8000 <image_name>
  ```


