# notes

path: zk/Joins_in_SQL.md
sortable_path: zkJoins_in_SQL.md
title: Joins in SQL
checksum: 6a774fe4238d77ff4c2738c5352ec7e5df7b5f564a96348deaa82f6c931f261f
created: 2024-03-30 09:03:19.864689426+00:00
modified: 2024-06-15 09:46:15.381586675+00:00
metadata: {"tags":["SQL","databases"]}
lead:
  <!-- Once a relationship has been created using primary and foreign keys (as detailed in the previous section), you are able to combine and integrate data from the different tables. This is known as performing **joins.** -->

body:
  <!-- Once a relationship has been created using primary and foreign keys (as detailed in the previous section), you are able to combine and integrate data from the different tables. This is known as performing **joins.** -->

  We perform joins in SQL when we combine and integrate data from multiple tables.
  In order for a join to be possible, **the tables you want to join must have rows
  that share the same fields**.

  The output will be represented as a table but it is virtual, not an actual
  table. (If you wish to actually create a table or a view off the back of a join
  operation you should combine the join with the
  [`CREATE TABLE`](Create_an_SQL_table.md) command etc.)

  ## Inner joins

  > An inner join combines rows that have matching values in two or more tables.
  > This join is based on the existence of a common field between the tables and
  > is used to retrieve data that appears in both tables.

  The following is a good scenario for creating an inner join:

  > We want to create a list of the name of all computers that have been sold and
  > when they were sold.

  We will be working from the following tables.

  _The `sales` table_:

  | saleId | modelId | saleDate   | employeeId | price   |
  | ------ | ------- | ---------- | ---------- | ------- |
  | 1      | 44      | 2020-07-27 | tbishop    | 40.99   |
  | 2      | 22      | 2021-02-05 | tbishop    | 986.99  |
  | 3      | 14      | 2022-11-16 | tgnomay    | 1248.99 |

  _The `model` table_:

  | modelId | modelName            |
  | ------- | -------------------- |
  | 44      | Raspberry Pi Model A |
  | 22      | Apple MacBook Air    |
  | 14      | Lenovo ThinkPad P15  |

  The data points we need are not provided in a single table. `sales` gives us the
  `saleDate` but not the `modelName`. In order to meet our requirements we must
  perform a join that combines `modelName` from the `model` table and `saleDate`
  from the `sale` table to give us a table like the following:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |

  We can do this by utilising the field they have in common: `modelId`.

  ### Implementation

  The general syntax for creating an inner join from two tables that share a
  common field is as follows:

  ```sql
  SELECT column_name(s)
  FROM table1
  INNER JOIN table2
  ON table1.columnName = table2.columnName
  ```

  So basically, look at `table1` and `table2` return the rows where the fields for
  `table1` and `table2` match, where these fields are the ones specified in the
  `SELECT`.

  Applied to our example:

  ```sql
  SELECT model.modelName, sales.saleDate -- Name the fields
  FROM model -- Specify the first table
  INNER JOIN sales -- Specify the second table
  ON model.model_id = sales.model_id; -- Specify the match criteria
  ```

  > It is important to note that the match must _be_ a match. Both tables must
  > have a row with the same value for `modelId` for the join to be applied. If
  > there is a `modelId` of X in `sales` but no corresponding X for `modelId` in
  > `model`, nothing will be returned for that row.

  We can represent the logical relationship that obtains between the `sales` and
  `model` tables as follows:

  ![](/img/sql-inner-join.png)

  ## Outer joins

  Outer joins are joins that return matched values **and unmatched** values from
  either or both tables.

  To explain outer joins we need to expand our data set:

  _The `sales` table_:

  | saleId | modelId | saleDate   | employeeId | price   |
  | ------ | ------- | ---------- | ---------- | ------- |
  | 1      | 44      | 2020-07-27 | tbishop    | 40.99   |
  | 2      | 22      | 2021-02-05 | tbishop    | 986.99  |
  | 3      | 14      | 2022-11-16 | tgnomay    | 1248.99 |
  | 4      |         | 2020-03-01 | msmelk     | 699.99  |

  _The `model` table_:

  | modelId | modelName            |
  | ------- | -------------------- |
  | 44      | Raspberry Pi Model A |
  | 22      | Apple MacBook Air    |
  | 14      | Lenovo ThinkPad P15  |
  | 66      | Google Chromebook    |

  > We will assume that `sales` is our left table and `model` our right.

  ### Left outer join

  A left outer join returns all records from the left or first table and the
  matching records from the right or second table. If there are not matches on the
  right then only the left data is returned.

  In the context of our dataset, in the _sales_ database we have a row where there
  is a sale with no corresponding `modelId`. If we performed a left outer join
  this would give us the following table in return:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  |                      | 2020-03-01 |

  The logical relationship sustained between `sales` and `model` by a left inner
  join is represented in the following diagram:

  ![](/img/sql-left-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  LEFT JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  LEFT JOIN model
  ON sales.modelId = model.modelId
  ```

  ### Right outer join

  This is the inverse of the right outer join. A right outer join returns all
  records from the right or second table and the matching records from the left or
  first table. If there are not matches on the left then only the right data is
  returned.

  In the context of our dataset, in the _model_ database we have a row where there
  is a `modelId` without a corresponding entry in the `sales` table. If we
  performed a right outer join this would give us the following table in return:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  | Google Chromebook    |            |

  The logical relationship sustained between `sales` and `model` by a right inner
  join is represented in the following diagram:

  ![](/img/sql-right-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  RIGHT JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  RIGHT JOIN model
  ON sales.modelId = model.modelId
  ```

  ### Full outer join

  The full outer join returns all rows when there is a match in either the left or
  the right table. So, for table A and table B the full join returns all rows in A
  and all rows in B including those where there is a match between A and B. This
  is the broadest type of join and can often result in large data sets.

  In the context of our dataset it would result in the following table being
  generated:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  |                      | 2020-03-01 |
  | Google Chromebook    |            |

  Represented by the following diagram:

  ![](/img/sql-full-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  FULL OUTER JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  FULL OUTER JOIN model
  ON sales.modelId = model.modelId
  ```

  This type of join is used when you want to discern when there is _not_ a match
  between two fields across tables. For example: imagine that you wanted a list of
  computers that had never been sold. In this case, you would be interested in
  rows where there is a `model_id` without a corresponding `sales_id` .

  In SQL this would be achieved with:

  ```sql
  SELECT model.name, sales.sale_date
  FROM model
  LEFT JOIN sales on model.model_id = sales.model_id;
  ```

  Note that this would return all the model names but where there isn't a sale
  data, `NULL` would be returned. This is an **important distinction :** the outer
  join method doesn't just return the rows with a `NULL` value for `sale_date` as
  we might expect. It returns all models along with those that have not been sold.
  This is because it is oriented to the "left" table, equivalent to the table in
  the SQL that we cited first with the `on` keyword.

  > A left outer join returns all the records from the left (model) table and
  > those that match in the right (sales) table. Where there are no matching
  > records in the right (sales) table, a `NULL` value is returned.

  A **right outer join**, often referred to as a right join, is the opposite of a
  left outer; it returns all the records from the right table and those that match
  in the left table. In our scenario this would be all the models that had a
  `sale_date` including models that didn't have a `sale_date` , i.e which returned
  `NULL`

  Finally, a **full outer join** returns all the records from both tables, and
  where a record cannot be matched, a NULL value is returned. So this would mean
  there could be `NULL`s in both fields of the returned rows.

  We can combine multiple types of join in the same SQL query:

  ```sql
  SELECT model.name, sales.sale_date, manufacturer.url
  FROM model
  LEFT JOIN sales on model.model_id = sales.model_id
  INNER JOIN manufacturer on model.manufacturer_id = manufacturer.manufacturer_id;
  ```

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

  # Joins in SQL

  <!-- Once a relationship has been created using primary and foreign keys (as detailed in the previous section), you are able to combine and integrate data from the different tables. This is known as performing **joins.** -->

  We perform joins in SQL when we combine and integrate data from multiple tables.
  In order for a join to be possible, **the tables you want to join must have rows
  that share the same fields**.

  The output will be represented as a table but it is virtual, not an actual
  table. (If you wish to actually create a table or a view off the back of a join
  operation you should combine the join with the
  [`CREATE TABLE`](Create_an_SQL_table.md) command etc.)

  ## Inner joins

  > An inner join combines rows that have matching values in two or more tables.
  > This join is based on the existence of a common field between the tables and
  > is used to retrieve data that appears in both tables.

  The following is a good scenario for creating an inner join:

  > We want to create a list of the name of all computers that have been sold and
  > when they were sold.

  We will be working from the following tables.

  _The `sales` table_:

  | saleId | modelId | saleDate   | employeeId | price   |
  | ------ | ------- | ---------- | ---------- | ------- |
  | 1      | 44      | 2020-07-27 | tbishop    | 40.99   |
  | 2      | 22      | 2021-02-05 | tbishop    | 986.99  |
  | 3      | 14      | 2022-11-16 | tgnomay    | 1248.99 |

  _The `model` table_:

  | modelId | modelName            |
  | ------- | -------------------- |
  | 44      | Raspberry Pi Model A |
  | 22      | Apple MacBook Air    |
  | 14      | Lenovo ThinkPad P15  |

  The data points we need are not provided in a single table. `sales` gives us the
  `saleDate` but not the `modelName`. In order to meet our requirements we must
  perform a join that combines `modelName` from the `model` table and `saleDate`
  from the `sale` table to give us a table like the following:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |

  We can do this by utilising the field they have in common: `modelId`.

  ### Implementation

  The general syntax for creating an inner join from two tables that share a
  common field is as follows:

  ```sql
  SELECT column_name(s)
  FROM table1
  INNER JOIN table2
  ON table1.columnName = table2.columnName
  ```

  So basically, look at `table1` and `table2` return the rows where the fields for
  `table1` and `table2` match, where these fields are the ones specified in the
  `SELECT`.

  Applied to our example:

  ```sql
  SELECT model.modelName, sales.saleDate -- Name the fields
  FROM model -- Specify the first table
  INNER JOIN sales -- Specify the second table
  ON model.model_id = sales.model_id; -- Specify the match criteria
  ```

  > It is important to note that the match must _be_ a match. Both tables must
  > have a row with the same value for `modelId` for the join to be applied. If
  > there is a `modelId` of X in `sales` but no corresponding X for `modelId` in
  > `model`, nothing will be returned for that row.

  We can represent the logical relationship that obtains between the `sales` and
  `model` tables as follows:

  ![](/img/sql-inner-join.png)

  ## Outer joins

  Outer joins are joins that return matched values **and unmatched** values from
  either or both tables.

  To explain outer joins we need to expand our data set:

  _The `sales` table_:

  | saleId | modelId | saleDate   | employeeId | price   |
  | ------ | ------- | ---------- | ---------- | ------- |
  | 1      | 44      | 2020-07-27 | tbishop    | 40.99   |
  | 2      | 22      | 2021-02-05 | tbishop    | 986.99  |
  | 3      | 14      | 2022-11-16 | tgnomay    | 1248.99 |
  | 4      |         | 2020-03-01 | msmelk     | 699.99  |

  _The `model` table_:

  | modelId | modelName            |
  | ------- | -------------------- |
  | 44      | Raspberry Pi Model A |
  | 22      | Apple MacBook Air    |
  | 14      | Lenovo ThinkPad P15  |
  | 66      | Google Chromebook    |

  > We will assume that `sales` is our left table and `model` our right.

  ### Left outer join

  A left outer join returns all records from the left or first table and the
  matching records from the right or second table. If there are not matches on the
  right then only the left data is returned.

  In the context of our dataset, in the _sales_ database we have a row where there
  is a sale with no corresponding `modelId`. If we performed a left outer join
  this would give us the following table in return:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  |                      | 2020-03-01 |

  The logical relationship sustained between `sales` and `model` by a left inner
  join is represented in the following diagram:

  ![](/img/sql-left-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  LEFT JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  LEFT JOIN model
  ON sales.modelId = model.modelId
  ```

  ### Right outer join

  This is the inverse of the right outer join. A right outer join returns all
  records from the right or second table and the matching records from the left or
  first table. If there are not matches on the left then only the right data is
  returned.

  In the context of our dataset, in the _model_ database we have a row where there
  is a `modelId` without a corresponding entry in the `sales` table. If we
  performed a right outer join this would give us the following table in return:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  | Google Chromebook    |            |

  The logical relationship sustained between `sales` and `model` by a right inner
  join is represented in the following diagram:

  ![](/img/sql-right-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  RIGHT JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  RIGHT JOIN model
  ON sales.modelId = model.modelId
  ```

  ### Full outer join

  The full outer join returns all rows when there is a match in either the left or
  the right table. So, for table A and table B the full join returns all rows in A
  and all rows in B including those where there is a match between A and B. This
  is the broadest type of join and can often result in large data sets.

  In the context of our dataset it would result in the following table being
  generated:

  | modelName            | sale_date  |
  | -------------------- | ---------- |
  | Raspberry Pi Model A | 2015-02-01 |
  | Apple Macbook Air    | 2018-11-01 |
  | Lenovo ThinkPad P15  | 2018-11-01 |
  |                      | 2020-03-01 |
  | Google Chromebook    |            |

  Represented by the following diagram:

  ![](/img/sql-full-outer-join.png)

  #### Implementation

  The schematic form:

  ```sql
  SELECT column_name(s)
  FROM table1
  FULL OUTER JOIN table2
  ON table1.column_name = table2.column_name;
  ```

  Applied to our data set:

  ```sql
  SELECT model.modelName, sales.saleDate
  FROM sales.saleDate
  FULL OUTER JOIN model
  ON sales.modelId = model.modelId
  ```

  This type of join is used when you want to discern when there is _not_ a match
  between two fields across tables. For example: imagine that you wanted a list of
  computers that had never been sold. In this case, you would be interested in
  rows where there is a `model_id` without a corresponding `sales_id` .

  In SQL this would be achieved with:

  ```sql
  SELECT model.name, sales.sale_date
  FROM model
  LEFT JOIN sales on model.model_id = sales.model_id;
  ```

  Note that this would return all the model names but where there isn't a sale
  data, `NULL` would be returned. This is an **important distinction :** the outer
  join method doesn't just return the rows with a `NULL` value for `sale_date` as
  we might expect. It returns all models along with those that have not been sold.
  This is because it is oriented to the "left" table, equivalent to the table in
  the SQL that we cited first with the `on` keyword.

  > A left outer join returns all the records from the left (model) table and
  > those that match in the right (sales) table. Where there are no matching
  > records in the right (sales) table, a `NULL` value is returned.

  A **right outer join**, often referred to as a right join, is the opposite of a
  left outer; it returns all the records from the right table and those that match
  in the left table. In our scenario this would be all the models that had a
  `sale_date` including models that didn't have a `sale_date` , i.e which returned
  `NULL`

  Finally, a **full outer join** returns all the records from both tables, and
  where a record cannot be matched, a NULL value is returned. So this would mean
  there could be `NULL`s in both fields of the returned rows.

  We can combine multiple types of join in the same SQL query:

  ```sql
  SELECT model.name, sales.sale_date, manufacturer.url
  FROM model
  LEFT JOIN sales on model.model_id = sales.model_id
  INNER JOIN manufacturer on model.manufacturer_id = manufacturer.manufacturer_id;
  ```


path: zk/Key_characteristics_of_GraphQL.md
sortable_path: zkKey_characteristics_of_GraphQL.md
title: Key characteristics of GraphQL
checksum: 67f1011150d2c40caae68f5b96c1c03339c9a9b90925c06b8b05534ca54fe2e9
created: 2024-03-30 09:03:19.874620569+00:00
modified: 2024-06-24 12:22:07.740149838+00:00
metadata: {"tags":["graphql","APIs"]}
lead:
  > In a nutshell, GraphQL is all about optimizing data communication between a
  > client and a server. This includes the client asking for needed data and
  > communicating that need to the server, and the server preparing a fulfillment
  > for that need and communicating the fulfillment back to the client. GraphQL
  > allows clients to ask for the exact data they need and makes it easier for
  > servers to aggregate data from multiple data storage resources.

body:
  > In a nutshell, GraphQL is all about optimizing data communication between a
  > client and a server. This includes the client asking for needed data and
  > communicating that need to the server, and the server preparing a fulfillment
  > for that need and communicating the fulfillment back to the client. GraphQL
  > allows clients to ask for the exact data they need and makes it easier for
  > servers to aggregate data from multiple data storage resources.

  - GraphQL is a **query language** from which you can build APIs. It exists as an
    abstraction layer between a client that issues requests and a backend
    comprising any number of databases that responds to queries.
  - Crucially, with applications that utilise GraphQL, both the backend and the
    frontend use the same GraphQL syntax. They speak the same language. This means
    that neither needs to take account of the way in which the other is
    implemented. For example, a client could request data from both a relational
    database and an object database without knowing there is any difference from
    its point of view. Conversely, client requests could be made through
    JavaScript or Python and the structure of the request received by the backend
    would be the same.
  - The main benefit is its interoperability and its network economy when compared
    to REST APIs: you ask only for what you want and you get only that; there is
    no surplus data, increasing efficiency, reducing the number of network
    requests and reducing the size of the payload.

  > The best way to think of GraphQL architecturally is to think of it as a
  > different technique for instantiating the _uniform interface_, _client/server
  > decoupling_, and _layered-system architecture_ contraints of traditional
  > [REST APIs](RESTful_APIs.md#rest). However, instead of making
  > GET or POST requests to a REST endpoint URL and passing parameters, you send a
  > structured query object.

  ## Architecture

  From the point of view of the frontend, GraphQL is a query language that may be
  used via wrappers for a given framework or programming language (such as Apollo)
  or directly simply by sending requests over HTTPS to a single URL.

  From the point of view of the backend, GraphQL is a **runtime** that provides a
  structure for servers to describe the data to be exposed in their APIs. We call
  this structure the **schema**. For example, the GraphQL server might translate
  the query into SQL statements for a relational database then take what the
  storage engine responds with, translate it into JSON and send it back to the
  client application.

  > The backend implementation of GraphQL is known as the **GraphQL server**. This
  > server is distinct from any of the physical servers that the backend may rely
  > on. The client queries the GraphQL server.

  Client requests are sent over HTTPS and the data is typically returned in the
  form of JSON:

  ![](/img/graphql-architecture.png)

  ## Implementation overview

  ### Queries, mutations, subscriptions

  Queries can be used for **both** reading and modifying data.

  - When you need to read data with GraphQL you use **queries**
  - When you need to modify data, you use **mutations**

  So, for comparison, a query would be akin to a `READ` instruction in SQL or a
  `GET` request send to a REST API. A mutation would be akin to `WRITE` then
  `READ` instruction in SQL or a `POST` request to a REST API.

  There is a third request type called a **subscription**. This is used for
  real-time data monitoring requests, like a data stream, similar to a continuous
  `READ` process. Mutations typically trigger events that can be subscribed to.

  ### Frontend

  Below is an example of a request that would be made to a GraphQL server from the
  frontend:

  ```graphql
  query {
    employee(id: 42) {
      name
      email
    }
  }
  ```

  ### Backend: structure and behaviour

  We define the structure of a GraphQL API on the backend through the schema. A
  schema is strongly typed and is basically a graph of fields that have types, e.g

  ```graphql
  type Employee(id: Int!) {
    name: String!
    email: String!
  }
  ```

  `!` stands for required. In addition to the standard primitive data-types you
  can also have custom types.

  We implement the behaviour of the API through functions called **resolver
  functions**. Each field in a GraphQL schema is backed by a resolver function. A
  resolver function defines what data to fetch for its field.

  > A resolver function represents the instructions on how and where to access raw
  > data. For example, a resolver function might issue a SQL statement to a
  > relational database, read a file’s data directly from the operating system, or
  > update some cached data in a document database. A resolver function is
  > directly related to a field in a GraphQL request, and it can represent a
  > single primitive value, an object, or a list of values or objects.

  ### GraphQL endpoint

  The GraphQL server exposes a single endpoint as a URL. This is in contrast to a
  REST API where there are multiple endpoints, each corresponding to a different
  resource. With GraphQL, the request is not specified by the URL and the payload
  but rather in the GraphQL string that you send to the endpoint, like the
  `employee` query above.

  ## Benefits

  ### Single requests

  With a REST API if you require multiple resources you have to make multiple
  requests to different endpoints and then once they resolve, synthesise them on
  the client side. With GraphQL you can send a single request that encompasses
  each individual resource:

  The REST scenario:

  ![](/img/REST_request-load.png)

  The GraphQL scenario:

  ![](/img/graphQL_request_load.png)

  ### Abstraction of multiple services

  ![](/img/graphql_multiple_resources.png)

  ### Stops overfetching

  In a strict REST API you must request all resources from a given resource and
  then filter on the client. With GraphQL you request only the resource you need
  at the client-level.

  ### Single endpoint

  In GraphQL you have a single endpoint and it always remains the same. This makes
  updates easier to manage, since you don't need to broadcast new endpoints as the
  API grows. It also simplifies frontend parsing, since you don't need to account
  for, or interpolate different endpoints in your request URL.

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

  # Key characteristics of GraphQL

  > In a nutshell, GraphQL is all about optimizing data communication between a
  > client and a server. This includes the client asking for needed data and
  > communicating that need to the server, and the server preparing a fulfillment
  > for that need and communicating the fulfillment back to the client. GraphQL
  > allows clients to ask for the exact data they need and makes it easier for
  > servers to aggregate data from multiple data storage resources.

  - GraphQL is a **query language** from which you can build APIs. It exists as an
    abstraction layer between a client that issues requests and a backend
    comprising any number of databases that responds to queries.
  - Crucially, with applications that utilise GraphQL, both the backend and the
    frontend use the same GraphQL syntax. They speak the same language. This means
    that neither needs to take account of the way in which the other is
    implemented. For example, a client could request data from both a relational
    database and an object database without knowing there is any difference from
    its point of view. Conversely, client requests could be made through
    JavaScript or Python and the structure of the request received by the backend
    would be the same.
  - The main benefit is its interoperability and its network economy when compared
    to REST APIs: you ask only for what you want and you get only that; there is
    no surplus data, increasing efficiency, reducing the number of network
    requests and reducing the size of the payload.

  > The best way to think of GraphQL architecturally is to think of it as a
  > different technique for instantiating the _uniform interface_, _client/server
  > decoupling_, and _layered-system architecture_ contraints of traditional
  > [REST APIs](RESTful_APIs.md#rest). However, instead of making
  > GET or POST requests to a REST endpoint URL and passing parameters, you send a
  > structured query object.

  ## Architecture

  From the point of view of the frontend, GraphQL is a query language that may be
  used via wrappers for a given framework or programming language (such as Apollo)
  or directly simply by sending requests over HTTPS to a single URL.

  From the point of view of the backend, GraphQL is a **runtime** that provides a
  structure for servers to describe the data to be exposed in their APIs. We call
  this structure the **schema**. For example, the GraphQL server might translate
  the query into SQL statements for a relational database then take what the
  storage engine responds with, translate it into JSON and send it back to the
  client application.

  > The backend implementation of GraphQL is known as the **GraphQL server**. This
  > server is distinct from any of the physical servers that the backend may rely
  > on. The client queries the GraphQL server.

  Client requests are sent over HTTPS and the data is typically returned in the
  form of JSON:

  ![](/img/graphql-architecture.png)

  ## Implementation overview

  ### Queries, mutations, subscriptions

  Queries can be used for **both** reading and modifying data.

  - When you need to read data with GraphQL you use **queries**
  - When you need to modify data, you use **mutations**

  So, for comparison, a query would be akin to a `READ` instruction in SQL or a
  `GET` request send to a REST API. A mutation would be akin to `WRITE` then
  `READ` instruction in SQL or a `POST` request to a REST API.

  There is a third request type called a **subscription**. This is used for
  real-time data monitoring requests, like a data stream, similar to a continuous
  `READ` process. Mutations typically trigger events that can be subscribed to.

  ### Frontend

  Below is an example of a request that would be made to a GraphQL server from the
  frontend:

  ```graphql
  query {
    employee(id: 42) {
      name
      email
    }
  }
  ```

  ### Backend: structure and behaviour

  We define the structure of a GraphQL API on the backend through the schema. A
  schema is strongly typed and is basically a graph of fields that have types, e.g

  ```graphql
  type Employee(id: Int!) {
    name: String!
    email: String!
  }
  ```

  `!` stands for required. In addition to the standard primitive data-types you
  can also have custom types.

  We implement the behaviour of the API through functions called **resolver
  functions**. Each field in a GraphQL schema is backed by a resolver function. A
  resolver function defines what data to fetch for its field.

  > A resolver function represents the instructions on how and where to access raw
  > data. For example, a resolver function might issue a SQL statement to a
  > relational database, read a file’s data directly from the operating system, or
  > update some cached data in a document database. A resolver function is
  > directly related to a field in a GraphQL request, and it can represent a
  > single primitive value, an object, or a list of values or objects.

  ### GraphQL endpoint

  The GraphQL server exposes a single endpoint as a URL. This is in contrast to a
  REST API where there are multiple endpoints, each corresponding to a different
  resource. With GraphQL, the request is not specified by the URL and the payload
  but rather in the GraphQL string that you send to the endpoint, like the
  `employee` query above.

  ## Benefits

  ### Single requests

  With a REST API if you require multiple resources you have to make multiple
  requests to different endpoints and then once they resolve, synthesise them on
  the client side. With GraphQL you can send a single request that encompasses
  each individual resource:

  The REST scenario:

  ![](/img/REST_request-load.png)

  The GraphQL scenario:

  ![](/img/graphQL_request_load.png)

  ### Abstraction of multiple services

  ![](/img/graphql_multiple_resources.png)

  ### Stops overfetching

  In a strict REST API you must request all resources from a given resource and
  then filter on the client. With GraphQL you request only the resource you need
  at the client-level.

  ### Single endpoint

  In GraphQL you have a single endpoint and it always remains the same. This makes
  updates easier to manage, since you don't need to broadcast new endpoints as the
  API grows. It also simplifies frontend parsing, since you don't need to account
  for, or interpolate different endpoints in your request URL.


path: zk/Killing_processes.md
sortable_path: zkKilling_processes.md
title: Kill a process running on a port
lead: For example a local server.
checksum: 3438a9d843154734e05b19caec59ae8f942d1fbee36e27fe6673d34f3dc099f5
created: 2024-03-30 09:03:19.881853829+00:00
modified: 2024-06-16 17:13:50.559113059+00:00
metadata: {"tags":["shell","procedural"]}
body:
  For example a local server.

  ```bash
  sudo lsof -t -i:8000
  # List files and proces ID (-t) and internet connections (-i) on port number

  $ 7890
  # Gives you process id

  sudo kill -9 7890
  # Kill the process that is running there
  ```

  ## Alterntive

  ```sh
  ss -ltnp | grep ':8000'
  ```

  Returns:

  ```
  LISTEN 0      511            [::1]:8000          [::]:*    users:(("node",pid=10493,fd=34))
  ```

  Kill with:

  ```
  kill 10493
  ```

raw_content:
  ---
  tags:
    - shell
    - procedural
  ---

  ## Kill a process running on a port

  For example a local server.

  ```bash
  sudo lsof -t -i:8000
  # List files and proces ID (-t) and internet connections (-i) on port number

  $ 7890
  # Gives you process id

  sudo kill -9 7890
  # Kill the process that is running there
  ```

  ## Alterntive

  ```sh
  ss -ltnp | grep ':8000'
  ```

  Returns:

  ```
  LISTEN 0      511            [::1]:8000          [::]:*    users:(("node",pid=10493,fd=34))
  ```

  Kill with:

  ```
  kill 10493
  ```


path: zk/LEDs.md
sortable_path: zkLEDs.md
title: LEDs
checksum: 5c8ac42013743c18c7549d23da080ac4901800a4fa11ec252f85f79d3992b0b8
created: 2024-03-30 09:03:19.885194386+00:00
modified: 2024-06-23 18:10:07.070437053+00:00
metadata: {"tags":["electronics"]}
lead:
  LED' stands for **Light Emitting Diode**, a [circuit]() component that emits
  light. The symbol for an LED is displayed below:

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

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

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

  ![](/img/diode.png)

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

raw_content:
  ---
  tags:
    - electronics
  ---

  # LEDs

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

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

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

  ![](/img/diode.png)

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


path: zk/Lambda_directory_structure.md
sortable_path: zkLambda_directory_structure.md
title: Lambda directory structure
lead: I am using the following structure for my lambdas:
checksum: 3a91446521948d44f2605951ee5bb5eef2a24d6133d7cd013309cce89c42bdd3
created: 2024-03-30 09:03:19.89308174+00:00
modified: 2024-06-16 17:13:55.909200076+00:00
metadata: {"tags":["AWS","aws-lambda"]}
body:
  I am using the following structure for my lambdas:

  ```
  lambda-name/
      events/
      function-name/
          src/
              index.js
          tests/
          local-env.json
          production-env.json
          package.json
      template.yaml
      README.md
  ```

raw_content:
  ---
  tags:
    - AWS
    - aws-lambda
  ---

  # Lambda directory structure

  I am using the following structure for my lambdas:

  ```
  lambda-name/
      events/
      function-name/
          src/
              index.js
          tests/
          local-env.json
          production-env.json
          package.json
      template.yaml
      README.md
  ```


path: zk/Lambda_handler_function.md
sortable_path: zkLambda_handler_function.md
title: AWS Lambda handler function
lead: Every Lambda function begins the same way with a handler function. In NodeJS:
checksum: db7083124ec79bc49cc7e3f28756a97c48e7d950d679c34bcb546b92a6982771
created: 2024-03-30 09:03:19.896420506+00:00
modified: 2024-06-15 09:50:14.019667391+00:00
metadata: {"tags":["AWS","aws-lambda"]}
body:
  Every Lambda function begins the same way with a handler function. In NodeJS:

  ```js
  module.exports.handler = function(event, context, callback){...}
  ```

  The handler is the entry point for the Lambda. It is a function that Lambda can
  call to start the execution of your code when an event triggers your Lambda
  function.

  You may put your whole code in the body of the handler, or you may call other
  modules and keep the handler clean and focused on bootstrapping.

  The `module.exports` part is just the standard way of exporting a module in
  Node. `handler` is a property that is being exported.

  You always have access to the parameters `event`, `context` and `callback`.

  - `event`
    - The triggering event of the Lambda function stored as an object. The
      object's contents will be different depending on the trigger. It could be
      any AWS service that serves a trigger (S3, DynamoDB, API Gateway etc).
    - For example, if the triggering function is a request to API Gateway, the
      `event` object will contain information about the HTTP request such as
      headers, path, and the request body.
  - `context`
    - contains information about the runtime environment and the current execution
      of the Lambda function. The context object provides properties and methods
      that you can use to interact with the AWS Lambda service and the current
      invocation.
  - `callback`
    - an optional parameter that represents a callback function provided by AWS
      Lambda. The callback function allows you to signal the completion of your
      Lambda function's execution and return a response or an error. The callback
      function accepts two arguments: error and result. If the first argument is
      not null, AWS Lambda treats it as an error, and the Lambda function
      invocation fails. If the first argument is null, AWS Lambda treats the
      second argument as the result and considers the invocation successful. Using
      the callback function is more common in non-async handler functions. When
      you use async functions, you can return a response or throw an error
      directly, without using the callback function.

  ### Full example:

  In JavaScript:

  ```js
  module.exports.handler = async (event, context) => {
    // Your logic to handle the incoming event
    // For example, you can call other AWS services, process the event data, etc.

    // Return a response
    return {
      statusCode: 200,
      body: JSON.stringify({
        message: "Your Lambda function has successfully executed!",
      }),
    };
  };
  ```

  In Python:

  ```py
  import json

  def handler(event, context):
      # Your logic to handle the incoming event
      # For example, you can call other AWS services, process the event data, etc.

      # Return a response
      response = {
          'statusCode': 200,
          'body': json.dumps({
              'message': 'Your Lambda function has successfully executed!'
          })
      }
      return response
  ```

raw_content:
  ---
  tags:
    - AWS
    - aws-lambda
  ---

  # AWS Lambda handler function

  Every Lambda function begins the same way with a handler function. In NodeJS:

  ```js
  module.exports.handler = function(event, context, callback){...}
  ```

  The handler is the entry point for the Lambda. It is a function that Lambda can
  call to start the execution of your code when an event triggers your Lambda
  function.

  You may put your whole code in the body of the handler, or you may call other
  modules and keep the handler clean and focused on bootstrapping.

  The `module.exports` part is just the standard way of exporting a module in
  Node. `handler` is a property that is being exported.

  You always have access to the parameters `event`, `context` and `callback`.

  - `event`
    - The triggering event of the Lambda function stored as an object. The
      object's contents will be different depending on the trigger. It could be
      any AWS service that serves a trigger (S3, DynamoDB, API Gateway etc).
    - For example, if the triggering function is a request to API Gateway, the
      `event` object will contain information about the HTTP request such as
      headers, path, and the request body.
  - `context`
    - contains information about the runtime environment and the current execution
      of the Lambda function. The context object provides properties and methods
      that you can use to interact with the AWS Lambda service and the current
      invocation.
  - `callback`
    - an optional parameter that represents a callback function provided by AWS
      Lambda. The callback function allows you to signal the completion of your
      Lambda function's execution and return a response or an error. The callback
      function accepts two arguments: error and result. If the first argument is
      not null, AWS Lambda treats it as an error, and the Lambda function
      invocation fails. If the first argument is null, AWS Lambda treats the
      second argument as the result and considers the invocation successful. Using
      the callback function is more common in non-async handler functions. When
      you use async functions, you can return a response or throw an error
      directly, without using the callback function.

  ### Full example:

  In JavaScript:

  ```js
  module.exports.handler = async (event, context) => {
    // Your logic to handle the incoming event
    // For example, you can call other AWS services, process the event data, etc.

    // Return a response
    return {
      statusCode: 200,
      body: JSON.stringify({
        message: "Your Lambda function has successfully executed!",
      }),
    };
  };
  ```

  In Python:

  ```py
  import json

  def handler(event, context):
      # Your logic to handle the incoming event
      # For example, you can call other AWS services, process the event data, etc.

      # Return a response
      response = {
          'statusCode': 200,
          'body': json.dumps({
              'message': 'Your Lambda function has successfully executed!'
          })
      }
      return response
  ```


path: zk/Lambda_programming_model.md
sortable_path: zkLambda_programming_model.md
title: AWS Lambda programming model
lead: The overall architecure consists in the following three processes:
checksum: fc67720166bcee6eceb573ee829de652e833340b4ad16163bb96ba51ee4ca7ad
created: 2024-03-30 09:03:19.899938286+00:00
modified: 2024-06-15 09:50:16.459714731+00:00
metadata: {"tags":["AWS","aws-lambda"]}
body:
  The overall architecure consists in the following three processes:

  1. Triggers
  2. Handler function
  3. Code

  ## Triggers

  See [AWS Lambda triggers](Lambda_triggers.md)

  ## Handler function

  See
  [AWS Lambda handler function](Lambda_handler_function.md)

  ## Code

  This is just the stuff that runs in the body of the handler or that is called
  from the handler body.

raw_content:
  ---
  tags:
    - AWS
    - aws-lambda
  ---

  # AWS Lambda programming model

  The overall architecure consists in the following three processes:

  1. Triggers
  2. Handler function
  3. Code

  ## Triggers

  See [AWS Lambda triggers](Lambda_triggers.md)

  ## Handler function

  See
  [AWS Lambda handler function](Lambda_handler_function.md)

  ## Code

  This is just the stuff that runs in the body of the handler or that is called
  from the handler body.


path: zk/Lambda_triggers.md
sortable_path: zkLambda_triggers.md
title: AWS Lambda triggers
lead: Here are some common trigger patterns
checksum: 3586771db03bd5e8ea9e70f94d4be5e2b0015c1b5c44765942f08868468cd483
created: 2024-03-30 09:03:19.91022386+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["AWS","aws-lambda"]}
body:
  Here are some common trigger patterns

  ### API Gateway trigger

  - an HTTP request is sent to AWS API Gateway
  - API Gateway triggers a Lambda function to execute

  ### DynamoDB table change trigger

  - a record in a DynamoDB table is modified
  - DynamoDB triggers a lambda function to execute

  ### S3 trigger

  - a new file is created in S3
  - S3 triggers a Lambda function to execute

  ### SQS trigger

  - a new message is added to an [SQS](AWS_Messaging_services.md#sqs)
    queue
  - SQS triggers a Lambda function to execute

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

  # AWS Lambda triggers

  Here are some common trigger patterns

  ### API Gateway trigger

  - an HTTP request is sent to AWS API Gateway
  - API Gateway triggers a Lambda function to execute

  ### DynamoDB table change trigger

  - a record in a DynamoDB table is modified
  - DynamoDB triggers a lambda function to execute

  ### S3 trigger

  - a new file is created in S3
  - S3 triggers a Lambda function to execute

  ### SQS trigger

  - a new message is added to an [SQS](AWS_Messaging_services.md#sqs)
    queue
  - SQS triggers a Lambda function to execute


path: zk/Lambdas_in_Python.md
sortable_path: zkLambdas_in_Python.md
title: Lambdas in Python
checksum: 9fe2f11066968420a8ac1ad471b719570bc546746041668abd6893421ce7c305
created: 2024-03-30 09:03:19.919939041+00:00
modified: 2024-06-16 17:14:08.962751462+00:00
metadata: {"tags":["python","aws-lambda","AWS"]}
lead:
  In Python, anonymous functions like arrow-functions in JavaScript (`() => {}`)
  are immediately invoked and unnamed. They are called lambdas.

body:
  In Python, anonymous functions like arrow-functions in JavaScript (`() => {}`)
  are immediately invoked and unnamed. They are called lambdas.

  Whilst they are unnamed, just like JS, the value they return can be stored in a
  variable. They do not require the `return` keyword.

  They are most often used unnamed with the functional methods
  [map, filter](Map_and_filter_in_Python.md)
  and reduce.

  Here is the two syntaxes side by side:

  ```js
  // JavaScript

  const double = (x) => x * x;
  ```

  ```py
  # Python

  double = lambda x: x * x
  ```

  Here is a lambda with multiple parameters:

  ```py
  func = lambda x, y, z: x + y + z
  print(func(2, 3, 4))
  # 9
  ```

  > Lambdas obviously enshrine functional programming paradigms. Therefore they
  > should be pure functions, not mutating values or issueing side effects. For
  > example, it would be improper (though syntactically well-formed) to use a
  > lambda to `print` something

raw_content:
  ---
  tags:
    - python
    - aws-lambda
    - AWS
  ---

  # Lambdas in Python

  In Python, anonymous functions like arrow-functions in JavaScript (`() => {}`)
  are immediately invoked and unnamed. They are called lambdas.

  Whilst they are unnamed, just like JS, the value they return can be stored in a
  variable. They do not require the `return` keyword.

  They are most often used unnamed with the functional methods
  [map, filter](Map_and_filter_in_Python.md)
  and reduce.

  Here is the two syntaxes side by side:

  ```js
  // JavaScript

  const double = (x) => x * x;
  ```

  ```py
  # Python

  double = lambda x: x * x
  ```

  Here is a lambda with multiple parameters:

  ```py
  func = lambda x, y, z: x + y + z
  print(func(2, 3, 4))
  # 9
  ```

  > Lambdas obviously enshrine functional programming paradigms. Therefore they
  > should be pure functions, not mutating values or issueing side effects. For
  > example, it would be improper (though syntactically well-formed) to use a
  > lambda to `print` something


path: zk/Latches.md
sortable_path: zkLatches.md
title: Latches
checksum: 456a5efe4f003860290d55f3eacc357ef5b2f103f0fb03419c1bb61e014f3cc0
created: 2024-03-30 09:03:19.929290597+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["logic-gates","binary","memory"]}
lead:
  The **combinatorial digital circuits** we have looked at so far have been
  non-sequential. The outcome is a function of its immediate set of inputs and
  everything happens at once: there is no means of storing state for future use.
  In other words there is no _[memory](Memory.md)_.

body:
  The **combinatorial digital circuits** we have looked at so far have been
  non-sequential. The outcome is a function of its immediate set of inputs and
  everything happens at once: there is no means of storing state for future use.
  In other words there is no _[memory](Memory.md)_.

  In contrast, the output of a **sequential digital circuit** depends not only on
  its present set of inputs but also on past inputs to the circuit. It has some
  knowledge of its own previous state through the existence of memory. This can be
  implemented via components that allow for the **storage and retrieval of binary
  data**.

  ## What is a latch?

  A latch is a circuit component that wor

  The **SR Latch** (for "set/reset") has two inputs: _S_ (for "set") and _R_ (for
  "reset") and one output, _Q_. _Q_ stands for the bit that is remembered. (There
  is also _not-Q_ which is the opposite of whatever _Q_ is currently set to.)

  The SR Latch goes through the following state changes:

  - When _S_ is set to 1, output _Q_ becomes 1 also
  - When _S_ goes to 0, _Q_ remains 1
  - When _R_ is set to 1, the memory bit is cleared and _Q_ becomes 0.
  - _Q_ remains at 0 even if _R_ goes back to 0

  This is represented more clearly in the table below:

  | S   | R   | Q   | Operation     |
  | --- | --- | --- | ------------- |
  | 1   | 1   | X   | Invalid, null |

  The most succinct account of a latch:

  > A latch is a 1-bit memory device that has a state _Q_ of either 1 or 0. The
  > _S_ input sets _Q_ to 1 and the _R_ input resets _Q_ to 0.

  _The representation of an SR Latch in a digital circuit diagram_:

  ![](/img/sr_latch_diagram.png)

  ## Creating a latch circuit

  The circuit diagram latch symbol obviously encapsulates more complex
  functionality that occurs at the sub-circuit level. We will demonstrate how this
  functionality can be achieved with two
  [NOR](Logic_gates.md#nor-gate) gates.

  The two gates are in a **cross-coupled configuration**. This basically means
  that the wires are crossed back on themselves such that the output of one is
  also an input of the other at a single stage in the sequence.

  The circuit is created as follows:

  ![](/img/sr_latch_logic_circuit.png)

  Interactive version:

  <iframe src="https://circuitverse.org/simulator/embed/nor-latch-0869192c-7d7b-4161-b13f-3f72c1bce8e9" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="400" width="600" allowFullScreen></iframe>

  <br />
  Let's talk through the logic at each state change:

  - When _S_ is set to 1, output _Q_ becomes 1 also
    - N1 is receiving 1 from S and 0 from R by way of N2. This is the inversion of
      OR so TF equals F. Thus N1 is outputting 0.
    - Thus N2 is receiving 0 from N1 as its top input and 0 from R as its bottom
      input. It is therefore outputting 1 because with NOR, FF equals T. For this
      reason, _Q_ is 1 because is directly connected to _R_.
  - When _S_ goes to 0, _Q_ remains 1
    - N2 is receiving 0 from N1 as the top input and 0 from R as the bottom input
      hence the overall input is FF which means N2 is outputting T and Q remains 1
    - N1 is outputting 0 because it is receiving 0 as its top input and 1 from its
      bottom input
  - When _R_ is set to 1, the memory bit is cleared and _Q_ becomes 0.
    - N2 is receiving 1 from R as its bottom input and 1 from the output of N1 as
      its top input. Therefore it is outputting TT which in NOR evaluates to F
      hence Q is 0
    - N1 is outputting 1 because it is receiving 0 from S as its top input and 0
      from its bottom input coming from N2. FF equals T in NOR therefore 1 is
      outputting 1
  - _Q_ remains at 0 even if _R_ goes back to 0
    - N2 is receiving 0 as its bottom input from R and 1 as its top input from N1.
      TF equals F in NOR hence the output of N2 is 0 and Q remains 0
    - N1 is outputting 1 because it is receiving 0 as its top input from S and 0
      as its bottom input from N2. FF equals T in NOR hence N1 is 1

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

  # Latches

  The **combinatorial digital circuits** we have looked at so far have been
  non-sequential. The outcome is a function of its immediate set of inputs and
  everything happens at once: there is no means of storing state for future use.
  In other words there is no _[memory](Memory.md)_.

  In contrast, the output of a **sequential digital circuit** depends not only on
  its present set of inputs but also on past inputs to the circuit. It has some
  knowledge of its own previous state through the existence of memory. This can be
  implemented via components that allow for the **storage and retrieval of binary
  data**.

  ## What is a latch?

  A latch is a circuit component that wor

  The **SR Latch** (for "set/reset") has two inputs: _S_ (for "set") and _R_ (for
  "reset") and one output, _Q_. _Q_ stands for the bit that is remembered. (There
  is also _not-Q_ which is the opposite of whatever _Q_ is currently set to.)

  The SR Latch goes through the following state changes:

  - When _S_ is set to 1, output _Q_ becomes 1 also
  - When _S_ goes to 0, _Q_ remains 1
  - When _R_ is set to 1, the memory bit is cleared and _Q_ becomes 0.
  - _Q_ remains at 0 even if _R_ goes back to 0

  This is represented more clearly in the table below:

  | S   | R   | Q   | Operation     |
  | --- | --- | --- | ------------- |
  | 1   | 1   | X   | Invalid, null |

  The most succinct account of a latch:

  > A latch is a 1-bit memory device that has a state _Q_ of either 1 or 0. The
  > _S_ input sets _Q_ to 1 and the _R_ input resets _Q_ to 0.

  _The representation of an SR Latch in a digital circuit diagram_:

  ![](/img/sr_latch_diagram.png)

  ## Creating a latch circuit

  The circuit diagram latch symbol obviously encapsulates more complex
  functionality that occurs at the sub-circuit level. We will demonstrate how this
  functionality can be achieved with two
  [NOR](Logic_gates.md#nor-gate) gates.

  The two gates are in a **cross-coupled configuration**. This basically means
  that the wires are crossed back on themselves such that the output of one is
  also an input of the other at a single stage in the sequence.

  The circuit is created as follows:

  ![](/img/sr_latch_logic_circuit.png)

  Interactive version:

  <iframe src="https://circuitverse.org/simulator/embed/nor-latch-0869192c-7d7b-4161-b13f-3f72c1bce8e9" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="400" width="600" allowFullScreen></iframe>

  <br />
  Let's talk through the logic at each state change:

  - When _S_ is set to 1, output _Q_ becomes 1 also
    - N1 is receiving 1 from S and 0 from R by way of N2. This is the inversion of
      OR so TF equals F. Thus N1 is outputting 0.
    - Thus N2 is receiving 0 from N1 as its top input and 0 from R as its bottom
      input. It is therefore outputting 1 because with NOR, FF equals T. For this
      reason, _Q_ is 1 because is directly connected to _R_.
  - When _S_ goes to 0, _Q_ remains 1
    - N2 is receiving 0 from N1 as the top input and 0 from R as the bottom input
      hence the overall input is FF which means N2 is outputting T and Q remains 1
    - N1 is outputting 0 because it is receiving 0 as its top input and 1 from its
      bottom input
  - When _R_ is set to 1, the memory bit is cleared and _Q_ becomes 0.
    - N2 is receiving 1 from R as its bottom input and 1 from the output of N1 as
      its top input. Therefore it is outputting TT which in NOR evaluates to F
      hence Q is 0
    - N1 is outputting 1 because it is receiving 0 from S as its top input and 0
      from its bottom input coming from N2. FF equals T in NOR therefore 1 is
      outputting 1
  - _Q_ remains at 0 even if _R_ goes back to 0
    - N2 is receiving 0 as its bottom input from R and 1 as its top input from N1.
      TF equals F in NOR hence the output of N2 is 0 and Q remains 0
    - N1 is outputting 1 because it is receiving 0 as its top input from S and 0
      as its bottom input from N2. FF equals T in NOR hence N1 is 1


path: zk/Law_of_Non_Contradiction.md
sortable_path: zkLaw_of_Non_Contradiction.md
title: Law of Non-Contradiction
lead: > A proposition cannot be true and false at the same time.
body:
  > A proposition cannot be true and false at the same time.

  $$
  \lnot (P \land \lnot P)
  $$

checksum: ddf0f94542234a0692e65d40c5bc7349d5e4783a9033ede76e874c531e7e2c0a
created: 2024-03-30 09:03:19.941153028+00:00
modified: 2024-06-16 16:52:15.016167468+00:00
metadata: {"tags":["propositional-logic","logic"]}
raw_content:
  ---
  tags:
    - propositional-logic
    - logic
  ---

  # Law of Non-Contradiction

  > A proposition cannot be true and false at the same time.

  $$
  \lnot (P \land  \lnot P)
  $$


path: zk/Law_of_the_Excluded_Middle.md
sortable_path: zkLaw_of_the_Excluded_Middle.md
title: Law of the Excluded Middle
lead:
  > Every proposition has to be either true or false. There can be no middle
  > ground.

checksum: a53ec515846745f28a9a89390a50710c2ca90f7f1a1180acfa550231761cc6b5
created: 2024-03-30 09:03:19.943841479+00:00
modified: 2024-06-16 16:52:18.949255609+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  > Every proposition has to be either true or false. There can be no middle
  > ground.

  $$
  P \lor \sim P
  $$

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

  # Law of the Excluded Middle

  > Every proposition has to be either true or false. There can be no middle
  > ground.

  $$
  P \lor \sim P
  $$


path: zk/Lifecycle_methods.md
sortable_path: zkLifecycle_methods.md
title: Lifecycle methods
checksum: f28d1ec8c52599af3685974a5c17a22f2d0042b296ee9ba49ed379592fa024b7
created: 2024-03-30 09:03:19.947024537+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["javascript","react"]}
lead:
  Lifecycles are familiar from nature: something is born, it grows and reaches
  maturity and then dies and the process continues as what dies is broken down
  into constituent parts which form the basis of new life. And so on and so on.

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

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

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

  ## Specific lifecycle phases

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

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

  The two methods above only occur once in a lifecycle.

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

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

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

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

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

  6. `componentWillUnmount()`

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

  ## Side-effects: why lifecycle phases matter

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

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

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

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

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

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

  # Lifecycle methods

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

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

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

  ## Specific lifecycle phases

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

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

  The two methods above only occur once in a lifecycle.

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

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

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

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

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

  6. `componentWillUnmount()`

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

  ## Side-effects: why lifecycle phases matter

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

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

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

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

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


path: zk/Lists_in_Python.md
sortable_path: zkLists_in_Python.md
title: Lists in Python
lead: Lists are the equivalent of a simple array in JavaScript.
checksum: 9e4e9326f23ad94585bf743e274e0c7164a599eea09914d92019bc054f780d5e
created: 2024-03-30 09:03:19.980140137+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["python","data-structures"]}
body:
  Lists are the equivalent of a simple array in JavaScript.

  Lists have the following properties:

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

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

  ## Duplicating lists

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

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

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

  ## Basic usage

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

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

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

  ## Slicing

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

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

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

  ## Adding additional values to existing list

  ### Single value

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

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

  ### Multiple values

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

  ### Specific index

  ```python

  ## Insert at specific index

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

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

  ## Removing elements

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

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

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

  ### `del`

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

  We can remove multiple items at once via a slice:

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

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

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

  ### `remove()`

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

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

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

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

  ### pop()

  Remove and return the element removed

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

  ```

  ## Return index of a list element

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

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

  ## Nesting lists

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

  ## Merging (flattening) lists

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

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

  ## See also

  [Sorting lists in Python](Sorting_lists_in_Python.md)

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

  # Lists in Python

  Lists are the equivalent of a simple array in JavaScript.

  Lists have the following properties:

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

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

  ## Duplicating lists

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

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

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

  ## Basic usage

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

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

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

  ## Slicing

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

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

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

  ## Adding additional values to existing list

  ### Single value

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

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

  ### Multiple values

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

  ### Specific index

  ```python

  ## Insert at specific index

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

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

  ## Removing elements

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

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

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

  ### `del`

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

  We can remove multiple items at once via a slice:

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

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

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

  ### `remove()`

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

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

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

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

  ### pop()

  Remove and return the element removed

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

  ```

  ## Return index of a list element

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

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

  ## Nesting lists

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

  ## Merging (flattening) lists

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

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

  ## See also

  [Sorting lists in Python](Sorting_lists_in_Python.md)


path: zk/Local_AWS_development_with_SAM.md
sortable_path: zkLocal_AWS_development_with_SAM.md
title: Local AWS development with SAM
checksum: 125550b5c17fa1ce41fcfd41f42c41f618882dfec80122c235a838aadee59d73
created: 2024-03-30 09:03:19.990530127+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["AWS","docker"]}
lead:
  We can run a local instance of our SAM stack for a given application without
  sending requests to the cloud. This is implemented through Docker.

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

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

  ## Basic set up

  Enter your project directory.

  First build your SAM application:

  ```sh
  sam build
  ```

  We then run:

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

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

  This will be indicated by:

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

  If we want to invoke the function directly we use:

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

  ## Using environment variables

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

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

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

  ## Create secret

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

  ## Create local ENV file

  > These must be in JSON to work with SAM:

  ### Local env

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

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

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

  ### Update `template.yaml`

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

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

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

  ### Pass in the environment variable at invocation:

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

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

  ### Create handler within the Lambda itself

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

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

  const secretsManager = new AWS.SecretsManager();

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

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

  # Local AWS development with SAM

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

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

  ## Basic set up

  Enter your project directory.

  First build your SAM application:

  ```sh
  sam build
  ```

  We then run:

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

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

  This will be indicated by:

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

  If we want to invoke the function directly we use:

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

  ## Using environment variables

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

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

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

  ## Create secret

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

  ## Create local ENV file

  > These must be in JSON to work with SAM:

  ### Local env

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

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

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

  ### Update `template.yaml`

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

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

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

  ### Pass in the environment variable at invocation:

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

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

  ### Create handler within the Lambda itself

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

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

  const secretsManager = new AWS.SecretsManager();

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


path: zk/Logarithms.md
sortable_path: zkLogarithms.md
title: Using exponents to calculate logarithms
checksum: 741db4c876f0fe9332002a963e75702af62c7cd91a1d7e03790b8b372d0b720c
created: 2024-03-30 09:03:19.994950595+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["algebra"]}
lead:
  This approach becomes rapidly difficult when working with larger numbers. It's
  not as obvious what $\log \_{5} 625$ would be using this method. For this
  reason, we use exponents which are intimately related to logarithms.

body:
  This approach becomes rapidly difficult when working with larger numbers. It's
  not as obvious what $\log \_{5} 625$ would be using this method. For this
  reason, we use exponents which are intimately related to logarithms.

  A logarithm can be expressed identically using exponents for example:

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


  $$

  By carrying out the conversion in stages, we can work out the answer to the
  question a logarithm poses.

  Let's work out $\log \_{2} 8$ using this method.

  1. First we add a variable (x) to the expression on the right hand:

     $$
     \log \_{2} 8 \leftrightarrow x


     $$

  1. Next we take the base of the logarithm and combine it with x as an exponent.
     Now our formula looks like this:

     $$
     \log \_{2} 8 \leftrightarrow 2^x


     $$

  1. Next we add an equals and the number that is left from the logarithm (8):

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


  $$

  Then the problem is reduced to: how many times do you need to multiply two by
  itself to get 8? The answer is 3 : 2 x 2 x 2 or 2 p3. Hence we have the balanced
  equation:

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


  $$

  ## Common base values

  Often times a base won't be specified in a log expression. For example:

  $$
  \log1000


  $$

  This is just a shorthand and it means that the base value is ten, i.e that the
  logarithm is written in denary (base 10). So the above actually means:

  $$
  \log \_{10} 1000 = 3


  $$

  This is referred to as the **common logarithm**

  Another frequent base is Euler's number (approx. 2.71828) known as the **natural
  logarithm**

  An example

  $$
  \log \_{e} 7.389 = 2


  $$

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

  Most simply a logarithm is a way of answering the question:

  > How many of one number do we need to get another number. How many of x do we
  > need to get y

  More formally:

  > x raised to what power gives me y

  Below is an example of a logarithm:

  $$
  \log \_{3} 9


  $$

  We read it:

  > log base 3 of 9

  And it means:

  > 3 raised to what power gives me 9?

  In this case the answer is easy: $3^2$ gives me nine, which is to say: three
  multiplied by itself.

  ## Using exponents to calculate logarithms

  This approach becomes rapidly difficult when working with larger numbers. It's
  not as obvious what $\log \_{5} 625$ would be using this method. For this
  reason, we use exponents which are intimately related to logarithms.

  A logarithm can be expressed identically using exponents for example:

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


  $$

  By carrying out the conversion in stages, we can work out the answer to the
  question a logarithm poses.

  Let's work out $\log \_{2} 8$ using this method.

  1. First we add a variable (x) to the expression on the right hand:

     $$
     \log \_{2} 8 \leftrightarrow x


     $$

  1. Next we take the base of the logarithm and combine it with x as an exponent.
     Now our formula looks like this:

     $$
     \log \_{2} 8 \leftrightarrow 2^x


     $$

  1. Next we add an equals and the number that is left from the logarithm (8):

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


  $$

  Then the problem is reduced to: how many times do you need to multiply two by
  itself to get 8? The answer is 3 : 2 x 2 x 2 or 2 p3. Hence we have the balanced
  equation:

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


  $$

  ## Common base values

  Often times a base won't be specified in a log expression. For example:

  $$
  \log1000


  $$

  This is just a shorthand and it means that the base value is ten, i.e that the
  logarithm is written in denary (base 10). So the above actually means:

  $$
  \log \_{10} 1000 = 3


  $$

  This is referred to as the **common logarithm**

  Another frequent base is Euler's number (approx. 2.71828) known as the **natural
  logarithm**

  An example

  $$
  \log \_{e} 7.389 = 2


  $$


path: zk/Logic_gates.md
sortable_path: zkLogic_gates.md
title: Logic gates
checksum: 2690421e13abf67dfa546b560e0b6867deaed7409216467fccebb8a9e82ff62e
created: 2024-03-30 09:03:20.002454066+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["logic-gates","binary"]}
lead:
  > [A logic gate consists in] three connections where there may or may not be
  > some electricity. Two of those connections are places where electricity may be
  > put into the device, and the third connection is a place where electricity may
  > come out of the device.

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

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

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

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

  ## Elementary and composite gates

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

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

  ## Gate interface / gate implementation

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

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

  // Add: Interface diagram

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

  // Add: Implementation diagram

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

  ## NOT gate

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

  ### Truth conditions

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

  ### Interactive circuit

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

  ### Truth conditions

  tion) truth functional connective

  ### Symbol

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

  ### Truth conditions

  ### Truth conditions

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

  ### Interactive circuit

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

  ## NAND gate

  > The NAND gate inverts the truth conditions of AND.

  ### Symbol

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

  ### Truth conditions

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

  ### Interactive circuit

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

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

  ## OR gate

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

  ### Symbol

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

  ### Truth condition

  ### Truth conditions

  s

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

  ### Interactive circui

  ### Truth conditions

  t

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

  ## XOR gate

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

  ### Symbol

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

  ### Truth conditions

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

  ### Interactive circuit

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

  ## NOR gate

  > The NOR gate inverts the function of an OR gate

  ### Symbol

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

  ### Truth conditions

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

  ### Interactive circuit

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

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

  # Logic gates

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

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

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

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

  ## Elementary and composite gates

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

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

  ## Gate interface / gate implementation

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

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

  // Add: Interface diagram

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

  // Add: Implementation diagram

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

  ## NOT gate

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

  ### Truth conditions

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

  ### Interactive circuit

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

  ### Truth conditions

  tion) truth functional connective

  ### Symbol

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

  ### Truth conditions

  ### Truth conditions

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

  ### Interactive circuit

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

  ## NAND gate

  > The NAND gate inverts the truth conditions of AND.

  ### Symbol

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

  ### Truth conditions

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

  ### Interactive circuit

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

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

  ## OR gate

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

  ### Symbol

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

  ### Truth condition

  ### Truth conditions

  s

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

  ### Interactive circui

  ### Truth conditions

  t

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

  ## XOR gate

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

  ### Symbol

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

  ### Truth conditions

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

  ### Interactive circuit

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

  ## NOR gate

  > The NOR gate inverts the function of an OR gate

  ### Symbol

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

  ### Truth conditions

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

  ### Interactive circuit

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


path: zk/Logical_consistency.md
sortable_path: zkLogical_consistency.md
title: Logical consistency
lead: ## Informal definition
checksum: 25b31fea3e617c20721052b3fb39a4b363a687e9183bbcf5cc6abab51e4219b6
created: 2024-03-30 09:03:20.041343934+00:00
modified: 2024-06-16 16:52:23.802295591+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  ## Informal definition

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

  ### Demonstration

  The following set of propositions form an inconsistent set:

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

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

  ## Formal definition

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

  ### Informal expression

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

  ### Formal expression

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

  ### Truth table

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

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

  ## Derivation

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

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

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

  ![](/img/derivation_from_contradiction.png)

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

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

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

  # Logical consistency

  ## Informal definition

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

  ### Demonstration

  The following set of propositions form an inconsistent set:

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

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

  ## Formal definition

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

  ### Informal expression

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

  ### Formal expression

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

  ### Truth table

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

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

  ## Derivation

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

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

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

  ![](/img/derivation_from_contradiction.png)

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

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


path: zk/Logical_equivalence.md
sortable_path: zkLogical_equivalence.md
title: Logical equivalence
checksum: b08ec98b4bd43e93cacd533e9b9d0847dc5ae2454cd8d160eed3a4b7a635fb90
created: 2024-03-30 09:03:20.055697681+00:00
modified: 2024-06-16 16:52:27.765396756+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  > Two sentences, P and Q, are truth-functionally equivalent if and only if there
  > is no truth assignment in which P is true and Q is false

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

  ### Informal expression

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

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

  ### Formal expression

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

  ### Truth-tables

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

  ### Derivation

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

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

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

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

  # Logical equivalence

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

  ### Informal expression

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

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

  ### Formal expression

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

  ### Truth-tables

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

  ### Derivation

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

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

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


path: zk/Logical_indeterminacy.md
sortable_path: zkLogical_indeterminacy.md
title: Logical indeterminacy
checksum: 32e3fa137e65eff2c6f39ea7a5fca9d67f8a160dfbe8ad1551a0c8995a001af3
created: 2024-03-30 09:03:20.068819073+00:00
modified: 2024-06-16 16:52:32.458463622+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  The vast majority of propositions in natural and formal logical languages are
  **neither
  [logically true](Logical_truth_and_falsity.md#logical-truth)
  or
  [logically false](Logical_truth_and_falsity.md#logical-falsity)**.
  This makes sense because propositions of this form are all either tautologies or
  contradictions and as such do not express information about the state of events
  in the world. We call propositions that are neither logically true or logically
  false, **logically indeterminate** propositions.

body:
  The vast majority of propositions in natural and formal logical languages are
  **neither
  [logically true](Logical_truth_and_falsity.md#logical-truth)
  or
  [logically false](Logical_truth_and_falsity.md#logical-falsity)**.
  This makes sense because propositions of this form are all either tautologies or
  contradictions and as such do not express information about the state of events
  in the world. We call propositions that are neither logically true or logically
  false, **logically indeterminate** propositions.

  ## Informal definition

  A proposition is logically indeterminate if it is neither logically true or
  logically false. This is to say: it can be both [consistently](Consistency.md)
  asserted and consistently denied.

  For example the proposition:

  ```
  It is raining.
  ```

  May be true or false thus it can it both be asserted and denied quite
  consistently. It is true if it actually is raining and false if it actually is
  not raining. There is no logical contradiction implied by saying it is raining
  when it isn't raining, this assertion is simply false. There is a contradiction
  in saying that both states obtain. Thus the proposition:

  ```
  It is raining and it is not raining.
  ```

  Cannot be consistently asserted as there is no possibility of the proposition
  being true. It is either raining or it isn't raining. Given the law for
  conjunction, both conjuncts must be true for the proposition as a whole to be
  true. But in the case of this proposition if one conjunct is true, the other
  must be false and vice versa, hence it is not possible for the proposition to be
  true at all. It can _only_ be false.

  Contrariwise the proposition:

  ```
  It is raining or it is not raining.
  ```

  Cannot be consistently denied as there is no possibility of it being false. It
  is either raining or not raining. Given the law for disjunction, either disjunct
  can be true to make the proposition as a whole true. Given that it is either
  raining or not raining in either scenario, the proposition as a whole will be
  true. Therefore there is no possibility of it being false, it can _only_ be
  true.

  ## Formal definition

  > A proposition P is truth-functionally indeterminate if and only if it is
  > neither truth-functionally true or truth-functionally false. should be avoided
  > in arguments, they 'prove' everything whi

  ```
  P
  ```

  ### Truth-table

  | $P$ | $P$ |
  | --- | --- |
  | T   | T   |
  | F   | F   |

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

  # Logical indeterminacy

  The vast majority of propositions in natural and formal logical languages are
  **neither
  [logically true](Logical_truth_and_falsity.md#logical-truth)
  or
  [logically false](Logical_truth_and_falsity.md#logical-falsity)**.
  This makes sense because propositions of this form are all either tautologies or
  contradictions and as such do not express information about the state of events
  in the world. We call propositions that are neither logically true or logically
  false, **logically indeterminate** propositions.

  ## Informal definition

  A proposition is logically indeterminate if it is neither logically true or
  logically false. This is to say: it can be both [consistently](Consistency.md)
  asserted and consistently denied.

  For example the proposition:

  ```
  It is raining.
  ```

  May be true or false thus it can it both be asserted and denied quite
  consistently. It is true if it actually is raining and false if it actually is
  not raining. There is no logical contradiction implied by saying it is raining
  when it isn't raining, this assertion is simply false. There is a contradiction
  in saying that both states obtain. Thus the proposition:

  ```
  It is raining and it is not raining.
  ```

  Cannot be consistently asserted as there is no possibility of the proposition
  being true. It is either raining or it isn't raining. Given the law for
  conjunction, both conjuncts must be true for the proposition as a whole to be
  true. But in the case of this proposition if one conjunct is true, the other
  must be false and vice versa, hence it is not possible for the proposition to be
  true at all. It can _only_ be false.

  Contrariwise the proposition:

  ```
  It is raining or it is not raining.
  ```

  Cannot be consistently denied as there is no possibility of it being false. It
  is either raining or not raining. Given the law for disjunction, either disjunct
  can be true to make the proposition as a whole true. Given that it is either
  raining or not raining in either scenario, the proposition as a whole will be
  true. Therefore there is no possibility of it being false, it can _only_ be
  true.

  ## Formal definition

  > A proposition P is truth-functionally indeterminate if and only if it is
  > neither truth-functionally true or truth-functionally false. should be avoided
  > in arguments, they 'prove' everything whi

  ```
  P
  ```

  ### Truth-table

  | $P$ | $P$ |
  | --- | --- |
  | T   | T   |
  | F   | F   |


path: zk/Logical_possibility_and_necessity.md
sortable_path: zkLogical_possibility_and_necessity.md
title: Logical possibility and necessity
lead: ## Logical possibility
checksum: f2b8d371f555e5b90f5e19beba80d8f0b6a488c50cc44d3ee4aca2bab05158c9
created: 2024-03-30 09:03:20.088733205+00:00
modified: 2024-06-16 16:52:38.408138711+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  ## Logical possibility

  In distinguishing the properties of
  [logical consistency](Logical_consistency.md) and
  [validity](Validity_and_entailment.md) we make tacit use
  of the notion of **possibility**. This is because when we consider the validity
  of an argument we are assessing truth-conditions and this consists in asking
  ourselves what could or could not be the case: were it such that _P_, then it
  would be the case that _Q_. It is important to understand what possibility means
  in the context of logic and how it differs from what we might mean ordinarily
  when we use the term.

  It is evident from the case of arguments that are valid but not sound that logic
  operates with a specialised notion of possibility. For example it has to be the
  case that the proposition _Every woman can levitate_ is logically possible since
  the following argument is valid:

  ```
  1.  P: Janice is a woman.
  2.  P: Every woman can levitate.
  3.  C: Janice can levitate.
  ```

  But we know of course that women cannot levitate. When we assert that this is
  impossible we are relying on a stronger notion of possibility than logical
  possibility. It follows that the concept of possibility can have different
  degrees. The scope of the concept of possibility has been the concern of
  logicians and philosophers since at least the time of Plato and numerous
  different formulations exist. The notion that we mostly work with unreflectively
  in everyday life is nomological possibility. This means ‘governed by the
  application of laws’ where these laws pertain to our current understanding of
  the natural world as determined by physics. Levitation is therefore
  nomologically impossible but logically possible.

  If logical possibility is not constrained by the laws of physics does it place
  any restrictions on what is possible? Logic applies a single restriction, the
  law of non-contradiction: a proposition cannot both be true and false at once.
  The following propositions are examples of a contradictory propositions.

  Some examples of contradictions:

  - There is a dog that is not a dog
  - Today is Tuesday and today is not Tuesday
  - The cat that is dead is alive

  From this we can derive the following property of logical possibility:

  > A proposition is logically possible just if it does not imply a contradiction.

  ## Logical necessity

  A proposition is _logically necessary_ if it is true in every logically possible
  circumstance which is to say: true on every possible truth functional
  assignment. Necessity and
  [logical truth](Logical_truth_and_falsity.md#logical-truth)
  are therefore synonyms: anything that is logically true (a tautology) is true by
  necessity (could not be otherwise.)

  Further, every logical truth is logically possible but not everything that is
  logically possible is logically true. It is possible that it is raining but this
  is not logically necessary - it could be otherwise, i.e not raining. However it
  is not possible that it could be both raining and not raining.

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

  # Logical possibility and necessity

  ## Logical possibility

  In distinguishing the properties of
  [logical consistency](Logical_consistency.md) and
  [validity](Validity_and_entailment.md) we make tacit use
  of the notion of **possibility**. This is because when we consider the validity
  of an argument we are assessing truth-conditions and this consists in asking
  ourselves what could or could not be the case: were it such that _P_, then it
  would be the case that _Q_. It is important to understand what possibility means
  in the context of logic and how it differs from what we might mean ordinarily
  when we use the term.

  It is evident from the case of arguments that are valid but not sound that logic
  operates with a specialised notion of possibility. For example it has to be the
  case that the proposition _Every woman can levitate_ is logically possible since
  the following argument is valid:

  ```
  1.  P: Janice is a woman.
  2.  P: Every woman can levitate.
  3.  C: Janice can levitate.
  ```

  But we know of course that women cannot levitate. When we assert that this is
  impossible we are relying on a stronger notion of possibility than logical
  possibility. It follows that the concept of possibility can have different
  degrees. The scope of the concept of possibility has been the concern of
  logicians and philosophers since at least the time of Plato and numerous
  different formulations exist. The notion that we mostly work with unreflectively
  in everyday life is nomological possibility. This means ‘governed by the
  application of laws’ where these laws pertain to our current understanding of
  the natural world as determined by physics. Levitation is therefore
  nomologically impossible but logically possible.

  If logical possibility is not constrained by the laws of physics does it place
  any restrictions on what is possible? Logic applies a single restriction, the
  law of non-contradiction: a proposition cannot both be true and false at once.
  The following propositions are examples of a contradictory propositions.

  Some examples of contradictions:

  - There is a dog that is not a dog
  - Today is Tuesday and today is not Tuesday
  - The cat that is dead is alive

  From this we can derive the following property of logical possibility:

  > A proposition is logically possible just if it does not imply a contradiction.

  ## Logical necessity

  A proposition is _logically necessary_ if it is true in every logically possible
  circumstance which is to say: true on every possible truth functional
  assignment. Necessity and
  [logical truth](Logical_truth_and_falsity.md#logical-truth)
  are therefore synonyms: anything that is logically true (a tautology) is true by
  necessity (could not be otherwise.)

  Further, every logical truth is logically possible but not everything that is
  logically possible is logically true. It is possible that it is raining but this
  is not logically necessary - it could be otherwise, i.e not raining. However it
  is not possible that it could be both raining and not raining.


path: zk/Logical_truth_and_falsity.md
sortable_path: zkLogical_truth_and_falsity.md
title: Logical truth and falsity
lead: We say of certain propositions that they are logically true or logically false.
checksum: 4a99a064e5afe470ad3eb6ae5bb245501ff8c76df7292f13e67109dd80525769
created: 2024-03-30 09:03:20.106473653+00:00
modified: 2024-06-16 16:52:42.537921525+00:00
metadata: {"tags":["propositional-logic","logic"]}
body:
  We say of certain propositions that they are logically true or logically false.

  ## Logical falsity

  ### Informal definition

  A proposition is logically false if and only if **it is not possible for the
  proposition to be true**. The proposition itself cannot be consistently
  asserted.

  **Demonstration**

  ```
  There is a country that is not a country.
  Apples are fruits and apples are not fruits
  ```

  Neither proposition can be true because the truth of the first clause is
  contradicted by the second. By the principle of
  [consistency](Logical_consistency.md), it is not
  possible for both clauses to be true at once therefore the proposition, overall
  has the truth value of false.

  ```
  It is raining and it is not raining.
  ```

  ### Formal definition

  > A proposition P is truth-functionally false if and only if P is false on every
  > truth-value assignment

  ### Formal expression

  $$ P \land \lnot P $$

  ### Truth-table

  | $P$ | $P \land \lnot P$ |
  | --- | ----------------- |
  | T   | F                 |
  | T   | F                 |

  ## Logical truth

  ### Informal definition

  A proposition is logically true if and only if it is not possible for the
  proposition to be false. The proposition itself cannot be consistently denied.

  **Demonstration**

  ```
  A rose is a rose.
  Today is Tuesday unless today is not Tuesday.
  ```

  Regardless of any facts obtaining in the world, these propositions cannot be
  false.should be avoided in arguments, they 'prove' everything whi

  As with logically false propositions, logical truth can also apply to compound
  propositions:

  ```
  A rose is a rose and a shoe is a shoe
  ```

  ### Formal definition

  > A proposition P is truth-functionally true if and only if P is true on every
  > truth-value assignment

  $$ P \lor \lnot P$$

  ### Truth-table

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

  ### Consequences

  The existence of logically false and logically true propositions affects the
  validity and soundness of arguments in which they are used. These are
  technicalities that have philosophically interesting consequences.

  - If an argument contains premises which are logically false than this argument
    will perforce be valid. This is because one cannot consistently assert the
    premises and deny the conclusion which is the definition of
    [validity](Validity_and_entailment.md). However the
    _reason_ why one cannot consistently assert the premises and deny the
    conclusions is because one cannot consistently assert the premises - they
    conflict with each other. Furthermore as the argument contains false premises,
    it cannot be sound.

    ```
    (P1) Russia is a country.
    (P2) Russia is not a country
    (P3) All countries have languages.
    ____________________________________________
    (C) Russian is a language.
    ```

  - Any argument with a logically true conclusion is valid. Because the conclusion
    cannot be consistently denied it follows that we cannot consistently assert
    the premises _and_ deny the conclusion. Whether or not the argument is sound
    remains an open question however. If the premises happen to be true then the
    argument will be sound on the strength of the conclusion being logically true
    but if the premises are false it will be unsound regardless of the truth of
    the conclusion.

    ```
    (P1) Horses have legs.
    (P2) Animals with legs can move.
    ____________________________________________
    (C) A horse is a horse
    ```

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

  # Logical truth and falsity

  We say of certain propositions that they are logically true or logically false.

  ## Logical falsity

  ### Informal definition

  A proposition is logically false if and only if **it is not possible for the
  proposition to be true**. The proposition itself cannot be consistently
  asserted.

  **Demonstration**

  ```
  There is a country that is not a country.
  Apples are fruits and apples are not fruits
  ```

  Neither proposition can be true because the truth of the first clause is
  contradicted by the second. By the principle of
  [consistency](Logical_consistency.md), it is not
  possible for both clauses to be true at once therefore the proposition, overall
  has the truth value of false.

  ```
  It is raining and it is not raining.
  ```

  ### Formal definition

  > A proposition P is truth-functionally false if and only if P is false on every
  > truth-value assignment

  ### Formal expression

  $$ P \land \lnot P $$

  ### Truth-table

  | $P$ | $P \land \lnot P$ |
  | --- | ----------------- |
  | T   | F                 |
  | T   | F                 |

  ## Logical truth

  ### Informal definition

  A proposition is logically true if and only if it is not possible for the
  proposition to be false. The proposition itself cannot be consistently denied.

  **Demonstration**

  ```
  A rose is a rose.
  Today is Tuesday unless today is not Tuesday.
  ```

  Regardless of any facts obtaining in the world, these propositions cannot be
  false.should be avoided in arguments, they 'prove' everything whi

  As with logically false propositions, logical truth can also apply to compound
  propositions:

  ```
  A rose is a rose and a shoe is a shoe
  ```

  ### Formal definition

  > A proposition P is truth-functionally true if and only if P is true on every
  > truth-value assignment

  $$ P \lor \lnot P$$

  ### Truth-table

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

  ### Consequences

  The existence of logically false and logically true propositions affects the
  validity and soundness of arguments in which they are used. These are
  technicalities that have philosophically interesting consequences.

  - If an argument contains premises which are logically false than this argument
    will perforce be valid. This is because one cannot consistently assert the
    premises and deny the conclusion which is the definition of
    [validity](Validity_and_entailment.md). However the
    _reason_ why one cannot consistently assert the premises and deny the
    conclusions is because one cannot consistently assert the premises - they
    conflict with each other. Furthermore as the argument contains false premises,
    it cannot be sound.

    ```
    (P1) Russia is a country.
    (P2) Russia is not a country
    (P3) All countries have languages.
    ____________________________________________
    (C) Russian is a language.
    ```

  - Any argument with a logically true conclusion is valid. Because the conclusion
    cannot be consistently denied it follows that we cannot consistently assert
    the premises _and_ deny the conclusion. Whether or not the argument is sound
    remains an open question however. If the premises happen to be true then the
    argument will be sound on the strength of the conclusion being logically true
    but if the premises are false it will be unsound regardless of the truth of
    the conclusion.

    ```
    (P1) Horses have legs.
    (P2) Animals with legs can move.
    ____________________________________________
    (C) A horse is a horse
    ```


path: zk/Loops_in_Python.md
sortable_path: zkLoops_in_Python.md
title: Loops in Python
lead: ## For
checksum: ca0f453f1cd1fc7e5ac9385243923ae593595fe1ded7c3961b3a7f4396369dc1
created: 2024-03-30 09:03:20.118761964+00:00
modified: 2024-06-28 13:00:29.868374908+00:00
metadata: {"tags":["python"]}
body:
  ## For

  There are three main types of `for` loop in Python and they all use the
  `for...in` clause:

  1. Traditional `for` loop with range:

     ```py
     for i in range(0, 5):
         print(i)
     ```

     This is equivalent to the following in JavaScript:

     ```js
     for (let i = 0; i <= 5: i++) {
         console.log(i)
     }
     ```

  2. `for` loop with iterable:

     ```py
     fruits = ["apple", "banana", "cherry"]
     for fruit in fruits:
         print(fruit)
     ```

  3. `for` loop with `enumerate()` function: Iterate over an iterable whilst also
     keeping track of the index of the current element:
     ```py
     for index, fruit in enumerate(fruits):
         print(f"Index {index}: {fruit}")
     ```

  ### Loop in increments greater than 1

  The following loop increments by 2 on each iteration

  ```py
  for i in range(0, 10, 2):
      print(i, ' ', end='')
  print()

  # 0  2  4  6  8
  ```

  ### Break and continue

  #### Break

  ```py
  num = int(input('Enter a number from 1-6 to check for: '))
  for i in range(0, 6):
      if i == num:
          break
      print(i, ' ')
  ```

  #### Continue

  ```py
  for i in range(0, 10):
      print(i, ' ', end='')
      if i % 2 == 1:
          continue
      print('hey its an even number')
      print('we love even numbers')
  print('Done')

  """
  0  hey its an even number
  we love even numbers
  1  2  hey its an even number
  we love even numbers
  3  4  hey its an even number
  we love even numbers
  5  6  hey its an even number
  we love even numbers
  7  8  hey its an even number
  we love even numbers
  9  Done
  """
  ```

  ## While

  ```python
  count = 0
  print('Starting')
  while count < 10:
      print(count, '', end='')
      count += 1

  print()  # not part of the while loop
  print('Done')

  """
  Starting
  0 1 2 3 4 5 6 7 8 9
  Done
  """
  ```

  > There are no `do while` loops in Python

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

  # Loops in Python

  ## For

  There are three main types of `for` loop in Python and they all use the
  `for...in` clause:

  1. Traditional `for` loop with range:

     ```py
     for i in range(0, 5):
         print(i)
     ```

     This is equivalent to the following in JavaScript:

     ```js
     for (let i = 0; i <= 5: i++) {
         console.log(i)
     }
     ```

  2. `for` loop with iterable:

     ```py
     fruits = ["apple", "banana", "cherry"]
     for fruit in fruits:
         print(fruit)
     ```

  3. `for` loop with `enumerate()` function: Iterate over an iterable whilst also
     keeping track of the index of the current element:
     ```py
     for index, fruit in enumerate(fruits):
         print(f"Index {index}: {fruit}")
     ```

  ### Loop in increments greater than 1

  The following loop increments by 2 on each iteration

  ```py
  for i in range(0, 10, 2):
      print(i, ' ', end='')
  print()

  # 0  2  4  6  8
  ```

  ### Break and continue

  #### Break

  ```py
  num = int(input('Enter a number from 1-6 to check for: '))
  for i in range(0, 6):
      if i == num:
          break
      print(i, ' ')
  ```

  #### Continue

  ```py
  for i in range(0, 10):
      print(i, ' ', end='')
      if i % 2 == 1:
          continue
      print('hey its an even number')
      print('we love even numbers')
  print('Done')

  """
  0  hey its an even number
  we love even numbers
  1  2  hey its an even number
  we love even numbers
  3  4  hey its an even number
  we love even numbers
  5  6  hey its an even number
  we love even numbers
  7  8  hey its an even number
  we love even numbers
  9  Done
  """
  ```

  ## While

  ```python
  count = 0
  print('Starting')
  while count < 10:
      print(count, '', end='')
      count += 1

  print()  # not part of the while loop
  print('Done')

  """
  Starting
  0 1 2 3 4 5 6 7 8 9
  Done
  """
  ```

  > There are no `do while` loops in Python


path: zk/Loops_in_bash.md
sortable_path: zkLoops_in_bash.md
title: Loops in Bash
lead: ## Traditional for loop
checksum: 021d8bf50cddf71c62034ac838af2c11526a8a1ce9f3debec29c67a6f8150ef6
created: 2024-03-30 09:03:20.122591455+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["shell"]}
body:
  ## Traditional for loop

  ```bash
  for (( i=0; i<=5;i++  )); do
    echo $i
  done

  # 1 2 3 4 5
  ```

  ## `for..in`: loop through an array

  ```bash
  for element in "${arr[@]}"
  do
      echo "$element"
  done
  ```

  ## While loop

  > `while` loops execute while a condition is true (0)

  We can use a `while` loop as a condition in two senses:

  - execute while a given condition obtains
  - expand and execute a given command as long as the final command in the `while'
    for the command has an exit status of zero (i.e. truthy)

  Here is an exampe of using `while` in the former case:

  ```sh
  declare -i n=0
  while (( n<10 ))
  do
    echo "n:$n"
    (( n++ ))
  done
  ```

  Here is an example of using `while` in the latter case:

  ```sh
  while read line;
  do
      # something
  done < "$file_to_read"
  ```

  ## Until loop

  > `until` loops execute until a condition is false (1)

  ```sh
  declare -i m=0
  until (( m==10 )): do
    echo "m:$m"
    (( m++ ))
  done
  ```

  This gives us the same output as `n` with the while loop but here it runs so
  long as `m==10` is false. As soon as `m` is equal to 100, the condition becomes
  true and hence the loop stops.

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

  # Loops in Bash

  ## Traditional for loop

  ```bash
  for (( i=0; i<=5;i++  )); do
    echo $i
  done

  # 1 2 3 4 5
  ```

  ## `for..in`: loop through an array

  ```bash
  for element in "${arr[@]}"
  do
      echo "$element"
  done
  ```

  ## While loop

  > `while` loops execute while a condition is true (0)

  We can use a `while` loop as a condition in two senses:

  - execute while a given condition obtains
  - expand and execute a given command as long as the final command in the `while'
    for the command has an exit status of zero (i.e. truthy)

  Here is an exampe of using `while` in the former case:

  ```sh
  declare -i n=0
  while (( n<10 ))
  do
    echo "n:$n"
    (( n++ ))
  done
  ```

  Here is an example of using `while` in the latter case:

  ```sh
  while read line;
  do
      # something
  done < "$file_to_read"
  ```

  ## Until loop

  > `until` loops execute until a condition is false (1)

  ```sh
  declare -i m=0
  until (( m==10 )): do
    echo "m:$m"
    (( m++ ))
  done
  ```

  This gives us the same output as `n` with the while loop but here it runs so
  long as `m==10` is false. As soon as `m` is equal to 100, the condition becomes
  true and hence the loop stops.


path: zk/Machine_code.md
sortable_path: zkMachine_code.md
title: Machine_code
lead: # Machine code
checksum: 0540c692b4392dc1e6743c5d7b945328dd2339ffde07aaeae08cc44d7820b0a7
created: 2024-03-30 09:03:20.126143504+00:00
modified: 2024-06-16 17:15:29.297594754+00:00
body:
  # Machine code

  No matter how a program was originally written, it eventually needs to execute
  on a [[CPU_architecture|CPU]] as a series of machine language instructions.

  ## Example machine instruction

  The following machine instruction is an instruction that would be understood by
  an [[Instruction_set_architectures| ARM ISA]]. It moves the number `4` into the
  `r7` register:

  ```
  11100011101000000111000000000100
  ```

  This 32-bit line of binary has a series of instructions embedded within it. We
  partition each part of the sequence below, from left to right, mapping it to the
  instruction:

  | Binary sequence | Instruction          | Action                                   |
  | --------------- | -------------------- | ---------------------------------------- |
  | 1110            | condition            | always execute (unconditional)           |
  | 00              | --                   | --                                       |
  | 1               | immediate            | value is in the last 8 bits              |
  | 1101            | opcode               | `mov` : move the value                   |
  | 0               | --                   | --                                       |
  | 0000            | --                   | --                                       |
  | 0111            | destination register | destination is 0111 which means `r7`     |
  | 0000            | --                   | --                                       |
  | 00000100        | immediate value      | the binary representation of decimal '4' |

  - The blank zeros are also instructions, they are just not used in this
    instruction

  - The condition sequence tells us the instruction should run in all conditions
    not only under certain circumstances

  - The immediate bit (`1`) tells us whether the the value we are accessing is
    contained within the instruction or whether it is stored in a register. In
    this scenario `1` means the value is in the instruction. If it were `0`, the
    register where the value is located would be specified elsewhere in the
    instruction (in one of the currently blank sequences).

  - The opcode correspoons to `mov` the value.

  - THe destination register details where the value should be moved to (`r7`)

  - Finally, the immediate value is equivalent to decimal `4`

  ## Difficulty

  Binary sequences are hard to understand, even if converted to the
  [[Hexadecimal_number_system]]. We have a better way of managing operations on
  the machine level: [assembly](Assembly.md)

raw_content:
  ---
  id: iqh8
  title: Machine_code
  tags:
    - binary
    - CPU
    - hardware
    - computer-architecture
  created: Monday, March 11, 2024
  ---

  # Machine code

  No matter how a program was originally written, it eventually needs to execute
  on a [[CPU_architecture|CPU]] as a series of machine language instructions.

  ## Example machine instruction

  The following machine instruction is an instruction that would be understood by
  an [[Instruction_set_architectures| ARM ISA]]. It moves the number `4` into the
  `r7` register:

  ```
  11100011101000000111000000000100
  ```

  This 32-bit line of binary has a series of instructions embedded within it. We
  partition each part of the sequence below, from left to right, mapping it to the
  instruction:

  | Binary sequence | Instruction          | Action                                   |
  | --------------- | -------------------- | ---------------------------------------- |
  | 1110            | condition            | always execute (unconditional)           |
  | 00              | --                   | --                                       |
  | 1               | immediate            | value is in the last 8 bits              |
  | 1101            | opcode               | `mov` : move the value                   |
  | 0               | --                   | --                                       |
  | 0000            | --                   | --                                       |
  | 0111            | destination register | destination is 0111 which means `r7`     |
  | 0000            | --                   | --                                       |
  | 00000100        | immediate value      | the binary representation of decimal '4' |

  - The blank zeros are also instructions, they are just not used in this
    instruction

  - The condition sequence tells us the instruction should run in all conditions
    not only under certain circumstances

  - The immediate bit (`1`) tells us whether the the value we are accessing is
    contained within the instruction or whether it is stored in a register. In
    this scenario `1` means the value is in the instruction. If it were `0`, the
    register where the value is located would be specified elsewhere in the
    instruction (in one of the currently blank sequences).

  - The opcode correspoons to `mov` the value.

  - THe destination register details where the value should be moved to (`r7`)

  - Finally, the immediate value is equivalent to decimal `4`

  ## Difficulty

  Binary sequences are hard to understand, even if converted to the
  [[Hexadecimal_number_system]]. We have a better way of managing operations on
  the machine level: [assembly](Assembly.md)

metadata:
  {"created":"Monday, March 11, 2024","id":"iqh8","tags":["binary","CPU","hardware","computer-architecture"],"title":"Machine_code"}


path: zk/Map_and_filter_in_Python.md
sortable_path: zkMap_and_filter_in_Python.md
title: Map and filter in Python
lead: ## Map
checksum: 657b2624c0782eaa5f2b92a064f41d79185fc42eef1be5b4c8ab202f87745c76
created: 2024-03-30 09:03:20.165674202+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["python"]}
body:
  ## Map

  ```py
  data = [1, 3, 5, 2, 7, 4, 10]

  new_data = map(lambda i: i + 10, data)
  print(new_data)
  ```

  We can also pass-in a function rather than use a lambda:

  ```py
  def add_one(i):
      return i + 1

  x = list(map(addOne, data))
  # [2, 4, 6, 3, 8, 5, 11]
  # necessary to add `list` to get some output
  ```

  ### Map returns an object

  When you run `map` against a list it returns a map object not a list. This can
  present problems if you are not aware of it since it is not an entity that you
  can loop through.

  The following map removes whitespaces from elements in a list:

  ```py
    lines = map(lambda x: x.strip(), lines)
  ```

  In order to be able to run list-like operations against the resulting `lines`
  object we would need to convert it to a list:

  ```py
    lines = list(map(lambda x: x.strip(), lines))
  ```

  ## Filter

  ```py
  data = [1, 3, 5, 2, 7, 4, 10]

  d1 = list(filter(lambda i: i % 2 == 0, data))
  print(d1)
  #  [2, 4, 10]

  def is_even(i):
      return i % 2 == 0


  # Filter for even numbers using a named function
  d2 = list(filter(is_even, data))

  # [2, 4, 10]
  ```

  ## Chaining

  ```py
  data = [1, 3, 5, 2, 7, 4, 10]
  new_data = list(map(lambda i: i + 10, filter(is_even, data)))
  print(new_data)
  # new_data: [12, 14, 20]
  ```

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

  # Map and filter in Python

  ## Map

  ```py
  data = [1, 3, 5, 2, 7, 4, 10]

  new_data = map(lambda i: i + 10, data)
  print(new_data)
  ```

  We can also pass-in a function rather than use a lambda:

  ```py
  def add_one(i):
      return i + 1

  x = list(map(addOne, data))
  # [2, 4, 6, 3, 8, 5, 11]
  # necessary to add `list` to get some output
  ```

  ### Map returns an object

  When you run `map` against a list it returns a map object not a list. This can
  present problems if you are not aware of it since it is not an entity that you
  can loop through.

  The following map removes whitespaces from elements in a list:

  ```py
    lines = map(lambda x: x.strip(), lines)
  ```

  In order to be able to run list-like operations against the resulting `lines`
  object we would need to convert it to a list:

  ```py
    lines = list(map(lambda x: x.strip(), lines))
  ```

  ## Filter

  ```py
  data = [1, 3, 5, 2, 7, 4, 10]

  d1 = list(filter(lambda i: i % 2 == 0, data))
  print(d1)
  #  [2, 4, 10]

  def is_even(i):
      return i % 2 == 0


  # Filter for even numbers using a named function
  d2 = list(filter(is_even, data))

  # [2, 4, 10]
  ```

  ## Chaining

  ```py
  data = [1, 3, 5, 2, 7, 4, 10]
  new_data = list(map(lambda i: i + 10, filter(is_even, data)))
  print(new_data)
  # new_data: [12, 14, 20]
  ```


path: zk/Match_statements_in_Python.md
sortable_path: zkMatch_statements_in_Python.md
title: Match statements in Python
lead: > A `match` statement is the equivalent of a switch or case statement in Python
checksum: 4eca8abdc2e26149bf6e163394aa1e01aaef771d47d653884dac8ec213313c40
created: 2024-03-30 09:03:20.181893761+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["python"]}
body:
  > A `match` statement is the equivalent of a switch or case statement in Python

  ```python
  command = input("What are you doing next? ")

  match command:
      case "quit":
          print("Goodbye!")
      case "look":
          print("Looking out")
      case "up" | "down":
          print("up or down")
      case _:
          print("The default")

  """
  What are you doing next? up
  up or down
  """

  match command.split():
      case ["go", "left"]:
          print("go left")
      case ["go", ("fast" | "slow")]:
          print("go fast or slow")

  point = (3, 3)
  match point:
      case (x, y) if x == y:
          print(f"The point is located on the diagonal Y=X at {x}.")
      case (x, y):
          print(f"Point is not on the diagonal.")
  """
  The point is located on the diagonal Y=X at 3.
  """
  ```

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

  # Match statements in Python

  > A `match` statement is the equivalent of a switch or case statement in Python

  ```python
  command = input("What are you doing next? ")

  match command:
      case "quit":
          print("Goodbye!")
      case "look":
          print("Looking out")
      case "up" | "down":
          print("up or down")
      case _:
          print("The default")

  """
  What are you doing next? up
  up or down
  """

  match command.split():
      case ["go", "left"]:
          print("go left")
      case ["go", ("fast" | "slow")]:
          print("go fast or slow")

  point = (3, 3)
  match point:
      case (x, y) if x == y:
          print(f"The point is located on the diagonal Y=X at {x}.")
      case (x, y):
          print(f"Point is not on the diagonal.")
  """
  The point is located on the diagonal Y=X at 3.
  """
  ```


path: zk/Matter_and_atoms.md
sortable_path: zkMatter_and_atoms.md
title: Matter and atoms
lead: ## Matter, elements, compounds and mixtures
checksum: 3f5628d93b77e4acadfd3a283c09d933482db0d3e12f6dda0d3487836d3b4174
created: 2024-03-30 09:03:20.185694027+00:00
modified: 2024-05-09 06:45:47.437065198+00:00
metadata: {"tags":["physics"]}
body:
  ## Matter, elements, compounds and mixtures

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

  ## Atoms and molecules

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

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



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

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

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

  ### Atomic number

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

  ### Atomic weight

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

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

  # Matter and atoms

  ## Matter, elements, compounds and mixtures

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

  ## Atoms and molecules

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

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



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

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

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

  ### Atomic number

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

  ### Atomic weight

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


path: zk/Memory.md
sortable_path: zkMemory.md
title: Memory
lead: ## Why do we need memory?
checksum: 970391d3e1e1ada268e6add602bf3aea5608e73902d1c3aca2bfb344b2829180
created: 2024-03-30 09:03:20.199851706+00:00
modified: 2024-06-15 10:24:26.003478772+00:00
metadata: {"tags":["memory","computer-architecture"]}
body:
  ## Why do we need memory?

  > A CPU is just an operator on memory. It reads its instructions and data from
  > the memory and writes back out to the memory. (Ward 2021)

  When a [CPU](CPU_architecture.md) executes a program,
  it needs a place to store the program's **instructions** and **related data**.
  This is the role of memory.

  ## What is memory?

  The data that comprises a program is a series of bits. The basic unit of memory
  storage is a **memory cell**: a circuit that can store a single bit.

  ## Memory types

  There are two types of memory: SRAM and DRAM. Both types of RAM memory are
  _volatile_ : the memory is only retained whilst the computer has a power supply
  and is wiped when the computer is rebooted. This contrasts with the memory of
  the harddisk which is non-volatile and is retained after a reboot.

  Programs that are executing are loaded into memory because the chips that
  comprise memory can read and store data much faster than the harddisk. It would
  be possible to run a program from the harddisk but it would be 500 - 1000 times
  slower than memory.

  #### DRAM

  DRAM uses capacitors to create the memory cell:

  > a **capacitor** is an electronic component that stores electrical energy in an
  > electrical field. A device which can accumulate and release electrical charge.

  In a DRAM cell, each bit of data is stored as a charge in a capacitor. The
  presence of charge represents a '1' bit and the absence of charge represents a
  '0' bit. Each of these cells is paired with a
  [transistor](Transistors.md) that
  controls the reading and writing of data.

  However capacitors lose
  [charge](Current.md) over time due
  to leaks. As a result DRAM is memory that needs to be refreshed (recharged)
  frequently. For this reason and because it only uses one transistor and
  capacitor per bit, DRAM is the less expensive form of volatile memory.

  #### SRAM

  SRAM (Static Random Access Memory) is also volatile memory but its electronical
  implementation is different. Unlike DRAM it doesn't use capacitors. Consequently
  the transistors do not leak or need to be refreshed, hence why SRAM is _static_
  and DRAM is _dynamic_.

  SRAM uses [flip flops](Flip_flops.md)
  to store the bits. It also uses multiple transistors per bit. This makes it
  faster than DRAM but more expensive. DRAM is at least ten times slower than
  SRAM.

  ## The role of memory in computation

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

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

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

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

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

  ## CPU register and cache memory

  As partly indicated in the diagram above, the CPU has its own memory in the form
  of registers and cache memory.

  Registers are a form of memory that are positioned on the same chip as the CPU.
  They are very fast but can only store a small amount of data. They are used to
  store the results of calculations and the addresses of the next instructions to
  be processed.

  The cache is SRAM memory that is separate from the DRAM memory which comprises
  the main memory. It exists in order to boost perfomance when executing the
  read/request cycles of the steps detailed above.

  There are two types of cache memory:

  - L1 cache
    - Situated on the CPU chip itself
    - Fastest to access but stores less
  - L2 cache
    - Situated outside of the CPU on its own chip
    - Slower to access than L1 but can store more data

  The L1 cache is the fastest since the data has less distance to travel when
  moving to and from the CPU. This said, the L2 cache is still very fast when
  compared to the main memory, both because it is SRAM rather than DRAM and
  because it is closer to the processor than the main memory.

  Cache controllers use complex algorithms to determine what should go into the
  cache to facilitate the best performance, but generally they work on the
  principle that what has been previously used by the CPU will be requested again
  soon. If the CPU has just asked for an instruction at memory location 555 it's
  very likely that it will next ask for the one at 556, and after that the one at
  557 and so on. The cache's controller circuits therefore go ahead and fetch
  these from slow DRAM to fast SRAM.

  ## The memory hierarchy

  The diagram below compares the different forms of memory within a computing
  device in terms of speed, monetary cost and capacity:

  ![](/img/Memory-Hierarchy.jpg)

  ## Memory addresses

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

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

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

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

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

  ### Demonstration

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

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

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

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

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

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

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

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

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

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

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

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

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

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

raw_content:
  ---
  tags:
    - memory
    - computer-architecture
  ---

  # Memory

  ## Why do we need memory?

  > A CPU is just an operator on memory. It reads its instructions and data from
  > the memory and writes back out to the memory. (Ward 2021)

  When a [CPU](CPU_architecture.md) executes a program,
  it needs a place to store the program's **instructions** and **related data**.
  This is the role of memory.

  ## What is memory?

  The data that comprises a program is a series of bits. The basic unit of memory
  storage is a **memory cell**: a circuit that can store a single bit.

  ## Memory types

  There are two types of memory: SRAM and DRAM. Both types of RAM memory are
  _volatile_ : the memory is only retained whilst the computer has a power supply
  and is wiped when the computer is rebooted. This contrasts with the memory of
  the harddisk which is non-volatile and is retained after a reboot.

  Programs that are executing are loaded into memory because the chips that
  comprise memory can read and store data much faster than the harddisk. It would
  be possible to run a program from the harddisk but it would be 500 - 1000 times
  slower than memory.

  #### DRAM

  DRAM uses capacitors to create the memory cell:

  > a **capacitor** is an electronic component that stores electrical energy in an
  > electrical field. A device which can accumulate and release electrical charge.

  In a DRAM cell, each bit of data is stored as a charge in a capacitor. The
  presence of charge represents a '1' bit and the absence of charge represents a
  '0' bit. Each of these cells is paired with a
  [transistor](Transistors.md) that
  controls the reading and writing of data.

  However capacitors lose
  [charge](Current.md) over time due
  to leaks. As a result DRAM is memory that needs to be refreshed (recharged)
  frequently. For this reason and because it only uses one transistor and
  capacitor per bit, DRAM is the less expensive form of volatile memory.

  #### SRAM

  SRAM (Static Random Access Memory) is also volatile memory but its electronical
  implementation is different. Unlike DRAM it doesn't use capacitors. Consequently
  the transistors do not leak or need to be refreshed, hence why SRAM is _static_
  and DRAM is _dynamic_.

  SRAM uses [flip flops](Flip_flops.md)
  to store the bits. It also uses multiple transistors per bit. This makes it
  faster than DRAM but more expensive. DRAM is at least ten times slower than
  SRAM.

  ## The role of memory in computation

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

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

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

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

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

  ## CPU register and cache memory

  As partly indicated in the diagram above, the CPU has its own memory in the form
  of registers and cache memory.

  Registers are a form of memory that are positioned on the same chip as the CPU.
  They are very fast but can only store a small amount of data. They are used to
  store the results of calculations and the addresses of the next instructions to
  be processed.

  The cache is SRAM memory that is separate from the DRAM memory which comprises
  the main memory. It exists in order to boost perfomance when executing the
  read/request cycles of the steps detailed above.

  There are two types of cache memory:

  - L1 cache
    - Situated on the CPU chip itself
    - Fastest to access but stores less
  - L2 cache
    - Situated outside of the CPU on its own chip
    - Slower to access than L1 but can store more data

  The L1 cache is the fastest since the data has less distance to travel when
  moving to and from the CPU. This said, the L2 cache is still very fast when
  compared to the main memory, both because it is SRAM rather than DRAM and
  because it is closer to the processor than the main memory.

  Cache controllers use complex algorithms to determine what should go into the
  cache to facilitate the best performance, but generally they work on the
  principle that what has been previously used by the CPU will be requested again
  soon. If the CPU has just asked for an instruction at memory location 555 it's
  very likely that it will next ask for the one at 556, and after that the one at
  557 and so on. The cache's controller circuits therefore go ahead and fetch
  these from slow DRAM to fast SRAM.

  ## The memory hierarchy

  The diagram below compares the different forms of memory within a computing
  device in terms of speed, monetary cost and capacity:

  ![](/img/Memory-Hierarchy.jpg)

  ## Memory addresses

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

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

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

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

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

  ### Demonstration

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

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

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

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

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

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

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

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

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

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

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

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

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

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


path: zk/Memory_leaks.md
sortable_path: zkMemory_leaks.md
title: Memory leaks
checksum: a4d5c6a8df018b82e31f5ba1746250ef20d36dca5a43b5a7d7e425cf5ab4db22
created: 2024-03-30 09:03:20.234256158+00:00
modified: 2024-04-16 06:25:37.254359454+00:00
metadata: {"tags":["memory"]}
lead:
  A memory leak occurs when you program memory and forget to delete it once you
  are finished. The consequences of memory leak is that it reduces the performance
  of the computer by reducing the amount of available memory. Eventually, in the
  worst case, too much of the available memory may become allocated and all or
  part of the system or device stops working correctly, the application fails, or
  the system slows down vastly.

body:
  A memory leak occurs when you program memory and forget to delete it once you
  are finished. The consequences of memory leak is that it reduces the performance
  of the computer by reducing the amount of available memory. Eventually, in the
  worst case, too much of the available memory may become allocated and all or
  part of the system or device stops working correctly, the application fails, or
  the system slows down vastly.

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

  # Memory leaks

  A memory leak occurs when you program memory and forget to delete it once you
  are finished. The consequences of memory leak is that it reduces the performance
  of the computer by reducing the amount of available memory. Eventually, in the
  worst case, too much of the available memory may become allocated and all or
  part of the system or device stops working correctly, the application fails, or
  the system slows down vastly.


path: zk/Messaging_services.md
sortable_path: zkMessaging_services.md
title: AWS Messaging services
lead: ## SQS
checksum: eb8a75e05ac26b869762b86506c063c553af24181b6b7f0d27f7d451127bc5b8
created: 2024-03-30 09:03:20.238576676+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["AWS"]}
body:
  ## SQS

  > SQS: Simple Queue Service

  SQS is a service that allows you to send, store and receive messages between
  apps and software components built in AWS, with automatic encryption. It helps
  with decoupling and scaling.

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

  An example application of this would be to set up an SQS queue that receives
  messages and triggers a lambda whenever a new message is added.

  ## SNS

  > SNS: Simple Notification Service

  Similar to SQS but the focus is on notifications rather than messages, i.e
  events that fire when something specific happens, not just a message-send event.
  It can be used for passing notifications between applications or to persons
  through SMS, text, push notifications and email.

  **SNS pushes messages out to subscribers while SQS stores the messages until
  someone reads them.**

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

  # AWS Messaging services

  ## SQS

  > SQS: Simple Queue Service

  SQS is a service that allows you to send, store and receive messages between
  apps and software components built in AWS, with automatic encryption. It helps
  with decoupling and scaling.

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

  An example application of this would be to set up an SQS queue that receives
  messages and triggers a lambda whenever a new message is added.

  ## SNS

  > SNS: Simple Notification Service

  Similar to SQS but the focus is on notifications rather than messages, i.e
  events that fire when something specific happens, not just a message-send event.
  It can be used for passing notifications between applications or to persons
  through SMS, text, push notifications and email.

  **SNS pushes messages out to subscribers while SQS stores the messages until
  someone reads them.**


path: zk/Mixed_and_improper_fractions.md
sortable_path: zkMixed_and_improper_fractions.md
title: Mixed and improper fractions
lead: ## Improper fractions
checksum: 591035861210f541355bffec2836d38c935d09f90152f5328479bf5fca7cd409
created: 2024-03-30 09:03:20.260272834+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra","fractions"]}
body:
  ## Improper fractions

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

  ## Mixed fractions

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

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

  ## Converting mixed fractions into improper fractions

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

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

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

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

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

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

  The procedure is as follows:

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

  Thus:

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

  ## Converting improper fractions into mixed fractions

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

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

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

  ## Multiplying and dividing by mixed fractions

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

  ### Demonstration of multiplication

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

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

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

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

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

  3. Then simplify:

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

  4. Finally, convert back into a mixed fraction:

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

  ## Demonstration of division

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

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

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

  ## Adding and subtracting mixed fractions

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

  # Mixed and improper fractions

  ## Improper fractions

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

  ## Mixed fractions

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

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

  ## Converting mixed fractions into improper fractions

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

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

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

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

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

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

  The procedure is as follows:

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

  Thus:

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

  ## Converting improper fractions into mixed fractions

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

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

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

  ## Multiplying and dividing by mixed fractions

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

  ### Demonstration of multiplication

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

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

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

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

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

  3. Then simplify:

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

  4. Finally, convert back into a mixed fraction:

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

  ## Demonstration of division

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

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

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

  ## Adding and subtracting mixed fractions


path: zk/Module_pattern.md
sortable_path: zkModule_pattern.md
title: Module pattern
lead: > Come back and compare this with learning from Node.js
checksum: d667727226143e5e85a1f24373d72e77864e183a5b286cf980447b711f112e10
created: 2024-03-30 09:03:20.283471991+00:00
modified: 2024-06-16 17:16:22.40540396+00:00
metadata: {"tags":["OOP","design-patterns"]}
body:
  > Come back and compare this with learning from Node.js

  With the module design pattern we create encapsulation: the variables and
  functions (as methods) are kept private inside the module and cannot be
  overwritten. This design pattern is familiar from Node.js development: every
  package you import into your project and download from NPM is a module.

  Generally you will create the module as a class, import it and then instantiate
  a new instance. However for private development, you could just as well use an
  object and duplicate it with `Object.create`.

  ## Example

  Here is an example of a simple module that returns the age of a person.

  ```js
  export default class Age {
    constructor(name, birthYear) {
      this.name = name;
      this.birthYear = birthYear;
    }
    currentYear() {
      return new Date().getFullYear();
    }
    get age() {
      return this.currentYear() - this.birthYear;
    }
  }
  ```

  ```js
  const martha = new Age("Martha", 1997);
  console.log(martha.age); // 24
  ```

  ## Controlling access

  In the example above, `aValue` could be edited in instantiations of the class.
  Given that modules should not be overwrittable, you could make it a private
  property on the class. The benefit of getters and setters is that they dictate
  what can be modified and retrieved from the module. So if you use `get` and
  `set` you can prevent overwrites.

  ## Object modules

  If you want to use an object instead of a class, you have to take greater care
  to ensure that the objects are not overwritable. **Also you cannot use the `#`
  modifier to make properties private.**

  - Use getters and setters for updating and retrieving values
  - Use `Object.seal` to prevent changes to the parent object
  - Instead of using getters and setters, for individual properties you can set
    `writable` to be `false` for properties that you don't want changed

  There are examples of each in the following:

  ```jsx
  export const age = {
    name: "",
    birthYear: new Number(),
    currentYear() {
      return new Date().getFullYear();
    },
    get age() {
      return this.currentYear() - this.birthYear;
    },
  };

  Object.seal(age);
  Object.defineProperty(age, "aValue", {
    value: 6,
    writable: false,
  });
  ```

raw_content:
  ---
  tags:
    - OOP
    - design-patterns
  ---

  # Module pattern

  > Come back and compare this with learning from Node.js

  With the module design pattern we create encapsulation: the variables and
  functions (as methods) are kept private inside the module and cannot be
  overwritten. This design pattern is familiar from Node.js development: every
  package you import into your project and download from NPM is a module.

  Generally you will create the module as a class, import it and then instantiate
  a new instance. However for private development, you could just as well use an
  object and duplicate it with `Object.create`.

  ## Example

  Here is an example of a simple module that returns the age of a person.

  ```js
  export default class Age {
    constructor(name, birthYear) {
      this.name = name;
      this.birthYear = birthYear;
    }
    currentYear() {
      return new Date().getFullYear();
    }
    get age() {
      return this.currentYear() - this.birthYear;
    }
  }
  ```

  ```js
  const martha = new Age("Martha", 1997);
  console.log(martha.age); // 24
  ```

  ## Controlling access

  In the example above, `aValue` could be edited in instantiations of the class.
  Given that modules should not be overwrittable, you could make it a private
  property on the class. The benefit of getters and setters is that they dictate
  what can be modified and retrieved from the module. So if you use `get` and
  `set` you can prevent overwrites.

  ## Object modules

  If you want to use an object instead of a class, you have to take greater care
  to ensure that the objects are not overwritable. **Also you cannot use the `#`
  modifier to make properties private.**

  - Use getters and setters for updating and retrieving values
  - Use `Object.seal` to prevent changes to the parent object
  - Instead of using getters and setters, for individual properties you can set
    `writable` to be `false` for properties that you don't want changed

  There are examples of each in the following:

  ```jsx
  export const age = {
    name: "",
    birthYear: new Number(),
    currentYear() {
      return new Date().getFullYear();
    },
    get age() {
      return this.currentYear() - this.birthYear;
    },
  };

  Object.seal(age);
  Object.defineProperty(age, "aValue", {
    value: 6,
    writable: false,
  });
  ```


path: zk/Module_wrapping_at_runtime.md
sortable_path: zkModule_wrapping_at_runtime.md
title: The Module Wrapper Function
checksum: fd32d3962325ba5c9693d32eb85052d24db6f81d4a181e4a0b5eed8718572d43
created: 2024-03-30 09:03:20.287849942+00:00
modified: 2024-06-16 17:16:31.062266779+00:00
metadata: {"tags":["node-js"]}
lead:
  When Node runs each of our module files are wrapped within an
  immediately-invoked function expression that has the following parameters:

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

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

  ```

  This is called the **module wrapper function**

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

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

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

  ## The Module Wrapper Function

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

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

  ```

  This is called the **module wrapper function**

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

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


path: zk/Monitoring_processes_and_resources.md
sortable_path: zkMonitoring_processes_and_resources.md
title: Monitoring processes and resources
lead: ## General purpose diagnostic programs (memory, CPU, I/O)
checksum: aba8ca7ab10e1624e6a830b9a98e2479627e63943d7a9232d53c77d167fbfbe4
created: 2024-03-30 09:03:20.303803846+00:00
modified: 2024-06-16 17:35:20.956928713+00:00
metadata: {"tags":["systems-programming","Linux","procedural"]}
body:
  ## General purpose diagnostic programs (memory, CPU, I/O)

  ### `top`/`htop`

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

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

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

  ![](/img/htop.png)

  ### Main commands

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

  ### Understanding the categories

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

  ### `vmstat`

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

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

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

  ```

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

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

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

  ![](/img/lsof.png)

  ## System calls: `strace`

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

  ## CPU performance

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

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

  `Uptime` gives you three load averages:

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

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

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

  ## Memory status

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

  ### System page size

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

  ```bash
  $ getconf PAGE_SIZE
  4096
  ```

  This will typically be the same for all Linux systems.

  ### `free` : available physical memory

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

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

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

  # Monitoring processes and resources

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

  ### `top`/`htop`

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

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

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

  ![](/img/htop.png)

  ### Main commands

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

  ### Understanding the categories

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

  ### `vmstat`

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

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

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

  ```

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

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

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

  ![](/img/lsof.png)

  ## System calls: `strace`

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

  ## CPU performance

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

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

  `Uptime` gives you three load averages:

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

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

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

  ## Memory status

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

  ### System page size

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

  ```bash
  $ getconf PAGE_SIZE
  4096
  ```

  This will typically be the same for all Linux systems.

  ### `free` : available physical memory

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

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


path: zk/Morgan.md
sortable_path: zkMorgan.md
title: Morgan
lead: Morgan is middleware that is used to log HTTP requests to the Express instance.
checksum: 26ede6ed4dc04da810194afc819dca34945f17243a0803689e91c1a03c4c2568
created: 2024-03-30 09:03:20.359592551+00:00
modified: 2024-06-15 09:50:31.790011694+00:00
metadata: {"tags":["node-js","middleware"]}
body:
  Morgan is middleware that is used to log HTTP requests to the Express instance.

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

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

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

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

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

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

  # Morgan

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

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

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

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

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

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


path: zk/Motherboard.md
sortable_path: zkMotherboard.md
title: Motherboard
lead: <img src="../img/motherboard-pi.jpg" width="400px"/>
checksum: 0b164f3be04afb2fabe087cf47088189f48663b1e63986bb463fa53d9e6680cf
created: 2024-03-30 09:03:20.364422156+00:00
modified: 2024-06-15 10:24:33.773630215+00:00
metadata: {"tags":["hardware","computer-architecture"]}
body:
  <img src="../img/motherboard-pi.jpg" width="400px"/>

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

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

raw_content:
  ---
  tags:
    - hardware
    - computer-architecture
  ---

  # Motherboard

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

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

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


path: zk/Multiple_pointers.md
sortable_path: zkMultiple_pointers.md
title: Multiple pointers
checksum: c247415db8592dc1c4b8fca26b76001b0c53636ef5d2ef739c91ce609c3f8618
created: 2024-03-30 09:03:20.37853018+00:00
modified: 2024-06-16 17:16:43.182545862+00:00
metadata: {"tags":["design-patterns"]}
lead:
  The multiple pointers pattern can be useful for moving through an array and
  comparing elements against each other in a time-efficient manner and without
  costly multiple loops.

body:
  The multiple pointers pattern can be useful for moving through an array and
  comparing elements against each other in a time-efficient manner and without
  costly multiple loops.

  ## Example: moving pairs

  In this example we shuffle through an array creating a pair from each element
  encountered. Thus for an array such as `[4, 3, 2, 1, 3, 6, 7]` we would return
  `[ [ 4, 3 ], [ 3, 2 ], [ 2, 1 ], [ 1, 3 ], [ 3, 6 ], [ 6, 7 ] ]`.

  ```js
  function movingPairs(arr) {
    const store = [];
    let left = 0;
    let right = 1;
    for (left; left < arr.length - right; left++) {
      store.push([arr[left], arr[right + left]]);
    }
    return store;
  }
  ```

  ## Example: identify unique values

  An example application of the above pattern would be identifying the number of
  unique values in an array that contains duplicates. One way to do this in
  JavaScript would be to use a Set however this is a more generic approach that
  only requires a single loop:

  ```js
  function uniqueValues(arr) {
    if (!arr.length) {
      return 0;
    } else {
      let count = 1;
      let left = 0;
      let right = 1;
      for (right; right < arr.length; right++) {
        if (arr[left] !== arr[right]) {
          count++;
        }
        left++;
      }

      return count;
    }
  }
  ```

  This works by having two pointers at either end of the array; one moving from
  the right and one moving from the left. Whilst the right pointer is less than
  the array length, the left pointer is incremented as the right moves closer to
  it. At each incrementation, the values of pointers (used as indexes) are
  compared for uniqueness, and a count is kept.

  ## Example: identify duplicates

  This is very similar to the above but this time we are looking for duplicates
  rather than unique values.

  ```js
  function areThereDuplicatesPointer(...input) {
    let start = 0;
    let next = 1;
    while (next < input.length) {
      let same = input[start] === input[next];
      if (same) {
        return true;
      }
      start++;
      next++;
    }

    return false;
  }
  console.log(areThereDuplicatesPointer(1, 2, 3)); // false
  console.log(areThereDuplicatesPointer(1, 2, 2)); // true
  ```

raw_content:
  ---
  tags:
    - design-patterns
  ---

  # Multiple pointers

  The multiple pointers pattern can be useful for moving through an array and
  comparing elements against each other in a time-efficient manner and without
  costly multiple loops.

  ## Example: moving pairs

  In this example we shuffle through an array creating a pair from each element
  encountered. Thus for an array such as `[4, 3, 2, 1, 3, 6, 7]` we would return
  `[ [ 4, 3 ], [ 3, 2 ], [ 2, 1 ], [ 1, 3 ], [ 3, 6 ], [ 6, 7 ] ]`.

  ```js
  function movingPairs(arr) {
    const store = [];
    let left = 0;
    let right = 1;
    for (left; left < arr.length - right; left++) {
      store.push([arr[left], arr[right + left]]);
    }
    return store;
  }
  ```

  ## Example: identify unique values

  An example application of the above pattern would be identifying the number of
  unique values in an array that contains duplicates. One way to do this in
  JavaScript would be to use a Set however this is a more generic approach that
  only requires a single loop:

  ```js
  function uniqueValues(arr) {
    if (!arr.length) {
      return 0;
    } else {
      let count = 1;
      let left = 0;
      let right = 1;
      for (right; right < arr.length; right++) {
        if (arr[left] !== arr[right]) {
          count++;
        }
        left++;
      }

      return count;
    }
  }
  ```

  This works by having two pointers at either end of the array; one moving from
  the right and one moving from the left. Whilst the right pointer is less than
  the array length, the left pointer is incremented as the right moves closer to
  it. At each incrementation, the values of pointers (used as indexes) are
  compared for uniqueness, and a count is kept.

  ## Example: identify duplicates

  This is very similar to the above but this time we are looking for duplicates
  rather than unique values.

  ```js
  function areThereDuplicatesPointer(...input) {
    let start = 0;
    let next = 1;
    while (next < input.length) {
      let same = input[start] === input[next];
      if (same) {
        return true;
      }
      start++;
      next++;
    }

    return false;
  }
  console.log(areThereDuplicatesPointer(1, 2, 3)); // false
  console.log(areThereDuplicatesPointer(1, 2, 2)); // true
  ```


path: zk/Multiplexers_and_demultiplexers.md
sortable_path: zkMultiplexers_and_demultiplexers.md
title: Multiplexers (MUX) and demultiplexers (DMUX)
lead: ## Multiplexer
checksum: 0323d69672331653828e0e1eb7e963a330e2e4b67995bc169955b8bc65148eb7
created: 2024-03-30 09:03:20.382660339+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["logic-gates","binary","nand-to-tetris"]}
body:
  ## Multiplexer

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

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

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

  ![](/img/MUX.png)

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

  ### Programable gates

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

  ## Demultiplexer

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

  ![](/img/DMUX.png)

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

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

  # Multiplexers (MUX) and demultiplexers (DMUX)

  ## Multiplexer

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

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

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

  ![](/img/MUX.png)

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

  ### Programable gates

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

  ## Demultiplexer

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

  ![](/img/DMUX.png)

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


path: zk/Multiplicative_identity.md
sortable_path: zkMultiplicative_identity.md
title: The Property of Multiplicative 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 \cdot 1 = a $$

checksum: 52f24c40c400082277ab21ab5b035d11337ff52976576ef802044d363d17c6d0
created: 2024-03-30 09:03:20.386291903+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra","theorems"]}
raw_content:
  ---
  tags:
    - prealgebra
    - theorems
  ---

  # The Property of Multiplicative Identity

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

  $$ a \cdot 1 = a $$


path: zk/Multiplicative_property_of_negative_one.md
sortable_path: zkMultiplicative_property_of_negative_one.md
title: The Multiplicative Property of Negative One
lead: > **Let $a$ represent any member of $\mathbb{Z}$, then:**
body:
  > **Let $a$ represent any member of $\mathbb{Z}$, then:**

  $$ (-1) \cdot a = -a $$

checksum: 3ea5a89ea5f9b12ba31084f176279eb700d16beb03d37584492c50100ba5a991
created: 2024-03-30 09:03:20.390062084+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra","theorems"]}
raw_content:
  ---
  tags:
    - prealgebra
    - theorems
  ---

  # The Multiplicative Property of Negative One

  > **Let $a$ represent any member of $\mathbb{Z}$, then:**

  $$ (-1) \cdot a = -a $$


path: zk/Multiplying_fractions.md
sortable_path: zkMultiplying_fractions.md
title: Multiplying fractions
checksum: e693fb7d5cd07d76626f4e31e791a7cbb8bbd2effeb8cfe3169c7f2999b58136
created: 2024-03-30 09:03:20.39289207+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra","fractions","arithmetic"]}
lead:
  To find the product of two fractions $\frac{a}{b}$ and $\frac{c}{d}$ multiply
  their numerators and denominators and then reduce:

body:
  To find the product of two fractions $\frac{a}{b}$ and $\frac{c}{d}$ multiply
  their numerators and denominators and then reduce:

  $$\frac{a}{b} \cdot \frac{c}{d} = \frac{a \cdot c}{b \cdot d}$$

  ### Example

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

  ## Prime factorisation in place

  The example above did not require a reduction, so here is a more complex
  example:

  $$
  \frac{14}{15} \cdot \frac{30}{140} = \frac{420}{2100}
  $$

  It would be laborious to reduce such a large product using factor trees or the
  repeated application of divisors, as defined in
  [reducing fractions](Reducing_fractions.md). We can use a more efficient
  method. This method can be applied at the point at which we conduct the
  multiplication rather than afterwards once we have the product. We express the
  the initial multiplicands as prime factors:

  $$
  \frac{14}{15} \cdot \frac{30}{140} = \frac{(2 \cdot 7) \cdot (2 \cdot 3 \cdot 5) }{(3 \cdot 5) \cdot (2 \cdot  2 \cdot 7 \cdot 5)}
  $$

  We now have the product in factorised form before we have applied the
  multiplication so we can go ahead and cancel:

  $$
  \frac{\cancel{2}, \cancel{7}, \cancel{2}, \cancel{3}, \cancel{5}}{\cancel{3}, \cancel{5}, \cancel{2}, \cancel{2}, \cancel{7}, 5} = \frac{1}{5}
  $$

  **Note that in the above case, there was only a single 5 left as a denominator
  and no value left as a numerator. This is equivalent to there just being "one
  five" so we write $\frac{1}{5}$**

  ## Example of multiplying fractions with negative fractions containing variables

  Calculate: $$- \frac{6x}{55y} \cdot - \frac{110y^2}{105x^2}$$

  First multiply in place:

  $$
  \frac{(3 \cdot 2 \cdot x) \cdot (5  \cdot 2 \cdot 11 \cdot y \cdot y)}{(5 \cdot 11 \cdot y) \cdot (7 \cdot 5 \cdot 3 \cdot x \cdot x)}
  $$

  Then cancel:

  $$
  \frac{(\cancel{3} \cdot 2 \cdot \cancel{x}) \cdot (\cancel{5}  \cdot 2 \cdot \cancel{11} \cdot \cancel{y} \cdot y)}{(\cancel{5} \cdot \cancel{11} \cdot \cancel{y}) \cdot (7 \cdot 5 \cdot \cancel{3} \cdot \cancel{x} \cdot x)} =
  \frac{2  \cdot 2 \cdot y}{7 \cdot 5 \cdot x}
  $$

  Then reduce:

  $$
  \frac{2  \cdot 2 \cdot y}{7 \cdot 5 \cdot x} = \frac{4y}{35x}
  $$

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

  # Multiplying fractions

  To find the product of two fractions $\frac{a}{b}$ and $\frac{c}{d}$ multiply
  their numerators and denominators and then reduce:

  $$\frac{a}{b} \cdot \frac{c}{d} = \frac{a \cdot c}{b \cdot d}$$

  ### Example

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

  ## Prime factorisation in place

  The example above did not require a reduction, so here is a more complex
  example:

  $$
  \frac{14}{15} \cdot \frac{30}{140} = \frac{420}{2100}
  $$

  It would be laborious to reduce such a large product using factor trees or the
  repeated application of divisors, as defined in
  [reducing fractions](Reducing_fractions.md). We can use a more efficient
  method. This method can be applied at the point at which we conduct the
  multiplication rather than afterwards once we have the product. We express the
  the initial multiplicands as prime factors:

  $$
  \frac{14}{15} \cdot \frac{30}{140} = \frac{(2 \cdot 7) \cdot (2 \cdot 3 \cdot 5) }{(3 \cdot 5) \cdot (2 \cdot  2 \cdot 7 \cdot 5)}
  $$

  We now have the product in factorised form before we have applied the
  multiplication so we can go ahead and cancel:

  $$
  \frac{\cancel{2}, \cancel{7}, \cancel{2}, \cancel{3}, \cancel{5}}{\cancel{3}, \cancel{5}, \cancel{2}, \cancel{2}, \cancel{7}, 5} = \frac{1}{5}
  $$

  **Note that in the above case, there was only a single 5 left as a denominator
  and no value left as a numerator. This is equivalent to there just being "one
  five" so we write $\frac{1}{5}$**

  ## Example of multiplying fractions with negative fractions containing variables

  Calculate: $$- \frac{6x}{55y} \cdot - \frac{110y^2}{105x^2}$$

  First multiply in place:

  $$
  \frac{(3 \cdot 2 \cdot x) \cdot (5  \cdot 2 \cdot 11 \cdot y \cdot y)}{(5 \cdot 11 \cdot y) \cdot (7 \cdot 5 \cdot 3 \cdot x \cdot x)}
  $$

  Then cancel:

  $$
  \frac{(\cancel{3} \cdot 2 \cdot \cancel{x}) \cdot (\cancel{5}  \cdot 2 \cdot \cancel{11} \cdot \cancel{y} \cdot y)}{(\cancel{5} \cdot \cancel{11} \cdot \cancel{y}) \cdot (7 \cdot 5 \cdot \cancel{3} \cdot \cancel{x} \cdot x)} =
  \frac{2  \cdot 2 \cdot y}{7 \cdot 5 \cdot x}
  $$

  Then reduce:

  $$
  \frac{2  \cdot 2 \cdot y}{7 \cdot 5 \cdot x} = \frac{4y}{35x}
  $$


path: zk/Mutations_with_Apollo_Client.md
sortable_path: zkMutations_with_Apollo_Client.md
title: Mutations with Apollo Client
lead: Queries are read-only operations. Mutations are write-only operations.
checksum: 1de70df0fe706eb5b212250f1aa7a3596e9b54cfc0587fcf729631b096a69d60
created: 2024-03-30 09:03:20.40307229+00:00
modified: 2024-06-24 13:10:28.736753145+00:00
metadata: {"tags":["graphql"]}
body:
  Queries are read-only operations. Mutations are write-only operations.

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

  ## Naming convention

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

  For example `addSpaceCat(){}`

  ## Demonstration mutation

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

  ## Updating the schema

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


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

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

  ## Updating the data source

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

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

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

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

  ## Adding resolver

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

  ### Success case

  As always we match the shape of the schema:

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

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

  ### Error case

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

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

  ```js
  const resolvers = {

      Query: {
          // ...query resolvers
      }

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

  ## The `useMutation` hook

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

  As with queries and
  [query constants](Apollo_Client.md#query-constants) we
  wrap our mutation in a `gql` template string:

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

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

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

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

  `useMutation` returns an array of two elements:

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

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

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

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

raw_content:
  ---
  tags: [graphql]
  ---
  	
  # Mutations with Apollo Client

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

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

  ## Naming convention

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

  For example `addSpaceCat(){}`

  ## Demonstration mutation

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

  ## Updating the schema

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


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

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

  ## Updating the data source

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

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

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

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

  ## Adding resolver

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

  ### Success case

  As always we match the shape of the schema:

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

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

  ### Error case

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

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

  ```js
  const resolvers = {

      Query: {
          // ...query resolvers
      }

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

  ## The `useMutation` hook

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

  As with queries and
  [query constants](Apollo_Client.md#query-constants) we
  wrap our mutation in a `gql` template string:

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

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

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

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

  `useMutation` returns an array of two elements:

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

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

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

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


path: zk/Naming_conventions.md
sortable_path: zkNaming_conventions.md
title: Python naming conventions
lead: ## Underscores
checksum: f491ff1235bf5bb1a424568b00d219bbb0760f8c2b82962ba863401322fbac87
created: 2024-03-30 09:03:20.408941584+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["python"]}
body:
  ## Underscores

  To name a variable or method with multiple words we use underscores to separate
  each word.

  ```python
  an_int = 32
  print(an_integer.is_integer) # true
  ```

  ## Constants

  There are no constants in Python. However there a pseudo-constants established
  by convention. To denote that a variable should not change you use `UPPER_CASE`.

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

  # Python naming conventions

  ## Underscores

  To name a variable or method with multiple words we use underscores to separate
  each word.

  ```python
  an_int = 32
  print(an_integer.is_integer) # true
  ```

  ## Constants

  There are no constants in Python. However there a pseudo-constants established
  by convention. To denote that a variable should not change you use `UPPER_CASE`.


path: zk/Natural_numbers.md
sortable_path: zkNatural_numbers.md
title: The set of natural numbers
lead: $$ \mathbb{N} = {1, 2, 3, ...} $$
checksum: e42ef90c0ecd54ecd903240e8eacb31b42de3577739ae5f5d31f5b0737ef78bc
created: 2024-03-30 09:03:20.412983069+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra"]}
body:
  $$ \mathbb{N} = {1, 2, 3, ...} $$

  Natural numbers are most simply expressed as **the set of numbers we use for
  counting**. The set of natural numbers start at one and continuity to infinity.

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

  ## The set of natural numbers

  $$ \mathbb{N} = {1, 2, 3, ...} $$

  Natural numbers are most simply expressed as **the set of numbers we use for
  counting**. The set of natural numbers start at one and continuity to infinity.


path: zk/Negation_Elimination.md
sortable_path: zkNegation_Elimination.md
title: Negation Elimination
checksum: 7d0d8a02311c15474ed12c7515ca6737cc8a23152c106636d6ba9197ad5c8596
created: 2024-03-30 09:03:20.417294279+00:00
modified: 2024-06-16 17:17:05.183065344+00:00
metadata: {"tags":["logic","propositional-logic"]}
lead:
  Like the [introduction](Negation_Introduction.md) rule for
  negation, the elimination rule also works by deriving a contradiction. It is
  basically _Negation Introduction_ in reverse. Instead of starting the sub-proof
  with a true proposition from which you derive a contradiction, you start with
  the negation of a proposition, derive a contradiction and then assert the
  positive of the negated proposition you started out with.

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

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

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

  # Negation Elimination

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

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


path: zk/Negation_Introduction.md
sortable_path: zkNegation_Introduction.md
title: Negation Introduction
checksum: d60e1513c026e4684b3285bf1af37e05e071a19a13e398ba5277532d38ee90cd
created: 2024-03-30 09:03:20.426980275+00:00
modified: 2024-06-16 17:17:11.559885611+00:00
metadata: {"tags":["logic","propositional-logic"]}
lead:
  This is also known as _proof by contradiction_. You start with an assumption
  declared in a sub-proof. If you can derive a contradiction from this assumption
  (typically from the introduction of another proposition and its negation), then
  you are permitted to derive the negation of the auxiliary assumption in the main
  proof.

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

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

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

  # Negation Introduction

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

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


path: zk/Negative_exponents.md
sortable_path: zkNegative_exponents.md
lead:
  ---
  tags: [algebra, exponents]
  ---

checksum: 83982af46d75ecee22726807728c9f8881c79e45e98165afb961368864727976
created: 2024-03-30 09:03:20.430766589+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["algebra","exponents"]}
body:
  ---
  tags: [algebra, exponents]
  ---

  When calculating the exponents of a negative number the answer will always will
  be positive:

  $$
  -5^2 = 25
  $$

  This confused me but it was because I was thinking of it in terms of
  $-5 \cdot 5$ when in fact it is $-5 \cdot -5$ and when two negative numbers are
  multiplied the product is always negative.

raw_content:
  ---
  tags: [algebra, exponents]
  ---

  When calculating the exponents of a negative number the answer will always will
  be positive:

  $$
  -5^2 = 25
  $$

  This confused me but it was because I was thinking of it in terms of
  $-5 \cdot 5$ when in fact it is $-5 \cdot -5$ and when two negative numbers are
  multiplied the product is always negative.


path: zk/Network_requests_in_Python.md
sortable_path: zkNetwork_requests_in_Python.md
title: Making network requests in Python
checksum: f3c9fa4c90fc5f9c3658fb0d8b0127b1faab85a129c65a5242078c2edc0dfb3a
created: 2024-03-30 09:03:20.434913949+00:00
modified: 2024-06-19 06:25:40.913024148+00:00
metadata: {"tags":["python","APIs","networks"]}
lead:
  We can use the `requests` package to make API requests to
  [RESTful](RESTful_APIs.md) resources and handle the data as JSON.

body:
  We can use the `requests` package to make API requests to
  [RESTful](RESTful_APIs.md) resources and handle the data as JSON.

  ```sh
  pip install requests
  ```

  Here is a basic architecture for making a `GET` request in Python.

  ```python
  import requests

  def get_data(url):
      response = requests.get(url)
      if response.status_code == 200:
          return response.json()
      else:
          raise Exception(
              f"Failed to fetch data from API. Status code: {response.status_code}")

  def main():
      url = "https://dummyjson.com/products/1"

      try:
          data = get_data(url)
          print(data)

      except Exception as e:
          prin(e)

  if __name__ == "__main__":
      main()
  ```

  ## Parsing returned data

  Running `main` returns:

  ```python
  {
     "id":1,
     "title":"iPhone 9",
     "description":"An apple mobile which is nothing like apple",
     "price":549,
     "discountPercentage":12.96,
     "rating":4.69,
     "stock":94,
     "brand":"Apple",
     "category":"smartphones",
     "thumbnail":"https://i.dummyjson.com/data/products/1/thumbnail.jpg",
     "images":[
        "https://i.dummyjson.com/data/products/1/1.jpg",
        "https://i.dummyjson.com/data/products/1/2.jpg",
        "https://i.dummyjson.com/data/products/1/3.jpg",
        "https://i.dummyjson.com/data/products/1/4.jpg",
        "https://i.dummyjson.com/data/products/1/thumbnail.jpg"
     ]
  }
  ```

  This is JSON but in Python is a [dictionary](Dictionaries_in_Python.md)

  We can use standard dictionary methods to handle the data. For example, we'll
  add to the existing `try` block:

  ```python
  example_key = "brand"  # Replace with the key you want to access from the JSON data
      if example_key in data:
          print(f"Value of '{example_key}':", data[example_key])
      else:
          print(f"'{example_key}' not found in the JSON data")
  ```

  Which, if successful, outputs:

  ```
  Value of 'brand': Apple
  ```

raw_content:
  ---
  tags:
    - python
    - APIs
    - networks
  ---

  # Making network requests in Python

  We can use the `requests` package to make API requests to
  [RESTful](RESTful_APIs.md) resources and handle the data as JSON.

  ```sh
  pip install requests
  ```

  Here is a basic architecture for making a `GET` request in Python.

  ```python
  import requests

  def get_data(url):
      response = requests.get(url)
      if response.status_code == 200:
          return response.json()
      else:
          raise Exception(
              f"Failed to fetch data from API. Status code: {response.status_code}")

  def main():
      url = "https://dummyjson.com/products/1"

      try:
          data = get_data(url)
          print(data)

      except Exception as e:
          prin(e)

  if __name__ == "__main__":
      main()
  ```

  ## Parsing returned data

  Running `main` returns:

  ```python
  {
     "id":1,
     "title":"iPhone 9",
     "description":"An apple mobile which is nothing like apple",
     "price":549,
     "discountPercentage":12.96,
     "rating":4.69,
     "stock":94,
     "brand":"Apple",
     "category":"smartphones",
     "thumbnail":"https://i.dummyjson.com/data/products/1/thumbnail.jpg",
     "images":[
        "https://i.dummyjson.com/data/products/1/1.jpg",
        "https://i.dummyjson.com/data/products/1/2.jpg",
        "https://i.dummyjson.com/data/products/1/3.jpg",
        "https://i.dummyjson.com/data/products/1/4.jpg",
        "https://i.dummyjson.com/data/products/1/thumbnail.jpg"
     ]
  }
  ```

  This is JSON but in Python is a [dictionary](Dictionaries_in_Python.md)

  We can use standard dictionary methods to handle the data. For example, we'll
  add to the existing `try` block:

  ```python
  example_key = "brand"  # Replace with the key you want to access from the JSON data
      if example_key in data:
          print(f"Value of '{example_key}':", data[example_key])
      else:
          print(f"'{example_key}' not found in the JSON data")
  ```

  Which, if successful, outputs:

  ```
  Value of 'brand': Apple
  ```


path: zk/Node_and_MySQL_db.md
sortable_path: zkNode_and_MySQL_db.md
title: Docker example: NodeJS backend with MySQL database
lead:
  We will utilise [Docker Compose](Docker_Compose.md) to combine
  two containers:

checksum: da18db716656813a6f25424eb45c5c29a3af3b45d1c3d300c401e507e2154925
created: 2024-03-30 09:03:20.446869632+00:00
modified: 2024-06-16 17:15:36.677747043+00:00
metadata: {"tags":["docker","SQL","node-js","databases"]}
body:
  We will utilise [Docker Compose](Docker_Compose.md) to combine
  two containers:

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

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

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

  ## Docker Compose file

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

  volumes:
    mysql-data:
  ```

  ## Dockerfile for the NodeJS backend

  ```Dockerfile
  # Dockerfile
  FROM node:14

  WORKDIR /usr/src/app

  COPY package*.json ./

  RUN npm install

  COPY . .

  EXPOSE 3000

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

  ## NodeJS project setup

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

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

  const app = express();

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

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

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

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

  To start up the environment you would then run:

  ```
  docker-compose -up
  ```

  ## Environments

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

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

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

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

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

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

  volumes:
    mysql-data:
  ```

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

  ### Development, staging, production environments

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

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

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

  For example, development and production:

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

  ```

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

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

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

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

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

raw_content:
  ---
  tags:
    - docker
    - SQL
    - node-js
    - databases
  ---

  # Docker example: NodeJS backend with MySQL database

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

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

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

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

  ## Docker Compose file

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

  volumes:
    mysql-data:
  ```

  ## Dockerfile for the NodeJS backend

  ```Dockerfile
  # Dockerfile
  FROM node:14

  WORKDIR /usr/src/app

  COPY package*.json ./

  RUN npm install

  COPY . .

  EXPOSE 3000

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

  ## NodeJS project setup

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

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

  const app = express();

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

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

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

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

  To start up the environment you would then run:

  ```
  docker-compose -up
  ```

  ## Environments

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

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

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

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

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

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

  volumes:
    mysql-data:
  ```

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

  ### Development, staging, production environments

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

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

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

  For example, development and production:

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

  ```

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

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

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

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

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


path: zk/Nodemon.md
sortable_path: zkNodemon.md
title: Nodemon
checksum: 223c81e0636c8f7c17244cad0b4e2dc871021f2954688aa7f70ee67f8a2d6bfb
created: 2024-03-30 09:03:20.460814283+00:00
modified: 2024-06-15 09:50:33.83338455+00:00
metadata: {"tags":["node-js"]}
lead:
  We don't want to have to restart the local server every time we make a change to
  our files. We can use `nodemon` instead of `node` when running our `index.js`
  file so that file-changes are immediately registered without the need for a
  restart.

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

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

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

  # Nodemon

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

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


path: zk/None_in_Python.md
sortable_path: zkNone_in_Python.md
title: None in Python
checksum: 185fdbfe796eb95a3ec84eab881a101129a444faade8c2bc76b831840d9c8a1b
created: 2024-03-30 09:03:20.477447631+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["python","data-types"]}
lead:
  `None` is not `null`, it is closer to `undefined` in JS. If you define a
  variable as `None`, the variable exists, it is just not yet defined.

body:
  `None` is not `null`, it is closer to `undefined` in JS. If you define a
  variable as `None`, the variable exists, it is just not yet defined.

  Using `None` is a pattern similar to using `let` in JS to name a variable and
  definine it later on.

  ```python
  temperature = None
  ```

  If we logged `temperature` it would give us `None` rather than a null pointer
  error.

  With None we can use `is None` and `is not None`, special predicates for working
  with `None` only. This is a akin to using `if (x !== undefined)` in TypeScript

  ```python
  winner = None
  print('winner:', winner)
  # winner: None
  print('winner is None:', winner is None)
  # winner is None: True
  print('winner is not None:', winner is not None)
  # winner is not None: False
  print(type(winner))
  # <class 'NoneType'>
  # Now set winner to be True
  print('Set winner to True')
  # Set winner to True
  winner = True
  print('winner:', winner)
  # winner: True
  print('winner is None:', winner is None)
  # winner is None: False
  print('winner is not None:', winner is not None)
  # winner is not None: True
  print(type(winner))
  # <class 'bool'>
  ```

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

  # None in Python

  `None` is not `null`, it is closer to `undefined` in JS. If you define a
  variable as `None`, the variable exists, it is just not yet defined.

  Using `None` is a pattern similar to using `let` in JS to name a variable and
  definine it later on.

  ```python
  temperature = None
  ```

  If we logged `temperature` it would give us `None` rather than a null pointer
  error.

  With None we can use `is None` and `is not None`, special predicates for working
  with `None` only. This is a akin to using `if (x !== undefined)` in TypeScript

  ```python
  winner = None
  print('winner:', winner)
  # winner: None
  print('winner is None:', winner is None)
  # winner is None: True
  print('winner is not None:', winner is not None)
  # winner is not None: False
  print(type(winner))
  # <class 'NoneType'>
  # Now set winner to be True
  print('Set winner to True')
  # Set winner to True
  winner = True
  print('winner:', winner)
  # winner: True
  print('winner is None:', winner is None)
  # winner is None: False
  print('winner is not None:', winner is not None)
  # winner is not None: True
  print(type(winner))
  # <class 'bool'>
  ```


path: zk/Numbers_in_Python.md
sortable_path: zkNumbers_in_Python.md
title: Numbers in Python
lead: ## Distinguishing `int` and `float`
checksum: 7686d82f635d53fb6d4a45f128d97422a6636463c04c96174ff31bbcbd339c69
created: 2024-03-30 09:03:20.481907953+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["python","data-types"]}
body:
  ## Distinguishing `int` and `float`

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

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

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

  print(float(count))
  # 1.0

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

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

  # Numbers in Python

  ## Distinguishing `int` and `float`

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

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

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

  print(float(count))
  # 1.0

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


path: zk/Object_language_and_meta_language.md
sortable_path: zkObject_language_and_meta_language.md
title: Object and metalanguages
checksum: 1be63bc78982eb0666e64209842912123a849dc70adfa066275c571ce3350c22
created: 2024-03-30 09:03:20.486105099+00:00
modified: 2024-06-16 16:52:46.35439344+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  When we talk about a language we call that language the **object language**. A
  **metalanguage** is a language used to describe some object language.

body:
  When we talk about a language we call that language the **object language**. A
  **metalanguage** is a language used to describe some object language.

  When we are developing a formal logical language (which we may call SL or PL for
  'sentential' and 'propositional' logic respectively), the formal language is the
  object language and natural language (e.g. English) is the metalanguage.

  **Demonstration**

  If we talk about German in English, German in the object language and English is
  the metalanguage.

  ## Use and mention

  There is an associated distinction: that of use and mention.

  When we create an expression in a language we are said to _use_ that language.
  When we remark upon said expression we are said to be _mentioning_ the language.
  This distinction may correspond to the object and metalanguage difference above
  but doesn't have to; use and mention can happen in the same language. For
  example:

  ```
  'London' is the word that denotes the capital of the UK.
  ```

  ## Metavariables

  A metalinguistic variable (metavariable for short) is an expression in the
  metalanguage that is used to talk generally about expressions of the object
  language. The convention in these notes will be to embolden single letters when
  these letters are used as metavariables.

  For example, instead of saying _'P & Q' is an expression comprising two atomic
  sentences and a conjunction_ we might say **\*P** is an expression comprising
  two atomic sentences and a conjunction. In this instance **P** is a metavariable
  in the metalanguage mentioning the expression P & Q in the object language

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

  # Object and metalanguages

  When we talk about a language we call that language the **object language**. A
  **metalanguage** is a language used to describe some object language.

  When we are developing a formal logical language (which we may call SL or PL for
  'sentential' and 'propositional' logic respectively), the formal language is the
  object language and natural language (e.g. English) is the metalanguage.

  **Demonstration**

  If we talk about German in English, German in the object language and English is
  the metalanguage.

  ## Use and mention

  There is an associated distinction: that of use and mention.

  When we create an expression in a language we are said to _use_ that language.
  When we remark upon said expression we are said to be _mentioning_ the language.
  This distinction may correspond to the object and metalanguage difference above
  but doesn't have to; use and mention can happen in the same language. For
  example:

  ```
  'London' is the word that denotes the capital of the UK.
  ```

  ## Metavariables

  A metalinguistic variable (metavariable for short) is an expression in the
  metalanguage that is used to talk generally about expressions of the object
  language. The convention in these notes will be to embolden single letters when
  these letters are used as metavariables.

  For example, instead of saying _'P & Q' is an expression comprising two atomic
  sentences and a conjunction_ we might say **\*P** is an expression comprising
  two atomic sentences and a conjunction. In this instance **P** is a metavariable
  in the metalanguage mentioning the expression P & Q in the object language


path: zk/Observer_pattern.md
sortable_path: zkObserver_pattern.md
title: Observer pattern
lead: The Observer design pattern is also known as the 'publish/subscribe' pattern.
checksum: 30dcbcaeaa06659911bb025762903a012710c53a980c163c8a78719369222dba
created: 2024-03-30 09:03:20.489374856+00:00
modified: 2024-06-16 17:17:49.767499887+00:00
metadata: {"tags":["OOP","design-patterns"]}
body:
  The Observer design pattern is also known as the 'publish/subscribe' pattern.

  > The observer pattern is a software design pattern in which an object, called
  > the subject, maintains a list of its dependents, called observers, and
  > notifies them automatically of any state changes, usually by calling one of
  > their methods.

  The observer pattern instantiates a **one-to-many** relationship between the
  subject and its observers: when one object updates, it notifies many other
  objects that it has been updated.

  This is another design pattern that is useful when implementing a global state.
  When the subject updates its subjects must follow suit and adapt to its changes.
  Here the subject is the instrument for maintaining the global state.

  ## Subject class

  This class maintains a list of observers that will be notified when it is
  updated. It should have the following attributes:

  - array of existing observers
  - an `addObserver` method which pushes an observer onto the observers array
  - a `removeObserver` method that does the opposite
  - a `notify` method that broadcasts to the observers that a change has happened

  ## Observer class

  Observers must implement an `update` method that will be called by the subject's
  `notify` method.

  ## Example

  ```js
  class Subject {
    observers = [];

    getCurrentTime() {
      var d = new Date();
      return `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`;
    }

    addObserver(...observer) {
      for (const o of observer) {
        this.observers.push(o);
      }
    }
    removeObserver(observer) {
      const match = this.observers.findIndex((element) => element === observer);
      this.observers.splice(match, 1);
    }
    notify() {
      for (const o of this.observers) {
        o.update(`I was updated at ${this.getCurrentTime()}`);
      }
    }
  }

  class ObserverA {
    state = "I am observer A";
    update(newState) {
      this.state = newState;
    }
  }

  class ObserverB {
    state = "I am observer B";

    update(newState) {
      this.state = newState;
    }
  }

  let subjectInstance = new Subject();
  let obsAInstance = new ObserverA();
  let obsBInstance = new ObserverB();

  subjectInstance.addObserver(obsAInstance, obsBInstance);

  subjectInstance.notify();
  console.log(obsBInstance.state);
  ```

raw_content:
  ---
  tags:
    - OOP
    - design-patterns
  ---

  # Observer pattern

  The Observer design pattern is also known as the 'publish/subscribe' pattern.

  > The observer pattern is a software design pattern in which an object, called
  > the subject, maintains a list of its dependents, called observers, and
  > notifies them automatically of any state changes, usually by calling one of
  > their methods.

  The observer pattern instantiates a **one-to-many** relationship between the
  subject and its observers: when one object updates, it notifies many other
  objects that it has been updated.

  This is another design pattern that is useful when implementing a global state.
  When the subject updates its subjects must follow suit and adapt to its changes.
  Here the subject is the instrument for maintaining the global state.

  ## Subject class

  This class maintains a list of observers that will be notified when it is
  updated. It should have the following attributes:

  - array of existing observers
  - an `addObserver` method which pushes an observer onto the observers array
  - a `removeObserver` method that does the opposite
  - a `notify` method that broadcasts to the observers that a change has happened

  ## Observer class

  Observers must implement an `update` method that will be called by the subject's
  `notify` method.

  ## Example

  ```js
  class Subject {
    observers = [];

    getCurrentTime() {
      var d = new Date();
      return `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`;
    }

    addObserver(...observer) {
      for (const o of observer) {
        this.observers.push(o);
      }
    }
    removeObserver(observer) {
      const match = this.observers.findIndex((element) => element === observer);
      this.observers.splice(match, 1);
    }
    notify() {
      for (const o of this.observers) {
        o.update(`I was updated at ${this.getCurrentTime()}`);
      }
    }
  }

  class ObserverA {
    state = "I am observer A";
    update(newState) {
      this.state = newState;
    }
  }

  class ObserverB {
    state = "I am observer B";

    update(newState) {
      this.state = newState;
    }
  }

  let subjectInstance = new Subject();
  let obsAInstance = new ObserverA();
  let obsBInstance = new ObserverB();

  subjectInstance.addObserver(obsAInstance, obsBInstance);

  subjectInstance.notify();
  console.log(obsBInstance.state);
  ```


path: zk/Ohms_Law.md
sortable_path: zkOhms_Law.md
title: Ohm's Law
checksum: d8bb6ce94a3a050e6f2494369e3a8fb2ae635a558fcf249321dd62a480ad4c31
created: 2024-03-30 09:03:20.493769503+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["physics","electricity"]}
lead:
  The relationship between
  [current](Current.md),
  [voltage](Voltage.md), and
  [resistance](Resistance.md) is
  defined by Ohm's Law:

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

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

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

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

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

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

raw_content:
  ---
  tags: [physics, electricity]
  ---

  # Ohm's Law

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

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

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

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

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

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


path: zk/OpenSearch.md
sortable_path: zkOpenSearch.md
title: OpenSearch
lead: ## Background
checksum: d9fbc072965e3e5b5484e8077a808c28dc8740b7014912eda50b714a9ec80193
created: 2024-03-30 09:03:20.510995926+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["AWS"]}
body:
  ## Background

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

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

  ## Key concepts

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

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

  ### Create domain

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

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

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

  #### Clusters and nodes

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

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

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

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

  ### Define index and mappings

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

  Our index will store each webpage as a document.

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

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

  The mapping will be utilised in the following scenarios:

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

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

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

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

  ### Querying and searching

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

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

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

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

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

  Example of the data returned:

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

  ## Search patterns

  Below are further examples of commonly used search patterns.

  ### Multiple conditions

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

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

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

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

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

  ### Query by date ranges

  Find pages published after a certain date:

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

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

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

  # OpenSearch

  ## Background

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

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

  ## Key concepts

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

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

  ### Create domain

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

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

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

  #### Clusters and nodes

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

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

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

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

  ### Define index and mappings

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

  Our index will store each webpage as a document.

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

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

  The mapping will be utilised in the following scenarios:

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

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

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

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

  ### Querying and searching

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

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

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

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

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

  Example of the data returned:

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

  ## Search patterns

  Below are further examples of commonly used search patterns.

  ### Multiple conditions

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

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

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

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

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

  ### Query by date ranges

  Find pages published after a certain date:

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

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


path: zk/Order_of_operations.md
sortable_path: zkOrder_of_operations.md
lead:
  ---
  tags:
  - prealgebra
  ---

checksum: 22b182e2b1de87fa63fc85630a6ea75a783842c449ca5cde022f417450197adc
created: 2024-03-30 09:03:20.515968282+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra"]}
body:
  ---
  tags:
    - prealgebra
  ---

  1. Evaluate expressions in **parentheses**
  1. Evaluate **exponents**
  1. Evaluate **multiplications and divisions** from left to right in the order
     that they appear
  1. Evaluate **additions and subtractions** from left to right in the order that
     they appear.

  In the absence of grouping symbols, addition holds no precedence over
  subtraction and vice versa.

  ```
  15 - 8 + 4 = 7 + 4
  					= 11
  ```

  ```
  15 - 8 + 4 = 15 - 12
  					 = 3
  ```

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

  1. Evaluate expressions in **parentheses**
  1. Evaluate **exponents**
  1. Evaluate **multiplications and divisions** from left to right in the order
     that they appear
  1. Evaluate **additions and subtractions** from left to right in the order that
     they appear.

  In the absence of grouping symbols, addition holds no precedence over
  subtraction and vice versa.

  ```
  15 - 8 + 4 = 7 + 4
  					= 11
  ```

  ```
  15 - 8 + 4 = 15 - 12
  					 = 3
  ```


path: zk/Pacman.md
sortable_path: zkPacman.md
title: Pacman
lead: ## List all installed packages
checksum: 04aeff0db5edcee02210da9ca4b1e812e15f973f4e6b9b0c2bf896b737105c56
created: 2024-03-30 09:03:20.530540885+00:00
modified: 2024-06-16 17:18:11.654729584+00:00
metadata: {"tags":["Linux","procedural","arch-linux"]}
body:
  ## List all installed packages

  ```
  pacman -Qe
  ```

  ## List all packages installed by current user

  ```
  pacman -Qqe
  ```

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

  ```
  pacman -Rcns
  ```

raw_content:
  ---
  tags:
    - Linux
    - procedural
    - arch-linux
  ---

  # Pacman

  ## List all installed packages

  ```
  pacman -Qe
  ```

  ## List all packages installed by current user

  ```
  pacman -Qqe
  ```

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

  ```
  pacman -Rcns
  ```


path: zk/Passing_arguments_and_options_to_Bash_scripts.md
sortable_path: zkPassing_arguments_and_options_to_Bash_scripts.md
title: Passing arguments and options to Bash scripts
lead: ## Relation between commands and programs
checksum: 6556e370417eac93215b75f6ced2cd075840bb80b8cb644530db26e18760bae8
created: 2024-03-30 09:03:20.559218792+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["shell"]}
body:
  ## Relation between commands and programs

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

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

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

  ## Passing arguments

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

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

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

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

  ```bash
  #!/bin/bash

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

  This outputs:

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

  Key points:

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

  ## Passing options

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

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

  We can handle an option-based input like:

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

  With the following construct:

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

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

  Which returns:

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

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

  ### The role of the colons

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

  This version demonstrates a more exhaustive use case:

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

  ```

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

  # Passing arguments and options to Bash scripts

  ## Relation between commands and programs

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

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

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

  ## Passing arguments

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

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

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

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

  ```bash
  #!/bin/bash

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

  This outputs:

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

  Key points:

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

  ## Passing options

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

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

  We can handle an option-based input like:

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

  With the following construct:

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

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

  Which returns:

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

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

  ### The role of the colons

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

  This version demonstrates a more exhaustive use case:

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

  ```


path: zk/Practical_walkthrough_Lambda_creation_within_AWS.md
sortable_path: zkPractical_walkthrough_Lambda_creation_within_AWS.md
title: Practical walkthrough of creating a Lambda function via the AWS Management Console
lead: ## Basic set-up
checksum: 8aaa595a4ee06aa4eaf98a9d65d0034ff5b0f795fe36d42fe503ee3453f7c696
created: 2024-03-30 09:03:20.578995245+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["AWS","aws-lambda","node-js"]}
body:
  ## Basic set-up

  First we name the function and accept the defaults:

  ![](/img/lambda_func_one.png)

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

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

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

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

  ## Adding a trigger

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

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

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

  Now we see this step displayed in the dashboard:

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

  With the endpoint and other settings displayed:

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

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

  ## Handling parameters

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

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

    let message = "Hello Lambda";

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

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

    }
  };
  ```

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

  We get `Hello Thomas` as output.

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

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

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

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

  ## Basic set-up

  First we name the function and accept the defaults:

  ![](/img/lambda_func_one.png)

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

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

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

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

  ## Adding a trigger

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

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

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

  Now we see this step displayed in the dashboard:

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

  With the endpoint and other settings displayed:

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

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

  ## Handling parameters

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

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

    let message = "Hello Lambda";

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

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

    }
  };
  ```

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

  We get `Hello Thomas` as output.

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

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


path: zk/Prefixes_for_units_of_electrical_measurement.md
sortable_path: zkPrefixes_for_units_of_electrical_measurement.md
title: Prefixes for unit of electrical measurement
checksum: 8862db3b8a39d32e027c6882099e701167c87d7d3d07d136fba4c0ad18720de3
created: 2024-03-30 09:03:20.591067186+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["physics","electricity","exponents"]}
lead:
  In electronics we are often dealing with units that are very large or very
  small, thus we rely on [exponents](Exponents.md) for formal
  expression.

body:
  In electronics we are often dealing with units that are very large or very
  small, thus we rely on [exponents](Exponents.md) for formal
  expression.

  | Prefix | Symbol | Expression as exponent | Expression as decimal value | English word |
  | ------ | ------ | ---------------------- | --------------------------- | ------------ |
  | Terra- | T      | $10^{12}$              | 1,000,000,000,000           | trillion     |
  | Giga-  | G      | $10^9$                 | 1,000,000,000               | billion      |
  | Mega-  | M      | $10^6$                 | 1,000,000                   | million      |
  | Kilo-  | k      | $10^3$                 | 1,000                       | thousand     |
  | Centi- | c      | $10^{-2}$              | 0.01                        | hundredth    |
  | Milli- | m      | $10^{-3}$              | 0.001                       | thousandth   |
  | Micro- | $\mu$  | $10^{-6}$              | 0.0000001                   | millionth    |
  | Nano-  | n      | $10^{-9}$              | 0.0000000001                | billionth    |
  | Pico-  | p      | $10^{-12}$             | 0.0000000000001             | trillionth   |

  For example, with Amps we tend not to use 1 whole amp as this is far too large
  for most electronics. More common is the milliampere (mA) and the microampere
  ($\mu$A).

  A mA is equal to one thousandth of an ampere: 0.001 A. It takes 1000
  milliamperes to equal one ampere.

  A $\mu$A is equal to one millionth of an ampere: 0.0000001 A. It takes one
  million micoramperes to equal one ampere.

raw_content:
  ---
  tags: [physics, electricity, exponents]
  ---

  # Prefixes for unit of electrical measurement

  In electronics we are often dealing with units that are very large or very
  small, thus we rely on [exponents](Exponents.md) for formal
  expression.

  | Prefix | Symbol | Expression as exponent | Expression as decimal value | English word |
  | ------ | ------ | ---------------------- | --------------------------- | ------------ |
  | Terra- | T      | $10^{12}$              | 1,000,000,000,000           | trillion     |
  | Giga-  | G      | $10^9$                 | 1,000,000,000               | billion      |
  | Mega-  | M      | $10^6$                 | 1,000,000                   | million      |
  | Kilo-  | k      | $10^3$                 | 1,000                       | thousand     |
  | Centi- | c      | $10^{-2}$              | 0.01                        | hundredth    |
  | Milli- | m      | $10^{-3}$              | 0.001                       | thousandth   |
  | Micro- | $\mu$  | $10^{-6}$              | 0.0000001                   | millionth    |
  | Nano-  | n      | $10^{-9}$              | 0.0000000001                | billionth    |
  | Pico-  | p      | $10^{-12}$             | 0.0000000000001             | trillionth   |

  For example, with Amps we tend not to use 1 whole amp as this is far too large
  for most electronics. More common is the milliampere (mA) and the microampere
  ($\mu$A).

  A mA is equal to one thousandth of an ampere: 0.001 A. It takes 1000
  milliamperes to equal one ampere.

  A $\mu$A is equal to one millionth of an ampere: 0.0000001 A. It takes one
  million micoramperes to equal one ampere.


path: zk/Prime_factorization.md
sortable_path: zkPrime_factorization.md
title: Prime factorisation
checksum: 11bafeef8c6f1f1dc3cb5fbb5977584a6326c723fb5f5980248e086a186e11a4
created: 2024-03-30 09:03:20.597398285+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["prealgebra","number-theory"]}
lead:
  Prime factorisation is the activity of expressing a composite number as the
  unique product of [prime numbers](Primes%20and%20composites.md). There are two
  main approaches to this:

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

  - - factor\* trees
  - repeated division by two

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

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

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

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

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

  ### Prime factorisation

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

  - - factor\* trees
  - repeated division by two

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

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

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

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


path: zk/Primes_and_composites.md
sortable_path: zkPrimes_and_composites.md
title: Prime and composite numbers
lead: Definition of a **prime number**:
checksum: 084d62e611aa66271096191f460ece84ec7824736295b5e16f5d3dbcc7ab8cef
created: 2024-03-30 09:03:20.608935468+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["prealgebra","number-theory"]}
body:
  Definition of a **prime number**:

  > For any whole number $n$ where $n \neq 1$, $n$ is prime if and only if its
  > sole [factors](Factors%20and%20divisors.md) are $1$ and $n$

  Definition of a **composite number**:

  > For any whole number $n$, $n$ is composite just if $n$ is not prime

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

  ## Prime and composite numbers

  Definition of a **prime number**:

  > For any whole number $n$ where $n \neq 1$, $n$ is prime if and only if its
  > sole [factors](Factors%20and%20divisors.md) are $1$ and $n$

  Definition of a **composite number**:

  > For any whole number $n$, $n$ is composite just if $n$ is not prime


path: zk/Primitive_types.md
sortable_path: zkPrimitive_types.md
title: Primitive types
checksum: 7e5afd3f7ab27ed61d7a12981598ff65f25a1a0bb6df6d01636b5b2332f9d00d
created: 2024-03-30 09:03:20.62007709+00:00
modified: 2024-06-16 16:59:59.6481695+00:00
metadata: {"tags":["typescript","data-types"]}
lead:
  The most basic type level is the one already present in JavaScript itself: the
  primitive data types: `boolean`, `string` and `number` . These are the types
  that would be returned from a `typeof` expression in ordinary JavaScript

body:
  The most basic type level is the one already present in JavaScript itself: the
  primitive data types: `boolean`, `string` and `number` . These are the types
  that would be returned from a `typeof` expression in ordinary JavaScript

  ## Type inference

  You can explicitly declare these data types yourself when you create a variable
  with `var`, `const`, or `let` if you like, but it is generally unnecessary since
  Typescript is intelligent enough to perform **type inference** and will know
  from what you write which type you mean.

  ```ts
  const age: number = 32;
  const name: string = "Thomas";
  const one: boolean = true;
  const zero: boolean = false;
  ```

  ## Arrays

  With arrays that you directly define the type will also be inferred but it can
  be helpful to explicitly declare them. If you declare an empty array, it is
  necessary to assign empty brackets.

  ```ts
  const store: number[] = [1, 2, 3]; // Populated array
  const store: string[] = []; // Empty array
  ```

  ## Object

  `Object` is a valid type declaration in TS but it is not particularly helpful
  since it becomes similar to using [any](Any.md) given that most primitive
  types in JavaScripts prototypically inherit from an Object.

  Generally, when you use objects in TypeScript you type them as
  [custom types](Custom_types_in_TypeScript.md).

  ## Array of (untyped) objects

  If we just know that it is going to be an array of objects we can use:

  ```ts
  const arrOfObj = {}[]
  ```

  If we wish to define a particular shape but without defining a type:

  ```ts
  const arrOfObj = { name: string, age: number }[]
  ```

  But better for reusability to do:

  ```ts
  type ArrayOfObj = {
    name: string,
    age: number
  }

  const arrOfObj: ArrayOfObj[] = [{}, ...]

  ```

raw_content:
  ---
  tags:
    - typescript
    - data-types
  ---

  # Primitive types

  The most basic type level is the one already present in JavaScript itself: the
  primitive data types: `boolean`, `string` and `number` . These are the types
  that would be returned from a `typeof` expression in ordinary JavaScript

  ## Type inference

  You can explicitly declare these data types yourself when you create a variable
  with `var`, `const`, or `let` if you like, but it is generally unnecessary since
  Typescript is intelligent enough to perform **type inference** and will know
  from what you write which type you mean.

  ```ts
  const age: number = 32;
  const name: string = "Thomas";
  const one: boolean = true;
  const zero: boolean = false;
  ```

  ## Arrays

  With arrays that you directly define the type will also be inferred but it can
  be helpful to explicitly declare them. If you declare an empty array, it is
  necessary to assign empty brackets.

  ```ts
  const store: number[] = [1, 2, 3]; // Populated array
  const store: string[] = []; // Empty array
  ```

  ## Object

  `Object` is a valid type declaration in TS but it is not particularly helpful
  since it becomes similar to using [any](Any.md) given that most primitive
  types in JavaScripts prototypically inherit from an Object.

  Generally, when you use objects in TypeScript you type them as
  [custom types](Custom_types_in_TypeScript.md).

  ## Array of (untyped) objects

  If we just know that it is going to be an array of objects we can use:

  ```ts
  const arrOfObj = {}[]
  ```

  If we wish to define a particular shape but without defining a type:

  ```ts
  const arrOfObj = { name: string, age: number }[]
  ```

  But better for reusability to do:

  ```ts
  type ArrayOfObj = {
    name: string,
    age: number
  }

  const arrOfObj: ArrayOfObj[] = [{}, ...]

  ```


path: zk/Proper_shebang_syntax.md
sortable_path: zkProper_shebang_syntax.md
title: Proper shebang syntax
checksum: edcb7998f0254eab27ba5419de171e27c3a25fc8f6863e4021a0bbb4a4fbbece
created: 2024-03-30 09:03:20.645741491+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["shell"]}
lead:
  This is a more portable way of writing the shebang because the location of Bash
  on different systems can vary.

body:
  This is a more portable way of writing the shebang because the location of Bash
  on different systems can vary.

  ```bash
  #!/usr/bin/env Bash
  ```

  Rather than:

  ```bash
  #!/bin/bash
  ```

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

  # Proper shebang syntax

  This is a more portable way of writing the shebang because the location of Bash
  on different systems can vary.

  ```bash
  #!/usr/bin/env Bash
  ```

  Rather than:

  ```bash
  #!/bin/bash
  ```


path: zk/Prototypical_inheritance.md
sortable_path: zkPrototypical_inheritance.md
title: Prototypical inheritance
lead: ## Object oriented basis of JavaScript and nomenclature
checksum: b332dfadb655c7db9d31679a6ca1dc695fef44a404f9abc92e05b34c43b517a7
created: 2024-03-30 09:03:20.655417991+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["javascript","OOP"]}
body:
  ## Object oriented basis of JavaScript and nomenclature

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

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

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

  ### Lack of classes in JS

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

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

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

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

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

  ### Example of a constructor function

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

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

  /* Then to create an object instance: */

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

  ## Prototypes

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

  > Prototypes are a special type of object that exist as a property within
  > function-objects.

  Whenever we create an instance-object from constructor function using `new`, the
  object is given a property with the key `_proto_` . It's value is the
  `[[prototype]]` property of the function that created, i.e the class of which
  the object is an instance:

  ```jsx
  {
    _proto_: prototype;
  }
  ```

  Note that we use the syntax `[[prototype]]` to indicate that **whilst the value
  exists, it is not accessible via JavaScript code**, in the manner of a normal
  object-literal or function-object, because it is the lower-level foundation of
  first-order JS code.

  So how would this work for the `slayer` object we created above?

  ```jsx
  Slayer._proto_ === Band.prototype;
  ```

  The thing is, you do not tend to see this prototype functionality and it is not
  possible to refer to it as a key:value directly. This is because it is something
  under-the-hood - it is a process that makes the object-oriented aspects of JS
  possible but it is not apparent on the first-order level of everyday coding.

  The prototype is like a hidden hook that facilitates the generation of objects
  from class-like constructor functions. With our new awareness of it, we are in a
  position _to understand how JavaScript works in foundational terms._

  ## Every non-primitive data type in JS possesses the `Object` prototype

  The title above indicates how and why JavaScript establishes its object-oriented
  data structure.

  When we create an object literal (i.e an every day object not a class-like
  function), under-the-hood JS is making a `new Object()` call in much the same
  way as we are calling the function-object `Band` when we declare
  `var slayer = new Band(...)` .

  All instance-objects in JavaScript (which, remember is the set of everything
  that is not a primitive) **inherit from the `Object` keyword**. And, to do this,
  prototypes are necessary. Now we see how integral prototypes are to JavaScript.

  ### Examples

  Here we create an object-literal and uncover how it has it's basis in the
  overall `Object` constructor.

  ```jsx
  var slayerMembers = {
    bassist: "Tom Araya",
    guitarist: "Kerry King",
    guitarist: "Jeff Hanneman",
    drummer: "Dave Lombardo",
  };

  //  slayerMembers._proto_ === Object.prototype
  ```

  Here we create an instance-object off of our `Band` constructor function and
  trace it back to `Object`

  ```jsx
  var deicide = new Band("Legion", "death metal");

  /* deicide object relies on Band prototype:
  		
  		deicide._proto_=== Band.prototype

  Band prototype in turn relies in turn on Object prototype: 

   Band.prototype._proto_=== Object.prototype

  */
  ```

  As the above examples show, ultimately all objects in JavaScript (whether
  variables, arrays, methods, or functions) eventually lead back to
  `Object.prototype` .

  ### How the browser uses prototypes when interpreting code

  When the browser interprets your code attempting to access a property or method
  of an object it runs through the following process:

  1. Search on the object itself. If fails →
  2. Search the object's prototype. If fails →
  3. Search the prototype of the linked object. If fails→
  4. Keep going until `Object.prototype` is reached

  Clearly with each step we are going to a lower level of abstraction, drawing the
  curtain back more and more until we got the point where JavaScript effectively
  stops because any lower would take you into its lower-level parent language
  (C/C++ depending on how the specific browser executes ECMAScript)

  Note (3) is basically the prototype of the component itself. So if you started
  at (1) with a custom array. At (3) the browser would be querying
  `Array.prototype` i.e the basic foundational array object.

  This explains why the full-name of certain methods on the global objects,
  includes reference to 'prototype', for instance:

  - `toFixed` → `Number.prototype.toFixed()`
  - `.trim` → `String.prototype.trim()`.

  ### Using the function-object of `Object` to create an object literal

  As a side-effect of uncovering the prototypical basis of `Object()` we can use
  it to create an object literal in another way.

  Standardly to create a new, empty object literal we would use:

  ```jsx
  var slayerMembers = {...}
  ```

  But now we could just as easily direct the code to `Object()` prototype and
  achieve the same outcome:

  ```jsx
  let slayerMembers = new Object();
  ```

  ### Finding the `[[Prototype]]` of a JS object with `getPrototypeOf()`

  We can find out the `[[prototype]]` of a given object in JavaScript with a
  special method applied to the object in question. The method we use is one of
  the inherent methods that belong to the `Object()` constructor function:
  `getPrototypeOf()` .

  Here is an example:

  ```jsx
  console.log(Object.getPrototypeOf(slayer));

  /*

  Returns: 

  Object {constructor: , __defineGetter__: ,
   __defineSetter__: , hasOwnProperty: , __lookupGetter__: , …}

  */
  ```

  We could also use the following approach to get the same output however this is
  not recommended for production:

  ```jsx
  console.log(slayer.__prototype__);
  ```

  The output we get when querying the prototype is the same in each instance.

  ## But why bother using prototypes in your code?

  It is informative to learn how prototypes are behind the object behaviour we
  observe at a high level in JavaScript and to know that there is a common,
  lower-level root but when would you actually need to use prototype methods?

  The reason is mainly to do with performance and optimisation

  > When we create a `deicide` and `slayer` object using the constructor function
  > `Band()` JS creates two _copies_ of the constructor function, one for each
  > generated object. This means there are two instances of `mainAlbum` for each
  > object even though each do exactly the same thing. This wastes memory and
  > performance time every time the script runs.

  As a prototype object is an object, we can attach properties and methods to the
  prototype object. Thus, enabling all the objects created using the constructor
  function to share those properties and methods. However, the beauty is that the
  method is applied to the prototype, so it is only stored in the memory once, but
  every instance of the object has access to it.

  ```jsx
  /* So say I wanted to add an age property to the Band()constructor that all objects of this type have access to. I could do this without wasting memory and without the need to change the original constructor function by referring to the prototype: */

  Band.prototype.age = null;

  Slayer.age = 25;
  ```

  ## Further steps

  We are going to stop here for the time being since the primary aim of uncovering
  prototypes and understanding how they underly all objects and object methods in
  JS has been achieved.

  However it is important to note that there is more that you can do with
  prototypes. The main additional thing is using the prototypical basis of
  constructor functions (i.e pseudo-classes) to add and remove key value pairs in
  the resulting objects. This is useful when you want to create behaviour that is
  much more like classes in compiled OOP languages.

raw_content:
  ---
  tags: [javascript, OOP]
  ---

  # Prototypical inheritance

  ## Object oriented basis of JavaScript and nomenclature

  As we know, everything in JS that isn't a primitive (i.e. a primitive data type:
  string, number, boolean, null) is an object. Where an object is a data structure
  that consists in key:value pairs.

  The key is always a string and the value can be: a primitive value, a method
  (function within an object), an array, another object etc.

  > In other object-oriented languages like C# and Java, **functions are called
  > methods** however in JS the term **method** is reserved for function that
  > exists as a property of an objects that contains it.

  ### Lack of classes in JS

  In contrast to more advanced object-oriented languages that are compiled (Java,
  C#), classes do not really exist in JavaScript. This is due to the
  prototype-based nature of JS (as we will see) however class-like functionality
  can be achieved through the use of functions.

  Remember in JS that functions can be simple code blocks that execute when called
  (which is what we mean most of the time by 'function') **or they can be
  constructor functions which create generic object types from which specific
  token instances can be generated as objects.**

  It is through these 'generic object types' that something akin to a class can be
  created in JavaScript. (These are sometimes called function-objects )

  The following is a good account of the differences between prototypal and
  class-based languages:

  > JavaScript is a prototype-based language, **meaning object properties and
  > methods can be shared through generalized objects that have the ability to be
  > cloned and extended**. This is known as **prototypical inheritance** and
  > differs from **class inheritance**. Among popular object-oriented programming
  > languages, JavaScript is relatively unique, as other prominent languages such
  > as PHP, Python, and Java are class-based languages, which instead **define
  > classes as blueprints for objects**.

  ### Example of a constructor function

  In order to understand prototypes in a moment, it will be helpful to have an
  example of a constructor function that can be used to generate object instances:

  ```jsx
  /* Create constructor function: */
  function Band(mainAlbum, subgenre) {
    // use Pascal for constructor function names
    this.mainAlbum = mainAlbum;
    this.subgenre = subgenre;
  }

  /* Then to create an object instance: */

  var slayer = new Band("Reign in Blood", "thrash metal");
  ```

  ## Prototypes

  Now that we know class-type functionality enters JS via constructor functions
  and we have an example, we can look at prototypes.

  > Prototypes are a special type of object that exist as a property within
  > function-objects.

  Whenever we create an instance-object from constructor function using `new`, the
  object is given a property with the key `_proto_` . It's value is the
  `[[prototype]]` property of the function that created, i.e the class of which
  the object is an instance:

  ```jsx
  {
    _proto_: prototype;
  }
  ```

  Note that we use the syntax `[[prototype]]` to indicate that **whilst the value
  exists, it is not accessible via JavaScript code**, in the manner of a normal
  object-literal or function-object, because it is the lower-level foundation of
  first-order JS code.

  So how would this work for the `slayer` object we created above?

  ```jsx
  Slayer._proto_ === Band.prototype;
  ```

  The thing is, you do not tend to see this prototype functionality and it is not
  possible to refer to it as a key:value directly. This is because it is something
  under-the-hood - it is a process that makes the object-oriented aspects of JS
  possible but it is not apparent on the first-order level of everyday coding.

  The prototype is like a hidden hook that facilitates the generation of objects
  from class-like constructor functions. With our new awareness of it, we are in a
  position _to understand how JavaScript works in foundational terms._

  ## Every non-primitive data type in JS possesses the `Object` prototype

  The title above indicates how and why JavaScript establishes its object-oriented
  data structure.

  When we create an object literal (i.e an every day object not a class-like
  function), under-the-hood JS is making a `new Object()` call in much the same
  way as we are calling the function-object `Band` when we declare
  `var slayer = new Band(...)` .

  All instance-objects in JavaScript (which, remember is the set of everything
  that is not a primitive) **inherit from the `Object` keyword**. And, to do this,
  prototypes are necessary. Now we see how integral prototypes are to JavaScript.

  ### Examples

  Here we create an object-literal and uncover how it has it's basis in the
  overall `Object` constructor.

  ```jsx
  var slayerMembers = {
    bassist: "Tom Araya",
    guitarist: "Kerry King",
    guitarist: "Jeff Hanneman",
    drummer: "Dave Lombardo",
  };

  //  slayerMembers._proto_ === Object.prototype
  ```

  Here we create an instance-object off of our `Band` constructor function and
  trace it back to `Object`

  ```jsx
  var deicide = new Band("Legion", "death metal");

  /* deicide object relies on Band prototype:
  		
  		deicide._proto_=== Band.prototype

  Band prototype in turn relies in turn on Object prototype: 

   Band.prototype._proto_=== Object.prototype

  */
  ```

  As the above examples show, ultimately all objects in JavaScript (whether
  variables, arrays, methods, or functions) eventually lead back to
  `Object.prototype` .

  ### How the browser uses prototypes when interpreting code

  When the browser interprets your code attempting to access a property or method
  of an object it runs through the following process:

  1. Search on the object itself. If fails →
  2. Search the object's prototype. If fails →
  3. Search the prototype of the linked object. If fails→
  4. Keep going until `Object.prototype` is reached

  Clearly with each step we are going to a lower level of abstraction, drawing the
  curtain back more and more until we got the point where JavaScript effectively
  stops because any lower would take you into its lower-level parent language
  (C/C++ depending on how the specific browser executes ECMAScript)

  Note (3) is basically the prototype of the component itself. So if you started
  at (1) with a custom array. At (3) the browser would be querying
  `Array.prototype` i.e the basic foundational array object.

  This explains why the full-name of certain methods on the global objects,
  includes reference to 'prototype', for instance:

  - `toFixed` → `Number.prototype.toFixed()`
  - `.trim` → `String.prototype.trim()`.

  ### Using the function-object of `Object` to create an object literal

  As a side-effect of uncovering the prototypical basis of `Object()` we can use
  it to create an object literal in another way.

  Standardly to create a new, empty object literal we would use:

  ```jsx
  var slayerMembers = {...}
  ```

  But now we could just as easily direct the code to `Object()` prototype and
  achieve the same outcome:

  ```jsx
  let slayerMembers = new Object();
  ```

  ### Finding the `[[Prototype]]` of a JS object with `getPrototypeOf()`

  We can find out the `[[prototype]]` of a given object in JavaScript with a
  special method applied to the object in question. The method we use is one of
  the inherent methods that belong to the `Object()` constructor function:
  `getPrototypeOf()` .

  Here is an example:

  ```jsx
  console.log(Object.getPrototypeOf(slayer));

  /*

  Returns: 

  Object {constructor: , __defineGetter__: ,
   __defineSetter__: , hasOwnProperty: , __lookupGetter__: , …}

  */
  ```

  We could also use the following approach to get the same output however this is
  not recommended for production:

  ```jsx
  console.log(slayer.__prototype__);
  ```

  The output we get when querying the prototype is the same in each instance.

  ## But why bother using prototypes in your code?

  It is informative to learn how prototypes are behind the object behaviour we
  observe at a high level in JavaScript and to know that there is a common,
  lower-level root but when would you actually need to use prototype methods?

  The reason is mainly to do with performance and optimisation

  > When we create a `deicide` and `slayer` object using the constructor function
  > `Band()` JS creates two _copies_ of the constructor function, one for each
  > generated object. This means there are two instances of `mainAlbum` for each
  > object even though each do exactly the same thing. This wastes memory and
  > performance time every time the script runs.

  As a prototype object is an object, we can attach properties and methods to the
  prototype object. Thus, enabling all the objects created using the constructor
  function to share those properties and methods. However, the beauty is that the
  method is applied to the prototype, so it is only stored in the memory once, but
  every instance of the object has access to it.

  ```jsx
  /* So say I wanted to add an age property to the Band()constructor that all objects of this type have access to. I could do this without wasting memory and without the need to change the original constructor function by referring to the prototype: */

  Band.prototype.age = null;

  Slayer.age = 25;
  ```

  ## Further steps

  We are going to stop here for the time being since the primary aim of uncovering
  prototypes and understanding how they underly all objects and object methods in
  JS has been achieved.

  However it is important to note that there is more that you can do with
  prototypes. The main additional thing is using the prototypical basis of
  constructor functions (i.e pseudo-classes) to add and remove key value pairs in
  the resulting objects. This is useful when you want to create behaviour that is
  much more like classes in compiled OOP languages.


path: zk/Python_data_types.md
sortable_path: zkPython_data_types.md
title: Python data-types
checksum: 87a8e9b94c5a6050161b68234964ee99022aef37f835f759d55a650977ba7b2f
created: 2024-03-30 09:03:20.659578738+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["python","data-types"]}
lead:
  - Python is dynamically typed rather than untyped. It updates the types on the
    fly as you are writing your code.
  - Type-hints in the editor like `-> str` mean "at the moment it is a string". It
    doesn't mean you can't redefine the value as something else.
  - Each data type in Python inherits off of a built-in class, similar to
    prototypes in JS

body:
  - Python is dynamically typed rather than untyped. It updates the types on the
    fly as you are writing your code.
  - Type-hints in the editor like `-> str` mean "at the moment it is a string". It
    doesn't mean you can't redefine the value as something else.
  - Each data type in Python inherits off of a built-in class, similar to
    prototypes in JS

  The core data-types are as follows:

  - [str](Strings_in_Python.md)
  - bool
  - float
  - double

  We can identify types using the built-in `type()` function:

  ```python
  # Integer number
  my_variable = 422
  print(my_variable)
  print(type(my_variable))
  # <class 'int'>

  # String type
  my_variable = 'Natalia'
  print(my_variable)
  print(type(my_variable))
  # <class 'str'>

  # Boolean type
  my_variable = True
  print(my_variable)
  print(type(my_variable))
  # <class 'bool'>
  ```

  ## Converting data-types

  For every data-type there is a corresponding converter method, e.g:

  ```python
  a_string = '32'
  print(f'a_string {a_string} is {type(a_string)}')
  an_int = int(a_string)

  print(f'an_int {a_string} is {type(an_int)}')
  a_float = float(a_string)

  print(f'a_float {a_string} is {type(a_float)}')
  another_string = str(42)

  print(f'another_string {a_string} is {type(another_string)}')
  ```

raw_content:
  ---
  tags: [python, data-types]
  ---

  # Python data-types

  - Python is dynamically typed rather than untyped. It updates the types on the
    fly as you are writing your code.
  - Type-hints in the editor like `-> str` mean "at the moment it is a string". It
    doesn't mean you can't redefine the value as something else.
  - Each data type in Python inherits off of a built-in class, similar to
    prototypes in JS

  The core data-types are as follows:

  - [str](Strings_in_Python.md)
  - bool
  - float
  - double

  We can identify types using the built-in `type()` function:

  ```python
  # Integer number
  my_variable = 422
  print(my_variable)
  print(type(my_variable))
  # <class 'int'>

  # String type
  my_variable = 'Natalia'
  print(my_variable)
  print(type(my_variable))
  # <class 'str'>

  # Boolean type
  my_variable = True
  print(my_variable)
  print(type(my_variable))
  # <class 'bool'>
  ```

  ## Converting data-types

  For every data-type there is a corresponding converter method, e.g:

  ```python
  a_string = '32'
  print(f'a_string {a_string} is {type(a_string)}')
  an_int = int(a_string)

  print(f'an_int {a_string} is {type(an_int)}')
  a_float = float(a_string)

  print(f'a_float {a_string} is {type(a_float)}')
  another_string = str(42)

  print(f'another_string {a_string} is {type(another_string)}')
  ```


path: zk/Python_execution.md
sortable_path: zkPython_execution.md
title: Python execution
lead: For immediately executable scripts, we have to have a Python shebang at the top:
checksum: 30db233c5b46a73df2c5ab204f80b8f28ab8e94586db8ea06e7bcbe8cb0c090c
created: 2024-03-30 09:03:20.670131186+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["python","data-types"]}
body:
  For immediately executable scripts, we have to have a Python shebang at the top:

  ```
  #! /usr/local/bin/python3
  ```

  With programs we can just run the `main` file with `python main.py`.

raw_content:
  ---
  tags: [python, data-types]
  ---

  # Python execution

  For immediately executable scripts, we have to have a Python shebang at the top:

  ```
  #! /usr/local/bin/python3
  ```

  With programs we can just run the `main` file with `python main.py`.


path: zk/Python_interpreter.md
sortable_path: zkPython_interpreter.md
title: Python interpreter
lead: ## What is an implementation of a programming language?
checksum: 76b7b7c27f575074f2e906518a4155469c62525b955059dce6f9d22f5e9046eb
created: 2024-03-30 09:03:20.674800238+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["python"]}
body:
  ## What is an implementation of a programming language?

  An implementation of Python:

  > means a program or environment which provides support for the execution of
  > programs written in the Python language

  As Python is interpreted, any implementation will perforce involve an
  interpreting language.

  There are three main implementations:

  - CPython
    - The default implementation, written in C
  - Jython
    - Python implemented by Java and running on the JVM
  - PyPy
    - A subset of Python which is faster due to machine code compilation

  ## CPython

  In the standard implementation, the language which interprets Python code is C.

  When Python runs in this implementation, code written in C converts it to
  bytecode (so-called because each instruction is
  [8-bits long](Binary_units_of_measurement.md)). This is a
  lower-level transliteration of Python is not meant to be understood by the CPU
  (since it is not binary) but rather to be run in the Python virtual machine
  which is equipped to understand bytecode. The Python Virtual Machine is
  therefore also known as "bytecode interpreter" or the CPython VM. The VM then
  converts bytecode into machine-executable code.

  The fact that Python does not compile to binary means that it can run on a
  greater variety of hardwares however it is slower than a language that compiles
  to machine code. PyPy is a solution for this because it is both interpreted and
  compiled to optimised machine code at run time.

  Where CPython is the interpreter, Python extensions can be written in C.

  ## PyPy

  PyPy is an alternative implementation to CPython. The main difference is that it
  uses a JIT-compiler and doesn't convert Python to the intermediary byte code. It
  is also written in Python itself, specifically RPython ('restricted Python').
  This is capable of converting Python directly to machine code without the
  CPython virtual machine.

  ## Jython

  Jython is an implementation of Python written in Java. Like CPython it converts
  Python to bytecode but this runs on the JVM. As it is written in Java, a Jython
  program can import and use any Java class.

  Where Jython is the interpreter, Python extension can be written in Java.

raw_content:
  ---
  tags: [python]
  ---

  # Python interpreter

  ## What is an implementation of a programming language?

  An implementation of Python:

  > means a program or environment which provides support for the execution of
  > programs written in the Python language

  As Python is interpreted, any implementation will perforce involve an
  interpreting language.

  There are three main implementations:

  - CPython
    - The default implementation, written in C
  - Jython
    - Python implemented by Java and running on the JVM
  - PyPy
    - A subset of Python which is faster due to machine code compilation

  ## CPython

  In the standard implementation, the language which interprets Python code is C.

  When Python runs in this implementation, code written in C converts it to
  bytecode (so-called because each instruction is
  [8-bits long](Binary_units_of_measurement.md)). This is a
  lower-level transliteration of Python is not meant to be understood by the CPU
  (since it is not binary) but rather to be run in the Python virtual machine
  which is equipped to understand bytecode. The Python Virtual Machine is
  therefore also known as "bytecode interpreter" or the CPython VM. The VM then
  converts bytecode into machine-executable code.

  The fact that Python does not compile to binary means that it can run on a
  greater variety of hardwares however it is slower than a language that compiles
  to machine code. PyPy is a solution for this because it is both interpreted and
  compiled to optimised machine code at run time.

  Where CPython is the interpreter, Python extensions can be written in C.

  ## PyPy

  PyPy is an alternative implementation to CPython. The main difference is that it
  uses a JIT-compiler and doesn't convert Python to the intermediary byte code. It
  is also written in Python itself, specifically RPython ('restricted Python').
  This is capable of converting Python directly to machine code without the
  CPython virtual machine.

  ## Jython

  Jython is an implementation of Python written in Java. Like CPython it converts
  Python to bytecode but this runs on the JVM. As it is written in Java, a Jython
  program can import and use any Java class.

  Where Jython is the interpreter, Python extension can be written in Java.


path: zk/Python_modules_and_imports.md
sortable_path: zkPython_modules_and_imports.md
title: Python modules
lead: ## Distinguishing _modules_ from _packages_
checksum: c8d45e1dabc5ec3c2d533fd819b72a8b38b47d7f12b3614dc0e4c5e3835581d2
created: 2024-03-30 09:03:20.683171526+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["python","OOP"]}
body:
  ## Distinguishing _modules_ from _packages_

  Modules and packages in Python are very similar to modules in Node.js: a
  combination of several pieces of functionality (often written in a mix of
  different syntactic styles) bundled together to be used as a unit.

  - A **module** is basically just a script - a single Python file that you can
    import using the `import` statement
  - A **package** is a _collection of modules_

  A package is a whole directory which has an `__init__.py` at its root which
  designates it as a package (this can be empty, but it is necessary to have this
  file for the interpreter to understand that the directory is a package).

  ## Creating modules

  ### Naming modules

  The name of a module is the name of its file, e.g. `utils.py` defines the module
  `utils`. When we import a module we do not need to specify the extension.

  ### Example module

  Here is an example of a module.

  ```py
  """utils module"""

  print('Hello I am the utils module')


  def printer(some_object):
      print('printer')
      print(some_object)
      print('done')


  class Shape:

      def __init__(self, id):
          self.id = id

      def __str__(self):
          return 'Shape - ' + self.id


  default_shape = Shape('square')


  def _special_function():
      print('Special function')

  ```

  ## Importing modules

  > Whether we are importing our own local module, a third-party module or a
  > built-in module native to Python, the syntax is the same.

  - If the module is in the same directory as the file you wish to import it into,
    you can just directly import with:
    ```py
    import utils
    utils.specialFunction
    shape = utils.Shape('circle')
    ```
  - If the module you want to import is in a subdirectory, you will need to make
    that subdirectory a package in order to be able to import from it. This means
    adding an `__init__.py`. Then you can use the following import syntax:
    ```py
    from subdir import utils
    ...
    ```

  ### Different import syntax

  - If you don't want to have to prepend the imported module's name when using it
    (e.g. `utils.specialFunction`). You can import like so:

    ```py
    from utils import *
    specialFunction()
    ```

  - By default an import makes everything in the module available, however you can
    choose to import only a subset. E.g.:
    ```py
    from utils import Shape
    #  or with an alias:
    from utils import Shape as lorem
    ```

  ## `__main__`

  The `__main__` attribute is important in Python.

  ### Usage in scripts/modules

  When you run a script (module) Python assigns the string `__main__` to the
  `__name__` attribute to the script that is being executed.

  If you run the script as an import into another script, the `__name__` attribute
  of the imported module is set to the module name, not `__main__`. This is why,
  when we import we say `import [module_name]` and not `import __main__`.

  When used with a module, `__main__` indicates code that should be run straight
  away when the given script file is run. You won't always want to execute
  everything in the module file on execution so you can put this stuff outside of
  `__main__`

  Typically you will only want immediate execution to happen when **the module is
  the entrypoint** to the program.

  We indicate this with `__main__` as follows:

  ```py
  if __name__ == "__main__":
      print("This runs only if the module is the main module")
  ```

  ### Usage in packages

  The only real difference with packages is that you can also define a
  `__main__.py` file in the root of the package directory.

  This functions in the same manner as `__main__` in an individual module however
  this time it is not the entry point for a single script but the entrypoint of
  the package as a whole.

  > When the package is run directly as a script, the code in `__main__.py` is
  > executed, just like it would be if it were a standalone script. This allows a
  > package to be run as a script, as well as letting it contain modules that can
  > be imported separately.

  You can of course still have individual entrypoints for the modules that
  comprise the package.

  ### Running the package as a package

  If you want to run the whole package at once, rather than individually importing
  the modules that belong to it. You can run the following from the command-line:

  ```
  python -m package_name
  ```

  With this, Python will try to locate the `__main__.py` file and execute it as
  though it were a standalone script.

  ### Using a `main()` function

  To ensure that certain code runs on initialisation we can define a special
  `main` function or class method that runs when the module or package is
  loaded.We would put the main set-up logic in `main` and coordinate the execution
  of the module or package through it.

  ```py
  def main():
      printer(default_shape)
      shape = Shape("circle")
      printer(shape)

  if __name__ == "__main__":
      main()
  ```

  ### Example

  ```py
  '''palindrome module'''

  def is_palindrome(inp):
      inp_list = list(inp)
      inp_list_len = len(inp_list)
      for i in range(inp_list_len):
          if inp_list[i] != inp_list[inp_list_len - 1 - i]:
              return False
      return True


  def main():
      palindrome_one = "soros"
      palindrome_two = "torot"
      not_palindrome = "chair"

      print(is_palindrome(palindrome_one))
      print(is_palindrome(palindrome_two))
      print(is_palindrome(not_palindrome))


  if __name__ == '__main__':
      main()
  ```

raw_content:
  ---
  tags: [python, OOP]
  ---

  # Python modules

  ## Distinguishing _modules_ from _packages_

  Modules and packages in Python are very similar to modules in Node.js: a
  combination of several pieces of functionality (often written in a mix of
  different syntactic styles) bundled together to be used as a unit.

  - A **module** is basically just a script - a single Python file that you can
    import using the `import` statement
  - A **package** is a _collection of modules_

  A package is a whole directory which has an `__init__.py` at its root which
  designates it as a package (this can be empty, but it is necessary to have this
  file for the interpreter to understand that the directory is a package).

  ## Creating modules

  ### Naming modules

  The name of a module is the name of its file, e.g. `utils.py` defines the module
  `utils`. When we import a module we do not need to specify the extension.

  ### Example module

  Here is an example of a module.

  ```py
  """utils module"""

  print('Hello I am the utils module')


  def printer(some_object):
      print('printer')
      print(some_object)
      print('done')


  class Shape:

      def __init__(self, id):
          self.id = id

      def __str__(self):
          return 'Shape - ' + self.id


  default_shape = Shape('square')


  def _special_function():
      print('Special function')

  ```

  ## Importing modules

  > Whether we are importing our own local module, a third-party module or a
  > built-in module native to Python, the syntax is the same.

  - If the module is in the same directory as the file you wish to import it into,
    you can just directly import with:
    ```py
    import utils
    utils.specialFunction
    shape = utils.Shape('circle')
    ```
  - If the module you want to import is in a subdirectory, you will need to make
    that subdirectory a package in order to be able to import from it. This means
    adding an `__init__.py`. Then you can use the following import syntax:
    ```py
    from subdir import utils
    ...
    ```

  ### Different import syntax

  - If you don't want to have to prepend the imported module's name when using it
    (e.g. `utils.specialFunction`). You can import like so:

    ```py
    from utils import *
    specialFunction()
    ```

  - By default an import makes everything in the module available, however you can
    choose to import only a subset. E.g.:
    ```py
    from utils import Shape
    #  or with an alias:
    from utils import Shape as lorem
    ```

  ## `__main__`

  The `__main__` attribute is important in Python.

  ### Usage in scripts/modules

  When you run a script (module) Python assigns the string `__main__` to the
  `__name__` attribute to the script that is being executed.

  If you run the script as an import into another script, the `__name__` attribute
  of the imported module is set to the module name, not `__main__`. This is why,
  when we import we say `import [module_name]` and not `import __main__`.

  When used with a module, `__main__` indicates code that should be run straight
  away when the given script file is run. You won't always want to execute
  everything in the module file on execution so you can put this stuff outside of
  `__main__`

  Typically you will only want immediate execution to happen when **the module is
  the entrypoint** to the program.

  We indicate this with `__main__` as follows:

  ```py
  if __name__ == "__main__":
      print("This runs only if the module is the main module")
  ```

  ### Usage in packages

  The only real difference with packages is that you can also define a
  `__main__.py` file in the root of the package directory.

  This functions in the same manner as `__main__` in an individual module however
  this time it is not the entry point for a single script but the entrypoint of
  the package as a whole.

  > When the package is run directly as a script, the code in `__main__.py` is
  > executed, just like it would be if it were a standalone script. This allows a
  > package to be run as a script, as well as letting it contain modules that can
  > be imported separately.

  You can of course still have individual entrypoints for the modules that
  comprise the package.

  ### Running the package as a package

  If you want to run the whole package at once, rather than individually importing
  the modules that belong to it. You can run the following from the command-line:

  ```
  python -m package_name
  ```

  With this, Python will try to locate the `__main__.py` file and execute it as
  though it were a standalone script.

  ### Using a `main()` function

  To ensure that certain code runs on initialisation we can define a special
  `main` function or class method that runs when the module or package is
  loaded.We would put the main set-up logic in `main` and coordinate the execution
  of the module or package through it.

  ```py
  def main():
      printer(default_shape)
      shape = Shape("circle")
      printer(shape)

  if __name__ == "__main__":
      main()
  ```

  ### Example

  ```py
  '''palindrome module'''

  def is_palindrome(inp):
      inp_list = list(inp)
      inp_list_len = len(inp_list)
      for i in range(inp_list_len):
          if inp_list[i] != inp_list[inp_list_len - 1 - i]:
              return False
      return True


  def main():
      palindrome_one = "soros"
      palindrome_two = "torot"
      not_palindrome = "chair"

      print(is_palindrome(palindrome_one))
      print(is_palindrome(palindrome_two))
      print(is_palindrome(not_palindrome))


  if __name__ == '__main__':
      main()
  ```


path: zk/Python_package_management.md
sortable_path: zkPython_package_management.md
title: Package management
checksum: 8d33751ae6c257ae49886e5c708ebcc0595691303be95e90dc2af0e07d212900
created: 2024-03-30 09:03:20.686965827+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["python","data-types"]}
lead:
  - You can use `conda` (the package manager that comes with `anaconda`). This
    makes it easier to work with conflicting package libraries (a bit like a
    package lock).

body:
  - You can use `conda` (the package manager that comes with `anaconda`). This
    makes it easier to work with conflicting package libraries (a bit like a
    package lock).

  - The alternative is the native `pip` but you have to create virtual
    environments (`venv`) to manage packages at different versions.

  To make use of virtual environments in `pip` you have to create the virtual
  environment before installing anything:

  ```
  python3 -m venv venv
  ```

  This will create a `venv` directory in your project that will manage the
  handling of modules.

  > This is especially important in ArchLinux since without a virtual environment
  > it will ask you to install packages with `pacman` which is annoying for local
  > packages.

  You then activate the environment with:

  ```
  source venv3/bin/activate
  ```

  Now you can install packages:

  ```
  pip [library_name]
  ```

  ## requirements.txt

  The `requirements.txt` file is similar to the `package.json` in Node projects.

  Each line of the `requirements.txt`` file specifies a package that your project
  depends on and, optionally, the desired version of that package. When you share
  your project with others or move it between different environments (e.g., from
  development to production), this file makes it easy to set up your environment
  with the right dependencies.

  ### Example

  ```
  Flask==1.1.2
  requests>=2.24.0
  numpy~=1.19.2
  pandas
  ```

  ### Generate requirements file

  ```
  pip freeze > requirements.txt

  ```

  ## Pypy

  - pypi.org is package registry like NPM

raw_content:
  ---
  tags: [python, data-types]
  ---

  # Package management

  - You can use `conda` (the package manager that comes with `anaconda`). This
    makes it easier to work with conflicting package libraries (a bit like a
    package lock).

  - The alternative is the native `pip` but you have to create virtual
    environments (`venv`) to manage packages at different versions.

  To make use of virtual environments in `pip` you have to create the virtual
  environment before installing anything:

  ```
  python3 -m venv venv
  ```

  This will create a `venv` directory in your project that will manage the
  handling of modules.

  > This is especially important in ArchLinux since without a virtual environment
  > it will ask you to install packages with `pacman` which is annoying for local
  > packages.

  You then activate the environment with:

  ```
  source venv3/bin/activate
  ```

  Now you can install packages:

  ```
  pip [library_name]
  ```

  ## requirements.txt

  The `requirements.txt` file is similar to the `package.json` in Node projects.

  Each line of the `requirements.txt`` file specifies a package that your project
  depends on and, optionally, the desired version of that package. When you share
  your project with others or move it between different environments (e.g., from
  development to production), this file makes it easy to set up your environment
  with the right dependencies.

  ### Example

  ```
  Flask==1.1.2
  requests>=2.24.0
  numpy~=1.19.2
  pandas
  ```

  ### Generate requirements file

  ```
  pip freeze > requirements.txt

  ```

  ## Pypy

  - pypi.org is package registry like NPM


path: zk/Queue.md
sortable_path: zkQueue.md
title: Queues
lead: _Visualization of the queue data structure_
checksum: 07770787a6624e0fccf35e0a770a641009f179bfe320a3ef9237fe7dcdc9210f
created: 2024-03-30 09:03:20.712348837+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["data-structures"]}
body:
  _Visualization of the queue data structure_

  ![queue.svg](queue.svg)

  ## A queue is a sequential data structure and most similar to a stack

  A queue is basically a stack inverted and can be visualised as a line of people
  waiting to be served. The person that is first into the queue is served first.
  The people who join the queue behind this person will be served after them.

  As a result it can be summarised as **'first in, first out'** or FIFO.

  Just like a stack it is a sequential data structure without random access. You
  cannot access all the elements at one instant, you can only access the oldest
  element. If you wish to access the newest element, you have to move through all
  the others that are ahead of it first, at which point it becomes the oldest.

  It differs from a stack in that a stack only has one point of transaction: the
  front or 'top' of the stack. With a stack you add and remove from the top or
  front. With a queue, you have two points of transaction: the front of the queue
  for removing elements and the back of the queue for adding elements.

  We can however add a 'peek' method to see which is the next element in line to
  come out.

  As we are removing the first element added, we use an `array shift` method to
  remove items from the front of the array.

  Removing an element from the queue is called **dequeuing**. Adding an element to
  the queue is called **enqueuing**. In terms of the tail/head nomenclature, the
  end of the queue where elements are enqueued is the **tail** and front of the
  queue, where elements are removed is the **head**.

  ```js
  class Queue {
    items = []; // array to store the elements comprising the queue
    enqueue = (element) => this.items.push(element); // add element to back
    dequeue = () => this.items.shift(); // remove element from the front

    // Optional helper methods:
    isEmpty = () => this.items.length === 0; // return true if the queue is empty
    clear = () => (this.items.length = 0); // empty the queue
    size = () => this.items.length; // count elements in queue
    peek = () => (!this.isEmpty() ? this.items[0] : undefined); // check which element is next in line
  }
  ```

  ## Use cases

  - A queue sequences data in the order that it was first received. Thus it is
    most beneficial in scenarios where receipt time is a factor. For example,
    imagine a service whereby tickets go on sale at a certain time for a limited
    period. You may want to prioritise those who sent their payment earliest over
    those who arrived later.
  - Serving requests on a single shared resource like a printer or CPU task
    scheduling.

raw_content:
  ---
  tags:
    - data-structures
  ---

  # Queues

  _Visualization of the queue data structure_

  ![queue.svg](queue.svg)

  ## A queue is a sequential data structure and most similar to a stack

  A queue is basically a stack inverted and can be visualised as a line of people
  waiting to be served. The person that is first into the queue is served first.
  The people who join the queue behind this person will be served after them.

  As a result it can be summarised as **'first in, first out'** or FIFO.

  Just like a stack it is a sequential data structure without random access. You
  cannot access all the elements at one instant, you can only access the oldest
  element. If you wish to access the newest element, you have to move through all
  the others that are ahead of it first, at which point it becomes the oldest.

  It differs from a stack in that a stack only has one point of transaction: the
  front or 'top' of the stack. With a stack you add and remove from the top or
  front. With a queue, you have two points of transaction: the front of the queue
  for removing elements and the back of the queue for adding elements.

  We can however add a 'peek' method to see which is the next element in line to
  come out.

  As we are removing the first element added, we use an `array shift` method to
  remove items from the front of the array.

  Removing an element from the queue is called **dequeuing**. Adding an element to
  the queue is called **enqueuing**. In terms of the tail/head nomenclature, the
  end of the queue where elements are enqueued is the **tail** and front of the
  queue, where elements are removed is the **head**.

  ```js
  class Queue {
    items = []; // array to store the elements comprising the queue
    enqueue = (element) => this.items.push(element); // add element to back
    dequeue = () => this.items.shift(); // remove element from the front

    // Optional helper methods:
    isEmpty = () => this.items.length === 0; // return true if the queue is empty
    clear = () => (this.items.length = 0); // empty the queue
    size = () => this.items.length; // count elements in queue
    peek = () => (!this.isEmpty() ? this.items[0] : undefined); // check which element is next in line
  }
  ```

  ## Use cases

  - A queue sequences data in the order that it was first received. Thus it is
    most beneficial in scenarios where receipt time is a factor. For example,
    imagine a service whereby tickets go on sale at a certain time for a limited
    period. You may want to prioritise those who sent their payment earliest over
    those who arrived later.
  - Serving requests on a single shared resource like a printer or CPU task
    scheduling.


path: zk/Quote_marks_in_Bash.md
sortable_path: zkQuote_marks_in_Bash.md
title: Quote marks in Bash
lead: ## Single-quotes (aka _strong_ quotes)
checksum: 9a8b2de955dcbf7f9a4a1cd5944dfd9aaf4e40a8d3eb764f5759eae1c1f39d51
created: 2024-03-30 09:03:20.717735752+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["shell"]}
body:
  ## Single-quotes (aka _strong_ quotes)

  Bash will interpret everything in the string as a literal:

  ```bash
  echo 'The directory is $(pwd)'
  # The directory is $(pwd)
  ```

  ## Double-quotes

  Bash will interpret strings as strings but will interpret expansions and
  substitutions as executable processes:

  ```bash
  $pointlessVar='directory'

  echo "The ${pointlessVar}"

  # The directory is /home/thomas
  ```

  It is therefore generally best to use double quotes whenever we wish to return
  mixed values.

raw_content:
  ---
  tags:
    - shell
  ---

  # Quote marks in Bash

  ## Single-quotes (aka _strong_ quotes)

  Bash will interpret everything in the string as a literal:

  ```bash
  echo 'The directory is $(pwd)'
  # The directory is $(pwd)
  ```

  ## Double-quotes

  Bash will interpret strings as strings but will interpret expansions and
  substitutions as executable processes:

  ```bash
  $pointlessVar='directory'

  echo "The ${pointlessVar}"

  # The directory is /home/thomas
  ```

  It is therefore generally best to use double quotes whenever we wish to return
  mixed values.


path: zk/RESTful_APIs.md
sortable_path: zkRESTful_APIs.md
title: RESTful APIs
lead: ## Definition of an API
checksum: facc11be40d1315db02b61b5b886a72db631eb09de9e4aa7aeeb283823c7b63a
created: 2024-03-30 09:03:20.722448378+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["APIs","REST"]}
body:
  ## Definition of an API

  An application programming interface (API) is a set of definitions and protocols
  for building and integrating application software. It can be thought of as a
  contract between an information provider and an informational consumer. The API
  is a mediator between the clients and the resources they wish to acquire from a
  server or database.

  ## REST

  REST stands for **Representational State Transfer**. It is a set of
  _architectural constraints_ on the structure of an API rather than a fixed
  protocol. It is a particular way of implementing client-server interaction over
  HTTP.

  When a request is made from a client to resources via RESTful API, the API
  transfers a representation of of the state of the resource to the requester or
  endpoint. The information is delivered via HTTP. The format can be of several
  types (HTML, XML, plaintext, Python, PHP etc) but is generally JSON because of
  its broad compatibility with multiple programming languages.

  ### Key constraints

  In order to qualify as RESTful, an API must meet the following constraints:

  1. **Uniform interface**: Possess a client-server architecture with request
     manage through HTTP
  1. **Client-server decoupling** : The client and server applications must be
     completely independent of one another. The _only_ information the client
     should know about the server is the URI it uses to request the resource, it
     can't interact with the server in any other way. Likewise, the server
     shouldn't modify the client application in any way (contrast for example SSR)
     other than passing the requested data via HTTP.
  1. **Statelessness** Server applications should not be able to store any data
     related to a client request. The request alone should contain all the
     information necessary for processing it, without recourse to any specifics of
     the client application. For example, a specification of POST with a certain
     JSON body and header authentication will be all that is provided to the
     server.
  1. **Cacheability** Where possible, resources should be cacheable on the client
     or server side. Server responses must contain information about whether
     caching is allowed for the delivered resource (you see this in the headers in
     the DevTools console). The goal here is to improve performance on the client
     side whilst increasing scaleability on the server side.
  1. **Layered system architecture** It may be the case that the data flow between
     the client and the server is not direct. For instance the request may be
     funneled through middleware or another program before it is received by the
     server. Similarly there may be several steps before the client receives the
     requested data. Whilst one should not assume a direct correspondence, REST
     APIs need to be designed so that neither the client nor the server can tell
     whether it communicates with the end application or an intermediary.

  ## Example

  A basic example of a REST API would be a series of methods corresponding to the
  main [HTTP request types](HTTP_request_types.md).

  | HTTP request type | URI                 | Action                      | Body ?                   |
  | ----------------- | ------------------- | --------------------------- | ------------------------ |
  | GET               | /api/customers      | Retrieve customers as array | No                       |
  | GET               | /api/customers/guid | Get a specific customer     | No, data comes from GUID |
  | PUT               | /api/customers/guid | Update an existing customer | Yes                      |
  | DELETE            | /api/customers/1    | Delete a customer           | No, data comes from GUID |
  | POST              | /api/customers      | Create a new customer       | Yes                      |

  // Add: PATCH and explain differenct from PUT

raw_content:
  ---
  tags: [APIs, REST]
  ---

  # RESTful APIs

  ## Definition of an API

  An application programming interface (API) is a set of definitions and protocols
  for building and integrating application software. It can be thought of as a
  contract between an information provider and an informational consumer. The API
  is a mediator between the clients and the resources they wish to acquire from a
  server or database.

  ## REST

  REST stands for **Representational State Transfer**. It is a set of
  _architectural constraints_ on the structure of an API rather than a fixed
  protocol. It is a particular way of implementing client-server interaction over
  HTTP.

  When a request is made from a client to resources via RESTful API, the API
  transfers a representation of of the state of the resource to the requester or
  endpoint. The information is delivered via HTTP. The format can be of several
  types (HTML, XML, plaintext, Python, PHP etc) but is generally JSON because of
  its broad compatibility with multiple programming languages.

  ### Key constraints

  In order to qualify as RESTful, an API must meet the following constraints:

  1. **Uniform interface**: Possess a client-server architecture with request
     manage through HTTP
  1. **Client-server decoupling** : The client and server applications must be
     completely independent of one another. The _only_ information the client
     should know about the server is the URI it uses to request the resource, it
     can't interact with the server in any other way. Likewise, the server
     shouldn't modify the client application in any way (contrast for example SSR)
     other than passing the requested data via HTTP.
  1. **Statelessness** Server applications should not be able to store any data
     related to a client request. The request alone should contain all the
     information necessary for processing it, without recourse to any specifics of
     the client application. For example, a specification of POST with a certain
     JSON body and header authentication will be all that is provided to the
     server.
  1. **Cacheability** Where possible, resources should be cacheable on the client
     or server side. Server responses must contain information about whether
     caching is allowed for the delivered resource (you see this in the headers in
     the DevTools console). The goal here is to improve performance on the client
     side whilst increasing scaleability on the server side.
  1. **Layered system architecture** It may be the case that the data flow between
     the client and the server is not direct. For instance the request may be
     funneled through middleware or another program before it is received by the
     server. Similarly there may be several steps before the client receives the
     requested data. Whilst one should not assume a direct correspondence, REST
     APIs need to be designed so that neither the client nor the server can tell
     whether it communicates with the end application or an intermediary.

  ## Example

  A basic example of a REST API would be a series of methods corresponding to the
  main [HTTP request types](HTTP_request_types.md).

  | HTTP request type | URI                 | Action                      | Body ?                   |
  | ----------------- | ------------------- | --------------------------- | ------------------------ |
  | GET               | /api/customers      | Retrieve customers as array | No                       |
  | GET               | /api/customers/guid | Get a specific customer     | No, data comes from GUID |
  | PUT               | /api/customers/guid | Update an existing customer | Yes                      |
  | DELETE            | /api/customers/1    | Delete a customer           | No, data comes from GUID |
  | POST              | /api/customers      | Create a new customer       | Yes                      |

  // Add: PATCH and explain differenct from PUT


path: zk/Reciprocals.md
sortable_path: zkReciprocals.md
title: Recipricols
checksum: ed560badcaa7ccb1a422db87818532d451e6e49d4b8d43d356089e8f9edf1af6
created: 2024-03-30 09:03:20.758504708+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["prealgebra","fractions","theorems"]}
lead:
  The [Property of Multiplicative Identity](Multiplicative%20identity.md) applies
  to fractions as well as to whole numbers:

body:
  The [Property of Multiplicative Identity](Multiplicative%20identity.md) applies
  to fractions as well as to whole numbers:

  $$
  \frac{a}{b} \cdot 1 = \frac{a}{b}
  $$

  With fractions there is a related property: the **Multiplicative Inverse**.

  > If $\frac{a}{b}$ is any fraction, the fraction $\frac{b}{a}$ is called the
  > _multiplicative inverse_ or _reciprocol_ of $\frac{a}{b}$. The product of a
  > fraction multiplied by its reciprocol will always be 1. $$ \frac{a}{b} \cdot
  > \frac{b}{a} = 1$$

  For example:

  $$
  \frac{3}{4} \cdot \frac{4}{3} = \frac{12}{12} = 1
  $$

  In this case $\frac{4}{3}$ is the reciprocol or multiplicative inverse of
  $\frac{3}{4}$.

  This accords with what we know a fraction to be: a representation of an amount
  that is less than one whole. When we multiply a fraction by its reciprocol, we
  demonstrate that it makes up one whole.

  This also means that whenever we have a whole number $n$, we can represent it
  fractionally by expressing it as $\frac{n}{1}$

raw_content:
  ---
  tags:
    - prealgebra
    - fractions
    - theorems
  ---

  # Recipricols

  The [Property of Multiplicative Identity](Multiplicative%20identity.md) applies
  to fractions as well as to whole numbers:

  $$
  \frac{a}{b} \cdot 1 = \frac{a}{b}
  $$

  With fractions there is a related property: the **Multiplicative Inverse**.

  > If $\frac{a}{b}$ is any fraction, the fraction $\frac{b}{a}$ is called the
  > _multiplicative inverse_ or _reciprocol_ of $\frac{a}{b}$. The product of a
  > fraction multiplied by its reciprocol will always be 1. $$ \frac{a}{b} \cdot
  > \frac{b}{a} = 1$$

  For example:

  $$
  \frac{3}{4} \cdot \frac{4}{3} = \frac{12}{12} = 1
  $$

  In this case $\frac{4}{3}$ is the reciprocol or multiplicative inverse of
  $\frac{3}{4}$.

  This accords with what we know a fraction to be: a representation of an amount
  that is less than one whole. When we multiply a fraction by its reciprocol, we
  demonstrate that it makes up one whole.

  This also means that whenever we have a whole number $n$, we can represent it
  fractionally by expressing it as $\frac{n}{1}$


path: zk/Recursion.md
sortable_path: zkRecursion.md
title: Schema
checksum: 4a8a39f5c37cf98784efe3d990418f7685f37a2348e7edcdb0b155b05a8e4b80
created: 2024-03-30 09:03:20.771235769+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["algorithms","data-structures","recursion"]}
lead:
  The general structure of a recursive function is as follows:
  ![javascript-recursion.png](javascript-recursion.png)

body:
  The general structure of a recursive function is as follows:
  ![javascript-recursion.png](javascript-recursion.png)

  ## Why use recursive functions?

  Recursion is made for solving problems that can be broken down into smaller,
  repetitive problems. It is especially good for working on things that have many
  possible branches but are too complex for an iterative approach or too costly in
  terms of memory and time complexity.

  Recursive programming differs from **iterative** programming but both have
  similar use cases. Looping is a canonical example of working iteratively.

  ## Base condition

  > Once a condition is met, the function stops calling itself. This is called a
  > base condition.

  Because recursion has the potential for infinity, to use it, we must specify a
  point at which it ends. However as we are not using an iterative approach we
  cannot rely on `while` or `foreach` to specify the boundaries of its operation.
  We call this the **base condition** and this will typically be specified using
  conditional logic.

  The schema for a recursive function with a base condition is:

  ```jsx
  function recurse() {
    if (condition) {
      recurse();
    } else {
      // stop calling recurse()
    }
  }

  recurse();
  ```

  ## Demonstrations

  ### Countdown

  ```jsx
  // program to count down numbers to 1
  function countDown(number) {
    // display the number
    console.log(number);

    // decrease the number value
    let newNumber = number - 1;

    // base case
    if (newNumber > 0) {
      countDown(newNumber);
    }
  }

  countDown(4);
  ```

  - This code takes `4` as it's input and then outputs a countdown returning:
    `4, 3, 2, 1`
  - In each iteration, the number value is decreased by 1
  - The base condition is `newNumber > 0` . This breaks the recursive loop once
    the output reaches `1` and stops it continuing into negative integers.

  Each stage in the process is noted below:

  ```
  countDown(4) prints 4 and calls countDown(3)
  countDown(3) prints 3 and calls countDown(2)
  countDown(2) prints 2 and calls countDown(1)
  countDown(1) prints 1 and calls countDown(0)
  ```

  ### Finding factorials

  > The factorial of a positive integer **n** is the product of all the positive
  > integers less than or equal to **n**.

  To arrive at the factorial of **n** you subtract 1 from **n** and multiply the
  result of the subtraction by **n**. You repeat until the subtractive process
  runs out of positive integers. For example, if 4 is **n,** the factorial of
  **n** is **24:**

  $$ 4 _ 3 _ 2 \*1 $$

  4 multiplied by 3 gives you 12, 12 multiplied by 2 gives you 24. 24 multiplied
  by 1 is 24.

  This is clearly a process that could be implemented with a recursive function:

  ```js
  // program to find the factorial of a number
  function factorial(x) {
    // if number is 0
    if (x === 0) {
      return 1;
    }

    // if number is positive
    else {
      return x * factorial(x - 1);
    }
  }

  let num = 3;

  // calling factorial() if num is non-negative
  if (num > 0) {
    let result = factorial(num);
    console.log(`The factorial of ${num} is ${result}`);
  }
  ```

  ![javascript-factorial 1.png](javascript-factorial%201.png)

raw_content:
  ---
  tags:
    - algorithms
    - data-structures
    - recursion
  ---

  > A recursive function is a function that calls itself in its definition.

  More generally recursion means when a thing is defined in terms of itself. There
  are visual analogues that help to represent the idea such as the Droste effect
  where an image contains a version of itself within itself. The ouroboros is
  another example. Also fractals display recursive properties.

  ## Schema

  The general structure of a recursive function is as follows:
  ![javascript-recursion.png](javascript-recursion.png)

  ## Why use recursive functions?

  Recursion is made for solving problems that can be broken down into smaller,
  repetitive problems. It is especially good for working on things that have many
  possible branches but are too complex for an iterative approach or too costly in
  terms of memory and time complexity.

  Recursive programming differs from **iterative** programming but both have
  similar use cases. Looping is a canonical example of working iteratively.

  ## Base condition

  > Once a condition is met, the function stops calling itself. This is called a
  > base condition.

  Because recursion has the potential for infinity, to use it, we must specify a
  point at which it ends. However as we are not using an iterative approach we
  cannot rely on `while` or `foreach` to specify the boundaries of its operation.
  We call this the **base condition** and this will typically be specified using
  conditional logic.

  The schema for a recursive function with a base condition is:

  ```jsx
  function recurse() {
    if (condition) {
      recurse();
    } else {
      // stop calling recurse()
    }
  }

  recurse();
  ```

  ## Demonstrations

  ### Countdown

  ```jsx
  // program to count down numbers to 1
  function countDown(number) {
    // display the number
    console.log(number);

    // decrease the number value
    let newNumber = number - 1;

    // base case
    if (newNumber > 0) {
      countDown(newNumber);
    }
  }

  countDown(4);
  ```

  - This code takes `4` as it's input and then outputs a countdown returning:
    `4, 3, 2, 1`
  - In each iteration, the number value is decreased by 1
  - The base condition is `newNumber > 0` . This breaks the recursive loop once
    the output reaches `1` and stops it continuing into negative integers.

  Each stage in the process is noted below:

  ```
  countDown(4) prints 4 and calls countDown(3)
  countDown(3) prints 3 and calls countDown(2)
  countDown(2) prints 2 and calls countDown(1)
  countDown(1) prints 1 and calls countDown(0)
  ```

  ### Finding factorials

  > The factorial of a positive integer **n** is the product of all the positive
  > integers less than or equal to **n**.

  To arrive at the factorial of **n** you subtract 1 from **n** and multiply the
  result of the subtraction by **n**. You repeat until the subtractive process
  runs out of positive integers. For example, if 4 is **n,** the factorial of
  **n** is **24:**

  $$ 4 _ 3 _ 2 \*1 $$

  4 multiplied by 3 gives you 12, 12 multiplied by 2 gives you 24. 24 multiplied
  by 1 is 24.

  This is clearly a process that could be implemented with a recursive function:

  ```js
  // program to find the factorial of a number
  function factorial(x) {
    // if number is 0
    if (x === 0) {
      return 1;
    }

    // if number is positive
    else {
      return x * factorial(x - 1);
    }
  }

  let num = 3;

  // calling factorial() if num is non-negative
  if (num > 0) {
    let result = factorial(num);
    console.log(`The factorial of ${num} is ${result}`);
  }
  ```

  ![javascript-factorial 1.png](javascript-factorial%201.png)


path: zk/Redirect_to_dev_null.md
sortable_path: zkRedirect_to_dev_null.md
title: Redirecting to /dev/null
lead: You'll see the following a lot when reading shell scripts:
checksum: 1864a7e2ce0dd078e01afeec825f780ffbceb38790e093fb936a971e522965c6
created: 2024-03-30 09:03:20.77646325+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["shell"]}
body:
  You'll see the following a lot when reading shell scripts:

  ```bash
  [some_command] > /dev/null 2>&1
  ```

  This is a redirection statement. It is redirecting data to the `null` device on
  Unix systems. Basically to a black hole or shredder where you can't access it
  because you don't want it to be output to `stdout`.

  The `2>&1` argument is the content: any errors that the program may generate and
  try to show in stout. Notice we are using the
  [file descriptors](File_descriptors_and_redirection.md)
  `1` and `2`.

  If you just want the errors regardless of whether they appear in `stdout` you
  do:

  ```bash
  [some_command] > /dev/null 2>
  ```

  ## Example

  I have used this in my Mongo start-up script:

  ```
  mongodb-compass > /dev/null 2>&1
  ```

  Here, I just want the MongoDB Compass application to start, I don't care if it
  wants to output complaints to the stout.

raw_content:
  ---
  tags:
    - shell
  ---

  # Redirecting to `/dev/null`

  You'll see the following a lot when reading shell scripts:

  ```bash
  [some_command] > /dev/null 2>&1
  ```

  This is a redirection statement. It is redirecting data to the `null` device on
  Unix systems. Basically to a black hole or shredder where you can't access it
  because you don't want it to be output to `stdout`.

  The `2>&1` argument is the content: any errors that the program may generate and
  try to show in stout. Notice we are using the
  [file descriptors](File_descriptors_and_redirection.md)
  `1` and `2`.

  If you just want the errors regardless of whether they appear in `stdout` you
  do:

  ```bash
  [some_command] > /dev/null 2>
  ```

  ## Example

  I have used this in my Mongo start-up script:

  ```
  mongodb-compass > /dev/null 2>&1
  ```

  Here, I just want the MongoDB Compass application to start, I don't care if it
  wants to output complaints to the stout.


path: zk/Reducing_fractions.md
sortable_path: zkReducing_fractions.md
title: Reducing fractions to their lowest terms
checksum: 703afbdd4fe816aed30ce456b551228944ce7cd82547bdac44fba35c544b2362
created: 2024-03-30 09:03:20.801961098+00:00
modified: 2024-03-27 10:45:52.506858751+00:00
metadata: {"tags":["prealgebra","fractions"]}
lead:
  > A fraction is said to be _reduced to its lowest terms_ if the
  > [greatest common divisor](Factors%20and%20divisors.md#greatest-common-divisor)
  > of the numerator and the denominator is $1$.

body:
  > A fraction is said to be _reduced to its lowest terms_ if the
  > [greatest common divisor](Factors%20and%20divisors.md#greatest-common-divisor)
  > of the numerator and the denominator is $1$.

  > Whenever we reduce a fraction, the resultant fraction will always be
  > [equivalent](Equivalent%20fractions.md) to the fraction we started with.

  Thus the fraction $\frac{2}{3}$ is reduced to its lowest terms because the
  greatest common divisor is 1. Neither the numerator or the denominator can be
  reduced to any lower terms. In contrast, the fraction $\frac{4}{6}$ is not
  reduced to its lowest terms because the greatest common divisor of both 4 and 6
  is 2, not 1.

  ### 1. Reducing with repeated application of divisors

  The following demonstrates the process of reducing a fraction to its lowest
  terms in a series of steps:

  $$
  \\frac{18}{24} = \frac{18/2}{24/2} = \frac{9}{12} = \frac{9/3}{12/3} = \frac{3}{4}
  $$

  \_Once we get to $\frac{3}{4}$ the greatest common divisor is 1, therefore
  $\frac{18}{24}$ has been reduced to its lowest terms \_.

  ### 2. Reducing in one step with the highest common divisor

  In the previous example the reduction took two steps: first we divided by two
  and then we divided by three. There is a more efficient way: find the
  [highest common divisor](Factors%20and%20divisors.md#greatest-common-divisor) of
  the numerator and denominator and then use this as the basis for the reduction.
  With this method, the reduction can be completed in a single step.

  The greatest common divisor of 18 and 24 is 6, thus:

  $$
  \\frac{18}{24} = \frac{18/6}{24/6} = \frac{3}{4}
  $$

  Note how our earlier two divisors 2 and 3 are
  [factors](Factors%20and%20divisors.md#factors) of 6, showing the consistency
  between the two methods.

  ### 3. Reducing with factors and cancellation

  The two methods above are not very systematic and are quite heuristic. The third
  approach is more systematic and relies on the
  [interchangeability of factors and divisors](Factors%20and%20divisors.md).

  Instead of thinking asking what is the greatest common divisor of 18 and 24 we
  could ask: which single number can we multiply by to get 18 and 24? Obviously
  both numbers are in the six times table. This is therefore to say that 6 is a
  [factor](Factors%20and%20divisors.md#factors) of both: we can multiply some
  number by 6 to arrive at both 18 and 24. The numbers are 3 and 4 respectively:

  $$
  \\begin{split}
  3 \cdot 6 = 18 \\
  4 \cdot 6  = 24
  \\end{split}
  $$

  Here, 3 and 4 are the multiplicands of the factor 6. As $\frac{3}{4}$ doesn't
  have a lower common factor, it is therefore defined in its lowest terms.

  Once we have reached this point, we no longer need the common factor 6, we can
  therefore cancel it out, leaving the multiplicands as the reduced fraction:

  $$
  \\begin{split}
  3  \cancel{\cdot6= 18}\\
  4  \cancel{\cdot6= 24}
  \\end{split}
  $$

  ### 4. Reducing with prime factorisation

  This is still a bit long-winded however particularly when finding the factors of
  larger numbers because we have to go through the factors of both numbers to find
  the largest held in common.

  A better method is to utilise [prime factorization](Prime%20factorization.md)
  combined with the canceling technique.

  First we find the prime factors of both the numerator and denominator:
  ![drawio-Page-7.drawio.png](drawio-Page-7.drawio.png)

  This gives us:

  $$
  \\frac{18}{24} = \frac{2 \cdot 3 \cdot 3}{2 \cdot 2 \cdot 2 \cdot 3}
  $$

  We then cancel out the factors held in common between the numerator and
  denominator:

  $$
  \\frac{\cancel{2} \cdot \cancel{3} \cdot 3}{\cancel{2} \cdot 2 \cdot 2 \cdot \cancel{3}}
  $$

  This gives us:

  $$
  \\frac{3}{2 \cdot 2}
  $$

  We then simplify the fraction as normal to its lowest term (conducting any
  multiplications required by what is left from the prime factorization):

  $$
  \\frac{3}{4}
  $$

  ## Reducing fractions that contain variables

  Superficially this looks to be more difficult but in fact we can apply the same
  prime factorization method to get the result.

  ### Demonstration

  _Reduce the following fraction to its lowest terms: $$\frac{25a^3b}{40a^2b^3}$$_

  The prime factors of the numerator and denominator:

  $$
  \\begin{split}
  25 = {5, 5} \\
  40 = {2,2,2,5}
  \\end{split}
  $$

  Now we apply canceling but we include the variable parts, treating them exactly
  the same as the coefficients. We break them out of their exponents however.

  $$\frac{25a^3b}{40a^2b^3} =\frac{5 \cdot 5 \cdot a \cdot a \cdot a \cdot b}{2 \cdot 2 \cdot 2 \cdot 5 \cdot a \cdot a \cdot b \cdot b \cdot b }$$

  Canceled:

  $$\frac{\cancel{5} \cdot 5 \cdot  \cancel{a} \cdot \cancel{a} \cdot a \cdot \cancel{b}}{2 \cdot 2 \cdot 2 \cdot \cancel{5} \cdot \cancel{a} \cdot \cancel{a} \cdot \cancel{b} \cdot b \cdot b }$$

  Which gives us:

  $$
  \\frac{5 \cdot a}{2 \cdot 2 \cdot 2 \cdot b \cdot b} = \frac{5a}{8b^2}
  $$

  ## Reducing fractions that contain negative values

  _Reduce the following fraction to its lowest terms: $$\frac{14y^5}{-35y^3}$$_

  - This fraction is an instance of a
    [fraction with unlike terms](Handling%20negative%20fractions.md#fractions-with-unlike-terms).

  - Apply [Prime factorization](Prime%20factorization.md):

    ![draw.io-Page-8.drawio.png](draw.io-Page-8.drawio.png)

  - Cancel the coefficients and variable parts

    $$
    \\frac{14y^5}{-35y^3}=\frac{5 \cdot 7 \cdot 2 \cdot y \cdot y \cdot y \cdot y \cdot y}{-5 \cdot 7  \cdot y \cdot y \cdot y} = - \frac{2y^2}{5}
    $$

  _Reduce the following fraction to its lowest terms:
  $$\frac{- 12xy^2}{ - 18xy^2}$$_

  - This fraction is an instance of a
    [fraction with like terms](Handling%20negative%20fractions.md#fractions-with-like-terms).

  - Apply [Prime factorization](Prime%20factorization.md):

  ![draw.io-Page-8.drawio 1.png](draw.io-Page-8.drawio%201.png)

  - Cancel the coefficients and variable parts

    $$

    * \\frac{12xy^2}{18xy^2}=\frac{3 \cdot 2 \cdot 2 \cdot x \cdot y \cdot y}{3 \cdot 7  \cdot 3 \cdot 2 \cdot x \cdot x \cdot y} = - \frac{2y}{3x}
    $$

raw_content:
  ---
  tags:
    - prealgebra
    - fractions
  ---

  ## Reducing fractions to their lowest terms

  > A fraction is said to be _reduced to its lowest terms_ if the
  > [greatest common divisor](Factors%20and%20divisors.md#greatest-common-divisor)
  > of the numerator and the denominator is $1$.

  > Whenever we reduce a fraction, the resultant fraction will always be
  > [equivalent](Equivalent%20fractions.md) to the fraction we started with.

  Thus the fraction $\frac{2}{3}$ is reduced to its lowest terms because the
  greatest common divisor is 1. Neither the numerator or the denominator can be
  reduced to any lower terms. In contrast, the fraction $\frac{4}{6}$ is not
  reduced to its lowest terms because the greatest common divisor of both 4 and 6
  is 2, not 1.

  ### 1. Reducing with repeated application of divisors

  The following demonstrates the process of reducing a fraction to its lowest
  terms in a series of steps:

  $$
  \\frac{18}{24} = \frac{18/2}{24/2} = \frac{9}{12} = \frac{9/3}{12/3} = \frac{3}{4}
  $$

  \_Once we get to $\frac{3}{4}$ the greatest common divisor is 1, therefore
  $\frac{18}{24}$ has been reduced to its lowest terms \_.

  ### 2. Reducing in one step with the highest common divisor

  In the previous example the reduction took two steps: first we divided by two
  and then we divided by three. There is a more efficient way: find the
  [highest common divisor](Factors%20and%20divisors.md#greatest-common-divisor) of
  the numerator and denominator and then use this as the basis for the reduction.
  With this method, the reduction can be completed in a single step.

  The greatest common divisor of 18 and 24 is 6, thus:

  $$
  \\frac{18}{24} = \frac{18/6}{24/6} = \frac{3}{4}
  $$

  Note how our earlier two divisors 2 and 3 are
  [factors](Factors%20and%20divisors.md#factors) of 6, showing the consistency
  between the two methods.

  ### 3. Reducing with factors and cancellation

  The two methods above are not very systematic and are quite heuristic. The third
  approach is more systematic and relies on the
  [interchangeability of factors and divisors](Factors%20and%20divisors.md).

  Instead of thinking asking what is the greatest common divisor of 18 and 24 we
  could ask: which single number can we multiply by to get 18 and 24? Obviously
  both numbers are in the six times table. This is therefore to say that 6 is a
  [factor](Factors%20and%20divisors.md#factors) of both: we can multiply some
  number by 6 to arrive at both 18 and 24. The numbers are 3 and 4 respectively:

  $$
  \\begin{split}
  3 \cdot 6 = 18 \\
  4 \cdot 6  = 24
  \\end{split}
  $$

  Here, 3 and 4 are the multiplicands of the factor 6. As $\frac{3}{4}$ doesn't
  have a lower common factor, it is therefore defined in its lowest terms.

  Once we have reached this point, we no longer need the common factor 6, we can
  therefore cancel it out, leaving the multiplicands as the reduced fraction:

  $$
  \\begin{split}
  3  \cancel{\cdot6= 18}\\
  4  \cancel{\cdot6= 24}
  \\end{split}
  $$

  ### 4. Reducing with prime factorisation

  This is still a bit long-winded however particularly when finding the factors of
  larger numbers because we have to go through the factors of both numbers to find
  the largest held in common.

  A better method is to utilise [prime factorization](Prime%20factorization.md)
  combined with the canceling technique.

  First we find the prime factors of both the numerator and denominator:
  ![drawio-Page-7.drawio.png](drawio-Page-7.drawio.png)

  This gives us:

  $$
  \\frac{18}{24} = \frac{2 \cdot 3 \cdot 3}{2 \cdot 2 \cdot 2 \cdot 3}
  $$

  We then cancel out the factors held in common between the numerator and
  denominator:

  $$
  \\frac{\cancel{2} \cdot \cancel{3} \cdot 3}{\cancel{2} \cdot 2 \cdot 2 \cdot \cancel{3}}
  $$

  This gives us:

  $$
  \\frac{3}{2 \cdot 2}
  $$

  We then simplify the fraction as normal to its lowest term (conducting any
  multiplications required by what is left from the prime factorization):

  $$
  \\frac{3}{4}
  $$

  ## Reducing fractions that contain variables

  Superficially this looks to be more difficult but in fact we can apply the same
  prime factorization method to get the result.

  ### Demonstration

  _Reduce the following fraction to its lowest terms: $$\frac{25a^3b}{40a^2b^3}$$_

  The prime factors of the numerator and denominator:

  $$
  \\begin{split}
  25 = {5, 5} \\
  40 = {2,2,2,5}
  \\end{split}
  $$

  Now we apply canceling but we include the variable parts, treating them exactly
  the same as the coefficients. We break them out of their exponents however.

  $$\frac{25a^3b}{40a^2b^3} =\frac{5 \cdot 5 \cdot a \cdot a \cdot a \cdot b}{2 \cdot 2 \cdot 2 \cdot 5 \cdot a \cdot a \cdot b \cdot b \cdot b }$$

  Canceled:

  $$\frac{\cancel{5} \cdot 5 \cdot  \cancel{a} \cdot \cancel{a} \cdot a \cdot \cancel{b}}{2 \cdot 2 \cdot 2 \cdot \cancel{5} \cdot \cancel{a} \cdot \cancel{a} \cdot \cancel{b} \cdot b \cdot b }$$

  Which gives us:

  $$
  \\frac{5 \cdot a}{2 \cdot 2 \cdot 2 \cdot b \cdot b} = \frac{5a}{8b^2}
  $$

  ## Reducing fractions that contain negative values

  _Reduce the following fraction to its lowest terms: $$\frac{14y^5}{-35y^3}$$_

  - This fraction is an instance of a
    [fraction with unlike terms](Handling%20negative%20fractions.md#fractions-with-unlike-terms).

  - Apply [Prime factorization](Prime%20factorization.md):

    ![draw.io-Page-8.drawio.png](draw.io-Page-8.drawio.png)

  - Cancel the coefficients and variable parts

    $$
    \\frac{14y^5}{-35y^3}=\frac{5 \cdot 7 \cdot 2 \cdot y \cdot y \cdot y \cdot y \cdot y}{-5 \cdot 7  \cdot y \cdot y \cdot y} = - \frac{2y^2}{5}
    $$

  _Reduce the following fraction to its lowest terms:
  $$\frac{- 12xy^2}{ - 18xy^2}$$_

  - This fraction is an instance of a
    [fraction with like terms](Handling%20negative%20fractions.md#fractions-with-like-terms).

  - Apply [Prime factorization](Prime%20factorization.md):

  ![draw.io-Page-8.drawio 1.png](draw.io-Page-8.drawio%201.png)

  - Cancel the coefficients and variable parts

    $$

    * \\frac{12xy^2}{18xy^2}=\frac{3 \cdot 2 \cdot 2 \cdot x \cdot y \cdot y}{3 \cdot 7  \cdot 3 \cdot 2 \cdot x \cdot x \cdot y} = - \frac{2y}{3x}
    $$


