# notes

path: zk/Current.md
sortable_path: zkCurrent.md
title: Current
checksum: b689469a60842075415535b6c96df93b85b0ad05280ef0c26502b6faf90ec404
created: 2024-03-30 09:03:19.289722477+00:00
modified: 2024-05-12 10:41:24.48393581+00:00
metadata: {"tags":["physics","electricity"]}
lead:
  > Electrical current is the movement of electrons from negatively charged atoms
  > to positively charged atoms when an appropriate external force is applied.

body:
  > Electrical current is the movement of electrons from negatively charged atoms
  > to positively charged atoms when an appropriate external force is applied.

  Current is the flow of electrons. Charge is the quantity that flows.

  > The amount of current is the sum of the charges of the moving electrons past a
  > given point.

  ## Why current exists

  Current exists because of the [first law of electrostatics](Coulombs_Laws.md).

  When there is an excess of electrons at one terminal (i.e. negatively charged
  atoms) and a deficiency of electrons at the other terminal (i.e. positively
  charged atoms), a [_difference of potential_](Voltage.md) exists between the two
  terminals.

  When the terminals are connected to each other via a conductor (e.g. copper
  wire) electrons will flow along the conductor. This is provided that there is a
  source to supply electrons at one end and remove them at the other. We call this
  force the **voltage source**.

  _The diagram below illustrates the flow of current where the circles are
  electrons knocking into each other and passing current:_

  ![](/img/charge-cylinder.svg)

  > Electrons travel very slowly through a conductor. This is in contrast to their
  > intrinsic motion which of course equal to the speed of light (186, 000 miles
  > per second).

  ## Formal expression

  We measure **charge** in Coulombs ($C$). A Coulomb is an aggregate of the charge
  of thousands of electrons because their individual charge is so small. One
  Coulomb is equal to the charge of $6.24 \times 10 ^{18}$ electrons.

  We measure **current** in amps. When one coulomb of charge moves past a point in
  one second it is called an **ampere** (amp) represented as $A$.

  This relationship is captured in the following equation:

  $$
  I = \frac{Q}{t}
  $$

  - $I$ = current measured in amps
  - $Q$ = quantity of electrical charge measured in coulombs
  - $t$ = time

  ### Application

  _Calculate the current in amps if 9 coulombs of charge flow past a point in an
  electric circuit in 3 seconds._

  $$
  I = \frac{9}{3} \\
  I = 3  A
  $$

  ## Direction of current

  In terms of the physics, current flows due to the movement of charged particles
  from atoms which are negatively charged to atoms which are positively charged.
  However when it comes to the representation of current in electrical circuits
  the convention is to view current as flowing from the positive to the negative
  terminal.

  This is obviously physically inaccurate however it became the convention before
  electrons were properly understood when charge was thought to be a property of
  protons. So we are stuck with this convention in electronics.

raw_content:
  ---
  tags: [physics, electricity]
  ---

  # Current

  > Electrical current is the movement of electrons from negatively charged atoms
  > to positively charged atoms when an appropriate external force is applied.

  Current is the flow of electrons. Charge is the quantity that flows.

  > The amount of current is the sum of the charges of the moving electrons past a
  > given point.

  ## Why current exists

  Current exists because of the [first law of electrostatics](Coulombs_Laws.md).

  When there is an excess of electrons at one terminal (i.e. negatively charged
  atoms) and a deficiency of electrons at the other terminal (i.e. positively
  charged atoms), a [_difference of potential_](Voltage.md) exists between the two
  terminals.

  When the terminals are connected to each other via a conductor (e.g. copper
  wire) electrons will flow along the conductor. This is provided that there is a
  source to supply electrons at one end and remove them at the other. We call this
  force the **voltage source**.

  _The diagram below illustrates the flow of current where the circles are
  electrons knocking into each other and passing current:_

  ![](/img/charge-cylinder.svg)

  > Electrons travel very slowly through a conductor. This is in contrast to their
  > intrinsic motion which of course equal to the speed of light (186, 000 miles
  > per second).

  ## Formal expression

  We measure **charge** in Coulombs ($C$). A Coulomb is an aggregate of the charge
  of thousands of electrons because their individual charge is so small. One
  Coulomb is equal to the charge of $6.24 \times 10 ^{18}$ electrons.

  We measure **current** in amps. When one coulomb of charge moves past a point in
  one second it is called an **ampere** (amp) represented as $A$.

  This relationship is captured in the following equation:

  $$
  I = \frac{Q}{t}
  $$

  - $I$ = current measured in amps
  - $Q$ = quantity of electrical charge measured in coulombs
  - $t$ = time

  ### Application

  _Calculate the current in amps if 9 coulombs of charge flow past a point in an
  electric circuit in 3 seconds._

  $$
  I = \frac{9}{3} \\
  I = 3  A
  $$

  ## Direction of current

  In terms of the physics, current flows due to the movement of charged particles
  from atoms which are negatively charged to atoms which are positively charged.
  However when it comes to the representation of current in electrical circuits
  the convention is to view current as flowing from the positive to the negative
  terminal.

  This is obviously physically inaccurate however it became the convention before
  electrons were properly understood when charge was thought to be a property of
  protons. So we are stuck with this convention in electronics.


path: zk/Daemons.md
sortable_path: zkDaemons.md
title: Daemons
checksum: c68d2518c9b3a47544ea580d1616605774e5f6bfc8a3bd913918499079f09432
created: 2024-03-30 09:03:19.30034991+00:00
modified: 2024-06-16 17:00:08.848453206+00:00
metadata: {"tags":["operating-systems"]}
lead:
  A programme that runs in the background, performing a system function. A daemon
  typically starts at boot or login without having to be specifically launched and
  keeps running permanently or until manually stopped.

body:
  A programme that runs in the background, performing a system function. A daemon
  typically starts at boot or login without having to be specifically launched and
  keeps running permanently or until manually stopped.

  The custom is for the process name of daemons to end in the letter `d`.

  > In the original Greek _daemon_ is translated as 'helper', it doesn't have the
  > medieval connotation of a dark spirit but was used to denote the actions of a
  > god rather than the god itself. A person could have a personal _daemon_ which
  > determines their fate.

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

  # Daemons

  A programme that runs in the background, performing a system function. A daemon
  typically starts at boot or login without having to be specifically launched and
  keeps running permanently or until manually stopped.

  The custom is for the process name of daemons to end in the letter `d`.

  > In the original Greek _daemon_ is translated as 'helper', it doesn't have the
  > medieval connotation of a dark spirit but was used to denote the actions of a
  > god rather than the god itself. A person could have a personal _daemon_ which
  > determines their fate.


path: zk/Data_types_in_Bash.md
sortable_path: zkData_types_in_Bash.md
title: Data types in Bash
lead: > There is no typing in Bash
checksum: a575453d8b34c83eb06466afb39cded6c2e1ccb29a3ad296de2380077c2180bf
created: 2024-03-30 09:03:19.302314203+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["shell","data-types"]}
body:
  > There is no typing in Bash

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

  ## Declarations

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

  ### `-r` : readonly

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

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

  ### `i` : integer

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

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

  ### `a` : array

  ```bash
  declare -a anArray
  ```

raw_content:
  ---
  tags:
    - shell
    - data-types
  ---

  # Data types in Bash

  > There is no typing in Bash

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

  ## Declarations

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

  ### `-r` : readonly

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

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

  ### `i` : integer

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

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

  ### `a` : array

  ```bash
  declare -a anArray
  ```


path: zk/Data_types_in_MySQL.md
sortable_path: zkData_types_in_MySQL.md
title: Data types in MySQL
checksum: 82304955f39da2a125b588c134f3a7669bdf3361175d7902dd983eded0067f80
created: 2024-03-30 09:03:19.304125358+00:00
modified: 2024-06-16 17:00:36.836017743+00:00
metadata: {"tags":["SQL","databases","data-types"]}
lead:
  An understanding of the differences between data types is important because it
  obviously affects the amount of memory the database uses.

body:
  An understanding of the differences between data types is important because it
  obviously affects the amount of memory the database uses.

  ## Numeric

  ### Integer (`INT`)

  - No fractional part, fixed number of digits
  - `TINYINT`, `BIGINT` etc are all instances of `INT`, the difference is the size
    of the number stored.

  ### Fixed point (`DECIMAL`)

  Contains decimal point. Use when accuracy is more important that representing
  very large or very small values. Hence you would use this for monetary amounts.

  ### Floating point (`FLOAT`)

  Contains decimal point. Use when the ability to represent very large and very
  small values is more important than precision. Hence you would not use this for
  monetary amounts.

  ## String

  ### Fixed-length character strings ()

  Spaces are stripped in storage but represented with a character set.

  An example would be `CHAR(10)` or `CHAR(3)`. Here we set the upper limit but it
  must be the case that no string exceeds it. For either of these, if you add a
  value that is, e.g, two characters in length, it will add spaces to pad it out
  and make up the upper limit.

  ### Variable-length character strings

  As above but allowing for variable-length strings.

  A common example is `VARCHAR(255)`. The 255 refers to the maximal character
  length, not the byte length. It is the largest number of characters that can be
  accommodated by an 8-bit number (byte). We must put `255` as the parameter even
  if our character lengths will be below this but where we don't know the minimum
  and maximum length.

  ## Large object storage

  ### Blob

  For storing images, audio and other binary media. Variants: `TINYBLOB`, `BLOB`,
  `MEDIUMBLOB` `LONGBLOB`

  ### Text

  Like blob but has character set Variants: `TINYTEXT`, `TEXT`, `MEDIUMTEXT`,
  `LONGTEXT`

  ## Date and time

  - `DATE`
  - `DATETIME`
  - `TIMESTAMP`

  ## Speciality

  ### Enumerations

  - `ENUM`: a single value from a list
  - `SET`: several values from a list

  ## Storage limits

  Each row can have a maximum of 65,535 bytes.

raw_content:
  ---
  tags:
    - SQL
    - databases
    - data-types
  ---

  # Data types in MySQL

  An understanding of the differences between data types is important because it
  obviously affects the amount of memory the database uses.

  ## Numeric

  ### Integer (`INT`)

  - No fractional part, fixed number of digits
  - `TINYINT`, `BIGINT` etc are all instances of `INT`, the difference is the size
    of the number stored.

  ### Fixed point (`DECIMAL`)

  Contains decimal point. Use when accuracy is more important that representing
  very large or very small values. Hence you would use this for monetary amounts.

  ### Floating point (`FLOAT`)

  Contains decimal point. Use when the ability to represent very large and very
  small values is more important than precision. Hence you would not use this for
  monetary amounts.

  ## String

  ### Fixed-length character strings ()

  Spaces are stripped in storage but represented with a character set.

  An example would be `CHAR(10)` or `CHAR(3)`. Here we set the upper limit but it
  must be the case that no string exceeds it. For either of these, if you add a
  value that is, e.g, two characters in length, it will add spaces to pad it out
  and make up the upper limit.

  ### Variable-length character strings

  As above but allowing for variable-length strings.

  A common example is `VARCHAR(255)`. The 255 refers to the maximal character
  length, not the byte length. It is the largest number of characters that can be
  accommodated by an 8-bit number (byte). We must put `255` as the parameter even
  if our character lengths will be below this but where we don't know the minimum
  and maximum length.

  ## Large object storage

  ### Blob

  For storing images, audio and other binary media. Variants: `TINYBLOB`, `BLOB`,
  `MEDIUMBLOB` `LONGBLOB`

  ### Text

  Like blob but has character set Variants: `TINYTEXT`, `TEXT`, `MEDIUMTEXT`,
  `LONGTEXT`

  ## Date and time

  - `DATE`
  - `DATETIME`
  - `TIMESTAMP`

  ## Speciality

  ### Enumerations

  - `ENUM`: a single value from a list
  - `SET`: several values from a list

  ## Storage limits

  Each row can have a maximum of 65,535 bytes.


path: zk/Database_indices.md
sortable_path: zkDatabase_indices.md
title: database indices
checksum: 9cfa9fde2d8540dd069121f58e1ec72718bcf6dfa36f6c85a9f4e52ca0720e9f
created: 2024-03-30 09:03:19.306481459+00:00
modified: 2024-06-15 09:49:16.038536139+00:00
metadata: {"tags":["databases"]}
lead:
  A database index is best understood by analogy with physical data. Imagine
  needing to find information about a specific topic in a very long book. Without
  being able to turn to the back and consult the index we would need to scan every
  page of the book looking for information on the topic we are seeking.

body:
  A database index is best understood by analogy with physical data. Imagine
  needing to find information about a specific topic in a very long book. Without
  being able to turn to the back and consult the index we would need to scan every
  page of the book looking for information on the topic we are seeking.

  Similarly with a database if we are looking for a specific value in a very long
  table it can be computationally costly and slow to iterate through every row
  looking for a specific value (this is known as a "full table scan").

  For this reason we use indexes. In a database an index is like a shortcut or
  quick reference guide. However instead of page numbers as in the book analogy,
  the database index has pointers to the actual location of the rows in the table.
  If an index is applied to a given column, when an element in that column is
  requested, the database will not scan every row, instead it will go straight to
  the element using the index.

  Say we are looking for the surname "Bishop" in a table that consists of
  `lastName` and `phoneNumber` . If we apply an index to the `lastName` column,
  the database will have a direct reference to the row that "Bishop" is in and
  thus can return the `phoneNumber` value more quickly.

  Although the programmer can explicitly create indexes, the way the database
  utilises it is largely abstracted from view. Most RDMSs will automatically
  decide which index to use for query optimisation however the programmer can also
  use hints to force the use of a specific index.

  The main thing is to think about data points that would benefit from an index,
  for instance for the most heavily used queries or those that run against the
  longest tables.

  While indices make lookups more performant, they should be used judiciously as
  they increase the storage space the database requires and can incresae the
  execution time of queries that insert or update records. (This makes sense, as
  any update to a row, requires a reindexation of the table.)

  The best use case for an index is for data retrieval that utilises `WHERE`
  clauses.

  ## SQL syntax

  ```sql
  CREATE INDEX index_name ON table_name(column_name)
  ```

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

  # database indices

  A database index is best understood by analogy with physical data. Imagine
  needing to find information about a specific topic in a very long book. Without
  being able to turn to the back and consult the index we would need to scan every
  page of the book looking for information on the topic we are seeking.

  Similarly with a database if we are looking for a specific value in a very long
  table it can be computationally costly and slow to iterate through every row
  looking for a specific value (this is known as a "full table scan").

  For this reason we use indexes. In a database an index is like a shortcut or
  quick reference guide. However instead of page numbers as in the book analogy,
  the database index has pointers to the actual location of the rows in the table.
  If an index is applied to a given column, when an element in that column is
  requested, the database will not scan every row, instead it will go straight to
  the element using the index.

  Say we are looking for the surname "Bishop" in a table that consists of
  `lastName` and `phoneNumber` . If we apply an index to the `lastName` column,
  the database will have a direct reference to the row that "Bishop" is in and
  thus can return the `phoneNumber` value more quickly.

  Although the programmer can explicitly create indexes, the way the database
  utilises it is largely abstracted from view. Most RDMSs will automatically
  decide which index to use for query optimisation however the programmer can also
  use hints to force the use of a specific index.

  The main thing is to think about data points that would benefit from an index,
  for instance for the most heavily used queries or those that run against the
  longest tables.

  While indices make lookups more performant, they should be used judiciously as
  they increase the storage space the database requires and can incresae the
  execution time of queries that insert or update records. (This makes sense, as
  any update to a row, requires a reindexation of the table.)

  The best use case for an index is for data retrieval that utilises `WHERE`
  clauses.

  ## SQL syntax

  ```sql
  CREATE INDEX index_name ON table_name(column_name)
  ```


path: zk/Database_options_on_AWS.md
sortable_path: zkDatabase_options_on_AWS.md
title: Database options
checksum: a5c244cc562aa40ca94942ab06e70fdc7a695aecd1f5ebae43330ff5938505a8
created: 2024-03-30 09:03:19.308378541+00:00
modified: 2024-06-16 17:00:45.486305241+00:00
metadata: {"tags":["AWS","databases"]}
lead:
  Amazon offers numerous database options however they are often named differently
  or are Amazon's own implementation of a certain database type:

body:
  Amazon offers numerous database options however they are often named differently
  or are Amazon's own implementation of a certain database type:

  - **Relational databases**

    - Amazon offers two core RDSs: **Amazon Aurora** and **Amazon RDS**. Both
      allow you to use MySQL and PostgreSQL as the management implementation. The
      differences come down to which management implmentations are supported,
      number of backups and throughput. For instance Aurora has higher
      performance, more logging and fail-safes.

  - **NoSQL**
    - Document-based databases
      - The obvious candidate for this kind of database is MongoDB. Amazon doesn't
        offer MongoDB it offers its own implementation which is almost identical
        and compatible with Mongo: **Amazon DocumentDB**. (There is some bitching
        about this from Mongo but they are competitors.)
    - Key-value database
      - A key-value database is basically a hash-table or a map
      - The Amazon implementation of this is **DynamoDB**
      - Confusingly, for NoSQL, a DynamoDB instance comprises "tables" which are
        groupings of keys and values. I guess "table" here is meant in terms of
        "hash table"
      - A cool feature is that you can subscribe to a DynamoDB table and run a
        lambda when a value changes.

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

  # Database options

  Amazon offers numerous database options however they are often named differently
  or are Amazon's own implementation of a certain database type:

  - **Relational databases**

    - Amazon offers two core RDSs: **Amazon Aurora** and **Amazon RDS**. Both
      allow you to use MySQL and PostgreSQL as the management implementation. The
      differences come down to which management implmentations are supported,
      number of backups and throughput. For instance Aurora has higher
      performance, more logging and fail-safes.

  - **NoSQL**
    - Document-based databases
      - The obvious candidate for this kind of database is MongoDB. Amazon doesn't
        offer MongoDB it offers its own implementation which is almost identical
        and compatible with Mongo: **Amazon DocumentDB**. (There is some bitching
        about this from Mongo but they are competitors.)
    - Key-value database
      - A key-value database is basically a hash-table or a map
      - The Amazon implementation of this is **DynamoDB**
      - Confusingly, for NoSQL, a DynamoDB instance comprises "tables" which are
        groupings of keys and values. I guess "table" here is meant in terms of
        "hash table"
      - A cool feature is that you can subscribe to a DynamoDB table and run a
        lambda when a value changes.


path: zk/Dates_in_Python.md
sortable_path: zkDates_in_Python.md
title: Dates in Python
checksum: ce2a04ff182611b8044c5cb5e1c817f34c6d919a8aa04c21bdc60e24a72a406a
created: 2024-03-30 09:03:19.310961637+00:00
modified: 2024-06-18 07:09:41.400853238+00:00
metadata: {"tags":["python","time"]}
lead:
  Python's built-in `datetime` module provides various classes for manipulating
  dates and times. Below are some common use-cases with examples.

body:
  Python's built-in `datetime` module provides various classes for manipulating
  dates and times. Below are some common use-cases with examples.

  ### Importing `datetime`

  First, you'll need to import the `datetime` module.

  ```python
  import datetime
  ```

  ### Getting Current Date and Time

  You can get the current date and time using `datetime.datetime.now()`.

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

  ### Creating Date Objects

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

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

  ### Creating Time Objects

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

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

  ### Creating Datetime Objects

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

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

  ### Extracting Components

  You can extract various components from a datetime object like so:

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

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

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

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

  In the example below we use `strftime` to express the current date as YYYY-MM:

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

  Another example, for YYYY-MM-DD H:M:S:

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

  Below is a real example that uses a [unix timestamp](./Time_and_computers.md) as
  the input:

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

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

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

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

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

  In the example below we receive a date in the format DD-MM-YYYY and we convert
  it to a datetime object:

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

raw_content:
  ---
  tags:
    - python
    - time
  ---

  # Dates in Python

  Python's built-in `datetime` module provides various classes for manipulating
  dates and times. Below are some common use-cases with examples.

  ### Importing `datetime`

  First, you'll need to import the `datetime` module.

  ```python
  import datetime
  ```

  ### Getting Current Date and Time

  You can get the current date and time using `datetime.datetime.now()`.

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

  ### Creating Date Objects

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

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

  ### Creating Time Objects

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

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

  ### Creating Datetime Objects

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

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

  ### Extracting Components

  You can extract various components from a datetime object like so:

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

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

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

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

  In the example below we use `strftime` to express the current date as YYYY-MM:

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

  Another example, for YYYY-MM-DD H:M:S:

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

  Below is a real example that uses a [unix timestamp](./Time_and_computers.md) as
  the input:

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

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

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

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

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

  In the example below we receive a date in the format DD-MM-YYYY and we convert
  it to a datetime object:

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


path: zk/DeMorgan's_Laws.md
sortable_path: zkDeMorgan's_Laws.md
title: DeMorgan's Laws
checksum: 7a208e606c14ba85bda0e5c2ade413a76b61fb299bba17055d8fd393b322aa2b
created: 2024-03-30 09:03:19.313628825+00:00
modified: 2024-06-16 17:01:11.297188906+00:00
metadata: {"tags":["theorems","logic","propositional-logic"]}
lead:
  DeMorgan's laws express some fundamental equivalences that obtain between the
  Boolean
  [connectives](Truth-functional_connectives.md).

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

  ## First Law

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

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

  The equivalence is demonstrated with the following truth-table

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

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

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

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

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

  # DeMorgan's Laws

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

  ## First Law

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

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

  The equivalence is demonstrated with the following truth-table

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

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

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

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


path: zk/Defining_a_computer.md
sortable_path: zkDefining_a_computer.md
title: Defining a computer
checksum: 91b1ae88b82b477f807eb2c374246e1f3509b02a425695543d589270133d9556
created: 2024-03-30 09:03:19.319880039+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["theory-of-computation"]}
lead:
  > A general-purpose computer is one that, given the appropriate instructions and
  > required time, should be able to perform most common computing tasks.

body:
  > A general-purpose computer is one that, given the appropriate instructions and
  > required time, should be able to perform most common computing tasks.

  This sets a general purpose computer aside from a special-purpose computer, like
  the one you might find in your dishwasher which may have its instructions
  hardwired or coded into the machine. Special purpose computers only perform a
  single set of tasks according to prewritten instructions. We’ll take the term
  _computer_ to mean general purpose computer.

  Simplified model of what a computer is:
  ![1.4-Input-Process-Output.png](1.4-Input-Process-Output.png)

  Although the input, output and storage parts of a computer are very important,
  they will not be the focus of this course. Instead we are going to learn all
  about the process part, which will focus on how the computer is able to follow
  instructions to make calculations.

  ## **Supplementary Resources**

  ### Early computing (_Crash Course Computer Science)_

  [Early Computing: Crash Course Computer Science #1](watch?v=O5nskjZ_GoI)

  - The abacus was created because the scale of society had become greater than
    what a single person could create and manipulate in their mind.
    - Eg thousands of people in a village and tens of thousands of cattle
  - In a basic abacus each row of beads (say its coloured) represents a different
    power of ten
  - As well as aiding calculation, the abacus acts as a primitive storage device
  - Similar early computing devices: astrolabe, slide rule, sunrise clocks, tide
    clocks

  > As each increase in knowledge, as well as on the contrivance of every new
  > tool, human labour becomes abridged. **Charles Babbage**

  - One of the first computers of the modern era was the Step Reckoner built by
    Leibniz in 1694.
    - In addition to adding, this machine was able to multiply and divide
      basically through hacks because from a mechanical point of view,
      multiplications and divisions are just many additions and subtractions
    - For example, to divide 17/5, we just subtract 5, then 5, then 5 again until
      we can't do anymore hence two left over
  - But as these machines were expensive and slow, people used pre-computed tables
    in book form generated by human computers. Useful particularly for things like
    square roots.
  - Similarly range tables were created that aided the military in calculating
    distances for gunboat artillery which factored in contextual factors like
    wind, drift, slope and elevation. These were used well into WW2 but they were
    limited to the particular type of cannon or shell

  ![Screenshot_2020-08-09_at_21.32.54 1.png](Screenshot_2020-08-09_at_21.32.54%201.png)

  ![Screenshot_2020-08-09_at_21.34.48.png](Screenshot_2020-08-09_at_21.34.48.png)

  > Before the invention of actual computers, 'computer' was a job-title denoting
  > people who were employed to conduct complex calculations, sometimes with the
  > aid of machinery, but most often not. This persisted until the late 18th
  > century when the word changed to include devices like adding machines.

  - Babbage sought to overcome this by designing the **Difference Engine** which
    was able to compute polynomials. Complex mathematical expressions that have
    constants, variables and exponent. He failed to complete it in his lifetime
    because of the complexity and number of intricate parts required. His model
    was eventually successfully created in the 90s using his designs and it
    worked.
    - But while he was coming up with this he also conceived of a better and
      general purpose computing device that wasn't limited to polynomial
      calculations → the Analytical Engine.
    - It could run operations in sequence and had memory and a primitive printer.
      It was way ahead of its time and was never completed.
    - Ada Lovelace wrote hypothetical programs for the Analytical Engine, hence
      she is considered the world's first computer programmer.
  - At this point then, computing was limited to scientific and engineering
    disciplines but in 1890, the US govt needed a computer in order to comply with
    the constitutional stipulation to have a census every ten years. This was
    getting increasingly difficult with the growing population - it would take
    more than 13 years to complete. This led to punch cards designed by Herman
    Hollereth. From this IBM was born

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

  # Defining a computer

  > A general-purpose computer is one that, given the appropriate instructions and
  > required time, should be able to perform most common computing tasks.

  This sets a general purpose computer aside from a special-purpose computer, like
  the one you might find in your dishwasher which may have its instructions
  hardwired or coded into the machine. Special purpose computers only perform a
  single set of tasks according to prewritten instructions. We’ll take the term
  _computer_ to mean general purpose computer.

  Simplified model of what a computer is:
  ![1.4-Input-Process-Output.png](1.4-Input-Process-Output.png)

  Although the input, output and storage parts of a computer are very important,
  they will not be the focus of this course. Instead we are going to learn all
  about the process part, which will focus on how the computer is able to follow
  instructions to make calculations.

  ## **Supplementary Resources**

  ### Early computing (_Crash Course Computer Science)_

  [Early Computing: Crash Course Computer Science #1](watch?v=O5nskjZ_GoI)

  - The abacus was created because the scale of society had become greater than
    what a single person could create and manipulate in their mind.
    - Eg thousands of people in a village and tens of thousands of cattle
  - In a basic abacus each row of beads (say its coloured) represents a different
    power of ten
  - As well as aiding calculation, the abacus acts as a primitive storage device
  - Similar early computing devices: astrolabe, slide rule, sunrise clocks, tide
    clocks

  > As each increase in knowledge, as well as on the contrivance of every new
  > tool, human labour becomes abridged. **Charles Babbage**

  - One of the first computers of the modern era was the Step Reckoner built by
    Leibniz in 1694.
    - In addition to adding, this machine was able to multiply and divide
      basically through hacks because from a mechanical point of view,
      multiplications and divisions are just many additions and subtractions
    - For example, to divide 17/5, we just subtract 5, then 5, then 5 again until
      we can't do anymore hence two left over
  - But as these machines were expensive and slow, people used pre-computed tables
    in book form generated by human computers. Useful particularly for things like
    square roots.
  - Similarly range tables were created that aided the military in calculating
    distances for gunboat artillery which factored in contextual factors like
    wind, drift, slope and elevation. These were used well into WW2 but they were
    limited to the particular type of cannon or shell

  ![Screenshot_2020-08-09_at_21.32.54 1.png](Screenshot_2020-08-09_at_21.32.54%201.png)

  ![Screenshot_2020-08-09_at_21.34.48.png](Screenshot_2020-08-09_at_21.34.48.png)

  > Before the invention of actual computers, 'computer' was a job-title denoting
  > people who were employed to conduct complex calculations, sometimes with the
  > aid of machinery, but most often not. This persisted until the late 18th
  > century when the word changed to include devices like adding machines.

  - Babbage sought to overcome this by designing the **Difference Engine** which
    was able to compute polynomials. Complex mathematical expressions that have
    constants, variables and exponent. He failed to complete it in his lifetime
    because of the complexity and number of intricate parts required. His model
    was eventually successfully created in the 90s using his designs and it
    worked.
    - But while he was coming up with this he also conceived of a better and
      general purpose computing device that wasn't limited to polynomial
      calculations → the Analytical Engine.
    - It could run operations in sequence and had memory and a primitive printer.
      It was way ahead of its time and was never completed.
    - Ada Lovelace wrote hypothetical programs for the Analytical Engine, hence
      she is considered the world's first computer programmer.
  - At this point then, computing was limited to scientific and engineering
    disciplines but in 1890, the US govt needed a computer in order to comply with
    the constitutional stipulation to have a census every ten years. This was
    getting increasingly difficult with the growing population - it would take
    more than 13 years to complete. This led to punch cards designed by Herman
    Hollereth. From this IBM was born


path: zk/Delete_a_branch.md
sortable_path: zkDelete_a_branch.md
title: Delete a branch
lead: ### Locally
checksum: 8bdc6ed30b6813dbda19b4722ec67bb23447d723185e9670f10e735cd171d9bf
created: 2024-03-30 09:03:19.324179349+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["git"]}
body:
  ### Locally

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

  ### Remote

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

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

  # Delete a branch

  ### Locally

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

  ### Remote

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


path: zk/Delete_records_in_an_SQL_table.md
sortable_path: zkDelete_records_in_an_SQL_table.md
title: Deleting data in SQL
lead: ## Deleting records
checksum: 0f5a8500dff9451b393026255cf78542e054fc9ca42f292d7fbb54d19d1e128d
created: 2024-03-30 09:03:19.327018217+00:00
modified: 2024-06-15 09:46:10.851493918+00:00
metadata: {"tags":["SQL","databases"]}
body:
  ## Deleting records

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

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

  ```sql
  DELETE FROM sales
  ```

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

  ```sql
  TRUNCATE TABLE sales
  ```

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

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

  ## Deleting tables

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

  ```sql
  DROP TABLE sales
  ```

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

  # Deleting data in SQL

  ## Deleting records

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

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

  ```sql
  DELETE FROM sales
  ```

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

  ```sql
  TRUNCATE TABLE sales
  ```

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

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

  ## Deleting tables

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

  ```sql
  DROP TABLE sales
  ```


path: zk/Dependency_injection.md
sortable_path: zkDependency_injection.md
title: Dependency injection
checksum: 406cdb5aa8d387282086d1dc9461cbabb5d4683f941b7eeb168ea8cac0ef15a4
created: 2024-03-30 09:03:19.330872222+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["OOP"]}
lead:
  Dependency injection is an object-oriented design pattern that allows for the
  decoupling of dependencies between objects making code more maintainable,
  testable and modular.

body:
  Dependency injection is an object-oriented design pattern that allows for the
  decoupling of dependencies between objects making code more maintainable,
  testable and modular.

  The basic idea is to resist having any class (C) depend on the specific
  implementation details of any other class to which it sustains a dependency (D).
  If we can abstract the implementation of D, this makes it easier to swap-out and
  change C's dependencies later on without having to re-write C as a result of
  changes to D.

  This is where interfaces become very helpful because they are schematic
  representations of the main methods in a given class (basically their names,
  params and return value). As long as this is kept consistent in D when changes
  are made, you avoid conflicts when changes or refactorings are made to D.

  ## Example

  In the example to follow we will have two classes:

  - `ConsoleLogger`
    - This will simply log something and be a dependency for the class below

  * `UserService`
    - This will depend on `ConsoleLogger`

  First we define an interface for the dependency:

  ```ts
  interface ILogger {
    log(message: string): void;
  }
  ```

  So this is a class that has a single method `log` which receives a `message`
  string as an argument and returns a side-effect.

  Now we'll implement this class:

  ```ts
  class ConsoleLogger implements ILogger {
    log(message: string): void {
      console.log(message);
    }
  }
  ```

  Now we create a class that depends on it:

  ```ts
  class UserService {
    private logger: ILogger;

    constructor(logger: ILogger) {
      this.logger = logger;
    }

    createUser(username: string): void {
      // ... (some logic to create a user)
      this.logger.log(`User created: ${username}`);
    }
  }
  ```

  We can see that the constructor references the `Logger` interface. Thus we
  inject the dependency when instantiating `UserService`:

  ```ts
  // First implement our earlier class that matches the `Logger` in its shape:
  const logger: ILogger = new ConsoleLogger();

  // Then pass it as a dependency:
  const userService: UserService = new UserService(logger);
  userService.createUser("John Doe");
  ```

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

  # Dependency injection

  Dependency injection is an object-oriented design pattern that allows for the
  decoupling of dependencies between objects making code more maintainable,
  testable and modular.

  The basic idea is to resist having any class (C) depend on the specific
  implementation details of any other class to which it sustains a dependency (D).
  If we can abstract the implementation of D, this makes it easier to swap-out and
  change C's dependencies later on without having to re-write C as a result of
  changes to D.

  This is where interfaces become very helpful because they are schematic
  representations of the main methods in a given class (basically their names,
  params and return value). As long as this is kept consistent in D when changes
  are made, you avoid conflicts when changes or refactorings are made to D.

  ## Example

  In the example to follow we will have two classes:

  - `ConsoleLogger`
    - This will simply log something and be a dependency for the class below

  * `UserService`
    - This will depend on `ConsoleLogger`

  First we define an interface for the dependency:

  ```ts
  interface ILogger {
    log(message: string): void;
  }
  ```

  So this is a class that has a single method `log` which receives a `message`
  string as an argument and returns a side-effect.

  Now we'll implement this class:

  ```ts
  class ConsoleLogger implements ILogger {
    log(message: string): void {
      console.log(message);
    }
  }
  ```

  Now we create a class that depends on it:

  ```ts
  class UserService {
    private logger: ILogger;

    constructor(logger: ILogger) {
      this.logger = logger;
    }

    createUser(username: string): void {
      // ... (some logic to create a user)
      this.logger.log(`User created: ${username}`);
    }
  }
  ```

  We can see that the constructor references the `Logger` interface. Thus we
  inject the dependency when instantiating `UserService`:

  ```ts
  // First implement our earlier class that matches the `Logger` in its shape:
  const logger: ILogger = new ConsoleLogger();

  // Then pass it as a dependency:
  const userService: UserService = new UserService(logger);
  userService.createUser("John Doe");
  ```


path: zk/Devices.md
sortable_path: zkDevices.md
title: Devices
checksum: 6de61052c674a54d88ad453d796fb611ab5c138dcbb087c4ab2118ca47f14a94
created: 2024-03-30 09:03:19.332697951+00:00
modified: 2024-06-16 17:07:34.947487389+00:00
metadata: {"tags":["computer-architecture","Linux"]}
lead:
  Devices are hardware that require access to the CPU in order to function.
  Devices can either be external and plugged-in or internal to the motherboard.
  The most common type of device that you will work with are [disks](Disks.md).

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

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

  Some of the most important device files are:

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

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

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

  ## Listing devices

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

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

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

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

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

  ## /dev/null

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

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

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

raw_content:
  ---
  tags:
    - computer-architecture
    - Linux
  ---

  # Devices

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

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

  Some of the most important device files are:

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

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

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

  ## Listing devices

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

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

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

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

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

  ## /dev/null

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

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

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


path: zk/Dictionaries_in_Python.md
sortable_path: zkDictionaries_in_Python.md
title: Dictionaries in Python
lead: Dictionaries are basically the Python equivalent of objects in JS.
checksum: a5f6f775ead758dd7de19dba2c5e1bda25e1db7198d7c5e6d7e9f4b66511a060
created: 2024-03-30 09:03:19.3441987+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["python","data-structures"]}
body:
  Dictionaries are basically the Python equivalent of objects in JS.

  Dictionaries:

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

  ## Basic usage

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

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

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

  ## Accessing entries

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

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

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

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

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

  ## Predicates

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

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

  ## Looping

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

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

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

  ## Updating values

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

  ## Removing values

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

  # Remove specific entry by key

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

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

  ## Containers as values

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

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

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

  ```

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

  # Dictionaries in Python

  Dictionaries are basically the Python equivalent of objects in JS.

  Dictionaries:

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

  ## Basic usage

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

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

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

  ## Accessing entries

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

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

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

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

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

  ## Predicates

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

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

  ## Looping

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

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

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

  ## Updating values

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

  ## Removing values

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

  # Remove specific entry by key

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

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

  ## Containers as values

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

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

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

  ```


path: zk/Difference_between_remote_origin_and_head.md
sortable_path: zkDifference_between_remote_origin_and_head.md
title: Difference between remote, origin, HEAD
checksum: c0b7aae8d9772b0f16670bb0380ab04a0489b3ce8a8f3f2d089153edae18aa6c
created: 2024-03-30 09:03:19.346076645+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["git"]}
lead:
  <dl>
  <dt> remote
  </dt>
  <dd>
  A remote is a git repository that is hosted on a server which you can interact with over a network. You can have more than one remote for a local repository.
  </dd>

body:
  <dl>
  <dt> remote
  </dt>
  <dd>
  A remote is a git repository that is hosted on a server which you can interact with over a network. You can have more than one remote for a local repository.
  </dd>

  <dt>
  origin
  </dt>
  <dd>
  The default name given to the remote repository that was cloned. When you clone a repository, git automatically creates a default remote called `origin`. This is essentially an alias for the URL of this repository.
  </dd>

  <dt>HEAD</dt>
  <dd>A reference to the most recent commit on the currently checked-out branch. It points to the tip of the branch which is the commit that was most recently added. If you have changes in your working directory that have yet to be committed, the HEAD will be behind them. Once you commit them, the HEAD will update to reflect the new commit.</dd>
  </dl>

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

  # Difference between _remote_, _origin_, _HEAD_

  <dl>
  <dt> remote
  </dt>
  <dd>
  A remote is a git repository that is hosted on a server which you can interact with over a network. You can have more than one remote for a local repository.
  </dd>

  <dt>
  origin
  </dt>
  <dd>
  The default name given to the remote repository that was cloned. When you clone a repository, git automatically creates a default remote called `origin`. This is essentially an alias for the URL of this repository.
  </dd>

  <dt>HEAD</dt>
  <dd>A reference to the most recent commit on the currently checked-out branch. It points to the tip of the branch which is the commit that was most recently added. If you have changes in your working directory that have yet to be committed, the HEAD will be behind them. Once you commit them, the HEAD will update to reflect the new commit.</dd>
  </dl>


path: zk/Digital_circuits.md
sortable_path: zkDigital_circuits.md
title: Digital circuits
checksum: 49b7645e2344f15361e859e27e3f1c0b1855c63f5bb45211e8125b1747c0771a
created: 2024-03-30 09:03:19.348306154+00:00
modified: 2024-06-16 17:04:59.436222257+00:00
metadata: {"tags":["electronics"]}
lead:
  Ultimately every process in a computer is the product of a digital
  [circuit](Circuits.md) that is
  working on binary values. In contrast to electrical circuits, digital circuits
  are not represented in an
  [analogue](Analogue_and_digital.md) fashion.

body:
  Ultimately every process in a computer is the product of a digital
  [circuit](Circuits.md) that is
  working on binary values. In contrast to electrical circuits, digital circuits
  are not represented in an
  [analogue](Analogue_and_digital.md) fashion.

  Analogue circuits work on the basis of real continuous phenomena in the world:
  charges and currents. As a result, the key properties of a circuit - voltage,
  current and resistance - can vary over a wide range of values. This is the
  reason that we require components like batteries and resistors: to control the
  natural flow of current and ensure that it only runs within desired parameters.

  In a standard electrical circuit, voltage, current and resistance can vary over
  a wide range of values however in the binary context we want to deal with
  discrete values (zeros and ones) which can be fed into the various
  [logic gates](Logic_gates.md).

  We therefore need a way to represent 'on' and 'off' as single quantities. We do
  this by stipulating that a given voltage corresponds to 'on' (high) and another
  corresponds to 'off' (low). Of course these are not really discrete values since
  voltage is inherently analogue but we basically binary-encode them. Formally
  'on' has a voltage of 1 and 'off' has a voltage of 0. In reality 'on' tends to
  be within 2-5V depending on the circuit design and anything between 0 - 0.8V is
  considered off.

  The [transistor](Transistors.md) is
  the electrical component that enables us to represent given voltage ranges as
  being 'on' or 'off'.

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

  # Digital circuits

  Ultimately every process in a computer is the product of a digital
  [circuit](Circuits.md) that is
  working on binary values. In contrast to electrical circuits, digital circuits
  are not represented in an
  [analogue](Analogue_and_digital.md) fashion.

  Analogue circuits work on the basis of real continuous phenomena in the world:
  charges and currents. As a result, the key properties of a circuit - voltage,
  current and resistance - can vary over a wide range of values. This is the
  reason that we require components like batteries and resistors: to control the
  natural flow of current and ensure that it only runs within desired parameters.

  In a standard electrical circuit, voltage, current and resistance can vary over
  a wide range of values however in the binary context we want to deal with
  discrete values (zeros and ones) which can be fed into the various
  [logic gates](Logic_gates.md).

  We therefore need a way to represent 'on' and 'off' as single quantities. We do
  this by stipulating that a given voltage corresponds to 'on' (high) and another
  corresponds to 'off' (low). Of course these are not really discrete values since
  voltage is inherently analogue but we basically binary-encode them. Formally
  'on' has a voltage of 1 and 'off' has a voltage of 0. In reality 'on' tends to
  be within 2-5V depending on the circuit design and anything between 0 - 0.8V is
  considered off.

  The [transistor](Transistors.md) is
  the electrical component that enables us to represent given voltage ranges as
  being 'on' or 'off'.


path: zk/Disjunction_Elimination.md
sortable_path: zkDisjunction_Elimination.md
title: Disjunction Elimination
checksum: 7f173ec0a93b95d149f19249c22f5a5ffccfdf6df0d2f3e3fda3c98360e87747
created: 2024-03-30 09:03:19.359457739+00:00
modified: 2024-06-16 17:05:04.796453326+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  This rule is sometimes also referred to as _Constructive Dilemma_. This can be a
  bit tricky to understand because the goal is to derive or _introduce_ a new
  proposition separate from the disjunction you start out with. This may be
  disjunction, a single proposition or a proposition containing any other logical
  connective. You do this by constructing two sub-proofs, one for each of the
  disjuncts comprising the disjunction you start out with. If you can derive your
  target proposition as the conclusion of each subproof then you may invoke the
  conclusion in the main proof and take it to be derived.

body:
  This rule is sometimes also referred to as _Constructive Dilemma_. This can be a
  bit tricky to understand because the goal is to derive or _introduce_ a new
  proposition separate from the disjunction you start out with. This may be
  disjunction, a single proposition or a proposition containing any other logical
  connective. You do this by constructing two sub-proofs, one for each of the
  disjuncts comprising the disjunction you start out with. If you can derive your
  target proposition as the conclusion of each subproof then you may invoke the
  conclusion in the main proof and take it to be derived.

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

  _Here is an example where Disjunction Elimination is used to derive a new
  disjunction._

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

  _Here are two further examples that use Disjunction Elimination to derive
  singular propositions_

  ![](/img/ORelim1.png) ![](/img/ORelim2.png)

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

  # Disjunction Elimination

  This rule is sometimes also referred to as _Constructive Dilemma_. This can be a
  bit tricky to understand because the goal is to derive or _introduce_ a new
  proposition separate from the disjunction you start out with. This may be
  disjunction, a single proposition or a proposition containing any other logical
  connective. You do this by constructing two sub-proofs, one for each of the
  disjuncts comprising the disjunction you start out with. If you can derive your
  target proposition as the conclusion of each subproof then you may invoke the
  conclusion in the main proof and take it to be derived.

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

  _Here is an example where Disjunction Elimination is used to derive a new
  disjunction._

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

  _Here are two further examples that use Disjunction Elimination to derive
  singular propositions_

  ![](/img/ORelim1.png) ![](/img/ORelim2.png)


path: zk/Disjunction_Introduction.md
sortable_path: zkDisjunction_Introduction.md
title: Disjunction Introduction
checksum: 4576bf445c7c1d2bdd9b1e94c21ecfcb8ba78f6ce1a1418624d1f31c303f8718
created: 2024-03-30 09:03:19.362440853+00:00
modified: 2024-06-16 17:05:10.790045764+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  This rule can seem a little odd: like we are randomly introducing an additional
  proposition without giving any justification. However this is just a consequence
  of the fact if $P$ is true, so is $P \lor Q$ since disjunction is not the same
  as conjunction: only one disjunct needs to be true for the compound disjunction
  to be true. This is represented in the context of
  [truth-trees](Truth-trees.md#disjunction-decomposition) by the fact that truth
  can pass up via either branch of a disjunction pattern. This rule is sometimes
  also referred to (confusingly) as _Addition_.

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

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

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

  # Disjunction Introduction

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

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


path: zk/Disk_info.md
sortable_path: zkDisk_info.md
title: Disk info routines
lead: ## Current disk capacity
body:
  ## Current disk capacity

  ```bash
  df -h
  ```

  ## Disk last modified

  ```bash
  stat [disk_name]
  ```

checksum: bdeb75c2bcaddad15572392e84d8debbe782a6d8b4916f8eb312eb97da586c28
created: 2024-03-30 09:03:19.367355916+00:00
modified: 2024-06-16 17:05:20.193787139+00:00
metadata: {"tags":["systems-programming","disks","procedural","Linux"]}
raw_content:
  ---
  tags:
    - systems-programming
    - disks
    - procedural
    - Linux
  ---

  # Disk info routines

  ## Current disk capacity

  ```bash
  df -h
  ```

  ## Disk last modified

  ```bash
  stat [disk_name]
  ```


path: zk/Distributivity.md
sortable_path: zkDistributivity.md
title: The Distributive Property of Multiplication
lead: **Let $a$, $b$ represent members of $\mathbb{W}$ or $\mathbb{Z}$ then:**
checksum: fa7ad55cc241b2dc37b5bf9989ac5ca93c402ee18f91c5fe0c4baa3ad25c9956
created: 2024-03-30 09:03:19.369779952+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["theorems","prealgebra"]}
body:
  **Let $a$, $b$ represent members of $\mathbb{W}$ or $\mathbb{Z}$ then:**

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

  ### Demonstration

  When faced with $4(2\cdot3)$ we may proceed with the official order of
  operations in algebra, namely:

  ```
  4 x (2 + 3) = 4 x (5)
  = 20
  ```

  In other words we find the sum of the values in parentheses and then multiply
  this by the value outside of the brackets.

  When we use distributive property we _distribute_ each value in the parentheses
  against the value outside of the parentheses:

  ```
  4 x (2 + 3) = (4 x 2) + (4 x 3)
  8 + 12 = 20
  ```

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

  # The Distributive Property of Multiplication

  **Let $a$, $b$ represent members of $\mathbb{W}$ or $\mathbb{Z}$ then:**

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

  ### Demonstration

  When faced with $4(2\cdot3)$ we may proceed with the official order of
  operations in algebra, namely:

  ```
  4 x (2 + 3) = 4 x (5)
  = 20
  ```

  In other words we find the sum of the values in parentheses and then multiply
  this by the value outside of the brackets.

  When we use distributive property we _distribute_ each value in the parentheses
  against the value outside of the parentheses:

  ```
  4 x (2 + 3) = (4 x 2) + (4 x 3)
  8 + 12 = 20
  ```


path: zk/Dividing_fractions.md
sortable_path: zkDividing_fractions.md
title: Dividing fractions
lead: Suppose you have the following shape:
checksum: 1fe7f186db7bc0e8c405690c643beddd293f0bbddb16ad23660201ae7c1e86a3
created: 2024-03-30 09:03:19.373227338+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["prealgebra","fractions"]}
body:
  Suppose you have the following shape:

  ![draw.io-Page-9.drawio 1.png](draw.io-Page-9.drawio.png)

  One part is shaded. This represents one-eighth of the original shape.

  ![one-eighth-a.png](one-eighth-a.png)

  Now imagine there are four instances of the shape and one-eighth remains shaded.
  How man one-eighths are there in four?

  ![draw.io-Page-9.drawio 2.png](draw.io-Page-9.drawio.png)

  The shaded proportion represents $\frac{1}{8}$ of the shape. Imagine four of
  these shapes, how many eighths are there?

  This is a division statement: to find how many one-eighths there are we would
  calculate:

  $$
  4 \div \frac{1}{8}
  $$

  But actually it makes more sense to think of this as a multiplication. There are
  four shapes of eight parts meaning there are $4 \cdot 8$ parts in total, 32. One
  of these parts is shaded making it equal to $\frac{1}{32}$.

  From this we realise that when we divide fractions by an amount, we can express
  the calculation in terms of multiplication and arrive at the correct answer:

  $$
  4 \div \frac{1}{8} = 4 \cdot 8 = 32
  $$

  Note that we omit the numerator but that technically the answer would be
  $\frac{1}{32}$.

  ### Formal specification of how to divide fractions

  We combine the foregoing (that it is easier to divide by fractional amounts
  using multiplication) with the concept of a [reciprocol](Reciprocals.md) to
  arrive at a definitive method for dividing two fractions. It boils down to:
  _invert and multiply_:

  If $\frac{a}{b}$ and $\frac{c}{d}$ are fractions then:
  $$\frac{a}{b} \div \frac{c}{d} = \frac{a}{b} \cdot \frac{d}{c}$$

  We invert the divisor (the second factor) and change the operator from division
  to multiplication.

  #### Demonstration

  Divide $\frac{1}{2}$ by $\frac{3}{5}$

  $$
  \begin{split}
  \frac{1}{2} \div \frac{3}{5}  = \frac{1}{2} \cdot \frac{5}{3}  \\
  = \frac{5}{5}
  \end{split}
  $$

  Divide $\frac{-6}{x}$ by $\frac{-12}{x^2}$

  $$
  \begin{split}
  \frac{-6}{x} \div \frac{12}{x^2} = \frac{-6}{x} \cdot \frac{x^2}{-12} \\ =
  \frac{(\cancel{3} \cdot \cancel{2} )}{\cancel{x}} \cdot \frac{(\cancel{x} \cdot \cancel{x} )}{\cancel{3} \cdot \cancel{2} \cdot 2} \\ =
  \frac{x}{2}

  \end{split}


  $$

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

  # Dividing fractions

  Suppose you have the following shape:

  ![draw.io-Page-9.drawio 1.png](draw.io-Page-9.drawio.png)

  One part is shaded. This represents one-eighth of the original shape.

  ![one-eighth-a.png](one-eighth-a.png)

  Now imagine there are four instances of the shape and one-eighth remains shaded.
  How man one-eighths are there in four?

  ![draw.io-Page-9.drawio 2.png](draw.io-Page-9.drawio.png)

  The shaded proportion represents $\frac{1}{8}$ of the shape. Imagine four of
  these shapes, how many eighths are there?

  This is a division statement: to find how many one-eighths there are we would
  calculate:

  $$
  4 \div \frac{1}{8}
  $$

  But actually it makes more sense to think of this as a multiplication. There are
  four shapes of eight parts meaning there are $4 \cdot 8$ parts in total, 32. One
  of these parts is shaded making it equal to $\frac{1}{32}$.

  From this we realise that when we divide fractions by an amount, we can express
  the calculation in terms of multiplication and arrive at the correct answer:

  $$
  4 \div \frac{1}{8} = 4 \cdot 8 = 32
  $$

  Note that we omit the numerator but that technically the answer would be
  $\frac{1}{32}$.

  ### Formal specification of how to divide fractions

  We combine the foregoing (that it is easier to divide by fractional amounts
  using multiplication) with the concept of a [reciprocol](Reciprocals.md) to
  arrive at a definitive method for dividing two fractions. It boils down to:
  _invert and multiply_:

  If $\frac{a}{b}$ and $\frac{c}{d}$ are fractions then:
  $$\frac{a}{b} \div \frac{c}{d} = \frac{a}{b} \cdot \frac{d}{c}$$

  We invert the divisor (the second factor) and change the operator from division
  to multiplication.

  #### Demonstration

  Divide $\frac{1}{2}$ by $\frac{3}{5}$

  $$
  \begin{split}
  \frac{1}{2} \div \frac{3}{5}  = \frac{1}{2} \cdot \frac{5}{3}  \\
  = \frac{5}{5}
  \end{split}
  $$

  Divide $\frac{-6}{x}$ by $\frac{-12}{x^2}$

  $$
  \begin{split}
  \frac{-6}{x} \div \frac{12}{x^2} = \frac{-6}{x} \cdot \frac{x^2}{-12} \\ =
  \frac{(\cancel{3} \cdot \cancel{2} )}{\cancel{x}} \cdot \frac{(\cancel{x} \cdot \cancel{x} )}{\cancel{3} \cdot \cancel{2} \cdot 2} \\ =
  \frac{x}{2}

  \end{split}


  $$


path: zk/Docker_Compose.md
sortable_path: zkDocker_Compose.md
title: Docker Compose
checksum: d7ae24169a9881091c16be662c88c1e102b5f7654eec1d7471dfb39d5b0a4ffc
created: 2024-03-30 09:03:19.378617871+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["docker"]}
lead:
  - Docker Compose (DC) is a tool for defining and running multi-container
    applications. A typical example would be combining a FE with a BE
    implementation and database as three related but distinct containers.

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

  - DC is a prime example of _infrastructure as code_.

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

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

  ## Composition

  Three step process:

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

  ### `docker-compose.yml`

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

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

  ```yml
  # docker-compose.yml

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

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

  ## Main commands

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

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

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

  # view processes by activity
  docker compose top
  ```

  ## Environment variables

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

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

  ## See also

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

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

  # Docker Compose

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

  - DC is a prime example of _infrastructure as code_.

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

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

  ## Composition

  Three step process:

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

  ### `docker-compose.yml`

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

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

  ```yml
  # docker-compose.yml

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

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

  ## Main commands

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

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

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

  # view processes by activity
  docker compose top
  ```

  ## Environment variables

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

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

  ## See also

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


path: zk/Docker_architecture.md
sortable_path: zkDocker_architecture.md
title: Docker architecture
lead: ## Overview
checksum: 5bd498b3b2d4a80db9a340c58d2f12ed9ad49bc4b4299fa46cfde9e72c538a09
created: 2024-03-30 09:03:19.384369594+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["docker","containerization"]}
body:
  ## Overview

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

  - The Docker Client is a thin API for making
    [REST API](RESTful_APIs.md) to the Docker Server. Any CLI
    command beginning `docker...` is an API request to the server.
  - The internal process name for the server is `dockerd`.
  - On `docker run...`, `dockerd` calls `containerd`. This process starts the
    container runtimes and configures container-level storage and networking.
  - This then spawns `container-shim` procecesses which takes any output from you
    container and puts it in a logfile. (A redirection of `stdout` and `stderr`)
    One shim is spawned for each container.
  - The shim then launches a container runtime called `runc`. The `runc` performs
    all the complex configuration of cgroups and namespaces to create the
    container environment.
  - `containerd` is purely a bootstrapper. Once it has fulfilled its function by
    initiating `runc`, the Docker instance is up and running and the kernel takes
    it from there. It is like a launch scaffold that can be discarded.

  > If you are running Docker desktop on Mac or Windows, Docker creates a Linux
  > virtual machine, since it cannot run natively on Windows or Mac. In these
  > cases the the Docker Server runs inside this VM adding an additional layer of
  > abstraction.

  > Docker server also creates an internal virtual network and assigns IP
  > addresses to the containers. (This can be used to get containers to
  > communicate to each other over TCP/IP, since they are isolated by default.)

  ## Images

  Containers are created from images stored in a registry (either the public
  Docker Hub or privately). `containerd` uses the image to tell `runc` how to
  construct and build the image.

  ```sh
  docker run [image_name]
  ```

  The images you have pulled from the registry are stored in a local registry on
  your machine.

  When you run `docker run`, Docker will look first for the image in your local
  registry. If it cannot find it there, it will attempt to pull it from the remote
  registry.

  This assumes you are using a third-party image. You can also create your own
  images by writing a Dockerfile:

  ```sh
  docker build [docker_file]
  docker run [resulting_image]
  ```

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

  # Docker architecture

  ## Overview

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

  - The Docker Client is a thin API for making
    [REST API](RESTful_APIs.md) to the Docker Server. Any CLI
    command beginning `docker...` is an API request to the server.
  - The internal process name for the server is `dockerd`.
  - On `docker run...`, `dockerd` calls `containerd`. This process starts the
    container runtimes and configures container-level storage and networking.
  - This then spawns `container-shim` procecesses which takes any output from you
    container and puts it in a logfile. (A redirection of `stdout` and `stderr`)
    One shim is spawned for each container.
  - The shim then launches a container runtime called `runc`. The `runc` performs
    all the complex configuration of cgroups and namespaces to create the
    container environment.
  - `containerd` is purely a bootstrapper. Once it has fulfilled its function by
    initiating `runc`, the Docker instance is up and running and the kernel takes
    it from there. It is like a launch scaffold that can be discarded.

  > If you are running Docker desktop on Mac or Windows, Docker creates a Linux
  > virtual machine, since it cannot run natively on Windows or Mac. In these
  > cases the the Docker Server runs inside this VM adding an additional layer of
  > abstraction.

  > Docker server also creates an internal virtual network and assigns IP
  > addresses to the containers. (This can be used to get containers to
  > communicate to each other over TCP/IP, since they are isolated by default.)

  ## Images

  Containers are created from images stored in a registry (either the public
  Docker Hub or privately). `containerd` uses the image to tell `runc` how to
  construct and build the image.

  ```sh
  docker run [image_name]
  ```

  The images you have pulled from the registry are stored in a local registry on
  your machine.

  When you run `docker run`, Docker will look first for the image in your local
  registry. If it cannot find it there, it will attempt to pull it from the remote
  registry.

  This assumes you are using a third-party image. You can also create your own
  images by writing a Dockerfile:

  ```sh
  docker build [docker_file]
  docker run [resulting_image]
  ```


path: zk/Docker_compose_and_Dockerfile_difference.md
sortable_path: zkDocker_compose_and_Dockerfile_difference.md
title: Docker compose and Dockerfile difference
lead: # Docker compose and Dockerfile difference
checksum: 10fe2b668d11c38238e187249110300de3611b5117e84680d6c7ee242ae00c2a
created: 2024-03-30 09:03:19.390881235+00:00
modified: 2024-06-16 17:05:38.577923303+00:00
body:
  # Docker compose and Dockerfile difference

  ### Key Differences

  - **Scope**: A `Dockerfile` is for building a single Docker image, while
    `docker-compose.yml` is for orchestrating multiple containers (that might be
    built from `Dockerfiles`) to work together as a unified application.
  - **Use Case**: A `Dockerfile` is necessary when you need to create a custom
    image. In contrast, `docker-compose.yml` is used when you want to deploy and
    manage an application that consists of multiple containers.
  - **Command**: You build an image from a `Dockerfile` using `docker build`. You
    start the application defined by `docker-compose.yml` using
    `docker-compose up`.

  ### Dockerfile

  - **Purpose**: A `Dockerfile` is a text document that contains all the commands
    a user could call on the command line to assemble an image. It automates the
    process of creating Docker images by specifying a sequence of steps and
    configurations such as setting environment variables, installing software, and
    copying files from the local directory into the image.
  - **Functionality**: It is essentially a blueprint for building Docker images.
    You define the base image, software packages, scripts, files, environment
    variables, and other components you need in your Docker image.
  - **Usage**: You use a `Dockerfile` when you need to create a custom Docker
    image based on your application's specific dependencies and runtime
    environment. After defining your `Dockerfile`, you use the `docker build`
    command to create an image.

    Example structure of a Dockerfile:

    ```Dockerfile
    FROM python:3.8
    WORKDIR /app
    COPY . /app
    RUN pip install -r requirements.txt
    CMD ["python", "./my_script.py"]
    ```

  ### docker-compose.yml

  - **Purpose**: A `docker-compose.yml` file is used to define and run
    multi-container Docker applications. With a single command, you can configure
    all of your application's services, networks, and volumes in this YAML file
    and bring everything up or down.
  - **Functionality**: It simplifies the deployment and management of application
    components that are meant to run in separate containers while still needing to
    communicate or work together. For example, a web application might consist of
    a web server, a database, and a caching system, each running in its container.
  - **Usage**: `docker-compose` is used for defining and running complex
    applications with Docker. Instead of using lengthy `docker run` commands with
    many flags, you write a `docker-compose.yml` file and use `docker-compose up`
    to start the application.

    Example structure of a docker-compose.yml file:

    ```yaml
    version: "3"
    services:
      web:
        build: .
        ports:
          - "5000:5000"
      db:
        image: postgres
        volumes:
          - ./data:/var/lib/postgresql/data
    ```

raw_content:
  ---
  id: fw56
  title: Docker compose and Dockerfile difference
  tags:
    - docker
  created: Tuesday, March 26, 2024
  ---

  # Docker compose and Dockerfile difference

  ### Key Differences

  - **Scope**: A `Dockerfile` is for building a single Docker image, while
    `docker-compose.yml` is for orchestrating multiple containers (that might be
    built from `Dockerfiles`) to work together as a unified application.
  - **Use Case**: A `Dockerfile` is necessary when you need to create a custom
    image. In contrast, `docker-compose.yml` is used when you want to deploy and
    manage an application that consists of multiple containers.
  - **Command**: You build an image from a `Dockerfile` using `docker build`. You
    start the application defined by `docker-compose.yml` using
    `docker-compose up`.

  ### Dockerfile

  - **Purpose**: A `Dockerfile` is a text document that contains all the commands
    a user could call on the command line to assemble an image. It automates the
    process of creating Docker images by specifying a sequence of steps and
    configurations such as setting environment variables, installing software, and
    copying files from the local directory into the image.
  - **Functionality**: It is essentially a blueprint for building Docker images.
    You define the base image, software packages, scripts, files, environment
    variables, and other components you need in your Docker image.
  - **Usage**: You use a `Dockerfile` when you need to create a custom Docker
    image based on your application's specific dependencies and runtime
    environment. After defining your `Dockerfile`, you use the `docker build`
    command to create an image.

    Example structure of a Dockerfile:

    ```Dockerfile
    FROM python:3.8
    WORKDIR /app
    COPY . /app
    RUN pip install -r requirements.txt
    CMD ["python", "./my_script.py"]
    ```

  ### docker-compose.yml

  - **Purpose**: A `docker-compose.yml` file is used to define and run
    multi-container Docker applications. With a single command, you can configure
    all of your application's services, networks, and volumes in this YAML file
    and bring everything up or down.
  - **Functionality**: It simplifies the deployment and management of application
    components that are meant to run in separate containers while still needing to
    communicate or work together. For example, a web application might consist of
    a web server, a database, and a caching system, each running in its container.
  - **Usage**: `docker-compose` is used for defining and running complex
    applications with Docker. Instead of using lengthy `docker run` commands with
    many flags, you write a `docker-compose.yml` file and use `docker-compose up`
    to start the application.

    Example structure of a docker-compose.yml file:

    ```yaml
    version: "3"
    services:
      web:
        build: .
        ports:
          - "5000:5000"
      db:
        image: postgres
        volumes:
          - ./data:/var/lib/postgresql/data
    ```

metadata:
  {"created":"Tuesday, March 26, 2024","id":"fw56","tags":["docker"],"title":"Docker compose and Dockerfile difference"}


path: zk/Docker_containers.md
sortable_path: zkDocker_containers.md
title: Docker containers
lead: ## Launch a container
checksum: 40ca337e88192c27ec4798cdd5212f93a71aa4330184418c0cb98c52095110e6
created: 2024-03-30 09:03:19.3932422+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["docker","containerization"]}
body:
  ## Launch a container

  We launch a container by running, e.g

  ```sh
  docker run hello-world
  ```

  Docker looks for the `hello-world` image in order to start the container. If it
  cannot find it, it will fetch the image from DockerHub.

  (`docker run` assumes you are saying "run image in container" but you can
  specify other Docker objects such as networks.)

  The `docker run` command is actually a composite of the following two commands:

  ```sh
  docker create --name [my_container_name] hello-world
  docker start [my_container]
  ```

  Here is an applied example:

  ```
  docker run --name my-container debian /bin/echo "Hello, world"
  ```

  This creates and runs a container from the Debian image and executes `/bin/echo`
  inside of it. Once you have run the above line, if the process is successful it
  will exit. The container will stop running but it will remain in memory. Nothing
  will be output (no "Hello, world"), because when you run it, you are _outside_
  of the container.

  ## Launch a container as a daemon

  You can also run a container as a [daemon](). In this mode, the container will
  run in the background and detach from the console. For example:

  ```sh
  docker run -d debian /bin/sh -c /bin/sh -c "while true; do echo 'Hello!'; sleep 1; done"
  ```

  Inside the container, this will echo "Hello!" every second in an infinite loop
  whilst the daemon is active.

  The above command will not actually output anything. Instead it wil ouput the
  container ID, e.g:

  ```
  2749d796cbd64e9cf57307329e792587c39d8244f2377e62d78f3f3f77eecdb4
  ```

  You can use this to access the log for the container. When you do so, you will
  then see the output:

  ```sh
  docker log 2749
  hello
  hello
  hello
  ...
  ```

  (We could also use the container name to reference the container, if we launched
  it with the `--name` param.)

  ## Stopping a container

  ```
  docker stop 2749
  ```

  There will be a delay because it shuts down gracefully. It sends a SIGINT to the
  process in the container with PID 1 (i.e the root or parent process for the
  container).

  `stop` will keep the container in memory. This means you can still refer bak to
  the logs and that it can be restarted.

  Instead of `stop`, if you were to use:

  ```
  docker rm 2749
  ```

  The container will be stopped and deleted. The logs are deleted and the
  container cannot be recovered.

  We can also tell Docker to immediately remove a container after it exits:

  ```
  docker run --rm [image]
  ```

  ## Interacting with containers

  In the examples so far the container is a closed box. You don't have a terminal
  through which you can interact with the container as you would with an OS. You
  can only start the container and view its activities by accessing the logs.

  For images that have an OS we can use `-i -t` to launch a terminal in
  interactive mode.

  ```sh
  docker run -i -t debian /bin/bash
  root@0022da12f2f2:/# ls
  bin  boot  dev	etc  home  lib	media  mnt  opt  proc  root  run  sbin	srv  sys  tmp  usr  var
  root@0022da12f2f2:/# whoami
  root
  root@0022da12f2f2:/# ls usr/
  bin  games  include  lib  libexec  local  sbin	share  src
  ```

  Note that it defaults to the root user.

  Containers are lightweight and should only contain the minium environment needed
  to run an application. For this reason OSs that are included in an image are
  often very stripped-back and many programs and processes will not be there by
  default. (Although you could install a package manager and install the commands
  you need.)

  The previous command launches the container and enters into it with a terminal.
  If a container is already running, you can enter into it in terminal mode with
  `exec`:

  ```
  docker exec -i -t my_container bash
  ```

  If a container is running in detached mode we can attach to the main process
  with `docker attach`

  ```
  docker attach my_container
  ```

  ## Container lifecycle

  ![](/img/container-lifecycle.png)

  All containers have a lifecycle represented by five distinct states. Each state
  has an associated command:

  - created
    - `docker create`, `docker run`
  - running
  - paused
    - `docker pause`, `docker unpause`
  - stopped
    - `docker stop`, `docker kill`
  - removed
    - `docker rm`

  ## Main container commands

  ```sh
  docker create # create container from image
  docker run # create and start container from image
  docker run --name # add custom name for container
  docker ps # list currently active containers
  docker ps -a # list all containers (inactive and active)
  docker logs # display a container log
  docker stop # stop a running container
  docker start # starts a stopped (exited) container
  docker rm # remove a stopped (exited) container
  ```

  ## Container troubleshooting

  Lots of diagnostic output:

  ```
  docker inspect container_name
  ```

  List processes (from outside):

  ```
  docker top container_name
  ```

  View logs

  ```
  docker logs container_name
  ```

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

  # Docker containers

  ## Launch a container

  We launch a container by running, e.g

  ```sh
  docker run hello-world
  ```

  Docker looks for the `hello-world` image in order to start the container. If it
  cannot find it, it will fetch the image from DockerHub.

  (`docker run` assumes you are saying "run image in container" but you can
  specify other Docker objects such as networks.)

  The `docker run` command is actually a composite of the following two commands:

  ```sh
  docker create --name [my_container_name] hello-world
  docker start [my_container]
  ```

  Here is an applied example:

  ```
  docker run --name my-container debian /bin/echo "Hello, world"
  ```

  This creates and runs a container from the Debian image and executes `/bin/echo`
  inside of it. Once you have run the above line, if the process is successful it
  will exit. The container will stop running but it will remain in memory. Nothing
  will be output (no "Hello, world"), because when you run it, you are _outside_
  of the container.

  ## Launch a container as a daemon

  You can also run a container as a [daemon](). In this mode, the container will
  run in the background and detach from the console. For example:

  ```sh
  docker run -d debian /bin/sh -c /bin/sh -c "while true; do echo 'Hello!'; sleep 1; done"
  ```

  Inside the container, this will echo "Hello!" every second in an infinite loop
  whilst the daemon is active.

  The above command will not actually output anything. Instead it wil ouput the
  container ID, e.g:

  ```
  2749d796cbd64e9cf57307329e792587c39d8244f2377e62d78f3f3f77eecdb4
  ```

  You can use this to access the log for the container. When you do so, you will
  then see the output:

  ```sh
  docker log 2749
  hello
  hello
  hello
  ...
  ```

  (We could also use the container name to reference the container, if we launched
  it with the `--name` param.)

  ## Stopping a container

  ```
  docker stop 2749
  ```

  There will be a delay because it shuts down gracefully. It sends a SIGINT to the
  process in the container with PID 1 (i.e the root or parent process for the
  container).

  `stop` will keep the container in memory. This means you can still refer bak to
  the logs and that it can be restarted.

  Instead of `stop`, if you were to use:

  ```
  docker rm 2749
  ```

  The container will be stopped and deleted. The logs are deleted and the
  container cannot be recovered.

  We can also tell Docker to immediately remove a container after it exits:

  ```
  docker run --rm [image]
  ```

  ## Interacting with containers

  In the examples so far the container is a closed box. You don't have a terminal
  through which you can interact with the container as you would with an OS. You
  can only start the container and view its activities by accessing the logs.

  For images that have an OS we can use `-i -t` to launch a terminal in
  interactive mode.

  ```sh
  docker run -i -t debian /bin/bash
  root@0022da12f2f2:/# ls
  bin  boot  dev	etc  home  lib	media  mnt  opt  proc  root  run  sbin	srv  sys  tmp  usr  var
  root@0022da12f2f2:/# whoami
  root
  root@0022da12f2f2:/# ls usr/
  bin  games  include  lib  libexec  local  sbin	share  src
  ```

  Note that it defaults to the root user.

  Containers are lightweight and should only contain the minium environment needed
  to run an application. For this reason OSs that are included in an image are
  often very stripped-back and many programs and processes will not be there by
  default. (Although you could install a package manager and install the commands
  you need.)

  The previous command launches the container and enters into it with a terminal.
  If a container is already running, you can enter into it in terminal mode with
  `exec`:

  ```
  docker exec -i -t my_container bash
  ```

  If a container is running in detached mode we can attach to the main process
  with `docker attach`

  ```
  docker attach my_container
  ```

  ## Container lifecycle

  ![](/img/container-lifecycle.png)

  All containers have a lifecycle represented by five distinct states. Each state
  has an associated command:

  - created
    - `docker create`, `docker run`
  - running
  - paused
    - `docker pause`, `docker unpause`
  - stopped
    - `docker stop`, `docker kill`
  - removed
    - `docker rm`

  ## Main container commands

  ```sh
  docker create # create container from image
  docker run # create and start container from image
  docker run --name # add custom name for container
  docker ps # list currently active containers
  docker ps -a # list all containers (inactive and active)
  docker logs # display a container log
  docker stop # stop a running container
  docker start # starts a stopped (exited) container
  docker rm # remove a stopped (exited) container
  ```

  ## Container troubleshooting

  Lots of diagnostic output:

  ```
  docker inspect container_name
  ```

  List processes (from outside):

  ```
  docker top container_name
  ```

  View logs

  ```
  docker logs container_name
  ```


path: zk/Docker_general_overview.md
sortable_path: zkDocker_general_overview.md
title: Docker: general overview
lead:
  > Docker is an ecosystem of tools for creating, running and transporting
  > containers.

checksum: 297a1567c932b0951c962b18a1d683d2552fcf1f9a81242d96eff88644b0a822
created: 2024-03-30 09:03:19.395305585+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["docker","containerization"]}
body:
  > Docker is an ecosystem of tools for creating, running and transporting
  > containers.

  The main product of the Docker company is Docker Desktop - a UI for running
  containers however the actual `docker` software is free and open source.

  Docker standardised the use of containers set up the open-source Open Container
  Initiative which is a convention agreed upon by the major tech companies. It
  codifies the following:

  - Docker runtime specification
  - Docker image specification
  - Docker registry specification

  ## Main components

  - **Docker Engine**
    - The core component
    - Exposes the Docker API and manages the entire workflow around creating,
      running and deploying containers
  - **Docker Client**
    - The Docker CLI
    - The primary means of interacting with the Docker Engine
    - Provides commands for managing images, containers and other Docker objects
  - **Docker Compose**
    - A tool for defining and running multi-container applications
    - Define the configuration for all of the containers that make up the
      application in a single YAML file
  - **Docker Images**
    - See below
  - **Docker Containers**
    - See below
  - **Docker Registry**
    - A centralised location where Docker iamges can be stored and shared
    - DockerHub is the registry provided by Docker
    - Organisations can set up their own private registries

  ## Docker images

  An image is a blueprint that contains the instructions to build a container.
  It's an immutable snapshot of the file system and configuration of an
  application. Images can be easily shared between developers.

  They are runnable packages that contain everything needed to run an application
  including the binaries, libraries, resources and other dependencies.

  It does not contain an operating sytem but may include a particular Linux
  distribution.

  ## Docker containers

  A container is a runnign instance of a Docker image. Multiple, identical
  containers can be launched from the same, single image.

  ## Docker architectural overview

  See [Docker architecture](Docker_architecture.md).

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

  # Docker: general overview

  > Docker is an ecosystem of tools for creating, running and transporting
  > containers.

  The main product of the Docker company is Docker Desktop - a UI for running
  containers however the actual `docker` software is free and open source.

  Docker standardised the use of containers set up the open-source Open Container
  Initiative which is a convention agreed upon by the major tech companies. It
  codifies the following:

  - Docker runtime specification
  - Docker image specification
  - Docker registry specification

  ## Main components

  - **Docker Engine**
    - The core component
    - Exposes the Docker API and manages the entire workflow around creating,
      running and deploying containers
  - **Docker Client**
    - The Docker CLI
    - The primary means of interacting with the Docker Engine
    - Provides commands for managing images, containers and other Docker objects
  - **Docker Compose**
    - A tool for defining and running multi-container applications
    - Define the configuration for all of the containers that make up the
      application in a single YAML file
  - **Docker Images**
    - See below
  - **Docker Containers**
    - See below
  - **Docker Registry**
    - A centralised location where Docker iamges can be stored and shared
    - DockerHub is the registry provided by Docker
    - Organisations can set up their own private registries

  ## Docker images

  An image is a blueprint that contains the instructions to build a container.
  It's an immutable snapshot of the file system and configuration of an
  application. Images can be easily shared between developers.

  They are runnable packages that contain everything needed to run an application
  including the binaries, libraries, resources and other dependencies.

  It does not contain an operating sytem but may include a particular Linux
  distribution.

  ## Docker containers

  A container is a runnign instance of a Docker image. Multiple, identical
  containers can be launched from the same, single image.

  ## Docker architectural overview

  See [Docker architecture](Docker_architecture.md).


path: zk/Docker_images.md
sortable_path: zkDocker_images.md
title: Docker images
checksum: aaf4b10f96ca33ea85bb8d6038fb7fb3b1cc802b051c02b28a18b5986374f1f9
created: 2024-03-30 09:03:19.399152581+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["docker"]}
lead:
  Docker needs an image to run a container. Images are stored in the local
  registry. If it can't find it, it will try and pull it from Docker Hub.

body:
  Docker needs an image to run a container. Images are stored in the local
  registry. If it can't find it, it will try and pull it from Docker Hub.

  ## View your local images

  ```
  docker images
  ```

  ```sh
  REPOSITORY                     TAG       IMAGE ID       CREATED         SIZE
  debian                         latest    e6d9c9c3e100   12 days ago     118MB
  ubuntu                         latest    bab8ce5c00ca   6 weeks ago     69.2MB
  cda/mysql-5.7                  latest    312ce2a6cea5   3 months ago    495MB
  cda/node-16                    latest    b26e54c8fa11   3 months ago    1.44GB
  bitnami/minio                  latest    ef6d14df2158   3 months ago    229MB
  opensearchproject/opensearch   1.3.7     0f8ef57c3629   4 months ago    831MB
  hello-world                    latest    46331d942d63   13 months ago   9.14kB
  ```

  ## Repository, registry, tag

  When you specify the name of an image, you are referring to the image's
  repository. The registry is the list of repositories available on the Docker
  Hub. Each repository has a tag, indicating the current production branch.

  For example:

  ```
  docker run -it ubuntu:18:04 /bin/bash
  ```

  - `ubuntu` is the image repository
  - `18.04` is the image tag

  > The Image ID distinguishes the image globally and is not local to you.

  To get detailed info on an image:

  ```
  docker image inspect debian
  ```

  ## "Dangling" images

  A "dangling" image is an unused image - one that is not associated with a
  container. Re move dangling images with

  ```
  docker image prune
  ```

  ## Anatomy of a Docker image: layering

  The process of constructing a Docker image involves creating a series of
  intermediate layers, each representing a change or addition to the filesystem.
  These layers are stacked on top of one another, forming the final image. This
  approach is called layering.

  Layering in Docker images offers several benefits:

  1. Reusability: Common layers can be shared between multiple images, reducing
     disk space usage and build time. For example, if multiple images use the same
     base operating system, they can all share the same base layer.

  2. Caching: During the build process, Docker caches layers that have already
     been built. If a layer hasn't changed, Docker will reuse it in subsequent
     builds, speeding up the build process.

  3. Incremental updates: When updating an image, only the layers that have
     changed need to be updated and redistributed. This reduces the size of
     updates and makes the process more efficient.

  Each instruction in the Dockerfile creates a new layer in the image. Some common
  instructions include:

  - `FROM`: Specifies the base image to start with (e.g., an operating system or
    an existing image).

  - `RUN`: Executes a command, often used for installing packages or configuring
    the system.

  - `COPY`: Copies files or directories from the host machine to the image.

  - `ADD`: Similar to COPY but can also handle URLs and automatic extraction of
    archives.

  - `ENV`: Sets environment variables that will be available inside the container.

  - `CMD`: Specifies the default command to run when a container is started from
    the image.

  - `ENTRYPOINT` : Similar to CMD, but allows the container to be run as an
    executable, with additional arguments.

  When building an image, Docker starts with the base image specified in the FROM
  instruction and applies each subsequent instruction, creating a new layer for
  each step. The final result is a layered image that contains the application and
  its dependencies, ready to be run as a container.

  ### The read-write layer

  The final layer at the "top" of the image is a thin read-write layer. In
  contrast to the intermediate layers it can execute read-write functionality and
  is not read-only.

  Files in the intermediate layer cannot be modified, only the read-write layer
  can be modified. Files are copied over from intermediate layers when requested
  by the read-write layer, rather being modified themselves.

  Tip for writing build files:

  Things that change less should be higher up (earlier). Things that change more
  should be twoardds the end. This allows for better layer caching. Particularly
  bear this in mind with the commands: RUN, COPY, ADD.

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

  # Docker images

  Docker needs an image to run a container. Images are stored in the local
  registry. If it can't find it, it will try and pull it from Docker Hub.

  ## View your local images

  ```
  docker images
  ```

  ```sh
  REPOSITORY                     TAG       IMAGE ID       CREATED         SIZE
  debian                         latest    e6d9c9c3e100   12 days ago     118MB
  ubuntu                         latest    bab8ce5c00ca   6 weeks ago     69.2MB
  cda/mysql-5.7                  latest    312ce2a6cea5   3 months ago    495MB
  cda/node-16                    latest    b26e54c8fa11   3 months ago    1.44GB
  bitnami/minio                  latest    ef6d14df2158   3 months ago    229MB
  opensearchproject/opensearch   1.3.7     0f8ef57c3629   4 months ago    831MB
  hello-world                    latest    46331d942d63   13 months ago   9.14kB
  ```

  ## Repository, registry, tag

  When you specify the name of an image, you are referring to the image's
  repository. The registry is the list of repositories available on the Docker
  Hub. Each repository has a tag, indicating the current production branch.

  For example:

  ```
  docker run -it ubuntu:18:04 /bin/bash
  ```

  - `ubuntu` is the image repository
  - `18.04` is the image tag

  > The Image ID distinguishes the image globally and is not local to you.

  To get detailed info on an image:

  ```
  docker image inspect debian
  ```

  ## "Dangling" images

  A "dangling" image is an unused image - one that is not associated with a
  container. Re move dangling images with

  ```
  docker image prune
  ```

  ## Anatomy of a Docker image: layering

  The process of constructing a Docker image involves creating a series of
  intermediate layers, each representing a change or addition to the filesystem.
  These layers are stacked on top of one another, forming the final image. This
  approach is called layering.

  Layering in Docker images offers several benefits:

  1. Reusability: Common layers can be shared between multiple images, reducing
     disk space usage and build time. For example, if multiple images use the same
     base operating system, they can all share the same base layer.

  2. Caching: During the build process, Docker caches layers that have already
     been built. If a layer hasn't changed, Docker will reuse it in subsequent
     builds, speeding up the build process.

  3. Incremental updates: When updating an image, only the layers that have
     changed need to be updated and redistributed. This reduces the size of
     updates and makes the process more efficient.

  Each instruction in the Dockerfile creates a new layer in the image. Some common
  instructions include:

  - `FROM`: Specifies the base image to start with (e.g., an operating system or
    an existing image).

  - `RUN`: Executes a command, often used for installing packages or configuring
    the system.

  - `COPY`: Copies files or directories from the host machine to the image.

  - `ADD`: Similar to COPY but can also handle URLs and automatic extraction of
    archives.

  - `ENV`: Sets environment variables that will be available inside the container.

  - `CMD`: Specifies the default command to run when a container is started from
    the image.

  - `ENTRYPOINT` : Similar to CMD, but allows the container to be run as an
    executable, with additional arguments.

  When building an image, Docker starts with the base image specified in the FROM
  instruction and applies each subsequent instruction, creating a new layer for
  each step. The final result is a layered image that contains the application and
  its dependencies, ready to be run as a container.

  ### The read-write layer

  The final layer at the "top" of the image is a thin read-write layer. In
  contrast to the intermediate layers it can execute read-write functionality and
  is not read-only.

  Files in the intermediate layer cannot be modified, only the read-write layer
  can be modified. Files are copied over from intermediate layers when requested
  by the read-write layer, rather being modified themselves.

  Tip for writing build files:

  Things that change less should be higher up (earlier). Things that change more
  should be twoardds the end. This allows for better layer caching. Particularly
  bear this in mind with the commands: RUN, COPY, ADD.


path: zk/Docker_storage.md
sortable_path: zkDocker_storage.md
title: Docker storage
lead: The following problems related to storage face containerized environments:
checksum: 8ccae5324edf851d7bd9dedd5671523132195e7c7a8096bf5d47fc6a97dfa541
created: 2024-03-30 09:03:19.401913884+00:00
modified: 2024-06-16 17:06:27.195124185+00:00
metadata: {"tags":["docker"]}
body:
  The following problems related to storage face containerized environments:

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

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

  ### Docker volumes

  Docker volumes are the solution to this. Volumes:

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

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

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

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

  ### Creating a volume

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

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

  To create a named volume you first have to create:

  ```
  docker volume create volume_name
  ```

  Then you can designate it:

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

  The actual location is still hidden and decided by Docker.

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

  // Add: screenshot

  ### Commands

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

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

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

  # Docker storage

  The following problems related to storage face containerized environments:

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

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

  ### Docker volumes

  Docker volumes are the solution to this. Volumes:

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

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

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

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

  ### Creating a volume

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

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

  To create a named volume you first have to create:

  ```
  docker volume create volume_name
  ```

  Then you can designate it:

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

  The actual location is still hidden and decided by Docker.

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

  // Add: screenshot

  ### Commands

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

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


path: zk/Effective_logging_in_Git.md
sortable_path: zkEffective_logging_in_Git.md
title: Effective logging in Git
checksum: 6ce23680a9fc26f6f5b023e17e88a87b6f9d8b4ec6795307fb87953e2b5b68f6
created: 2024-03-30 09:03:19.403846901+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["git","procedural"]}
lead:
  | Root command      | Variant      | Output                                                   |
  | ----------------- | ------------ | -------------------------------------------------------- |
  | `git log`         | filename.txt | List the commits that changed the given file             |
  | `git log --patch` | filename.txt | Show the diff changes for each commit for the given file |
  | `git log --patch` | null         | Show the diff changes for each commit for all files      |

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

raw_content:
  ---
  tags: [git, procedural]
  ---

  # Effective logging in Git

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


path: zk/Elastic_Compute_Cloud.md
sortable_path: zkElastic_Compute_Cloud.md
title: Elastic Compute Cloud (EC2)
lead: # Elastic Compute Cloud (EC2)
checksum: c06db02442d393ad85b13136d8e744bb1b04acd58234897c8b5c045f98e98e10
created: 2024-03-30 09:03:19.406058152+00:00
modified: 2024-06-15 09:33:17.27066175+00:00
body:
  # Elastic Compute Cloud (EC2)

  - Cloud-based or virtual server (virtual machine), basically a hypervisor
    (compare [[zk/Docker_architecture]])
  - It is virtual because you are not responsible for its physical implementation
  - Still needs considerable configuration compared to a serverless
    [[zk/Lambda_programming_model]]
  - Like lambdas has the benefit of easy scaling: you can add compute capacity on
    demand (elasticity)

  The following needs to be considered:

  - Naming
  - Application and OS image (known as "Amazon Machine Image"):
    - Which OS you want to use (Linux distribution, Windows, etc)
    - Applications you want pre-installed
    - Block device mapping
  - Instance type an size (basically the type of processor and how powerful you
    want it to be)
  - An encrypted key-pair for login
  - Your network (typically managed via Amazon VPC (Virtual Private Cloud)) and
    network security
  - Storage: size and type (eg. GP3)
  - Location and latency

  > EC2 is more expensive copared to serverless options. Accordingly it is better
  > to run smaller servers (in terms of processor and memory) at higher capacity
  > than larger servers under-capacity.

raw_content:
  ---
  id: 
  tags:
    - AWS
  created: Monday, February 19, 2024 | 18:58
  last_modified: Monday, February 19, 2024 | 18:58
  since: just now
  title: Elastic Compute Cloud (EC2)
  ---

  # Elastic Compute Cloud (EC2)

  - Cloud-based or virtual server (virtual machine), basically a hypervisor
    (compare [[zk/Docker_architecture]])
  - It is virtual because you are not responsible for its physical implementation
  - Still needs considerable configuration compared to a serverless
    [[zk/Lambda_programming_model]]
  - Like lambdas has the benefit of easy scaling: you can add compute capacity on
    demand (elasticity)

  The following needs to be considered:

  - Naming
  - Application and OS image (known as "Amazon Machine Image"):
    - Which OS you want to use (Linux distribution, Windows, etc)
    - Applications you want pre-installed
    - Block device mapping
  - Instance type an size (basically the type of processor and how powerful you
    want it to be)
  - An encrypted key-pair for login
  - Your network (typically managed via Amazon VPC (Virtual Private Cloud)) and
    network security
  - Storage: size and type (eg. GP3)
  - Location and latency

  > EC2 is more expensive copared to serverless options. Accordingly it is better
  > to run smaller servers (in terms of processor and memory) at higher capacity
  > than larger servers under-capacity.

metadata:
  {"created":"Monday, February 19, 2024 | 18:58","id":null,"last_modified":"Monday, February 19, 2024 | 18:58","since":"just now","tags":["AWS"],"title":"Elastic Compute Cloud (EC2)"}


path: zk/Electromagnetism.md
sortable_path: zkElectromagnetism.md
title: Electromagnetism
checksum: 764e825d0d259a80e7f98d9672b72df2c262891b2eca1d6046026072b5782e70
created: 2024-03-30 09:03:19.417001338+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["physics","electricity","electromagnetism"]}
lead:
  > Electromagnetism is the physical interaction among **electric charges,
  > magnetic moments and the electromagnetic field**.

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

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

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

  ## Electric charge

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

  ## Magnetism

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

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

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

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

  ### Relation to electrons

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

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

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

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

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

  ## Fields and forces

  ### What is a force in physics?

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

  ### What is a field

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

  ## The electric field

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

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

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

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

  ## The magnetic field

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

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

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

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

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

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

  ## The electromagnetic field

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

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

  ### Maxwell's Equations

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

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

  ## The electromagnetic force

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

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

  ## Electromagnetic radiation / waves

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

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

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

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

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

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

  ## Using magnetism to generate electricity

  ## Understand better:

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

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

raw_content:
  ---
  tags: [physics, electricity, electromagnetism]
  ---

  # Electromagnetism

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

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

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

  ## Electric charge

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

  ## Magnetism

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

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

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

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

  ### Relation to electrons

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

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

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

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

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

  ## Fields and forces

  ### What is a force in physics?

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

  ### What is a field

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

  ## The electric field

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

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

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

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

  ## The magnetic field

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

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

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

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

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

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

  ## The electromagnetic field

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

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

  ### Maxwell's Equations

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

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

  ## The electromagnetic force

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

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

  ## Electromagnetic radiation / waves

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

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

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

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

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

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

  ## Using magnetism to generate electricity

  ## Understand better:

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

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


path: zk/Electrons.md
sortable_path: zkElectrons.md
title: Electrons
lead: ## Shells
checksum: c8fc472ebfb270382d7063ff848a52c9797647fe49083e3e0de359f20315b410
created: 2024-03-30 09:03:19.420823489+00:00
modified: 2024-05-15 06:24:22.76625875+00:00
metadata: {"tags":["physics","electricity"]}
body:
  ## Shells

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

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

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

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

  ## Valence

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

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

  ## Conductivity and insularity

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

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

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

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

  ### Semiconductors

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

  ## Ionization

  Ionization is the process of atoms gaining and losing electrons.

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

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

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

raw_content:
  ---
  tags: [physics, electricity]
  ---

  # Electrons

  ## Shells

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

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

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

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

  ## Valence

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

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

  ## Conductivity and insularity

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

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

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

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

  ### Semiconductors

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

  ## Ionization

  Ionization is the process of atoms gaining and losing electrons.

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

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

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


path: zk/Enums.md
sortable_path: zkEnums.md
title: Enums
checksum: f9900d25506f658092971bb78562868fdc6235b199c2a473db275338a3ee50e9
created: 2024-03-30 09:03:19.423257332+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["typescript"]}
lead:
  In essence an `enum` in TypeScript is an incremented store of immutable
  variables. The only way I can understand them is just to think of them as a bag
  of constants.

body:
  In essence an `enum` in TypeScript is an incremented store of immutable
  variables. The only way I can understand them is just to think of them as a bag
  of constants.

  Below is an example of an `enum`:

  ```ts
  enum Continents {
    NorthAmerica,
    SouthAmerica,
    Africa,
    Asia,
    Europe,
    Antartica,
    Australia,
  }

  // usage
  var region = Continents.Africa;
  ```

  Here is an example of a string-based enum from the Studio codebase:

  ```tsx
  export enum HelpLinksEnum {
    composerView = "composerView",
    conditionalHelp = "conditionalHelp",
    configView = "configView",
    dashboard = "dashboard",
    dataView = "dataView",
    functionHelp = "functionHelp",
    previewMode = "previewMode",
  }
  ```

  ### Main properties

  - **Auto-incrementation (numeric enums only):**
    - Enums are auto-incrementing.
    - If we wanted to change the automatic sequence we could do something like the
      below and TS would automatically update the index to reflect our change
    ```ts
    enum NewProjectMenuItems {
        Row = 5
        Table // would automatically become `6` in the index
    }
    ```
  - **Enums are types**

    - Enums are interpreted as types by TS. Consequently we can an enum as a type
      annotation. This can be particularly useful when typing function parameters.
      For example:

    ```ts
    enum UserResponse {
      No = 0,
      Yes = 1,
    }

    function respond(recipient: string, message: UserResponse): void {
      // ...
    }
    ```

  - **Enums accept computed values**
    - As is the case with objects generally, we are not limited to primitive data
      types when setting values on enums, we can also assign functions.
  - **Reverse mapping**

    - Reverse mapping is the ability to access a key via its value in addition to
      the more standard practice of accessing a value via its key.
    - Demonstration:

      ```ts
      enum PrintMedia {
        Newspaper = 1,
        Newsletter,
        Magazine,
        Book,
      }

      PrintMedia.Magazine; // returns  3
      PrintMedia["Magazine"]; // returns  3
      PrintMedia[3]; // returns  Magazine
      ```

  - **Useful in switch statements**
    - Enums come in handy when you want to reduce the verbosity and repetition of
      a large switch statement.
    - The following is an example from the codebase:
      ```tsx
      public projectMenuItemClickHandler(e: IUserProjectMenuItemClick): void {
          switch (e.menuItem) {
            case UserProjectMenuItems.Rename:
              e.item.rename = true;
              break;
            case UserProjectMenuItems.Export:
              this.exportProject(e.item);
              break;
            case UserProjectMenuItems.Clone:
              this.cloneProject(e.item);
              break;
          }
        }
      ```

  ### Main benefits

  More generally, by using enums we ensure that a given data structure is
  preserved unaltered and intact throughout the codebase which is very useful when
  multiple developers are working with the same classes and components. This
  creates a high-level canonical form of a given data structure that may be used
  in multiple contexts. This reduces the likelihood of different developers
  'reinventing the wheel' and duplicating common data structures, helping to
  promote modularity and minimise technical debt.

  ### Constraints and best practices

  There are some important constraints that should be borne in mind when using
  enums:

  - **Initialise string enums!**
    - String enums must be initialised with a string or a reference to another
      enum. They cannot be tacitly defined as is the case with numeric enums
  - **Don't mix data types!**
    - It is technically possible to construct an enum that contains both numbers
      and strings but this should be avoided as it can lead to unintended
      side-effects in the incrementation process. It also kind of undermines the
      purpose of enums. In this scenario, a custom type would probably be more
      appropriate.
  - **Strings only as keys**
    - In contrast to a `Map` or object an enum cannot have a numeric key. It must
      always be a string.

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

  # Enums

  In essence an `enum` in TypeScript is an incremented store of immutable
  variables. The only way I can understand them is just to think of them as a bag
  of constants.

  Below is an example of an `enum`:

  ```ts
  enum Continents {
    NorthAmerica,
    SouthAmerica,
    Africa,
    Asia,
    Europe,
    Antartica,
    Australia,
  }

  // usage
  var region = Continents.Africa;
  ```

  Here is an example of a string-based enum from the Studio codebase:

  ```tsx
  export enum HelpLinksEnum {
    composerView = "composerView",
    conditionalHelp = "conditionalHelp",
    configView = "configView",
    dashboard = "dashboard",
    dataView = "dataView",
    functionHelp = "functionHelp",
    previewMode = "previewMode",
  }
  ```

  ### Main properties

  - **Auto-incrementation (numeric enums only):**
    - Enums are auto-incrementing.
    - If we wanted to change the automatic sequence we could do something like the
      below and TS would automatically update the index to reflect our change
    ```ts
    enum NewProjectMenuItems {
        Row = 5
        Table // would automatically become `6` in the index
    }
    ```
  - **Enums are types**

    - Enums are interpreted as types by TS. Consequently we can an enum as a type
      annotation. This can be particularly useful when typing function parameters.
      For example:

    ```ts
    enum UserResponse {
      No = 0,
      Yes = 1,
    }

    function respond(recipient: string, message: UserResponse): void {
      // ...
    }
    ```

  - **Enums accept computed values**
    - As is the case with objects generally, we are not limited to primitive data
      types when setting values on enums, we can also assign functions.
  - **Reverse mapping**

    - Reverse mapping is the ability to access a key via its value in addition to
      the more standard practice of accessing a value via its key.
    - Demonstration:

      ```ts
      enum PrintMedia {
        Newspaper = 1,
        Newsletter,
        Magazine,
        Book,
      }

      PrintMedia.Magazine; // returns  3
      PrintMedia["Magazine"]; // returns  3
      PrintMedia[3]; // returns  Magazine
      ```

  - **Useful in switch statements**
    - Enums come in handy when you want to reduce the verbosity and repetition of
      a large switch statement.
    - The following is an example from the codebase:
      ```tsx
      public projectMenuItemClickHandler(e: IUserProjectMenuItemClick): void {
          switch (e.menuItem) {
            case UserProjectMenuItems.Rename:
              e.item.rename = true;
              break;
            case UserProjectMenuItems.Export:
              this.exportProject(e.item);
              break;
            case UserProjectMenuItems.Clone:
              this.cloneProject(e.item);
              break;
          }
        }
      ```

  ### Main benefits

  More generally, by using enums we ensure that a given data structure is
  preserved unaltered and intact throughout the codebase which is very useful when
  multiple developers are working with the same classes and components. This
  creates a high-level canonical form of a given data structure that may be used
  in multiple contexts. This reduces the likelihood of different developers
  'reinventing the wheel' and duplicating common data structures, helping to
  promote modularity and minimise technical debt.

  ### Constraints and best practices

  There are some important constraints that should be borne in mind when using
  enums:

  - **Initialise string enums!**
    - String enums must be initialised with a string or a reference to another
      enum. They cannot be tacitly defined as is the case with numeric enums
  - **Don't mix data types!**
    - It is technically possible to construct an enum that contains both numbers
      and strings but this should be avoided as it can lead to unintended
      side-effects in the incrementation process. It also kind of undermines the
      purpose of enums. In this scenario, a custom type would probably be more
      appropriate.
  - **Strings only as keys**
    - In contrast to a `Map` or object an enum cannot have a numeric key. It must
      always be a string.


path: zk/Environmental_and_shell_variables.md
sortable_path: zkEnvironmental_and_shell_variables.md
title: Important!
lead:
  To understand the difference between environmental and shell variables know
  that:

checksum: b9ab7221b2df9a01aeca76250329e16e2f21efb2501547dce572908d9aa192c9
created: 2024-03-30 09:03:19.42597607+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["shell"]}
body:
  To understand the difference between environmental and shell variables know
  that:

  - You can spawn child shells from the parent shell that is initiated when you
    first open the terminal. To do this just run `bash` or `zsh` .
  - This is a self-contained new instance of the shell. This means:
    - It **will have** access to environmental variables (since they belong to the
      parent / are global)
    - It **will not have** access to any shell variables that are defined in the
      parent.
  - **How do you get back to the ‘upper’ parent shell?** Type `exit` .
  - Note that:
    - Custom (user-created) shell variables **do not** pass down to spawned shell
      instances, nor do they pass up to the parent
    - Custom (user-created) environment variables do pass down to spawned shell
      instances but do not pass up to the parent. They are lost on `exit` .

  Q. What methods are there for keeping track of, preserving, and jumping between
  spawned instances? Is this even possible or do they die on `exit` .

  ## Questions, research

  1. If you create a variable manually I guess it won’t make it to any config
     file. How would you create a persistent var that is added to the `.bashrc`
     and thus which would be initialised on every session? Is this where the path
     comes in?
  1. What methods are there for keeping track of, preserving, and jumping between
     spawned instances? Is this even possible or do they die on `exit` ?

  ## What is the shell environment and what are environment variables?

  - Every time that you interact with the shell you do so within an
    **environment**. This is the context within which you are working and it
    determines your access to resources and the behaviour that is permitted.

  - The environment is an area that the shell builds every time that it starts a
    session. It contains variables that define system properties.

  - Every time a
    [shell session](Shell-sessions-e6dd743dec1d4fe3b1ee672c8f9731f6)
    spawns, a process takes place to gather and compile information that should be
    available to the shell process and its child processes. It obtains the data
    for these settings from a variety of different files and settings on the
    system.

  - The environment is represented by strings comprising key-value pairs. For
    example:

    ```bash
    KEY=value1:value2
    KEY="value with spaces":"another value with spaces"
    ```

    As the above shows, a key can have multiple related values. Each one is
    demarcated with a `:` . If the value is longer than a single word, quotation
    marks are used.

  - The keys are **variables**. They come in two types: **environmental
    variables** and **shell variables:**

    - Environmental variables are much more permanent and pertain to things like
      the user and his path (the overall session)
    - Shell variables are more changeable for instance the current working
      directory (the current program instance)

  Variables can be created via config files that run on the initialisation of the
  session or manually created via the command line in the current session

  ## What are shell variables useful for?

  Some deployment mechanisms rely on environmental variables to configure
  authentication information. This is useful because it does not require keeping
  these in files that may be seen by outside parties.

  More generally they are used for when you will need to read or alter the
  environment of your system.

  ## Viewing shell and environmental variables

  To view the settings of your current environment you can execute the `env`
  command which returns a list of the key-value pairs introduced above. Here are
  some of the more intelligible variables that are returned when I run this
  command:

  ```bash
  SHELL=/usr/bin/zsh
  DESKTOP_SESSION=plasma
  HOME=/home/thomas
  USER=thomas
  PWD=/home/thomas/repos/bash-scripting
  PATH=/home/thomas/.nvm/versions/node/v16.8.0/bin:/home/thomas/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/var/lib/snapd/snap/bin
  ```

  However if you want to target a specific viable you need to invoke `printenv`
  with the relevant key, for example:

  ```bash
  printenv SHELL
  # SHELL=/usr/bin/zsh
  ```

  Note that `env` and `printenv` do not show all the shell variables, only a
  selection. To view all the shell variables along with the environmental
  variables use `set` .

  ## Creating, exporting and deleting variable shell and environment variables

  - You set shell variables using the same syntax you would within a script file:

    ```bash
    TEST_SHELL_VAR="This is a test"
    set | grep TEST_SH
    TEST_SHELL_VAR='This is a test'

    # We can also print it with an echo, again exactly as we would with a shell script
    echo S{TEST_SHELL_VAR}
    ```

  - We can verify that it is not an environmental variable based on the fact that
    following does not return anything:

    ```bash
    printenv | grep TEST-SH
    ```

  - We can verify that this is a shell variable by spawning a new shell and
    calling it. Nothing will be returned from the child shell.

  - You can upgrade a shell variable to an environment variable with `export` :

    ```bash
    export TEST_SHELL_VAR
    # And confirm:
    printenv | grep TEST_SH
    TEST_SHELL_VAR='This is a test'
    ```

  - We can use the same syntax to create new environment variables from scratch:

    ```bash
    export NEW_ENV_VAR="A new var"
    ```

  ### Using config files to create variables

  You can also add variables to config files that run on login such as your user
  `.bashrc` / `.zshrc` . This is obviously best for when you want the variables to
  persist and be accessible within every
  [shell session](Shell-sessions-e6dd743dec1d4fe3b1ee672c8f9731f6).

  ## Important environmental and shell variables

  ### `PATH`

  A list of directories that the system will check when looking for commands. When
  a user types in a command, the system will check directories in this order for
  the executable.

  ```bash
  echo ${PATH}
  # /home/thomas/.nvm/versions/node/v16.8.0/bin:/home/thomas/.local/bin:/usr/local/sbin:/usr/local/bin:
  # /usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/var/lib/snapd/snap/bin
  ```

  For example, if you wish to use `npm` commands globally (in any directory) you
  will need to have the requisite Node executable in your path, which you can see
  above.

  ### `SHELL`

  This describes the shell that will be interpreting any commands you type in. In
  most cases, this will be bash by default, but other values can be set if you
  prefer other options.

  ```bash
  echo ${SHELL}
  # /usr/bin/zsh
  ```

  ### `USER`

  The current logged in user.

  ```bash
  echo ${USER}
  # thomas
  ```

  ### `PWD`

  The current working directory.

  ```bash
  echo ${PWD}
  # /home/thomas
  ```

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

  ## Important!

  To understand the difference between environmental and shell variables know
  that:

  - You can spawn child shells from the parent shell that is initiated when you
    first open the terminal. To do this just run `bash` or `zsh` .
  - This is a self-contained new instance of the shell. This means:
    - It **will have** access to environmental variables (since they belong to the
      parent / are global)
    - It **will not have** access to any shell variables that are defined in the
      parent.
  - **How do you get back to the ‘upper’ parent shell?** Type `exit` .
  - Note that:
    - Custom (user-created) shell variables **do not** pass down to spawned shell
      instances, nor do they pass up to the parent
    - Custom (user-created) environment variables do pass down to spawned shell
      instances but do not pass up to the parent. They are lost on `exit` .

  Q. What methods are there for keeping track of, preserving, and jumping between
  spawned instances? Is this even possible or do they die on `exit` .

  ## Questions, research

  1. If you create a variable manually I guess it won’t make it to any config
     file. How would you create a persistent var that is added to the `.bashrc`
     and thus which would be initialised on every session? Is this where the path
     comes in?
  1. What methods are there for keeping track of, preserving, and jumping between
     spawned instances? Is this even possible or do they die on `exit` ?

  ## What is the shell environment and what are environment variables?

  - Every time that you interact with the shell you do so within an
    **environment**. This is the context within which you are working and it
    determines your access to resources and the behaviour that is permitted.

  - The environment is an area that the shell builds every time that it starts a
    session. It contains variables that define system properties.

  - Every time a
    [shell session](Shell-sessions-e6dd743dec1d4fe3b1ee672c8f9731f6)
    spawns, a process takes place to gather and compile information that should be
    available to the shell process and its child processes. It obtains the data
    for these settings from a variety of different files and settings on the
    system.

  - The environment is represented by strings comprising key-value pairs. For
    example:

    ```bash
    KEY=value1:value2
    KEY="value with spaces":"another value with spaces"
    ```

    As the above shows, a key can have multiple related values. Each one is
    demarcated with a `:` . If the value is longer than a single word, quotation
    marks are used.

  - The keys are **variables**. They come in two types: **environmental
    variables** and **shell variables:**

    - Environmental variables are much more permanent and pertain to things like
      the user and his path (the overall session)
    - Shell variables are more changeable for instance the current working
      directory (the current program instance)

  Variables can be created via config files that run on the initialisation of the
  session or manually created via the command line in the current session

  ## What are shell variables useful for?

  Some deployment mechanisms rely on environmental variables to configure
  authentication information. This is useful because it does not require keeping
  these in files that may be seen by outside parties.

  More generally they are used for when you will need to read or alter the
  environment of your system.

  ## Viewing shell and environmental variables

  To view the settings of your current environment you can execute the `env`
  command which returns a list of the key-value pairs introduced above. Here are
  some of the more intelligible variables that are returned when I run this
  command:

  ```bash
  SHELL=/usr/bin/zsh
  DESKTOP_SESSION=plasma
  HOME=/home/thomas
  USER=thomas
  PWD=/home/thomas/repos/bash-scripting
  PATH=/home/thomas/.nvm/versions/node/v16.8.0/bin:/home/thomas/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/var/lib/snapd/snap/bin
  ```

  However if you want to target a specific viable you need to invoke `printenv`
  with the relevant key, for example:

  ```bash
  printenv SHELL
  # SHELL=/usr/bin/zsh
  ```

  Note that `env` and `printenv` do not show all the shell variables, only a
  selection. To view all the shell variables along with the environmental
  variables use `set` .

  ## Creating, exporting and deleting variable shell and environment variables

  - You set shell variables using the same syntax you would within a script file:

    ```bash
    TEST_SHELL_VAR="This is a test"
    set | grep TEST_SH
    TEST_SHELL_VAR='This is a test'

    # We can also print it with an echo, again exactly as we would with a shell script
    echo S{TEST_SHELL_VAR}
    ```

  - We can verify that it is not an environmental variable based on the fact that
    following does not return anything:

    ```bash
    printenv | grep TEST-SH
    ```

  - We can verify that this is a shell variable by spawning a new shell and
    calling it. Nothing will be returned from the child shell.

  - You can upgrade a shell variable to an environment variable with `export` :

    ```bash
    export TEST_SHELL_VAR
    # And confirm:
    printenv | grep TEST_SH
    TEST_SHELL_VAR='This is a test'
    ```

  - We can use the same syntax to create new environment variables from scratch:

    ```bash
    export NEW_ENV_VAR="A new var"
    ```

  ### Using config files to create variables

  You can also add variables to config files that run on login such as your user
  `.bashrc` / `.zshrc` . This is obviously best for when you want the variables to
  persist and be accessible within every
  [shell session](Shell-sessions-e6dd743dec1d4fe3b1ee672c8f9731f6).

  ## Important environmental and shell variables

  ### `PATH`

  A list of directories that the system will check when looking for commands. When
  a user types in a command, the system will check directories in this order for
  the executable.

  ```bash
  echo ${PATH}
  # /home/thomas/.nvm/versions/node/v16.8.0/bin:/home/thomas/.local/bin:/usr/local/sbin:/usr/local/bin:
  # /usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/var/lib/snapd/snap/bin
  ```

  For example, if you wish to use `npm` commands globally (in any directory) you
  will need to have the requisite Node executable in your path, which you can see
  above.

  ### `SHELL`

  This describes the shell that will be interpreting any commands you type in. In
  most cases, this will be bash by default, but other values can be set if you
  prefer other options.

  ```bash
  echo ${SHELL}
  # /usr/bin/zsh
  ```

  ### `USER`

  The current logged in user.

  ```bash
  echo ${USER}
  # thomas
  ```

  ### `PWD`

  The current working directory.

  ```bash
  echo ${PWD}
  # /home/thomas
  ```


path: zk/Equivalent_equations.md
sortable_path: zkEquivalent_equations.md
title: Equivalent equations
checksum: 7a67ff67b281c886ff916a2869b4e29b69a7f85a2d45a4acfdfb71cf8c92bfae
created: 2024-03-30 09:03:19.439050371+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["algebra"]}
lead:
  > Two equations are equivalent if they have the same
  > [solution](Algebra%20key%20terms.md#678811) set.

body:
  > Two equations are equivalent if they have the same
  > [solution](Algebra%20key%20terms.md#678811) set.

  We know from the distributive property of multiplication that the equation
  $a \cdot (b + c )$ is equivalent to $a \cdot b + a \cdot c$. If we assign values
  to the variables such that $b$ is $5$ and $c$ is $2$ we can demonstrate the
  equivalence that obtains in the case of the distributive property by showing
  that both $a \cdot (b + c )$ and $a \cdot b + a \cdot c$ have the same solution:

  $$ 2 \cdot (5 + 2) = 14 $$

  $$ 2 \cdot 5 + 2 \cdot 2 =14 $$

  When we substitute $a$ with $2$ (the solution) we arrive at a true statement
  (the assertion that arrangement of values results in $14$). Since both
  expressions have the same solution they are equivalent.

  ## Creating equivalent equations

  We can create equivalent equations by adding, subtracting, multiplying and
  dividing the _same quantity_ from both sides of the equation (i.e. either side
  of the $=$ symbol). Adding or subtracting the same quantity from both sides
  (either side of the $=$ ) of the equation results in an equivalent equation.

  ### Demonstration with addition

  $$ x - 4 = 3
  $$
  The [solution](Algebra%20key%20terms.md#678811) to this equation is $7$
  $$ x -
  4 (+4) = 3 (+ 4) $$

  Here we have added $4$ to each side of the equation. If $x = 7$ then:

  $$ 7 - 4 (+ 4) = 7 $$

  and:

  $$ 3 + 4 = 7 $$

  ### Demonstration with subtraction

  $$ x + 4 = 9
  $$
  The [solution](Algebra%20key%20terms.md#678811) to this equation is $5$.
  $$ x +
  4 (-4) = 9(-4) $$

  Here we have subtracted $4$ from each side of the equation. If $x = 5$ then:

  $$ 5 + 4 (-4) = 5 $$

  and

  $$ 9 - 4 = 5 $$

  ### Demonstration with multiplication

  $$x \cdot 2 = 10  $$
  The [solution](Algebra%20key%20terms.md#678811) to this equation is $5$.
  $$
  (x \cdot 2) \cdot 3 = 10 \cdot 3 $$ Here we have multiplied each side of the
  equation by $3$. If $x =5$ then

  $$ (5 \cdot 2) \cdot 3 = 30$$
  $$ 10 \cdot 3 = 30$$

  ### Demonstration with division

  $$x \cdot 3 = 18  $$
  The [solution](Algebra%20key%20terms.md#678811) to this equation is $6$.
  $$\frac{x
  \cdot 3}{3} = \frac{18}{3}
  $$
  Here we have divided each side of the equation by $3$. If $x$ is 6, then
  $$\frac{6
  \cdot 3}{3} = 6$$
  $$\frac{18}{3} = 6 $$

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

  ## Equivalent equations

  > Two equations are equivalent if they have the same
  > [solution](Algebra%20key%20terms.md#678811) set.

  We know from the distributive property of multiplication that the equation
  $a \cdot (b + c )$ is equivalent to $a \cdot b + a \cdot c$. If we assign values
  to the variables such that $b$ is $5$ and $c$ is $2$ we can demonstrate the
  equivalence that obtains in the case of the distributive property by showing
  that both $a \cdot (b + c )$ and $a \cdot b + a \cdot c$ have the same solution:

  $$ 2 \cdot (5 + 2) = 14 $$

  $$ 2 \cdot 5 + 2 \cdot 2 =14 $$

  When we substitute $a$ with $2$ (the solution) we arrive at a true statement
  (the assertion that arrangement of values results in $14$). Since both
  expressions have the same solution they are equivalent.

  ## Creating equivalent equations

  We can create equivalent equations by adding, subtracting, multiplying and
  dividing the _same quantity_ from both sides of the equation (i.e. either side
  of the $=$ symbol). Adding or subtracting the same quantity from both sides
  (either side of the $=$ ) of the equation results in an equivalent equation.

  ### Demonstration with addition

  $$ x - 4 = 3
  $$
  The [solution](Algebra%20key%20terms.md#678811) to this equation is $7$
  $$ x -
  4 (+4) = 3 (+ 4) $$

  Here we have added $4$ to each side of the equation. If $x = 7$ then:

  $$ 7 - 4 (+ 4) = 7 $$

  and:

  $$ 3 + 4 = 7 $$

  ### Demonstration with subtraction

  $$ x + 4 = 9
  $$
  The [solution](Algebra%20key%20terms.md#678811) to this equation is $5$.
  $$ x +
  4 (-4) = 9(-4) $$

  Here we have subtracted $4$ from each side of the equation. If $x = 5$ then:

  $$ 5 + 4 (-4) = 5 $$

  and

  $$ 9 - 4 = 5 $$

  ### Demonstration with multiplication

  $$x \cdot 2 = 10  $$
  The [solution](Algebra%20key%20terms.md#678811) to this equation is $5$.
  $$
  (x \cdot 2) \cdot 3 = 10 \cdot 3 $$ Here we have multiplied each side of the
  equation by $3$. If $x =5$ then

  $$ (5 \cdot 2) \cdot 3 = 30$$
  $$ 10 \cdot 3 = 30$$

  ### Demonstration with division

  $$x \cdot 3 = 18  $$
  The [solution](Algebra%20key%20terms.md#678811) to this equation is $6$.
  $$\frac{x
  \cdot 3}{3} = \frac{18}{3}
  $$
  Here we have divided each side of the equation by $3$. If $x$ is 6, then
  $$\frac{6
  \cdot 3}{3} = 6$$
  $$\frac{18}{3} = 6 $$


path: zk/Equivalent_fractions.md
sortable_path: zkEquivalent_fractions.md
lead:
  ---
  tags:
  - prealgebra
  - fractions
  ---

checksum: ac9969e2d22e5ee8ea792b1d99c110146d66d999f4c0fd1f550e320eeb837965
created: 2024-03-30 09:03:19.457422522+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["prealgebra","fractions"]}
body:
  ---
  tags:
    - prealgebra
    - fractions
  ---

  Two fractions are equivalent if they represent the same value. To begin with we
  can represent this visually:

  ![equiv-fractions.png](equiv-fractions.png)

  _Each shaded area is taking up the same proportion of the whole._

  The same properties can be represented arithmetically by multiplying the
  numerator and denominator at each step by 2. Thus:

  $$
  \\frac{1 (\cdot 2)}{3 (\cdot 2)} = \frac{2}{6}
  $$

  Therefore the following rule obtains:

  > If you start with a fraction and multiply both its numerator and denominator
  > by the same value, the resulting fraction is equivalent to the original
  > fraction.

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

  This process works in reverse when we invert the operator and use division:

  $$
  \\frac{2 (/ 2)}{6 (/ 2)} = \frac{1}{3}
  $$

  Thus:

  > If you start with a fraction and divide both its numerator and denominator by
  > the same value, the resulting fraction is equivalent to the original fraction.

  $$
  \\frac{a}{b} = \frac{a / x}{b / x}
  $$

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

  Two fractions are equivalent if they represent the same value. To begin with we
  can represent this visually:

  ![equiv-fractions.png](equiv-fractions.png)

  _Each shaded area is taking up the same proportion of the whole._

  The same properties can be represented arithmetically by multiplying the
  numerator and denominator at each step by 2. Thus:

  $$
  \\frac{1 (\cdot 2)}{3 (\cdot 2)} = \frac{2}{6}
  $$

  Therefore the following rule obtains:

  > If you start with a fraction and multiply both its numerator and denominator
  > by the same value, the resulting fraction is equivalent to the original
  > fraction.

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

  This process works in reverse when we invert the operator and use division:

  $$
  \\frac{2 (/ 2)}{6 (/ 2)} = \frac{1}{3}
  $$

  Thus:

  > If you start with a fraction and divide both its numerator and denominator by
  > the same value, the resulting fraction is equivalent to the original fraction.

  $$
  \\frac{a}{b} = \frac{a / x}{b / x}
  $$


path: zk/Error_handling_in_Python.md
sortable_path: zkError_handling_in_Python.md
title: Error handling and exceptions in Python
lead: Errors can typically occur in the following scenarios:
checksum: 40e1c6225751211bac5aebfa58f20208c81965329365f67e0906dffdcd55d98c
created: 2024-03-30 09:03:19.462414706+00:00
modified: 2024-03-27 10:45:52.500191986+00:00
metadata: {"tags":["python"]}
body:
  Errors can typically occur in the following scenarios:

  - A file or resource that is referred to does not exist
  - A network connection fails
  - A module import error occurs

  In these scenarios you would want to allow for error handling, so that you can
  detect errors and respond appropriately.

  ## Difference between errors and exceptions.

  Python distinguises between errors and exceptions.

  - An error typically indicates a situration where something goes wrong before
    the execution of the program begins.
  - An exception arises during the execution of the program

  In contrast to a error, when an exception occurs, the program doesn't
  necessarily stop immediately. Instead, Python provides a way to handle the
  exception, allowing you to potentially recover from it, or at least, to handle
  it gracefully before the program stops.

  However if you do not implement exception handling, the program will stop
  immediately when an exception occurs, similar to a error.

  ## The Exception hierarchy

  Errors and Exceptions are objects in Python and there is no real syntactic
  distinction between the two since all errors and exceptions in herit from a base
  exception class.

  The root class is `BaseException` which all errors and exeptions extend as
  subclasses as demonstrated by this diagram:

  ![](/img/python-exception-hierarchy.png)

  ## Exception syntax

  ### Difference between `raise` and `except`:

  - `raise` is used to explicitly trigger an exception - it means that you are
    signalling that an exception condition _has_ occured in your program.
  - `except` is used in conjunction with `try`blocks to catch and handle
    exceptions. Here you are saying "I think this _might_ cause an exception, so
    let's be prepared to handle it".

  Exaple of `raise`:

  ```py
  x = -10
  if x < 0:
      raise ValueError("The value should not be negative!")
  ```

  Example of `except`:

  ```py
  try:
      result = 10 / 0
  except ZeroDivisionError:
      print("You can't divide by zero!")

  ```

  ### Scaffolding exception handling

  There is a general procedure for handling exceptions denoted by certain
  keywords:

  - `try`
    - The process you want to run
  - `except`
    - The errors that could occur. You can have multiple `except` clauses for
      different exceptions
  - `else`
    - Some code you want to run after each of the `except` clauses have run
    - It must be written after the `except` clauses
    - It runs **if and only if** no exceptions were raised
    - If `try` succeeds or an exception is thrown, `else` will not run
  - `finally`
    - What you want to run at the end of the `try, except, else` sequence

  ```py
  try
      run_calculation(7)
  except ZeroDivisionError:
      print('something')
  except IndexError:
      print('something')
  except FileNotFoundError:
      print('something')
  except Exception:
      print('something')
  else
      # Do something after the exception blocks
  finally
      # Do concluding action
  ```

  ## Custom exceptions

  You can create your own custom exceptions by creating a class that inherits from
  the `Exception` class.

  ```py
  class CustomError(Exception):
      pass

  try:
      raise CustomError("This is a custom exception!")
  except CustomError as e:
      print(f"Caught an exception: {e}")
  ```

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

  # Error handling and exceptions in Python

  Errors can typically occur in the following scenarios:

  - A file or resource that is referred to does not exist
  - A network connection fails
  - A module import error occurs

  In these scenarios you would want to allow for error handling, so that you can
  detect errors and respond appropriately.

  ## Difference between errors and exceptions.

  Python distinguises between errors and exceptions.

  - An error typically indicates a situration where something goes wrong before
    the execution of the program begins.
  - An exception arises during the execution of the program

  In contrast to a error, when an exception occurs, the program doesn't
  necessarily stop immediately. Instead, Python provides a way to handle the
  exception, allowing you to potentially recover from it, or at least, to handle
  it gracefully before the program stops.

  However if you do not implement exception handling, the program will stop
  immediately when an exception occurs, similar to a error.

  ## The Exception hierarchy

  Errors and Exceptions are objects in Python and there is no real syntactic
  distinction between the two since all errors and exceptions in herit from a base
  exception class.

  The root class is `BaseException` which all errors and exeptions extend as
  subclasses as demonstrated by this diagram:

  ![](/img/python-exception-hierarchy.png)

  ## Exception syntax

  ### Difference between `raise` and `except`:

  - `raise` is used to explicitly trigger an exception - it means that you are
    signalling that an exception condition _has_ occured in your program.
  - `except` is used in conjunction with `try`blocks to catch and handle
    exceptions. Here you are saying "I think this _might_ cause an exception, so
    let's be prepared to handle it".

  Exaple of `raise`:

  ```py
  x = -10
  if x < 0:
      raise ValueError("The value should not be negative!")
  ```

  Example of `except`:

  ```py
  try:
      result = 10 / 0
  except ZeroDivisionError:
      print("You can't divide by zero!")

  ```

  ### Scaffolding exception handling

  There is a general procedure for handling exceptions denoted by certain
  keywords:

  - `try`
    - The process you want to run
  - `except`
    - The errors that could occur. You can have multiple `except` clauses for
      different exceptions
  - `else`
    - Some code you want to run after each of the `except` clauses have run
    - It must be written after the `except` clauses
    - It runs **if and only if** no exceptions were raised
    - If `try` succeeds or an exception is thrown, `else` will not run
  - `finally`
    - What you want to run at the end of the `try, except, else` sequence

  ```py
  try
      run_calculation(7)
  except ZeroDivisionError:
      print('something')
  except IndexError:
      print('something')
  except FileNotFoundError:
      print('something')
  except Exception:
      print('something')
  else
      # Do something after the exception blocks
  finally
      # Do concluding action
  ```

  ## Custom exceptions

  You can create your own custom exceptions by creating a class that inherits from
  the `Exception` class.

  ```py
  class CustomError(Exception):
      pass

  try:
      raise CustomError("This is a custom exception!")
  except CustomError as e:
      print(f"Caught an exception: {e}")
  ```


path: zk/Event_loop.md
sortable_path: zkEvent_loop.md
title: The Event Loop
checksum: ea6dc580b33452599f6c93bcba43fe41057e4d101b526cc0e9dc1c7a63e7e555
created: 2024-03-30 09:03:19.470963015+00:00
modified: 2024-06-16 17:26:03.896135861+00:00
metadata: {"tags":["node-js"]}
lead:
  Node.js provides a single-threaded asynchronous architecture which is achieved
  via means of the Event Loop.

body:
  Node.js provides a single-threaded asynchronous architecture which is achieved
  via means of the Event Loop.

  ## Multi-threaded synchronous architectures

  In the context of backend, a thread as an instance of a request-response
  transaction.

  For example a request is made from the client for a resource contained in a
  database. The back-end language is an intermediary between the client machine
  and the server. It receives the request and returns the resource as a response.

  Many backend frameworks are synchronous but multithreaded. This means that a
  thread can only process one request-response cycle at a time. The thread cannot
  initiate a new cycle until it has finished with its current cycle.

  If there was only one thread, this would be inefficient and unworkable.
  Therefore the framework will be multi-threaded: multiple request-response cycles
  can be executed at once by different threads.

  ![sync-thread.svg](sync-thread.svg)

  To accomodate the ability to increase the scale of synchronous applications you
  need to be able to spawn more threads commensurate to increased demand. This
  increases the resource consumption of the framework (more cores, more memory
  etc). Moreover it is possible to reach a point where all threads are active and
  no more can be spawned. In this case there will simply be delays in the return
  of data.

  ## Node as a single-threaded asynchronous architecture

  In contrast, Node only has a single thread but it works asynchronously, not
  synchronously. Thus it has a **single-threaded asynchronous architecture**. This
  means whilst there is only a single thread it can juggle responses by
  dispatching them asynchronously. When a request is made it sends it off and
  continues with its execution and handling new requests. Once these resolve, the
  data is returned to the main thread.

  ![async.svg](async.svg)

  ## The Event Loop

  Node implements its single-threaded asynchronous architecture through the Event
  Loop.

  This is the mechanism by which Node keeps track of incoming requests and their
  fulfillment status: whether the data has been returned successfully or if there
  has been error.

  Node is continually monitoring the Event Loop in the background.

  A running Node application is a single running process. Like everything that
  happens within the OS, a process is managed by the
  [kernel](The_kernel.md) that dispatches operations to the CPU
  in a clock cycle. A thread is a sequence of code that resides within the process
  and utilises its memory pool (the amount of memory assigned by the kernel to the
  Node process). The Event Loop runs on CPU ticks: a tick is a single run of the
  Event Loop.

  ### Phases of the Event Loop

  The Event Loop comprises six phases. The Event Loop starts at the moment Node
  begins to execute your `index.js` file or any other application entry point.
  These six phases create one cycle, or loop, equal to one **tick**. A Node.js
  process exits when there is no more pending work in the Event Loop, or when
  `process.exit()` is called manually. A program only runs for as long as there
  are tasks queued in the Event Loop, or present on the
  [call stack](Call_stack.md).

  ![](/img/node-event-loop.svg)

  The phases are as follows:

  1. **Timers**
     - These are functions that execute callbacks after a set period of time. As
       in standard JavaScript there are two global timer functions: `setTimeout`
       and `setInterval`. Interestingly these are not core parts of the JavaScript
       language, they are something that are made available to JS by the
       particular browser. As Node does not run in the browser, Node has to
       provide this functionality. It does so through the core `timers` module.
     - At the beginning of this phase the Event Loop updates its own time. Then it
       checks a queue, or pool, of timers. This queue consists of all timers that
       are currently set. The Event Loop takes the timer with the shortest wait
       time and compares it with the Event Loop's current time. If the wait time
       has elapsed, then the timer's callback is queued to be called once the
       [call stack](Call_stack.md) is empty.
  2. **I/O Callbacks**
     - Once timers have been checked and scheduled, Node jumps to I/O operations.
     - Node implements a non-blocking input/output interface. This is to say,
       writing and reading to disk (files in the Node application directory) is
       implemented asynchronously. The asynchronous I/O request is recorded into
       the queue and then the call stack continues.
  3. **Idle / waiting / preparation**
     - This phase is internal to Node and is not accessible to the programmer.
     - It is primarily used for gathering informtion, and planning what needs to
       be executed during the next tick of the Event Loop
  4. **I/O polling**
     - This is the phase at which the main block of code is read and executed by
       Node.
     - During this phase the Event Loop is managing the I/O workload, calling the
       functions in the queue until the queue is empty, and calculating how long
       it should wait until moving to the next phase. All callbacks in this phase
       are called synchronously (although they return asynchronously) in the order
       that they were added to the queue, from oldest to newest.
     - This is the phase that can potentially block our application if any of
       these callbacks are slow or do not return asynchronously.
  5. **`setImmediate` callbacks**
     - This phase runs as soon as the poll phase becomes idle. If `setImmediate()`
       is scheduled within the I/O cycle it will always be executed before other
       timers regardless of how many timers are present.
     - This is your opportunity to grant precedence to certain threads within the
       Node process
  6. **Close events**
     - This phase occurs when the Event Loop is wrapping up one cycle and is ready
       to move to the next one.
     - It is an opportunity for clean-up and to guard against memory leaks.
     - This phase can be targetted via the `process.exit()` function or the close
       event of a web-socket.

  ## Event _loop_ and event _queue_

  The terms _event loop_ and _event queue_ are often used interchangeably in the
  literature but in fact they are distinct.

  The Event Loop is the Node runtime's method of execution, the queue is the stack
  of tasks that are lined up and executed by the loop. We can think of the queue
  as being the input and the loop as what acts on the input. The queue obviously
  emerges from the program we write but it is scheduled, organised and sequenced
  by the loop.

  https://blog.appsignal.com/2022/07/20/an-introduction-to-multithreading-in-nodejs.html
  https://school.geekwall.in/p/Bk2xFs1DV

raw_content:
  ---
  tags:
    - node-js
  ---

  # The Event Loop

  Node.js provides a single-threaded asynchronous architecture which is achieved
  via means of the Event Loop.

  ## Multi-threaded synchronous architectures

  In the context of backend, a thread as an instance of a request-response
  transaction.

  For example a request is made from the client for a resource contained in a
  database. The back-end language is an intermediary between the client machine
  and the server. It receives the request and returns the resource as a response.

  Many backend frameworks are synchronous but multithreaded. This means that a
  thread can only process one request-response cycle at a time. The thread cannot
  initiate a new cycle until it has finished with its current cycle.

  If there was only one thread, this would be inefficient and unworkable.
  Therefore the framework will be multi-threaded: multiple request-response cycles
  can be executed at once by different threads.

  ![sync-thread.svg](sync-thread.svg)

  To accomodate the ability to increase the scale of synchronous applications you
  need to be able to spawn more threads commensurate to increased demand. This
  increases the resource consumption of the framework (more cores, more memory
  etc). Moreover it is possible to reach a point where all threads are active and
  no more can be spawned. In this case there will simply be delays in the return
  of data.

  ## Node as a single-threaded asynchronous architecture

  In contrast, Node only has a single thread but it works asynchronously, not
  synchronously. Thus it has a **single-threaded asynchronous architecture**. This
  means whilst there is only a single thread it can juggle responses by
  dispatching them asynchronously. When a request is made it sends it off and
  continues with its execution and handling new requests. Once these resolve, the
  data is returned to the main thread.

  ![async.svg](async.svg)

  ## The Event Loop

  Node implements its single-threaded asynchronous architecture through the Event
  Loop.

  This is the mechanism by which Node keeps track of incoming requests and their
  fulfillment status: whether the data has been returned successfully or if there
  has been error.

  Node is continually monitoring the Event Loop in the background.

  A running Node application is a single running process. Like everything that
  happens within the OS, a process is managed by the
  [kernel](The_kernel.md) that dispatches operations to the CPU
  in a clock cycle. A thread is a sequence of code that resides within the process
  and utilises its memory pool (the amount of memory assigned by the kernel to the
  Node process). The Event Loop runs on CPU ticks: a tick is a single run of the
  Event Loop.

  ### Phases of the Event Loop

  The Event Loop comprises six phases. The Event Loop starts at the moment Node
  begins to execute your `index.js` file or any other application entry point.
  These six phases create one cycle, or loop, equal to one **tick**. A Node.js
  process exits when there is no more pending work in the Event Loop, or when
  `process.exit()` is called manually. A program only runs for as long as there
  are tasks queued in the Event Loop, or present on the
  [call stack](Call_stack.md).

  ![](/img/node-event-loop.svg)

  The phases are as follows:

  1. **Timers**
     - These are functions that execute callbacks after a set period of time. As
       in standard JavaScript there are two global timer functions: `setTimeout`
       and `setInterval`. Interestingly these are not core parts of the JavaScript
       language, they are something that are made available to JS by the
       particular browser. As Node does not run in the browser, Node has to
       provide this functionality. It does so through the core `timers` module.
     - At the beginning of this phase the Event Loop updates its own time. Then it
       checks a queue, or pool, of timers. This queue consists of all timers that
       are currently set. The Event Loop takes the timer with the shortest wait
       time and compares it with the Event Loop's current time. If the wait time
       has elapsed, then the timer's callback is queued to be called once the
       [call stack](Call_stack.md) is empty.
  2. **I/O Callbacks**
     - Once timers have been checked and scheduled, Node jumps to I/O operations.
     - Node implements a non-blocking input/output interface. This is to say,
       writing and reading to disk (files in the Node application directory) is
       implemented asynchronously. The asynchronous I/O request is recorded into
       the queue and then the call stack continues.
  3. **Idle / waiting / preparation**
     - This phase is internal to Node and is not accessible to the programmer.
     - It is primarily used for gathering informtion, and planning what needs to
       be executed during the next tick of the Event Loop
  4. **I/O polling**
     - This is the phase at which the main block of code is read and executed by
       Node.
     - During this phase the Event Loop is managing the I/O workload, calling the
       functions in the queue until the queue is empty, and calculating how long
       it should wait until moving to the next phase. All callbacks in this phase
       are called synchronously (although they return asynchronously) in the order
       that they were added to the queue, from oldest to newest.
     - This is the phase that can potentially block our application if any of
       these callbacks are slow or do not return asynchronously.
  5. **`setImmediate` callbacks**
     - This phase runs as soon as the poll phase becomes idle. If `setImmediate()`
       is scheduled within the I/O cycle it will always be executed before other
       timers regardless of how many timers are present.
     - This is your opportunity to grant precedence to certain threads within the
       Node process
  6. **Close events**
     - This phase occurs when the Event Loop is wrapping up one cycle and is ready
       to move to the next one.
     - It is an opportunity for clean-up and to guard against memory leaks.
     - This phase can be targetted via the `process.exit()` function or the close
       event of a web-socket.

  ## Event _loop_ and event _queue_

  The terms _event loop_ and _event queue_ are often used interchangeably in the
  literature but in fact they are distinct.

  The Event Loop is the Node runtime's method of execution, the queue is the stack
  of tasks that are lined up and executed by the loop. We can think of the queue
  as being the input and the loop as what acts on the input. The queue obviously
  emerges from the program we write but it is scheduled, organised and sequenced
  by the loop.

  https://blog.appsignal.com/2022/07/20/an-introduction-to-multithreading-in-nodejs.html
  https://school.geekwall.in/p/Bk2xFs1DV


path: zk/Expansions_and_substitutions.md
sortable_path: zkExpansions_and_substitutions.md
title: Expansions and substitutions
checksum: 41c5576a75897c60f757ffd28740cf9fd778651ab006da1d75cdedebcea945b0
created: 2024-03-30 09:03:19.479541974+00:00
modified: 2024-06-16 17:34:56.685984824+00:00
metadata: {"tags":["shell"]}
lead:
  Bash is weird in that parentheses, braces and brackets are used not just as
  markers for different code blocks but as the designators of commands in their
  own right. The type of bracket you use effects how your input is interpreted.

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

  Below are the main forms of expansion and substitution:

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

  ## Brace expansion: `{..}`

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

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

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

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

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

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

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

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

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

  ### Example use case

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

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

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

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

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

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

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

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

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

  For example:

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

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

  We use arithmetic expansion when we want to calculate numerical values

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

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

  # Expansions and substitutions

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

  Below are the main forms of expansion and substitution:

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

  ## Brace expansion: `{..}`

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

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

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

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

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

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

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

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

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

  ### Example use case

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

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

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

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

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

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

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

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

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

  For example:

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

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

  We use arithmetic expansion when we want to calculate numerical values

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


path: zk/Exponents.md
sortable_path: zkExponents.md
title: Equivalent equations
lead: > Two equations are equivalent if they have the same solution set.
checksum: ed92b47092aa66865ecdaf0245394d399969417fb096a7b3f4d8d5454887833e
created: 2024-03-30 09:03:19.49319219+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["algebra","exponents"]}
body:
  > Two equations are equivalent if they have the same solution set.

  We know from the distributive property of multiplication that the equation
  $a \cdot (b + c )$ is equivalent to $a \cdot b + a \cdot c$. If we assign values
  to the variables such that $b$ is equal to $5$ and $c$ is equal to $2$ we can
  demonstrate the equivalence that obtains in the case of the distributive
  property by showing that both $a \cdot (b + c )$ and $a \cdot b + a \cdot c$
  have the same solution:

  $$ 2 \cdot (5 + 2) = 14 $$

  $$ 2 \cdot 5 + 2 \cdot 2 =14 $$

  When we substitute $a$ with $2$ (the solution) we arrive at a true statement
  (the assertion that arrangement of values results in $14$). Since both
  expressions have the same solution they are equivalent.

  ## Creating equivalent equations

  Adding or subtracting the same quantity from both sides (either side of the $=$
  ) of the equation results in an equivalent equation.

  ### Demonstration with addition

  $$ x - 4 = 3 \\ x -4 (+ 4) = 3 (+ 4) $$

  Here we have added $4$ to each side of the equation. If $x = 7$ then:

  $$ 7 - 4 (+ 4) = 7 $$

  and:

  $$ 3 + 4 = 7 $$

  ### Demonstration with subtraction

  $$ x + 4 = 9 \\ x + 4 (-4) = 9 (-4) $$

  Here we have subtracted $4$ from each side of the equation. If $x = 5$ then:

  $$ 5 + 4 (-4) = 5 $$

  and

  $$ 9 - 4 = 5 $$

raw_content:
  ---
  tags: [algebra, exponents]
  ---

  ## Equivalent equations

  > Two equations are equivalent if they have the same solution set.

  We know from the distributive property of multiplication that the equation
  $a \cdot (b + c )$ is equivalent to $a \cdot b + a \cdot c$. If we assign values
  to the variables such that $b$ is equal to $5$ and $c$ is equal to $2$ we can
  demonstrate the equivalence that obtains in the case of the distributive
  property by showing that both $a \cdot (b + c )$ and $a \cdot b + a \cdot c$
  have the same solution:

  $$ 2 \cdot (5 + 2) = 14 $$

  $$ 2 \cdot 5 + 2 \cdot 2 =14 $$

  When we substitute $a$ with $2$ (the solution) we arrive at a true statement
  (the assertion that arrangement of values results in $14$). Since both
  expressions have the same solution they are equivalent.

  ## Creating equivalent equations

  Adding or subtracting the same quantity from both sides (either side of the $=$
  ) of the equation results in an equivalent equation.

  ### Demonstration with addition

  $$ x - 4 = 3 \\ x -4 (+ 4) = 3 (+ 4) $$

  Here we have added $4$ to each side of the equation. If $x = 7$ then:

  $$ 7 - 4 (+ 4) = 7 $$

  and:

  $$ 3 + 4 = 7 $$

  ### Demonstration with subtraction

  $$ x + 4 = 9 \\ x + 4 (-4) = 9 (-4) $$

  Here we have subtracted $4$ from each side of the equation. If $x = 5$ then:

  $$ 5 + 4 (-4) = 5 $$

  and

  $$ 9 - 4 = 5 $$


path: zk/Expressing_whole_numbers_as_fractions.md
sortable_path: zkExpressing_whole_numbers_as_fractions.md
lead:
  ---
  tags:
  - prealgebra
  - fractions
  ---

checksum: a9d4634c9c9622b649bf1fc7f843b2c40e4ad75397e2ab4927891120ecc0b056
created: 2024-03-30 09:03:19.495549725+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra","fractions"]}
body:
  ---
  tags:
    - prealgebra
    - fractions
  ---

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

  _Express 8 as an equivalent fraction having the denominator 5_

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

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

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

  _Express 8 as an equivalent fraction having the denominator 5_

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


path: zk/Factors_and_divisors.md
sortable_path: zkFactors_and_divisors.md
title: Factors and divisors
checksum: f390ce010e89cffb1b9d946eb4697b18da72677d6c17037d1bf65f15b15cbdeb
created: 2024-03-30 09:03:19.498091682+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra"]}
lead:
  The terms **factor** and **divisor** are used interchangeably. They are
  different ways of expressing the same mathematical truth and this is because of
  the inverse relationship between division and multiplication.

body:
  The terms **factor** and **divisor** are used interchangeably. They are
  different ways of expressing the same mathematical truth and this is because of
  the inverse relationship between division and multiplication.

  ### Divisors

  > For a number $n$, its divisor is any number that divides $n$ evenly without
  > remainder: $$ \frac{a}{b} = 0 $$

  In this operation, $a$ is the **divisor**, $b$ is the **dividend** and $0$ is
  the **quotient**.

  ### Factors

  > For a given number $n$, its factors are any pair of numbers that when
  > multiplied together return $n$ as the product: $$ a \cdot b = n $$

  We can see the relationship consists in the fact that factors are associated
  with multiplication and divisors are associated with division: two different
  perspectives on the same number relationships.

  For example, 6 is both a factor and divisor of 18 and 24. To be precise, it is
  the greatest common divisor of these two numbers.

  As a divisor:

  $$
  \\frac{18/6}{24/6} = \frac{3}{4}
  $$

  As a factor:

  $$
  \\frac{3 \cdot 6}{4 \cdot 6} = \frac{18}{24}
  $$

  When we divide by the common divisor is acts as a divisor. When we multiply by
  the common divisor it acts as a factor. The fact that the fractions are
  [equivalent](Equivalent%20fractions.md) in both cases indicates that the
  properties are equivalent.

  ## Greatest common divisor

  > For two two integers $a, b$, $D$ is a common divisor of $a$ and $b$ if it is a
  > divisor of both. The greatest common divisor is the largest value that $D$ can
  > be whilst remaining a divisor to both $a$ and $b$.

  ### Demonstration

  _Find the greatest common divisor of $18$ and $24$_

  The divisors of 18: $$1, 2, 3, 6, 9, 18$$

  The divisors of 24: $$ 1, 2, 3, 4, 6, 8, 12, 24$$

  Thus the common divisors are: $$ 1, 2, 3, 6 $$

  The largest value in the above set is 6, thus 6 is the greatest common divisor.

  ## Heuristics for finding divisors

  1. For dividend $n$ , if $n$ ends in an even number or zero, $n$ is **divisible
     by 2**.
     1. $\frac{12}{2} = 6$
     1. $\frac{84}{2} = 42$
  1. For dividend $n$ if the sum of the digits is divisible by 3 then $n$ is
     **divisible by 3**.
     1. $\frac{72}{3} = 24$
     1. $\frac{21}{3} = 7$
  1. For a dividend $n$ if the number represented of the last two digits of $n$
     divides by 4 then $n$ is divisible by 4
     1. $\frac{324}{4} = 81$
     1. $\frac{532}{4} = 133$
  1. For a dividend $n$, if the last digit of $n$ is divisible by 0 or 5, then $n$
     is divisible by 5.
     1. $\frac{25}{5} = 5$
  1. For a dividend $n$, if $n$ is divisible by 2 and 3, then $n$ is divisible
     by 6.
     1. $\frac{12}{6} = 2$
     1. $\frac{18}{6} = 3$
  1. For a dividend $n$, if the last three digits of $n$ are divisible by 8, then
     $n$ is divisible by 8.
     1. $\frac{73024}{8} = 9128$
  1. For a dividend $n$, if the sum of the digits of $n$ is divisible by 9 then
     $n$ is divisible by 9.
     1. $\frac{117}{9} = 13$

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

  ## Factors and divisors

  The terms **factor** and **divisor** are used interchangeably. They are
  different ways of expressing the same mathematical truth and this is because of
  the inverse relationship between division and multiplication.

  ### Divisors

  > For a number $n$, its divisor is any number that divides $n$ evenly without
  > remainder: $$ \frac{a}{b} = 0 $$

  In this operation, $a$ is the **divisor**, $b$ is the **dividend** and $0$ is
  the **quotient**.

  ### Factors

  > For a given number $n$, its factors are any pair of numbers that when
  > multiplied together return $n$ as the product: $$ a \cdot b = n $$

  We can see the relationship consists in the fact that factors are associated
  with multiplication and divisors are associated with division: two different
  perspectives on the same number relationships.

  For example, 6 is both a factor and divisor of 18 and 24. To be precise, it is
  the greatest common divisor of these two numbers.

  As a divisor:

  $$
  \\frac{18/6}{24/6} = \frac{3}{4}
  $$

  As a factor:

  $$
  \\frac{3 \cdot 6}{4 \cdot 6} = \frac{18}{24}
  $$

  When we divide by the common divisor is acts as a divisor. When we multiply by
  the common divisor it acts as a factor. The fact that the fractions are
  [equivalent](Equivalent%20fractions.md) in both cases indicates that the
  properties are equivalent.

  ## Greatest common divisor

  > For two two integers $a, b$, $D$ is a common divisor of $a$ and $b$ if it is a
  > divisor of both. The greatest common divisor is the largest value that $D$ can
  > be whilst remaining a divisor to both $a$ and $b$.

  ### Demonstration

  _Find the greatest common divisor of $18$ and $24$_

  The divisors of 18: $$1, 2, 3, 6, 9, 18$$

  The divisors of 24: $$ 1, 2, 3, 4, 6, 8, 12, 24$$

  Thus the common divisors are: $$ 1, 2, 3, 6 $$

  The largest value in the above set is 6, thus 6 is the greatest common divisor.

  ## Heuristics for finding divisors

  1. For dividend $n$ , if $n$ ends in an even number or zero, $n$ is **divisible
     by 2**.
     1. $\frac{12}{2} = 6$
     1. $\frac{84}{2} = 42$
  1. For dividend $n$ if the sum of the digits is divisible by 3 then $n$ is
     **divisible by 3**.
     1. $\frac{72}{3} = 24$
     1. $\frac{21}{3} = 7$
  1. For a dividend $n$ if the number represented of the last two digits of $n$
     divides by 4 then $n$ is divisible by 4
     1. $\frac{324}{4} = 81$
     1. $\frac{532}{4} = 133$
  1. For a dividend $n$, if the last digit of $n$ is divisible by 0 or 5, then $n$
     is divisible by 5.
     1. $\frac{25}{5} = 5$
  1. For a dividend $n$, if $n$ is divisible by 2 and 3, then $n$ is divisible
     by 6.
     1. $\frac{12}{6} = 2$
     1. $\frac{18}{6} = 3$
  1. For a dividend $n$, if the last three digits of $n$ are divisible by 8, then
     $n$ is divisible by 8.
     1. $\frac{73024}{8} = 9128$
  1. For a dividend $n$, if the sum of the digits of $n$ is divisible by 9 then
     $n$ is divisible by 9.
     1. $\frac{117}{9} = 13$


path: zk/Factory_pattern.md
sortable_path: zkFactory_pattern.md
title: Factory pattern
checksum: b41d57d76bf4a6832fd6d794615930f6ec3b3087892d9cddb27e1f97f6d4a538
created: 2024-03-30 09:03:19.503613896+00:00
modified: 2024-06-16 17:07:08.714471178+00:00
metadata: {"tags":["OOP","design-patterns"]}
lead:
  The factory pattern pertains to how objects are created in the course of the
  code. It is class based.

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

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

  You have a high level class that sits on top of two or more lower level classes
  that are semantically related but different in function. Depending on the input
  the higher level class receives upon instantiation, it will generate one of the
  lower level classes.

  ### Advantages

  - allows for dynamic creation
    - we may not know what objects are required until runtime: the factory allows
      us to cover several eventualities
  - decoupling
    - in cases where the lower level classes combine to create something greater
      than their individual parts (see the Alligator example below), they do not
      have to know about each other.
  - maintenance
    - allows us to add/remove new object classes easily without changing a lot of
      code.

  ## Examples

  We have two types of notification: email and push. The creation of these two is
  handled by a factory class called `NotificationFactory` :

  ```jsx
  class PushNotification {
    constructor(sendTo, message) {
      this.sendTo = sendTo;
      this.message = message;
    }
  }

  class EmailNotification {
    constructor(sendTo, cc, emailContent) {
      this.sendTo = sendTo;
      this.cc = cc;
      this.emailContent = emailContent;
    }
  }
  ```

  ```jsx
  class NotificationFactory {
    createNotification(type, props) {
      switch (type) {
        case "email":
          return new EmailNotification(
            props.sendTo,
            props.cc,
            props.emailContent
          );
        case "push":
          return new PushNotification(props.sendTo, props.message);
      }
    }
  }
  ```

  We first instantiate an instance of the factory and then pass into it the
  parameters for either of the two lower level classes.

  ```jsx
  const factory = new NotificationFactory();

  // create email notification
  const emailNotification = factory.createNotification("email", {
    sendTo: "receiver@domain.com",
    cc: "test@domain.com",
    emailContent: "This is the email content to be delivered.!",
  });

  // create push notification
  const pushNotification = factory.createNotification("push", {
    sendTo: "receiver-device-id",
    message: "The push notification message",
  });
  ```

  This example doesn't use a constructor on the factory class, but we could easily
  do so. This would remove the need to call a method on the factory class when we
  want to instantiate one of the subclasses. Here is another example that does
  this with an alligator factory:

  ```jsx
  class ReptilePartFactory {
    constructor(type, props) {
      if (type === "tail") return new TailFactory(props);
      if (type === "torso") return new TorsoFactory(props);
      if (type === "head") return new HeadFactory(props);
    }
  }

  let alligator = {};
  let alligatorProps = {
    tailLength: 2.5,
    color: "green",
    snoutLenth: 1,
  };

  //gets a tail from the tail factory
  alligator.tail = new ReptilePartFactory("tail", alligatorProps);

  //gets a torso from the torso factory
  alligator.torso = new ReptilePartFactory("torso", alligatorProps);

  //gets a head from the head factory
  alligator.head = new ReptilePartFactory("head", alligatorProps);
  ```

raw_content:
  ---
  tags:
    - OOP
    - design-patterns
  ---

  # Factory pattern

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

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

  You have a high level class that sits on top of two or more lower level classes
  that are semantically related but different in function. Depending on the input
  the higher level class receives upon instantiation, it will generate one of the
  lower level classes.

  ### Advantages

  - allows for dynamic creation
    - we may not know what objects are required until runtime: the factory allows
      us to cover several eventualities
  - decoupling
    - in cases where the lower level classes combine to create something greater
      than their individual parts (see the Alligator example below), they do not
      have to know about each other.
  - maintenance
    - allows us to add/remove new object classes easily without changing a lot of
      code.

  ## Examples

  We have two types of notification: email and push. The creation of these two is
  handled by a factory class called `NotificationFactory` :

  ```jsx
  class PushNotification {
    constructor(sendTo, message) {
      this.sendTo = sendTo;
      this.message = message;
    }
  }

  class EmailNotification {
    constructor(sendTo, cc, emailContent) {
      this.sendTo = sendTo;
      this.cc = cc;
      this.emailContent = emailContent;
    }
  }
  ```

  ```jsx
  class NotificationFactory {
    createNotification(type, props) {
      switch (type) {
        case "email":
          return new EmailNotification(
            props.sendTo,
            props.cc,
            props.emailContent
          );
        case "push":
          return new PushNotification(props.sendTo, props.message);
      }
    }
  }
  ```

  We first instantiate an instance of the factory and then pass into it the
  parameters for either of the two lower level classes.

  ```jsx
  const factory = new NotificationFactory();

  // create email notification
  const emailNotification = factory.createNotification("email", {
    sendTo: "receiver@domain.com",
    cc: "test@domain.com",
    emailContent: "This is the email content to be delivered.!",
  });

  // create push notification
  const pushNotification = factory.createNotification("push", {
    sendTo: "receiver-device-id",
    message: "The push notification message",
  });
  ```

  This example doesn't use a constructor on the factory class, but we could easily
  do so. This would remove the need to call a method on the factory class when we
  want to instantiate one of the subclasses. Here is another example that does
  this with an alligator factory:

  ```jsx
  class ReptilePartFactory {
    constructor(type, props) {
      if (type === "tail") return new TailFactory(props);
      if (type === "torso") return new TorsoFactory(props);
      if (type === "head") return new HeadFactory(props);
    }
  }

  let alligator = {};
  let alligatorProps = {
    tailLength: 2.5,
    color: "green",
    snoutLenth: 1,
  };

  //gets a tail from the tail factory
  alligator.tail = new ReptilePartFactory("tail", alligatorProps);

  //gets a torso from the torso factory
  alligator.torso = new ReptilePartFactory("torso", alligatorProps);

  //gets a head from the head factory
  alligator.head = new ReptilePartFactory("head", alligatorProps);
  ```


path: zk/Fetch_decode_execute.md
sortable_path: zkFetch_decode_execute.md
title: Fetch, decode, execute, store
checksum: 889f8cf8ebe620ddb40cd972128cea8db0ab054426aa1a4637df56bf73e93c39
created: 2024-03-30 09:03:19.506475587+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["CPU"]}
lead:
  _Fetch, decode, execute_ is the operating cycle of the CPU. We will run through
  how this works with reference to the
  [CPU architecture](CPU_architecture.md).

body:
  _Fetch, decode, execute_ is the operating cycle of the CPU. We will run through
  how this works with reference to the
  [CPU architecture](CPU_architecture.md).

  ## Fetch

  ### Overview

  First, instructions and data are fetched from outside of the CPU chip (usually
  from the DRAM). In some cases, the data used by an instruction will be included
  with it. In other instance the instruction will reference the location where the
  data are held as an address.

  ### Specifics

  - The Program Counter register needs to keep track and sequence the different
    instructions that the CPU will work on. The first place it will look for an
    instruction is at the DRAM address `0000`, equivalent to 0 in the Program
    Counter register: the starting point. This is address therefore copied to the
    Memory Address Register for future reference.
  - This memory-storing event constitutes an instruction so it is copied to the
    Instruction Register.
  - As the first instruction has been fetched, the system reaches the end of the
    first cycle. Thus the Program counter increments by 1 to log this.
  - The next fetch cycle begins.

  ## Decode

  ### Overview

  Once the CPU has received the instruction, the CPU controller chip analyses it
  to determine which of its circuits should be used for processing.

  ### Specifics

  - Now that the instruction is fetched and stored in the RAM it needs to be
    decoded. It is therefore sent from the RAM to the Control Unit of the CPU.
    There are two parts to the instruction:
    1. The operation code ("op code"): the command that the computer will carry
       out.
    1. The operand: an address in RAM where the data will be read and written to
       as part of the execution
  - The Control Unit converts the operation code and operand into an instruction
    that is fed to the next stage in the cycle: execution. For example, for an add
    function, `add` would be the op code and `address x` and `address y` would be
    the operands, thus the instruction would be: `add` the value in `address x` to
    the value in `address y`.

  ## Execute

  Now the command will be executed. The operand is copied to the Memory Address
  Register and then passed to the Memory Data Register and the command is carried
  out by the ALU. The activities of ALU are covered in
  [CPU Architecture](CPU_architecture.md#arithmetic-logic-unit)
  and the notes on
  [Logic Gates](Logic_gates.md).

  ## Store

  ### Overview

  As well as telling the CPU what operation to perform, the instructions must
  specify where to store the result.

  ### Specifics

  - Data is stored differently depending on the instruction.
  - If the instruction is iterative (e.g. adding two numbers and then multiplying
    by another number), the instruction will tell the CPU to store the interim
    first value in the CPU's
    [registers](CPU_architecture.md#registers). As
    these are part of the CPU, the data can be retrieved more readily. If the
    value is not expected to be used again immediately, it goes to the DRAM or
    harddisk.

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

  # Fetch, decode, execute, store

  _Fetch, decode, execute_ is the operating cycle of the CPU. We will run through
  how this works with reference to the
  [CPU architecture](CPU_architecture.md).

  ## Fetch

  ### Overview

  First, instructions and data are fetched from outside of the CPU chip (usually
  from the DRAM). In some cases, the data used by an instruction will be included
  with it. In other instance the instruction will reference the location where the
  data are held as an address.

  ### Specifics

  - The Program Counter register needs to keep track and sequence the different
    instructions that the CPU will work on. The first place it will look for an
    instruction is at the DRAM address `0000`, equivalent to 0 in the Program
    Counter register: the starting point. This is address therefore copied to the
    Memory Address Register for future reference.
  - This memory-storing event constitutes an instruction so it is copied to the
    Instruction Register.
  - As the first instruction has been fetched, the system reaches the end of the
    first cycle. Thus the Program counter increments by 1 to log this.
  - The next fetch cycle begins.

  ## Decode

  ### Overview

  Once the CPU has received the instruction, the CPU controller chip analyses it
  to determine which of its circuits should be used for processing.

  ### Specifics

  - Now that the instruction is fetched and stored in the RAM it needs to be
    decoded. It is therefore sent from the RAM to the Control Unit of the CPU.
    There are two parts to the instruction:
    1. The operation code ("op code"): the command that the computer will carry
       out.
    1. The operand: an address in RAM where the data will be read and written to
       as part of the execution
  - The Control Unit converts the operation code and operand into an instruction
    that is fed to the next stage in the cycle: execution. For example, for an add
    function, `add` would be the op code and `address x` and `address y` would be
    the operands, thus the instruction would be: `add` the value in `address x` to
    the value in `address y`.

  ## Execute

  Now the command will be executed. The operand is copied to the Memory Address
  Register and then passed to the Memory Data Register and the command is carried
  out by the ALU. The activities of ALU are covered in
  [CPU Architecture](CPU_architecture.md#arithmetic-logic-unit)
  and the notes on
  [Logic Gates](Logic_gates.md).

  ## Store

  ### Overview

  As well as telling the CPU what operation to perform, the instructions must
  specify where to store the result.

  ### Specifics

  - Data is stored differently depending on the instruction.
  - If the instruction is iterative (e.g. adding two numbers and then multiplying
    by another number), the instruction will tell the CPU to store the interim
    first value in the CPU's
    [registers](CPU_architecture.md#registers). As
    these are part of the CPU, the data can be retrieved more readily. If the
    value is not expected to be used again immediately, it goes to the DRAM or
    harddisk.


path: zk/Fetch_from_Secrets_Manager.md
sortable_path: zkFetch_from_Secrets_Manager.md
title: Fetch from Secrets Manager
lead: ## TypeScript
checksum: 1868ed19af014838b8449c00ab8640193f95c5496e1b31f701fde06584f1be3d
created: 2024-03-30 09:03:19.516356482+00:00
modified: 2024-06-15 09:49:58.436031202+00:00
metadata: {"tags":["AWS"]}
body:
  ## TypeScript

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

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

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

  ## Python

  ```py
  import json
  import boto3

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

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

  # Fetch from Secrets Manager

  ## TypeScript

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

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

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

  ## Python

  ```py
  import json
  import boto3

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


path: zk/File_descriptors.md
sortable_path: zkFile_descriptors.md
title: File descriptors
lead: File descriptors are shorthand for `stdin`, `stdout` and `stderr`:
checksum: 02cec9154ffd41bbb3e9b313053b6a4daa94b89774e296d26a9bc469c70314e9
created: 2024-03-30 09:03:19.518935058+00:00
modified: 2024-06-16 17:21:21.730146514+00:00
metadata: {"tags":["shell"]}
body:
  File descriptors are shorthand for `stdin`, `stdout` and `stderr`:

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

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

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

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

  # File descriptors

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

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

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

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


path: zk/Filesystems.md
sortable_path: zkFilesystems.md
title: Filesystems
checksum: 63a227182b5bf2eb9d3fcc3a55453e60f945596d9797e15dc0c1be04b40aa3d6
created: 2024-03-30 09:03:19.540667185+00:00
modified: 2024-06-16 17:07:42.944071383+00:00
metadata: {"tags":["disks","computer-architecture","operating-systems"]}
lead:
  We cannot yet mount or interact with the partitions we have created. This is
  because we have not added a filesystem to each partition.

body:
  We cannot yet mount or interact with the partitions we have created. This is
  because we have not added a filesystem to each partition.

  > A filesytem is a form of [database](Basic_database_concepts.md); it
  > supplies the structure to transform a simple block device into the
  > sophisticated hierarchy of files and subdirectories that users can understand.

  Linux recognises many types of filesystems. The native Linux filesystem is the
  **ext4** (Fourth Extended Filesystem). Another common filesystem is **FAT**
  (File Allocation Table). Instances of this include _MSDOS_,_EXFAT_,_FAT-32_.
  They originate from Microsoft systems

  ## Creating a filesystem

  Remember we have two partitions on our external drive: `sda1` and `sda2`. We are
  going to use the `mkfs` utility to create an EXT4 system on both.

  ```bash
  mkfs -t ext4 /dev/sda1
  mkfs -t ext4 /dev/sda2
  ```

  ## Mounting a filesystem

  We can now mount our filesystems. When we mount, we must specify the following
  criteria with the request:

  - The name of the device we want to mount.
    - This will be the name or the partition. However the names (`sda` etc)
      assigned by the OS can change. In these cases and with GPT-based partitions
      you can use the UUID.
    - To see a list of devices and the corresponding filesystems and UUIDs on your
      system, you can use the **`blkid`** ('block id') program.
      ```
      /dev/nvme0n1p3: UUID="c53577b5-92ef-4a0a-9a19-e488bfdfa39c" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="e152b9f4-7ce8-e74b-94db-2731c6fce53d"
      /dev/nvme0n1p1: UUID="9920-636A" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="50592521-d386-194a-a362-bc8562ed6c82"
      /dev/nvme0n1p2: UUID="2ee6b834-0857-49dc-b8ba-a24d46d228ae" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="e08cc442-ef51-7b4f-9d55-e236c55c933c"
      /dev/sda2: UUID="abac6e2e-e3bf-40d3-a5ba-317c53eb27ce" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="4ef1b0e8-3d5b-c940-a3b1-0f85cddeca42"
      /dev/sda1: UUID="ba1e40c5-9b29-4309-a559-99bf8f68116f" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="b4983358-6036-df40-a1f8-793976f3dfb1"
      ```
  - The filesystem type (optional)
  - The **mount point**
    - This is the place within the existing filesystem where you want to mount the
      partition.
    - When you mount to a directory, this directory _becomes_ the disk you have
      mounted, you will not see it as a subdirectory within the the mount point,
      you will just see the contents of the disk itself

  ```bash
  mkdir mountpoint
  mount -t ext4 /dev/sda1 /mnt
  touch test.txt
  ```

  Our `sda1` partition is now mounted at `mountpoint`. We can go ahead and create
  files. If we now look within the graphical file manager when we click on the
  `sda1` volume, we will see the new file we have created in `mountpoint`.

  ![](/img/mount-directory.png)

  ## fstab

  In most cases you want your filesystem to mount automatically on boot and always
  to the same mount point. You can do this via the specialised `fstab` file on
  Linux systems within the `/etc/` directory.

  This is my current `fstab`:

  ```
  # <file system> <dir> <type> <options> <dump> <pass>
  # /dev/nvme0n1p2
  UUID=2ee6b834-0857-49dc-b8ba-a24d46d228ae	/         	ext4      	rw,relatime	0 1

  # /dev/nvme0n1p3
  UUID=c53577b5-92ef-4a0a-9a19-e488bfdfa39c	/home     	ext4      	rw,relatime	0 2

  /swapfile none swap sw 0 0

  ```

  It shows my root and home filesystems and my
  [swap](Swap_space.md) file. Note that we use the UUID
  to name the partition rather than its name in `/dev/`. The order of the
  parameters is as follows:

  - Device name or UUID
  - The mount point
  - The filesystem type (in the example there are two types (`ext4` and `swap`))
  - Options
  - Now largely irrelevant backup information, set to )
  - Filesystem integrity test order
    - Runs the `fsck` ('filesystem check') command against each filesystem
    - Put `1` against the root partition for this to be checked first
    - Put `0` for no checks to take place

raw_content:
  ---
  tags:
    - disks
    - computer-architecture
    - operating-systems
  ---

  # Filesystems

  We cannot yet mount or interact with the partitions we have created. This is
  because we have not added a filesystem to each partition.

  > A filesytem is a form of [database](Basic_database_concepts.md); it
  > supplies the structure to transform a simple block device into the
  > sophisticated hierarchy of files and subdirectories that users can understand.

  Linux recognises many types of filesystems. The native Linux filesystem is the
  **ext4** (Fourth Extended Filesystem). Another common filesystem is **FAT**
  (File Allocation Table). Instances of this include _MSDOS_,_EXFAT_,_FAT-32_.
  They originate from Microsoft systems

  ## Creating a filesystem

  Remember we have two partitions on our external drive: `sda1` and `sda2`. We are
  going to use the `mkfs` utility to create an EXT4 system on both.

  ```bash
  mkfs -t ext4 /dev/sda1
  mkfs -t ext4 /dev/sda2
  ```

  ## Mounting a filesystem

  We can now mount our filesystems. When we mount, we must specify the following
  criteria with the request:

  - The name of the device we want to mount.
    - This will be the name or the partition. However the names (`sda` etc)
      assigned by the OS can change. In these cases and with GPT-based partitions
      you can use the UUID.
    - To see a list of devices and the corresponding filesystems and UUIDs on your
      system, you can use the **`blkid`** ('block id') program.
      ```
      /dev/nvme0n1p3: UUID="c53577b5-92ef-4a0a-9a19-e488bfdfa39c" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="e152b9f4-7ce8-e74b-94db-2731c6fce53d"
      /dev/nvme0n1p1: UUID="9920-636A" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="50592521-d386-194a-a362-bc8562ed6c82"
      /dev/nvme0n1p2: UUID="2ee6b834-0857-49dc-b8ba-a24d46d228ae" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="e08cc442-ef51-7b4f-9d55-e236c55c933c"
      /dev/sda2: UUID="abac6e2e-e3bf-40d3-a5ba-317c53eb27ce" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="4ef1b0e8-3d5b-c940-a3b1-0f85cddeca42"
      /dev/sda1: UUID="ba1e40c5-9b29-4309-a559-99bf8f68116f" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="b4983358-6036-df40-a1f8-793976f3dfb1"
      ```
  - The filesystem type (optional)
  - The **mount point**
    - This is the place within the existing filesystem where you want to mount the
      partition.
    - When you mount to a directory, this directory _becomes_ the disk you have
      mounted, you will not see it as a subdirectory within the the mount point,
      you will just see the contents of the disk itself

  ```bash
  mkdir mountpoint
  mount -t ext4 /dev/sda1 /mnt
  touch test.txt
  ```

  Our `sda1` partition is now mounted at `mountpoint`. We can go ahead and create
  files. If we now look within the graphical file manager when we click on the
  `sda1` volume, we will see the new file we have created in `mountpoint`.

  ![](/img/mount-directory.png)

  ## fstab

  In most cases you want your filesystem to mount automatically on boot and always
  to the same mount point. You can do this via the specialised `fstab` file on
  Linux systems within the `/etc/` directory.

  This is my current `fstab`:

  ```
  # <file system> <dir> <type> <options> <dump> <pass>
  # /dev/nvme0n1p2
  UUID=2ee6b834-0857-49dc-b8ba-a24d46d228ae	/         	ext4      	rw,relatime	0 1

  # /dev/nvme0n1p3
  UUID=c53577b5-92ef-4a0a-9a19-e488bfdfa39c	/home     	ext4      	rw,relatime	0 2

  /swapfile none swap sw 0 0

  ```

  It shows my root and home filesystems and my
  [swap](Swap_space.md) file. Note that we use the UUID
  to name the partition rather than its name in `/dev/`. The order of the
  parameters is as follows:

  - Device name or UUID
  - The mount point
  - The filesystem type (in the example there are two types (`ext4` and `swap`))
  - Options
  - Now largely irrelevant backup information, set to )
  - Filesystem integrity test order
    - Runs the `fsck` ('filesystem check') command against each filesystem
    - Put `1` against the root partition for this to be checked first
    - Put `0` for no checks to take place


path: zk/Flip_flops.md
sortable_path: zkFlip_flops.md
title: Flip-Flops
checksum: 198fdae848e7544e8e4e8a33b32e28274dfad5f77979b255b188aa8666e898e6
created: 2024-03-30 09:03:19.55321343+00:00
modified: 2024-06-16 17:37:03.137593073+00:00
metadata: {"tags":["logic-gates","binary","memory"]}
lead:
  A flip-flop is a type of
  [latch](Latches.md) that is connected
  to a [clock signal](Clock_signals.md)
  and which executes in time with the clock's pulse. (Sometimes "latch" and
  "flip-flop" are used interchangeably but technically a latch is flip-flop
  without a clock connection.)

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

  ## JK Flip-Flop

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

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

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

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

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

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

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

  ## T Flip-Flops

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

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

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

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

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

  # Flip-Flops

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

  ## JK Flip-Flop

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

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

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

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

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

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

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

  ## T Flip-Flops

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

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

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

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


path: zk/Foreign_keys_in_SQL.md
sortable_path: zkForeign_keys_in_SQL.md
title: Creating views with foreign keys
checksum: 188b7858a8887ea1c737fe2bcd06c8282cbbf98697381d1aedde97f5e2469572
created: 2024-03-30 09:03:19.560819418+00:00
modified: 2024-06-15 09:46:12.664864395+00:00
metadata: {"tags":["SQL","databases"]}
lead:
  We utilise **foreign** and
  [primary keys](Primary_key.md) to create
  relationships between tables in SQL. Foreign keys link data in one table to the
  data in another table and are how we cross-reference data in SQL.

body:
  We utilise **foreign** and
  [primary keys](Primary_key.md) to create
  relationships between tables in SQL. Foreign keys link data in one table to the
  data in another table and are how we cross-reference data in SQL.

  In essence you use the primary key of one table to access data in another table.
  When one table _A_ contains the primary key of another table _B_ as a field,
  that key is "foreign" to _A_ hence the name.

  Let's say we have a `sales` table:

  | saleId | modelId | saleDate   | employeeId | price  |
  | ------ | ------- | ---------- | ---------- | ------ |
  | 1      | 44      | 2020-07-27 | tbishop    | 399.99 |
  | 2      | 22      | 2021-02-05 | tbishop    | 200.99 |

  In SQL this would be set up as follows:

  ```sql
  CREATE TABLE sales (
    saleId integer PRIMARY KEY,
    modelId integer,
    saleDate date,
    employeeId text,
    price float
  )
  ```

  For every model that is sold it is possible for the customer to return it. This
  data will be kept in another table `returns`. Every model sold will have an
  entry in `sales` but may or may not have an entry in `returns` (not every
  customer will return an item).

  We want to establish a relationship between the two tables so that if an item is
  returned we can trace it back to its original sale.

  As the `saleId` is the primary key in `sales` this means it is a unique
  identifier for each model sold. We will therefore use this in our `returns`
  table to track the sale data.

  Here's our `returns` table:

  | returnId | saleId | returnDate  | reason                                         |
  | -------- | ------ | ----------- | ---------------------------------------------- |
  | 7899     | 1      | 2020-11-218 | New device issued under warranty due to defect |
  | 6711     | 2      | 2022-09-02  | Returned gift                                  |

  In this table `saleId` is identical to `saleId` in sales. It is the primary key
  in `sales` but a foreign key in `returns`. If a device has been returned it must
  have an entry in `returns` and the `salesId` of the entry in `returns` must
  match the `salesId` in `sales`.

  This is the primary benefit of utilising foreign keys: they add a restriction.
  Entries to the table with a foreign key **must** have a value that corresponds
  with the foreign key column.

  We call this a **foreign key contraint**. More explicitly, our contraint is as
  follows:

  > Any value entered into returns.saleId must already exist in sales.salesId

  A secondary benefit is that they save us the trouble of repeating data. Without
  foreign keys we would have to input `saleId` twice in two different tables.

  We establish the foreign key reference with ther following SQL:

  ```sql
  CREATE TABLE returns (
  	returnId integer PRIMARY KEY,
  	saleId integer NOT NULL,
  	returnDate date,
  	reason text,
  	FOREIGN KEY (sale_id) REFERENCES sales(sale_id)
  	);
  ```

  A table can have more than one foreign key.

  If you delete the source of the foreign key, you also delete its references in
  tables for which it is a foreign key. This is important to remember. So if a row
  was deleted from `sales` the row in `returns` with the corresponding `saleId`
  would also be deleted.

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

  # Creating views with foreign keys

  We utilise **foreign** and
  [primary keys](Primary_key.md) to create
  relationships between tables in SQL. Foreign keys link data in one table to the
  data in another table and are how we cross-reference data in SQL.

  In essence you use the primary key of one table to access data in another table.
  When one table _A_ contains the primary key of another table _B_ as a field,
  that key is "foreign" to _A_ hence the name.

  Let's say we have a `sales` table:

  | saleId | modelId | saleDate   | employeeId | price  |
  | ------ | ------- | ---------- | ---------- | ------ |
  | 1      | 44      | 2020-07-27 | tbishop    | 399.99 |
  | 2      | 22      | 2021-02-05 | tbishop    | 200.99 |

  In SQL this would be set up as follows:

  ```sql
  CREATE TABLE sales (
    saleId integer PRIMARY KEY,
    modelId integer,
    saleDate date,
    employeeId text,
    price float
  )
  ```

  For every model that is sold it is possible for the customer to return it. This
  data will be kept in another table `returns`. Every model sold will have an
  entry in `sales` but may or may not have an entry in `returns` (not every
  customer will return an item).

  We want to establish a relationship between the two tables so that if an item is
  returned we can trace it back to its original sale.

  As the `saleId` is the primary key in `sales` this means it is a unique
  identifier for each model sold. We will therefore use this in our `returns`
  table to track the sale data.

  Here's our `returns` table:

  | returnId | saleId | returnDate  | reason                                         |
  | -------- | ------ | ----------- | ---------------------------------------------- |
  | 7899     | 1      | 2020-11-218 | New device issued under warranty due to defect |
  | 6711     | 2      | 2022-09-02  | Returned gift                                  |

  In this table `saleId` is identical to `saleId` in sales. It is the primary key
  in `sales` but a foreign key in `returns`. If a device has been returned it must
  have an entry in `returns` and the `salesId` of the entry in `returns` must
  match the `salesId` in `sales`.

  This is the primary benefit of utilising foreign keys: they add a restriction.
  Entries to the table with a foreign key **must** have a value that corresponds
  with the foreign key column.

  We call this a **foreign key contraint**. More explicitly, our contraint is as
  follows:

  > Any value entered into returns.saleId must already exist in sales.salesId

  A secondary benefit is that they save us the trouble of repeating data. Without
  foreign keys we would have to input `saleId` twice in two different tables.

  We establish the foreign key reference with ther following SQL:

  ```sql
  CREATE TABLE returns (
  	returnId integer PRIMARY KEY,
  	saleId integer NOT NULL,
  	returnDate date,
  	reason text,
  	FOREIGN KEY (sale_id) REFERENCES sales(sale_id)
  	);
  ```

  A table can have more than one foreign key.

  If you delete the source of the foreign key, you also delete its references in
  tables for which it is a foreign key. This is important to remember. So if a row
  was deleted from `sales` the row in `returns` with the corresponding `saleId`
  would also be deleted.


path: zk/Formal_conventions.md
sortable_path: zkFormal_conventions.md
title: Grouping symbols
checksum: 927610c64facf1a7ccb8b0ef4c67ff919e81ce9dfdcbbf3f60b0b6f2465ae903
created: 2024-03-30 09:03:19.566591234+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra"]}
lead:
  We use parentheses to delimit the part of an expression we want evaluated first.
  If grouping symbols are nested, evaluate the expression in the innermost pair of
  grouping symbols first,

body:
  We use parentheses to delimit the part of an expression we want evaluated first.
  If grouping symbols are nested, evaluate the expression in the innermost pair of
  grouping symbols first,

  ## Writing mathematical statements: placement of $=$

  We only write one equals sign per line. For example, if we are resolving
  parentheses:

  $$ \begin{equation} \begin{split} 2 + \[3 + (4+5)\] = 2 + \[3 +9\] \\ = 2 + 12
  \\ = 14 \end{split} \end{equation} $$

  We call parentheses (`()`), brackets (`[]`) and braces `{}`) grouping symbols.
  When groupings (say parentheses) are used, the expression inside any pair of
  parentheses must be evaluated first.

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

  ## Grouping symbols

  We use parentheses to delimit the part of an expression we want evaluated first.
  If grouping symbols are nested, evaluate the expression in the innermost pair of
  grouping symbols first,

  ## Writing mathematical statements: placement of $=$

  We only write one equals sign per line. For example, if we are resolving
  parentheses:

  $$ \begin{equation} \begin{split} 2 + \[3 + (4+5)\] = 2 + \[3 +9\] \\ = 2 + 12
  \\ = 14 \end{split} \end{equation} $$

  We call parentheses (`()`), brackets (`[]`) and braces `{}`) grouping symbols.
  When groupings (say parentheses) are used, the expression inside any pair of
  parentheses must be evaluated first.


path: zk/Formal_proofs_in_propositional_logic.md
sortable_path: zkFormal_proofs_in_propositional_logic.md
title: Formal proofs in propositional logic
checksum: a122046abb81e7039c1b4c3d9a2e1ae493e2d3b61c43bbaefcfb61f1b46481eb
created: 2024-03-30 09:03:19.569666452+00:00
modified: 2024-06-16 16:52:10.169812296+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  When we construct a formal proof in logic we are seeking to show that a certain
  proposition is **derivable** from other propositions. We use the words
  _derivation_ and _proof_ interchangeably.

body:
  When we construct a formal proof in logic we are seeking to show that a certain
  proposition is **derivable** from other propositions. We use the words
  _derivation_ and _proof_ interchangeably.

  > A sentence $P$ is derivable in a system of propositional logic from a finite
  > set of sentences if and only if there is a derivation in this system in which
  > all and only the members of the set are **primary assumptions** and $P$ is the
  > sentence on the last line.

  We express the above symbolically as $\Gamma \vdash P$ . (Be careful not to
  confuse _derivable_ ($\vdash$) from _entails_ ($\vDash$).

  Derivability is therefore a property that a proposition can possess relative to
  a set.

  For instance to demonstrate derivability for:

  $$
  \{ \lnot F \lor D, F, D \rightarrow (G \land H) \} \vdash G \land H
  $$

  We would establish $\lnot F \lor D, F, D \rightarrow (G \land H)$ as primary
  assumptions and then, using the derivation rules of the system conclude with
  $G\land H$. Every sentence in the derivation is either a **primary assumption**
  or an **auxiliary** assumption or justified by the rules of the derivation.

  An auxiliary assumption is an assumption belonging to a sub-derivation. Primary
  assumptions belong to the main derivation. For any given derivation of the form
  $\Gamma \vdash P$ there may be a number of ways of demonstrating the derivation
  (more than one application of the rules governing the system) but one alone is
  sufficient to establish derivability.

  > We will tend to use the terms _derivation_ and _proof_ interchangeably but we
  > should note that there is a technical distinction in that a **proof is a
  > derivation in which all of the assumptions have been discharged**

  ## Constructing proofs

  We place assumptions above derivations and mark them _A_ in the right-hand
  margin. We use a shorthand for the derivation rules and also place these in the
  right-hand margin.

  We divide assumptions from derivations with a horizontal line. We number each
  line and use this to refer to the line we are applying the derivation to.
  Sub-proofs follow this structure recursively. This is known as _Fitch notation_

  _Schematically_:

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

  _Applied example_:

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

  ## Sub-proofs

  When a sub-proof is terminated, the assumption with which it starts is said to
  be _discharged_. It's conclusion can then be drawn upon and invoked within the
  main proof by reference to its start and end line number. However statements
  within the sub-proof cannot be referred to again from within the main proof,
  only its result.

  ## Derivation rules

  Derivation rules are
  [syntactic](Syntax_of_sentential_logic.md) rather
  than semantic. They are applied on the basis of their form rather than on the
  basis of the truth conditions of the sentences they are applied to.

  > Derivation rules can be applied without having an interpretation of the
  > symbols in mind. A derivation rule tells us that: given a group of symbols
  > with a certain structure, we can write down another group of symbols with a
  > certain structure.

  Each of the main logical connectives has an associated derivation rule. The
  binary connectives have pairs of rules, one for the introduction of the
  connective and one for its elimination.

  The main derivation rules:

  - [Negation Introduction](Negation_Introduction.md)
  - [Negation Elimination](Negation_Elimination.md)
  - [Conjunction Introduction](Conjunction_Introduction.md)
  - [Conjunction Elimination](Conditional_Elimination.md)
  - [Disjunction Introduction](Disjunction_Introduction.md)
  - [Disjunction Elimination](Disjunction_Elimination.md)
  - [Conditional Introduction](Conditional_Introduction.md)
  - [Disjunction Elimination](Disjunction_Elimination.md)
  - [Biconditional Introduction](Biconditional_Introduction.md)
  - [Biconditional Elimination](Biconditional_Elimination.md)

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

  # Formal proofs in propositional logic

  When we construct a formal proof in logic we are seeking to show that a certain
  proposition is **derivable** from other propositions. We use the words
  _derivation_ and _proof_ interchangeably.

  > A sentence $P$ is derivable in a system of propositional logic from a finite
  > set of sentences if and only if there is a derivation in this system in which
  > all and only the members of the set are **primary assumptions** and $P$ is the
  > sentence on the last line.

  We express the above symbolically as $\Gamma \vdash P$ . (Be careful not to
  confuse _derivable_ ($\vdash$) from _entails_ ($\vDash$).

  Derivability is therefore a property that a proposition can possess relative to
  a set.

  For instance to demonstrate derivability for:

  $$
  \{ \lnot F \lor D, F, D \rightarrow (G \land H) \} \vdash G \land H
  $$

  We would establish $\lnot F \lor D, F, D \rightarrow (G \land H)$ as primary
  assumptions and then, using the derivation rules of the system conclude with
  $G\land H$. Every sentence in the derivation is either a **primary assumption**
  or an **auxiliary** assumption or justified by the rules of the derivation.

  An auxiliary assumption is an assumption belonging to a sub-derivation. Primary
  assumptions belong to the main derivation. For any given derivation of the form
  $\Gamma \vdash P$ there may be a number of ways of demonstrating the derivation
  (more than one application of the rules governing the system) but one alone is
  sufficient to establish derivability.

  > We will tend to use the terms _derivation_ and _proof_ interchangeably but we
  > should note that there is a technical distinction in that a **proof is a
  > derivation in which all of the assumptions have been discharged**

  ## Constructing proofs

  We place assumptions above derivations and mark them _A_ in the right-hand
  margin. We use a shorthand for the derivation rules and also place these in the
  right-hand margin.

  We divide assumptions from derivations with a horizontal line. We number each
  line and use this to refer to the line we are applying the derivation to.
  Sub-proofs follow this structure recursively. This is known as _Fitch notation_

  _Schematically_:

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

  _Applied example_:

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

  ## Sub-proofs

  When a sub-proof is terminated, the assumption with which it starts is said to
  be _discharged_. It's conclusion can then be drawn upon and invoked within the
  main proof by reference to its start and end line number. However statements
  within the sub-proof cannot be referred to again from within the main proof,
  only its result.

  ## Derivation rules

  Derivation rules are
  [syntactic](Syntax_of_sentential_logic.md) rather
  than semantic. They are applied on the basis of their form rather than on the
  basis of the truth conditions of the sentences they are applied to.

  > Derivation rules can be applied without having an interpretation of the
  > symbols in mind. A derivation rule tells us that: given a group of symbols
  > with a certain structure, we can write down another group of symbols with a
  > certain structure.

  Each of the main logical connectives has an associated derivation rule. The
  binary connectives have pairs of rules, one for the introduction of the
  connective and one for its elimination.

  The main derivation rules:

  - [Negation Introduction](Negation_Introduction.md)
  - [Negation Elimination](Negation_Elimination.md)
  - [Conjunction Introduction](Conjunction_Introduction.md)
  - [Conjunction Elimination](Conditional_Elimination.md)
  - [Disjunction Introduction](Disjunction_Introduction.md)
  - [Disjunction Elimination](Disjunction_Elimination.md)
  - [Conditional Introduction](Conditional_Introduction.md)
  - [Disjunction Elimination](Disjunction_Elimination.md)
  - [Biconditional Introduction](Biconditional_Introduction.md)
  - [Biconditional Elimination](Biconditional_Elimination.md)


path: zk/Formatting_output_text_in_Bash.md
sortable_path: zkFormatting_output_text_in_Bash.md
title: Formatting output text in Bash
checksum: fac1b5b4deee65d0a974c706274a2198a56bd3dfc53a818655e33e362e7fff6d
created: 2024-03-30 09:03:19.600654686+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["shell"]}
lead:
  We can use the `-e` flag with `echo` to have greater control over the output of
  text. `-e` allows us to use linebreaks, tabs and other formattings within a
  string.

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

  ## Output text in columns

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

  Name		Number
  Thomas		123
  ```

  ## Break text over several lines

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

  This text
  breaks over
  three lines
  ```

  ## Colour outputs

  ![](/img/terminal_colours.png)

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

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

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

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

  This underlines the output.

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

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

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

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

  # Formatting output text in Bash

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

  ## Output text in columns

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

  Name		Number
  Thomas		123
  ```

  ## Break text over several lines

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

  This text
  breaks over
  three lines
  ```

  ## Colour outputs

  ![](/img/terminal_colours.png)

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

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

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

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

  This underlines the output.

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

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

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


path: zk/Four_bit_adder.md
sortable_path: zkFour_bit_adder.md
title: Four-bit adder
checksum: a803c519509da2a87666647933e8f7817c59adbc65482c5dde208fb806b5bcd7
created: 2024-03-30 09:03:19.60803564+00:00
modified: 2024-06-16 17:08:40.660323997+00:00
metadata: {"tags":["logic-gates","binary"]}
lead:
  A single
  [half adder](Half_adder_and_full_adder.md#half-adder)
  and
  [full adder](Half_adder_and_full_adder.md#fufll-adder)
  allows us to calculate the sum of two 1-bit numbers, but this is not much use in
  practice. To approximate what is really happening at the circuit level in
  computers we need to be able to add bigger binary numbers. We will demonstrate
  how this can be achieved for a four-bit number (nibble) using repeated full
  adders and half adders.

body:
  A single
  [half adder](Half_adder_and_full_adder.md#half-adder)
  and
  [full adder](Half_adder_and_full_adder.md#fufll-adder)
  allows us to calculate the sum of two 1-bit numbers, but this is not much use in
  practice. To approximate what is really happening at the circuit level in
  computers we need to be able to add bigger binary numbers. We will demonstrate
  how this can be achieved for a four-bit number (nibble) using repeated full
  adders and half adders.

  We want to be able to calculate the following sum:

  ```
  0010
  0011
  ____
  0101
  ```

  We will achieve this by using three full adders and one half adder, moving from
  right to left. The half adder will be used at the beginning to calculate the
  least significant bit as it will have no carry-in. The subsequent three bits
  will all be added using a full adder.

  Let's walk through the process:

  >

  1. HA receives the bits $0$ and $1$ as inputs. It outputs $1$ as the sum bit and
     $0$ as the carry-out.
  2. FA1 receives $0$ as the carry-in bit plus $1$ and $1$ as its input. This
     means it has the following calculation to execute: $1 + 1 + 0$. This gives
     $0$ as the sum bit and $1$ as the carry-out bit.
  3. FA2 receives $1$ as the carry-in (the carry-out from FA1) plus $0$ and $0$ as
     its own inputs. $0 + 0 + 1$ gives us $1$ as the sum bit and $0$ as the
     carry-out.
  4. Finally FA3 receives $0$ as its carry-in plus $0$ and $0$ as its inputs.
     $0 + 0 + 0 = 0$, therefore its sum bit is $0$ and the overall sum of the four
     bits is $0101$. There is no carry out as there are no more bits to add.

  ## Ripple carry adder

  We notice that as the calculation proceeds, the carry bits propagate or _ripple_
  through the circuit. For this reason, a four-bit adder constructed in the manner
  of this circuit is called a **ripple carry adder**.

  There are two important consequences to note about this type of circuit:

  - Each carry bit that ripples to the next full adder introduces a small delay.
    Therefore extending the circuit to handle more bits will make the circuit
    slower overall.
  - Because the value of the bits changes through the course of the ripple, the
    output of the circuit will be innacurate until all the carry bits have had
    time to propagate.

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

  # Four-bit adder

  A single
  [half adder](Half_adder_and_full_adder.md#half-adder)
  and
  [full adder](Half_adder_and_full_adder.md#fufll-adder)
  allows us to calculate the sum of two 1-bit numbers, but this is not much use in
  practice. To approximate what is really happening at the circuit level in
  computers we need to be able to add bigger binary numbers. We will demonstrate
  how this can be achieved for a four-bit number (nibble) using repeated full
  adders and half adders.

  We want to be able to calculate the following sum:

  ```
  0010
  0011
  ____
  0101
  ```

  We will achieve this by using three full adders and one half adder, moving from
  right to left. The half adder will be used at the beginning to calculate the
  least significant bit as it will have no carry-in. The subsequent three bits
  will all be added using a full adder.

  Let's walk through the process:

  >

  1. HA receives the bits $0$ and $1$ as inputs. It outputs $1$ as the sum bit and
     $0$ as the carry-out.
  2. FA1 receives $0$ as the carry-in bit plus $1$ and $1$ as its input. This
     means it has the following calculation to execute: $1 + 1 + 0$. This gives
     $0$ as the sum bit and $1$ as the carry-out bit.
  3. FA2 receives $1$ as the carry-in (the carry-out from FA1) plus $0$ and $0$ as
     its own inputs. $0 + 0 + 1$ gives us $1$ as the sum bit and $0$ as the
     carry-out.
  4. Finally FA3 receives $0$ as its carry-in plus $0$ and $0$ as its inputs.
     $0 + 0 + 0 = 0$, therefore its sum bit is $0$ and the overall sum of the four
     bits is $0101$. There is no carry out as there are no more bits to add.

  ## Ripple carry adder

  We notice that as the calculation proceeds, the carry bits propagate or _ripple_
  through the circuit. For this reason, a four-bit adder constructed in the manner
  of this circuit is called a **ripple carry adder**.

  There are two important consequences to note about this type of circuit:

  - Each carry bit that ripples to the next full adder introduces a small delay.
    Therefore extending the circuit to handle more bits will make the circuit
    slower overall.
  - Because the value of the bits changes through the course of the ripple, the
    output of the circuit will be innacurate until all the carry bits have had
    time to propagate.


path: zk/Frequency_counters.md
sortable_path: zkFrequency_counters.md
title: Frequency counters
lead:
  Here is a concise method for counting the number instances of each type of array
  element.

checksum: 2cf0f15b006fadb1c49fd3ea77798d83b846acd0594cf66aedc372f533b3edf2
created: 2024-03-30 09:03:19.61962523+00:00
modified: 2024-06-16 17:08:54.526936576+00:00
metadata: {"tags":["data-structures"]}
body:
  Here is a concise method for counting the number instances of each type of array
  element.

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

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

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

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

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

  # Frequency counters

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

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

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

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

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


path: zk/Function_overloads.md
sortable_path: zkFunction_overloads.md
title: Function overloads
checksum: 87852e5b84337d535639055d15b1785225e6a06a613f594ecc3995a161a76311
created: 2024-03-30 09:03:19.630274798+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["typescript"]}
lead:
  Function overloading is not a feature of JavaScript but something close to it
  can be achieved with TypeScript. It proceeds by defining multiple function types
  (defined above the function) that may serve as the actual function's parameters.
  Then with the actual function, you leave the changeable parameters open as
  optional unions and/or `unknown` :

body:
  Function overloading is not a feature of JavaScript but something close to it
  can be achieved with TypeScript. It proceeds by defining multiple function types
  (defined above the function) that may serve as the actual function's parameters.
  Then with the actual function, you leave the changeable parameters open as
  optional unions and/or `unknown` :

  ```ts
  // First oveload type:
  function logSearch(term: string, options?: string): void;

  // Second overload type:
  function logSearch(term: string, options?: number): void;

  // Implementation:
  function logSearch(term: string, p2?: unknown) {
    let query = `https://searchdatabase/${term}`;
    if (typeof p2 === "string") {
      query = `${query}/tag=${p2}`;
      console.log(query);
    } else {
      query = `${query}/page=${p2}`;
      console.log(query);
    }
  }

  logSearch("apples", "braeburn");
  logSearch("bananas", 3);
  ```

  ```ts
  // First overload type:
  function logSearchUnion(term: string, options?: string): void;

  // Second overload type:
  function logSearchUnion(term: string, options?: number): void;

  // Implementation:
  function logSearchUnion(term: string, p2?: string | number) {
    let query = `https://searchdatabase/${term}`;
    if (typeof p2 === "string") {
      query = `${query}/tag=${p2}`;
      console.log(query);
    } else {
      query = `${query}/page=${p2}`;
      console.log(query);
    }
  }

  logSearchUnion("melon", "honey-dew");
  logSearchUnion("oranges", 4);
  ```

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

  ## Function overloads

  Function overloading is not a feature of JavaScript but something close to it
  can be achieved with TypeScript. It proceeds by defining multiple function types
  (defined above the function) that may serve as the actual function's parameters.
  Then with the actual function, you leave the changeable parameters open as
  optional unions and/or `unknown` :

  ```ts
  // First oveload type:
  function logSearch(term: string, options?: string): void;

  // Second overload type:
  function logSearch(term: string, options?: number): void;

  // Implementation:
  function logSearch(term: string, p2?: unknown) {
    let query = `https://searchdatabase/${term}`;
    if (typeof p2 === "string") {
      query = `${query}/tag=${p2}`;
      console.log(query);
    } else {
      query = `${query}/page=${p2}`;
      console.log(query);
    }
  }

  logSearch("apples", "braeburn");
  logSearch("bananas", 3);
  ```

  ```ts
  // First overload type:
  function logSearchUnion(term: string, options?: string): void;

  // Second overload type:
  function logSearchUnion(term: string, options?: number): void;

  // Implementation:
  function logSearchUnion(term: string, p2?: string | number) {
    let query = `https://searchdatabase/${term}`;
    if (typeof p2 === "string") {
      query = `${query}/tag=${p2}`;
      console.log(query);
    } else {
      query = `${query}/page=${p2}`;
      console.log(query);
    }
  }

  logSearchUnion("melon", "honey-dew");
  logSearchUnion("oranges", 4);
  ```


path: zk/Functions.md
sortable_path: zkFunctions.md
title: Functions
checksum: d07fc49bfba92f05596275674fd922ea512e4efde44e593414c7127dfe821e8e
created: 2024-03-30 09:03:19.633898896+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["typescript","react"]}
lead:
  Continuing from the other examples of React Typescript, we could do standard
  listing function, like:

body:
  Continuing from the other examples of React Typescript, we could do standard
  listing function, like:

  ```tsx
  <ul>
    {people.map((person) => {
      return <li>{person.name}</li>;
    })}
  </ul>
  ```

  But it's neater to do it with a function defined within the `List` component:

  ```tsx
  const renderList = (): JSX.Element[] => {
    return people.map((person) => {
      return (
        <li>
          <div>{person.name}</div>
          <div>{person.age}</div>
        </li>
      );
    });
  };
  ```

  And then change the eariler list to a function invocation:

  ```tsx
      <ul>{renderList()}<ul>
  ```

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

  # Functions

  Continuing from the other examples of React Typescript, we could do standard
  listing function, like:

  ```tsx
  <ul>
    {people.map((person) => {
      return <li>{person.name}</li>;
    })}
  </ul>
  ```

  But it's neater to do it with a function defined within the `List` component:

  ```tsx
  const renderList = (): JSX.Element[] => {
    return people.map((person) => {
      return (
        <li>
          <div>{person.name}</div>
          <div>{person.age}</div>
        </li>
      );
    });
  };
  ```

  And then change the eariler list to a function invocation:

  ```tsx
      <ul>{renderList()}<ul>
  ```


path: zk/Functions_in_Bash.md
sortable_path: zkFunctions_in_Bash.md
title: Functions in Bash
checksum: 1436902153eb2fb5b49756f000141710ad80901576075f3acc535f4985194065
created: 2024-03-30 09:03:19.641370438+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["shell"]}
lead:
  We don't name function parameters in the function declaration. Instead we have
  an implied index of arguments: `$1, $2, $3,...`. When the function is called,
  the first value after the function name becomes `$1` by default, then the
  subsequent arguments.

body:
  We don't name function parameters in the function declaration. Instead we have
  an implied index of arguments: `$1, $2, $3,...`. When the function is called,
  the first value after the function name becomes `$1` by default, then the
  subsequent arguments.

  ```bash
  function expandRange() {
      declare -a expandedRange=()
      for (( i=$1; i<=$2; i++ )); do
          expandedRange+=($i)
      done
      echo "${expandedRange[@]}"
  }
  ```

  ```bash
  expandedRange=$(expandRange 1 4)
  echo $expandedRange
  # 1 2 3 4
  ```

  ## Get all arguments as an array

  We can access all the arguments passed to a function using the `$@` syntax we
  encountered before when
  [passing arguments to scripts](Passing_arguments_to_Bash_scripts.md).
  (Here a function is a kind of script in miniature so the process is the same.)

  ```sh
  function numberThings() {
    i=1
    for f in "$@"; do
      echo $i: "$f"
      (( i++ ))
    done
  }
  ```

  ## Local variables

  ```sh
  var1="I'm variable 1"

  function myfunction() {
    var2="I'm variable 2"
    local var3="I'm variable 3"
  }

  myfunction
  echo $var1
  echo $var2
  echo $var3

  # I'm variable 1
  # I'm variable 2
  ```

  > The convention is to put functions at the top of the script, after the shebang
  > and after the global variables

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

  # Functions in Bash

  We don't name function parameters in the function declaration. Instead we have
  an implied index of arguments: `$1, $2, $3,...`. When the function is called,
  the first value after the function name becomes `$1` by default, then the
  subsequent arguments.

  ```bash
  function expandRange() {
      declare -a expandedRange=()
      for (( i=$1; i<=$2; i++ )); do
          expandedRange+=($i)
      done
      echo "${expandedRange[@]}"
  }
  ```

  ```bash
  expandedRange=$(expandRange 1 4)
  echo $expandedRange
  # 1 2 3 4
  ```

  ## Get all arguments as an array

  We can access all the arguments passed to a function using the `$@` syntax we
  encountered before when
  [passing arguments to scripts](Passing_arguments_to_Bash_scripts.md).
  (Here a function is a kind of script in miniature so the process is the same.)

  ```sh
  function numberThings() {
    i=1
    for f in "$@"; do
      echo $i: "$f"
      (( i++ ))
    done
  }
  ```

  ## Local variables

  ```sh
  var1="I'm variable 1"

  function myfunction() {
    var2="I'm variable 2"
    local var3="I'm variable 3"
  }

  myfunction
  echo $var1
  echo $var2
  echo $var3

  # I'm variable 1
  # I'm variable 2
  ```

  > The convention is to put functions at the top of the script, after the shebang
  > and after the global variables


path: zk/Functions_in_Python.md
sortable_path: zkFunctions_in_Python.md
title: Functions in Python
checksum: 02169f9893dea8f188e6c3ea80dca601bb3edef2f5ad05b6f5cf5875b8760e09
created: 2024-03-30 09:03:19.649234225+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["python"]}
lead:
  - Convention is to leave a double line-break after a function definition (but
    not with nested functions - here, a single linebreak is sufficient)
  - Scope within functions is demarcated by indents, as everything in Python
  - We use a docstring _within_ the function body, to document our function. This
    text will then show up in Intellisense etc.

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

  ## Basic examples

  ```py

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

  print_msg()
  # Hello World!

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

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

  print_my_msg('Good day')
  # Good day

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

  print(square(2))
  # 4

  result = square(4)
  print(result)

  # 16

  ```

  ## Default parameters

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

  greeter('Eloise')

  # Welcome Eloise - Live Long and Prosper
  ```

  ## Optional parameters

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


  ## Function with arbitrary parameter list

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

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

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

  ## Scoping

  Function variables are locally scoped by default.

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

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

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

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


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

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

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

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

  print(myfunc1())

  # hello
  ```

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

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

  # Functions in Python

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

  ## Basic examples

  ```py

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

  print_msg()
  # Hello World!

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

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

  print_my_msg('Good day')
  # Good day

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

  print(square(2))
  # 4

  result = square(4)
  print(result)

  # 16

  ```

  ## Default parameters

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

  greeter('Eloise')

  # Welcome Eloise - Live Long and Prosper
  ```

  ## Optional parameters

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


  ## Function with arbitrary parameter list

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

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

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

  ## Scoping

  Function variables are locally scoped by default.

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

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

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

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


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

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

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

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

  print(myfunc1())

  # hello
  ```

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


path: zk/Fundamental_Theorem_of_Arithmetic.md
sortable_path: zkFundamental_Theorem_of_Arithmetic.md
lead:
  ---
  tags:
  - prealgebra
  - theorems
  ---

checksum: 71bc228e57d5941a05c676ee5eaee935bdf63391796039f106b192afc9af7807
created: 2024-03-30 09:03:19.65193431+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra","theorems"]}
body:
  ---
  tags:
    - prealgebra
    - theorems
  ---

  > Every integer greater than one is either a prime number itself or is product
  > of a unique combination of primes.

  This is also known as the **Unique Factorisation Theorem**.

  'Unique' means that there is not more than one way to derive the whole number.
  Once you reduce the factorisation to primes, there can only be one set of
  numbers that results in the target number.

  For example, $24$ has the following factors: ${12, 24}$ and $6, 4$ but these are
  composite numbers. The unique factorisation combination for 24 is $2, 2, 3$.

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

  > Every integer greater than one is either a prime number itself or is product
  > of a unique combination of primes.

  This is also known as the **Unique Factorisation Theorem**.

  'Unique' means that there is not more than one way to derive the whole number.
  Once you reduce the factorisation to primes, there can only be one set of
  numbers that results in the target number.

  For example, $24$ has the following factors: ${12, 24}$ and $6, 4$ but these are
  composite numbers. The unique factorisation combination for 24 is $2, 2, 3$.


path: zk/Git_bisect.md
sortable_path: zkGit_bisect.md
title: Bisect
lead:
  Bisect can be used to identify commits that introduce a bug or regression in our
  code.

checksum: 582180e21ae5adc1089dce7b0205d74e9345d25d43bb03b7a0d473da0f4a3774
created: 2024-03-30 09:03:19.661589073+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["git"]}
body:
  Bisect can be used to identify commits that introduce a bug or regression in our
  code.

  It is most useful for when we know there is a problem in the code and we know
  there was a point in the past where the bug did not exist. We can compare the
  two points and try to identify where things went wrong.

  We mark the last good revision and the first bad revision. Bisect will the reset
  the code to the midpoint between the good and bad versions and let you test it.
  You mark that as a good or bad version and then bisect repeats the process.

  ![](/img/git-bisect.svg)

  ## Procedure

  ```
  git bisect start

  git bisect good <SHA, tag, branch>

  git bisect bad <SHA, tag, branch>
  ```

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

  # Bisect

  Bisect can be used to identify commits that introduce a bug or regression in our
  code.

  It is most useful for when we know there is a problem in the code and we know
  there was a point in the past where the bug did not exist. We can compare the
  two points and try to identify where things went wrong.

  We mark the last good revision and the first bad revision. Bisect will the reset
  the code to the midpoint between the good and bad versions and let you test it.
  You mark that as a good or bad version and then bisect repeats the process.

  ![](/img/git-bisect.svg)

  ## Procedure

  ```
  git bisect start

  git bisect good <SHA, tag, branch>

  git bisect bad <SHA, tag, branch>
  ```


path: zk/Grep.md
sortable_path: zkGrep.md
title: Grep
lead: ## Purpose of `grep`
checksum: be1a28cd33fe7976d80627887f90d029ed84591c7e294bc75165973cc3f70c59
created: 2024-03-30 09:03:19.668728834+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["shell"]}
body:
  ## Purpose of `grep`

  `grep` stands for “global regular expression print”. It allows you to search
  plain text data sets for strings which match a regular expression or pattern.

  ## Syntax

  ### Schematic

  ```bash
  grep [options] [pattern] [source file] > [output file]
  ```

  Note that above we redirect the file matches to a new file. You don't have to do
  this. If you omit the redirection, `grep` will output to standard output.

  ### Applied

  ```bash
  grep -i -n "banana" fruits.txt > banana.txt
  ```

  The above example searches, using regex, for strings matching the pattern
  “banana” in the file `fruits.txt` regardless of the character case (`-i` ensures
  this) and outputs its findings to the file `banana.txt`, with the line number
  where the match occurs appended to the output (`-n` takes care of this).

  Note that for simplicity, you can chain optional values together, i.e. the
  options in the above example could be input as `-in`.

  ## Useful options

  - ignore case: `i`
  - count matches instead of returning actual match: `-c`
  - precede each match with the line number where it occurs: `-n`
  - invert the match (show everything that doesn't match the expression): `-v`
  - search entire directories recursively: `-r`
  - list file names where matches occur (in the scenario of a recursive match):
    `-l`

  ## `ripgrep`

  `ripgrep` is generally faster however it does not come as default with Unix and
  only works recursively, i.e. it is designed to find strings within files within
  multiple directories not just single files or piped streams.

  It also respects `.gitignore` files that it finds within directories by default
  and `node_modules` which is really handy.

  Most of the standard `grep` options transfer over.

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

  # Grep

  ## Purpose of `grep`

  `grep` stands for “global regular expression print”. It allows you to search
  plain text data sets for strings which match a regular expression or pattern.

  ## Syntax

  ### Schematic

  ```bash
  grep [options] [pattern] [source file] > [output file]
  ```

  Note that above we redirect the file matches to a new file. You don't have to do
  this. If you omit the redirection, `grep` will output to standard output.

  ### Applied

  ```bash
  grep -i -n "banana" fruits.txt > banana.txt
  ```

  The above example searches, using regex, for strings matching the pattern
  “banana” in the file `fruits.txt` regardless of the character case (`-i` ensures
  this) and outputs its findings to the file `banana.txt`, with the line number
  where the match occurs appended to the output (`-n` takes care of this).

  Note that for simplicity, you can chain optional values together, i.e. the
  options in the above example could be input as `-in`.

  ## Useful options

  - ignore case: `i`
  - count matches instead of returning actual match: `-c`
  - precede each match with the line number where it occurs: `-n`
  - invert the match (show everything that doesn't match the expression): `-v`
  - search entire directories recursively: `-r`
  - list file names where matches occur (in the scenario of a recursive match):
    `-l`

  ## `ripgrep`

  `ripgrep` is generally faster however it does not come as default with Unix and
  only works recursively, i.e. it is designed to find strings within files within
  multiple directories not just single files or piped streams.

  It also respects `.gitignore` files that it finds within directories by default
  and `node_modules` which is really handy.

  Most of the standard `grep` options transfer over.


path: zk/Ground.md
sortable_path: zkGround.md
title: Ground
checksum: 06d6a82b347c9527a50fa40afccff167a415ff9ab7bfed33ff87c58dc8ae473d
created: 2024-03-30 09:03:19.673447106+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["physics","electricity"]}
lead:
  The term 'ground' is used as a general reference for 'zero potential': the point
  in an electrical circuit where there is no potential difference between the
  positive and negative terminals of the voltage source.

body:
  The term 'ground' is used as a general reference for 'zero potential': the point
  in an electrical circuit where there is no potential difference between the
  positive and negative terminals of the voltage source.

  As a consequence of the lack of potential, ground is a point at which no current
  can be generated.

  The term originates from domestic electrical systems where the circuit box is
  literally attached to the ground (represented by the green and yellow ribboned
  wires in plug sockets and light fixtures etc) where current is discharged to
  prevent appliances emitting electrical shocks.

  In circuit diagrams with simple DC current, ground is taken to be the negative
  terminal of the voltage source.

  The symbol for ground in circuit diagrams:

  <img src="/home/thomas/repos/computer_science/img/ground-symbol.png" width="300px">

raw_content:
  ---
  tags: [physics, electricity]
  ---

  # Ground

  The term 'ground' is used as a general reference for 'zero potential': the point
  in an electrical circuit where there is no potential difference between the
  positive and negative terminals of the voltage source.

  As a consequence of the lack of potential, ground is a point at which no current
  can be generated.

  The term originates from domestic electrical systems where the circuit box is
  literally attached to the ground (represented by the green and yellow ribboned
  wires in plug sockets and light fixtures etc) where current is discharged to
  prevent appliances emitting electrical shocks.

  In circuit diagrams with simple DC current, ground is taken to be the negative
  terminal of the voltage source.

  The symbol for ground in circuit diagrams:

  <img src="/home/thomas/repos/computer_science/img/ground-symbol.png" width="300px">


path: zk/Guide_to_YAML.md
sortable_path: zkGuide_to_YAML.md
title: Guide to YAML
checksum: 22c8571439ba88961c2f29fa85f550ded8ffcbe85324bac952d3ee59bf419992
created: 2024-03-30 09:03:19.678384789+00:00
modified: 2024-06-16 17:11:41.254134838+00:00
metadata: {"tags":["yaml"]}
lead:
  - Data description language
  - Alternative to XML and JSON
  - Easy to read by humans and process by code

body:
  - Data description language
  - Alternative to XML and JSON
  - Easy to read by humans and process by code

  ## Main rule

  > Never use tabs for indentation, use two spaces

  ## Available data types

  - strings
  - numbers
  - booleans
  - arrays
  - maps (key, value pairs)

  ### Example

  ````yaml
  # Basic scalar data types
  age: 51
  greeting: Hello, world
  length: 182.3
  happy: true
  ``

  ```yaml
  # array
  pets:
    - cat
    - dog

  # nested array
  pets:
    - cat
    - dog
      - staffy
      - pitty
  ````

  ```yaml
  # map
  languages:
    - java: advanced
    - python: average
    - javascript: basic
  ```

  ### Strings

  - Don't have to use quotes unless string contains reserved YAML characters
    (dashes, colons etc)
  - Can use single or double quotes

  ### Multi-line text

  Two methods:

  ```yaml
  # Literal block preserves new line characters
  literal_block: |
    Lorem ipsum dolar sit avcc lorem dolar 
    Blah blah
    Blah
  ```

  ```yaml
  folded_block: >
    Each different line will be folded back on itself so you don't have to keep
    scrolling to the right
  ```

raw_content:
  ---
  tags:
    - yaml
  ---

  # Guide to YAML

  - Data description language
  - Alternative to XML and JSON
  - Easy to read by humans and process by code

  ## Main rule

  > Never use tabs for indentation, use two spaces

  ## Available data types

  - strings
  - numbers
  - booleans
  - arrays
  - maps (key, value pairs)

  ### Example

  ````yaml
  # Basic scalar data types
  age: 51
  greeting: Hello, world
  length: 182.3
  happy: true
  ``

  ```yaml
  # array
  pets:
    - cat
    - dog

  # nested array
  pets:
    - cat
    - dog
      - staffy
      - pitty
  ````

  ```yaml
  # map
  languages:
    - java: advanced
    - python: average
    - javascript: basic
  ```

  ### Strings

  - Don't have to use quotes unless string contains reserved YAML characters
    (dashes, colons etc)
  - Can use single or double quotes

  ### Multi-line text

  Two methods:

  ```yaml
  # Literal block preserves new line characters
  literal_block: |
    Lorem ipsum dolar sit avcc lorem dolar 
    Blah blah
    Blah
  ```

  ```yaml
  folded_block: >
    Each different line will be folded back on itself so you don't have to keep
    scrolling to the right
  ```


path: zk/HTTP_request_types.md
sortable_path: zkHTTP_request_types.md
title: GET
lead: - Get data
body:
  - Get data

  ## POST

  - Create data

  ## PUT

  - Update data

  ## DELETE

  - Remove data

checksum: 6321d7aa9c2f0f53996638c9c4d606d65e0e27fe1ccda26e81faf7f8c405b2a1
created: 2024-03-30 09:03:19.682695112+00:00
modified: 2024-06-16 17:12:53.454940331+00:00
metadata: {"tags":["REST"]}
raw_content:
  ---
  tags:
    - REST
  ---

  ## GET

  - Get data

  ## POST

  - Create data

  ## PUT

  - Update data

  ## DELETE

  - Remove data


path: zk/Half_adder_and_full_adder.md
sortable_path: zkHalf_adder_and_full_adder.md
title: The half adder and full adder
lead: ## Binary arithmetic
checksum: 6c57575017ae451e8a8b357b8e8a6c8cdab2de4305b514ab62eaa9bfeb654556
created: 2024-03-30 09:03:19.685941999+00:00
modified: 2024-04-11 18:03:36.776792856+00:00
metadata: {"tags":["logic-gates","binary"]}
body:
  ## Binary arithmetic

  The half adder and full adder are components of digital circuits that enable us
  to carry out binary addition. Using adders and half adders we can add two binary
  numbers together. Adders are a type of [integrated circuit]() comprising certain
  [logic gates](Logic_gates.md) where the arrangement allows for the
  representation of the addition of bits.

  ### Example addition

  Consider the following binary addition:

  ```
    c_1
  0   0   1   0
  0   0   1   1
  _____________
  0   1   0   1
  ```

  How does the calculation proceed? Moving from the right-most column:

  - $1 + 0 = 1$ hence the sum of the first column (the binary 1 coumn) is $1$ with
    no carry
  - Moving to the second column (the binary 2 column), $1 + 1 = 2$ however we do
    not have the digit "2" in binary, therefore we must bump the place value by
    one place and carry this sum as a $1$ leaving a zero to mark the place value
  - Thus the third column (the binary 4 column) has the calculation:
    $1 + 0 + 0 = 1$ with no carry.

  ### Bits

  From the previous example we can identified the following repeated actions:

  - For calculations of the form $1 + 0$ or $0 + 0$ there will be no carried digit
  - For calculations of the form $1 + 1$ there will be a carried digit

  In the context of adders the digits are bits. We distinguish three core bits by
  their role in the calculation: the carry, the **least significant bit** and the
  **most significant bit**. The least significant bit is the right-most digit in
  the calculation. The most significant bit is the leftmost.

  The two types of adders are distinguished by which bits of the calculation they
  operate on. The half adder focuses on the least significant bit whereas the full
  adder focuses on the most significant bit.

  ## Half adder

  The half adder receives two bits (A and B) which are to be added together. It
  outputs this value as the **sum bit**. If there is a bit to be carried to the
  next column in the binary calculation this will be output as the **carry-out**
  bit.

  | A                            | B                             | S           | C_out             |
  | ---------------------------- | ----------------------------- | ----------- | ----------------- |
  | The first number to be added | The second number to be added | The sum bit | The carry-out bit |

  The diagram below shows the circuit representation of a half-adder and an
  example calculation. This calculation matches the ones column of the earlier
  binary addition example: $0011 + 0010$.

  ![](/img/half-adder-new.png)

  ### Implementation with logic gates

  If we think about it, the possible inputs and outputs of a half adder are highly
  circumscribed:

  - If the sum exceeds $1$, the sum bit will be $0$ and the carry-out bit will be
    $1$
  - In all other cases the carry-out bit will be $0$. These other cases are when
    the sum bit is either $0$ or $1$, e.g: $1 + 0$ or $0 + 0$.

  We can represent this with a simple truth-table:

  | A   | B   | S   | C_out |
  | --- | --- | --- | ----- |
  | 0   | 0   | 0   | 0     |
  | 0   | 1   | 1   | 0     |
  | 1   | 0   | 1   | 0     |
  | 1   | 1   | 0   | 1     |

  We can see that the sum bit column replicates the truth-conditions of
  [XOR](Logic_gates.md#xor-gate):

  | P   | Q   | P V Q |
  | --- | --- | ----- |
  | T   | T   | F     |
  | T   | F   | T     |
  | F   | T   | T     |
  | F   | F   | F     |

  And the carry-out bit replicates the truth conditions of
  [AND](Logic_gates.md#and-gate):

  | P   | Q   | P & Q |
  | --- | --- | ----- |
  | T   | T   | F     |
  | T   | F   | F     |
  | F   | T   | F     |
  | F   | F   | T     |

  It is therefore possible to implement a half-adder with just these two logic
  gates:

  ![](/img/half-adder-gates-three.png)

  The digital circuit above has the same inputs and outputs as the half adder
  diagram above.

  <iframe src="https://circuitverse.org/simulator/embed/half-adder-67b14a9f-d1ed-4240-ab19-1d753f18a40d" 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>

  ## Full adder

  As the half adder only calculates the least significant bit, it is not
  sufficient by itself to complete a binary addition; it cannot account for
  movements in binary place value. To carry out full calculations it must be
  supplemented with the full adder.

  The full adder takes in three inputs and has two outputs. It is identical to the
  half-adder apart from the fact that one of its inputs is **carry-in**. This is
  equivalent to the value that is designated as **carry-out** in a half adder. It
  is an incoming value that is the product of a previous operation that resulted
  in a carry-out. This is added together with the full adders own two inputs (A
  and B) and like the half adder, generates a sum bit and a carry-out bit.

  | A                            | B                             | C_in                     | S                      | C_out                        |
  | ---------------------------- | ----------------------------- | ------------------------ | ---------------------- | ---------------------------- |
  | The first number to be added | The second number to be added | The incoming carried bit | The sum bit (A+B+C_in) | The carry-out bit (A+B+C_in) |

  ![](/img/full-adder-new.png)

  The diagram above is equivalent to the calculation taking place in the fours
  column. It has received a carry from the twos column ($1 + 1$ results in $1$ as
  a carry) and then adds this together with its own inputs ($0$ and $0$).

  ```
    c_1
  0   0   1   0
  0   0   1   1
  _____________
  0   1   0   1
  ```

  ### Implementation with logic gates

  When it comes to implementing the full adder with logic gates we can allow
  ourselves greater abstraction as we already have the half adder to work with. We
  don't need to create a half adder from scratch, we can reuse it.

  We are adding three bits: $1$, $0$ and $0$. This can be achieved with two half
  adders:

  - One half adder (HA1) for the sum of $0 + 0$
  - One half adder (HA2) for the sum of $(0 + 0) + 1$ (the previous sum plus the
    third bit)

  The sum of HA1 ($0 + 0$) is passed in to the B input on HA2 and the $1$ is
  passed in as the A input of HA2. This gives us $1$ as the sum bit of HA2.

  At this point we have completed the addition and have successfully added the
  three bits: $1$, $0$, and $0$ to get $1$ as ths sum. However we also have to
  account for the fact that the addition may result in its own carry-out bit. What
  if the inputs were $1, 1, 0$ for example?

  <iframe src="https://circuitverse.org/simulator/embed/full-adder-674f8ddf-c5aa-42fa-9c0f-4349ffabe723" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="400" width="700" allowFullScreen></iframe>

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

  # The half adder and full adder

  ## Binary arithmetic

  The half adder and full adder are components of digital circuits that enable us
  to carry out binary addition. Using adders and half adders we can add two binary
  numbers together. Adders are a type of [integrated circuit]() comprising certain
  [logic gates](Logic_gates.md) where the arrangement allows for the
  representation of the addition of bits.

  ### Example addition

  Consider the following binary addition:

  ```
    c_1
  0   0   1   0
  0   0   1   1
  _____________
  0   1   0   1
  ```

  How does the calculation proceed? Moving from the right-most column:

  - $1 + 0 = 1$ hence the sum of the first column (the binary 1 coumn) is $1$ with
    no carry
  - Moving to the second column (the binary 2 column), $1 + 1 = 2$ however we do
    not have the digit "2" in binary, therefore we must bump the place value by
    one place and carry this sum as a $1$ leaving a zero to mark the place value
  - Thus the third column (the binary 4 column) has the calculation:
    $1 + 0 + 0 = 1$ with no carry.

  ### Bits

  From the previous example we can identified the following repeated actions:

  - For calculations of the form $1 + 0$ or $0 + 0$ there will be no carried digit
  - For calculations of the form $1 + 1$ there will be a carried digit

  In the context of adders the digits are bits. We distinguish three core bits by
  their role in the calculation: the carry, the **least significant bit** and the
  **most significant bit**. The least significant bit is the right-most digit in
  the calculation. The most significant bit is the leftmost.

  The two types of adders are distinguished by which bits of the calculation they
  operate on. The half adder focuses on the least significant bit whereas the full
  adder focuses on the most significant bit.

  ## Half adder

  The half adder receives two bits (A and B) which are to be added together. It
  outputs this value as the **sum bit**. If there is a bit to be carried to the
  next column in the binary calculation this will be output as the **carry-out**
  bit.

  | A                            | B                             | S           | C_out             |
  | ---------------------------- | ----------------------------- | ----------- | ----------------- |
  | The first number to be added | The second number to be added | The sum bit | The carry-out bit |

  The diagram below shows the circuit representation of a half-adder and an
  example calculation. This calculation matches the ones column of the earlier
  binary addition example: $0011 + 0010$.

  ![](/img/half-adder-new.png)

  ### Implementation with logic gates

  If we think about it, the possible inputs and outputs of a half adder are highly
  circumscribed:

  - If the sum exceeds $1$, the sum bit will be $0$ and the carry-out bit will be
    $1$
  - In all other cases the carry-out bit will be $0$. These other cases are when
    the sum bit is either $0$ or $1$, e.g: $1 + 0$ or $0 + 0$.

  We can represent this with a simple truth-table:

  | A   | B   | S   | C_out |
  | --- | --- | --- | ----- |
  | 0   | 0   | 0   | 0     |
  | 0   | 1   | 1   | 0     |
  | 1   | 0   | 1   | 0     |
  | 1   | 1   | 0   | 1     |

  We can see that the sum bit column replicates the truth-conditions of
  [XOR](Logic_gates.md#xor-gate):

  | P   | Q   | P V Q |
  | --- | --- | ----- |
  | T   | T   | F     |
  | T   | F   | T     |
  | F   | T   | T     |
  | F   | F   | F     |

  And the carry-out bit replicates the truth conditions of
  [AND](Logic_gates.md#and-gate):

  | P   | Q   | P & Q |
  | --- | --- | ----- |
  | T   | T   | F     |
  | T   | F   | F     |
  | F   | T   | F     |
  | F   | F   | T     |

  It is therefore possible to implement a half-adder with just these two logic
  gates:

  ![](/img/half-adder-gates-three.png)

  The digital circuit above has the same inputs and outputs as the half adder
  diagram above.

  <iframe src="https://circuitverse.org/simulator/embed/half-adder-67b14a9f-d1ed-4240-ab19-1d753f18a40d" 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>

  ## Full adder

  As the half adder only calculates the least significant bit, it is not
  sufficient by itself to complete a binary addition; it cannot account for
  movements in binary place value. To carry out full calculations it must be
  supplemented with the full adder.

  The full adder takes in three inputs and has two outputs. It is identical to the
  half-adder apart from the fact that one of its inputs is **carry-in**. This is
  equivalent to the value that is designated as **carry-out** in a half adder. It
  is an incoming value that is the product of a previous operation that resulted
  in a carry-out. This is added together with the full adders own two inputs (A
  and B) and like the half adder, generates a sum bit and a carry-out bit.

  | A                            | B                             | C_in                     | S                      | C_out                        |
  | ---------------------------- | ----------------------------- | ------------------------ | ---------------------- | ---------------------------- |
  | The first number to be added | The second number to be added | The incoming carried bit | The sum bit (A+B+C_in) | The carry-out bit (A+B+C_in) |

  ![](/img/full-adder-new.png)

  The diagram above is equivalent to the calculation taking place in the fours
  column. It has received a carry from the twos column ($1 + 1$ results in $1$ as
  a carry) and then adds this together with its own inputs ($0$ and $0$).

  ```
    c_1
  0   0   1   0
  0   0   1   1
  _____________
  0   1   0   1
  ```

  ### Implementation with logic gates

  When it comes to implementing the full adder with logic gates we can allow
  ourselves greater abstraction as we already have the half adder to work with. We
  don't need to create a half adder from scratch, we can reuse it.

  We are adding three bits: $1$, $0$ and $0$. This can be achieved with two half
  adders:

  - One half adder (HA1) for the sum of $0 + 0$
  - One half adder (HA2) for the sum of $(0 + 0) + 1$ (the previous sum plus the
    third bit)

  The sum of HA1 ($0 + 0$) is passed in to the B input on HA2 and the $1$ is
  passed in as the A input of HA2. This gives us $1$ as the sum bit of HA2.

  At this point we have completed the addition and have successfully added the
  three bits: $1$, $0$, and $0$ to get $1$ as ths sum. However we also have to
  account for the fact that the addition may result in its own carry-out bit. What
  if the inputs were $1, 1, 0$ for example?

  <iframe src="https://circuitverse.org/simulator/embed/full-adder-674f8ddf-c5aa-42fa-9c0f-4349ffabe723" style="border-width:; border-style: solid; border-color:;" name="myiframe" id="projectPreview" scrolling="no" frameborder="1" marginheight="0px" marginwidth="0px" height="400" width="700" allowFullScreen></iframe>


path: zk/Handle_multiple_endpoints_single_lambda.md
sortable_path: zkHandle_multiple_endpoints_single_lambda.md
title: Handle multiple API Gateway endpoints in a single lambda
lead:
  With the following config we have a single lambda (`/query-waktime`) with
  multiple endpoints:

checksum: 64aa42198251e03ef3600ba126849e631e989fd286ce097a316ad6fe91a04379
created: 2024-03-30 09:03:19.699910599+00:00
modified: 2024-06-15 09:50:08.206221184+00:00
metadata: {"tags":["AWS","aws-lambda"]}
body:
  With the following config we have a single lambda (`/query-waktime`) with
  multiple endpoints:

  ```yml
  Resources:
    QueryWakatimeFunction:
      Type: AWS::Serverless::Function
      Properties:
        CodeUri: src/query-wakatime/
        Handler: index.handler
        Events:
          MainMetrics:
            Type: Api
            Properties:
              Path: /query-wakatime/main-metrics
              Method: get
              Auth:
                Authorizer: AWS_IAM
          Durations:
            Type: Api
            Properties:
              Path: /query-wakatime/durations
              Method: get
              Auth:
                Authorizer: AWS_IAM
          TodayOnly:
            Type: Api
            Properties:
              Path: /query-wakatime/today-only
              Method: get
              Auth:
                Authorizer: AWS_IAM
  ```

  Our endpoints are as follows:

  - `/query-wakatime/main-metrics`
  - `/query-wakatime/durations`
  - `/query-wakatime/today-only`

  Each returns a different dataset from an external API. The lambda function makes
  a different request to the API based on the endpoint it receives and the time
  period that is passed as a query string parameter, e.g.
  `/query-wakatime?range=last_7_days`.

  Below is a template for concisely handling requests to the different endpoints
  in the single lambda function wihout excessive repetition.

  ```js
  const getEndpoint = (path: string, timePeriod?: string | undefined) => {
    const basePath = "/users/current";
    switch (path) {
      case "/query-wakatime/main-metrics":
        return `${basePath}/stats/${timePeriod}`;
      case "/query-wakatime/durations":
        return `${basePath}/summaries?range=${timePeriod}`;
      case "/query-wakatime/today-only":
        return `${basePath}/status_bar/today`;
    }
  };

  export const handler = async (event) => {
    const fetchClient = new FetchClient("https://wakatime.com/api/v1/");
    const path = event.path;
    const timePeriod = event.queryStringParameters?.timePeriod;
    const endpoint = getEndpoint(path, timePeriod);

    const init: RequestInit = {
      headers: {
        Authorization: `Basic ${encodedApiKey}`,
      },
    };

    if (!endpoint) {
      throw new Error("Endpoint is undefined")
    }

   let data = await fetchClient.get(endpoint, init)
   let dataJson = (await data.json()) as string

   response = {
      statusCode: 200,
      body: JSON.stringify(dataJson),
      headers: responseHeaders,
      }
  };



  ```

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

  # Handle multiple API Gateway endpoints in a single lambda

  With the following config we have a single lambda (`/query-waktime`) with
  multiple endpoints:

  ```yml
  Resources:
    QueryWakatimeFunction:
      Type: AWS::Serverless::Function
      Properties:
        CodeUri: src/query-wakatime/
        Handler: index.handler
        Events:
          MainMetrics:
            Type: Api
            Properties:
              Path: /query-wakatime/main-metrics
              Method: get
              Auth:
                Authorizer: AWS_IAM
          Durations:
            Type: Api
            Properties:
              Path: /query-wakatime/durations
              Method: get
              Auth:
                Authorizer: AWS_IAM
          TodayOnly:
            Type: Api
            Properties:
              Path: /query-wakatime/today-only
              Method: get
              Auth:
                Authorizer: AWS_IAM
  ```

  Our endpoints are as follows:

  - `/query-wakatime/main-metrics`
  - `/query-wakatime/durations`
  - `/query-wakatime/today-only`

  Each returns a different dataset from an external API. The lambda function makes
  a different request to the API based on the endpoint it receives and the time
  period that is passed as a query string parameter, e.g.
  `/query-wakatime?range=last_7_days`.

  Below is a template for concisely handling requests to the different endpoints
  in the single lambda function wihout excessive repetition.

  ```js
  const getEndpoint = (path: string, timePeriod?: string | undefined) => {
    const basePath = "/users/current";
    switch (path) {
      case "/query-wakatime/main-metrics":
        return `${basePath}/stats/${timePeriod}`;
      case "/query-wakatime/durations":
        return `${basePath}/summaries?range=${timePeriod}`;
      case "/query-wakatime/today-only":
        return `${basePath}/status_bar/today`;
    }
  };

  export const handler = async (event) => {
    const fetchClient = new FetchClient("https://wakatime.com/api/v1/");
    const path = event.path;
    const timePeriod = event.queryStringParameters?.timePeriod;
    const endpoint = getEndpoint(path, timePeriod);

    const init: RequestInit = {
      headers: {
        Authorization: `Basic ${encodedApiKey}`,
      },
    };

    if (!endpoint) {
      throw new Error("Endpoint is undefined")
    }

   let data = await fetchClient.get(endpoint, init)
   let dataJson = (await data.json()) as string

   response = {
      statusCode: 200,
      body: JSON.stringify(dataJson),
      headers: responseHeaders,
      }
  };



  ```


path: zk/Handling_negative_fractions.md
sortable_path: zkHandling_negative_fractions.md
title: Negative fractions
checksum: 134790bda657057e507a4087eb4d4ce42197cdcf7f3eb41586397ff6af015de4
created: 2024-03-30 09:03:19.702385792+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra","fractions"]}
lead:
  To work with negative fractions we draw on the
  [Rules for operations on like and unlike terms](Rules%20for%20operations%20on%20like%20and%20unlike%20terms.md).

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

  ## Fractions with unlike terms

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

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

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

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

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

  Therefore:

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

  ## Fractions with like terms

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

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

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

  # Negative fractions

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

  ## Fractions with unlike terms

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

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

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

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

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

  Therefore:

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

  ## Fractions with like terms

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

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


path: zk/Hardware_Description_Language.md
sortable_path: zkHardware_Description_Language.md
title: Hardware Description Language
checksum: 165dfe31b4879e4bb4e7cb751f68c3732809d918fb7c78529bc82727f3eadbcb
created: 2024-03-30 09:03:19.715156042+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["nand-to-tetris"]}
lead:
  An HDL is a declarative programming language used to describe the behaviour or
  structure of
  [digital circuits](Integrated_circuits.md).
  They are used to simulate the circuit and check its response.

body:
  An HDL is a declarative programming language used to describe the behaviour or
  structure of
  [digital circuits](Integrated_circuits.md).
  They are used to simulate the circuit and check its response.

  The hardware designer specifies a chip's logic by writing an HDL program which
  is then rigorously tested. At this stage, a
  [hardware simulator](Hardware_simulation.md) takes the
  HDL program as input and creates a software representation of the chip logic.
  The designer can instruct the simulator to test the virtual chip on various sets
  of inputs. This is done to check the chip's functionality but also to benchmark
  a variety of other parameters such as speed of computation and energy
  consumption.

  There are many HDLs but the most popular are VHDL ("very high speed
  integrated-circuit description language") and Verilog.

  ## Usage in _NAND to Tetris_

  We won't use an actual HDL language, instead we will use a simplified toy
  language called HDL that is simple enough that when it is used with a simulator,
  we can learn the main facets of chip design. Its syntax is very similar to VHDL.

  ## Demonstration of HDL program

  ### Boolean function to enact

  We will create an HDL program for an XOR gate that is implemented through the
  following arrangement of NOT, AND, and OR gates:

  ![](/img/xor-hdl.png)

  ### HDL file (`Xor.hdl`):

  Here is our HDL file:

  ```
  /* Xor gate
     If a!=b out=1 else out=0
  */
  CHIP Xor {
    IN a, b;
    OUT out;
    PARTS:
    Not (in=a, out=nota);
    Not (in=b, out=notb);
    And (a=a, b=notb, out=w1);
    And (a=nota, b=b, out=w2);
    Or  (a=w1, b=w2, out=out)
  }
  ```

  #### Interface (`CHIP, IN, OUT`)

  At the top level of the HDL program, the `CHIP` name and `IN`/`OUT` declaration
  is the _interface_ of the chip. Here we specify our naming convention for the
  `IN` and `OUT` values which we will refer to in the implementation declaration
  in `PARTS`.

  #### Implementation (`PARTS`)

  Everything under the `PARTS` section is the chip _implementation_. We can draw
  on composite gates in the `PARTS` declaration (e.g. `Not`, `And`, `Or`). The
  convention is to work from left to right when transcribing from a digital
  circuit diagram

  #### Pins

  In an HDL program we distinguish internal pins along with the standard
  [input and output pins](Integrated_circuits.md).
  At the level of the interface, we are concerned only with input and output pins
  (in the example program these are `a`, `b` and `out`). It is at the level of the
  implementation that internal pins are encountered. In the example these are the
  connections between, e.g. the AND and NOT gates such as
  `And (a=a, b-notb, out=w1)`. This means the AND gate is receiving through its
  `a` pin the input `a` value and through its `b` pin the value of `b` inverted by
  a NOT. `out` is the value that is computed based on the input pins of `a` and
  `b`.

  ### Test file (`Xor.tst`)

  Along with the HDL file we also create a test file. This runs the chip against
  the inputs we supply, these will typically be equivalent to the (left-hand)
  truth-values column in a truth table which is the same as the parameters passed
  to a [Boolean function](Boolean_functions.md), for
  example:

  ```vhdl
  load Xor.hdl
  output-list a, b, out;
  set a 0, set b 0, eval, output;
  set a 0, set b 1, eval, output;
  set a 1, set b 0, eval, output;
  set a 1, set b =, eval, output;
  ```

  ### Output file (`Xor.out`)

  When the test file is run against the HDL file it will generate an output file.
  This is effectively the result of the unit test. And will take the form of a
  truth table:

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

raw_content:
  ---
  tags: [nand-to-tetris]
  ---

  # Hardware Description Language

  An HDL is a declarative programming language used to describe the behaviour or
  structure of
  [digital circuits](Integrated_circuits.md).
  They are used to simulate the circuit and check its response.

  The hardware designer specifies a chip's logic by writing an HDL program which
  is then rigorously tested. At this stage, a
  [hardware simulator](Hardware_simulation.md) takes the
  HDL program as input and creates a software representation of the chip logic.
  The designer can instruct the simulator to test the virtual chip on various sets
  of inputs. This is done to check the chip's functionality but also to benchmark
  a variety of other parameters such as speed of computation and energy
  consumption.

  There are many HDLs but the most popular are VHDL ("very high speed
  integrated-circuit description language") and Verilog.

  ## Usage in _NAND to Tetris_

  We won't use an actual HDL language, instead we will use a simplified toy
  language called HDL that is simple enough that when it is used with a simulator,
  we can learn the main facets of chip design. Its syntax is very similar to VHDL.

  ## Demonstration of HDL program

  ### Boolean function to enact

  We will create an HDL program for an XOR gate that is implemented through the
  following arrangement of NOT, AND, and OR gates:

  ![](/img/xor-hdl.png)

  ### HDL file (`Xor.hdl`):

  Here is our HDL file:

  ```
  /* Xor gate
     If a!=b out=1 else out=0
  */
  CHIP Xor {
    IN a, b;
    OUT out;
    PARTS:
    Not (in=a, out=nota);
    Not (in=b, out=notb);
    And (a=a, b=notb, out=w1);
    And (a=nota, b=b, out=w2);
    Or  (a=w1, b=w2, out=out)
  }
  ```

  #### Interface (`CHIP, IN, OUT`)

  At the top level of the HDL program, the `CHIP` name and `IN`/`OUT` declaration
  is the _interface_ of the chip. Here we specify our naming convention for the
  `IN` and `OUT` values which we will refer to in the implementation declaration
  in `PARTS`.

  #### Implementation (`PARTS`)

  Everything under the `PARTS` section is the chip _implementation_. We can draw
  on composite gates in the `PARTS` declaration (e.g. `Not`, `And`, `Or`). The
  convention is to work from left to right when transcribing from a digital
  circuit diagram

  #### Pins

  In an HDL program we distinguish internal pins along with the standard
  [input and output pins](Integrated_circuits.md).
  At the level of the interface, we are concerned only with input and output pins
  (in the example program these are `a`, `b` and `out`). It is at the level of the
  implementation that internal pins are encountered. In the example these are the
  connections between, e.g. the AND and NOT gates such as
  `And (a=a, b-notb, out=w1)`. This means the AND gate is receiving through its
  `a` pin the input `a` value and through its `b` pin the value of `b` inverted by
  a NOT. `out` is the value that is computed based on the input pins of `a` and
  `b`.

  ### Test file (`Xor.tst`)

  Along with the HDL file we also create a test file. This runs the chip against
  the inputs we supply, these will typically be equivalent to the (left-hand)
  truth-values column in a truth table which is the same as the parameters passed
  to a [Boolean function](Boolean_functions.md), for
  example:

  ```vhdl
  load Xor.hdl
  output-list a, b, out;
  set a 0, set b 0, eval, output;
  set a 0, set b 1, eval, output;
  set a 1, set b 0, eval, output;
  set a 1, set b =, eval, output;
  ```

  ### Output file (`Xor.out`)

  When the test file is run against the HDL file it will generate an output file.
  This is effectively the result of the unit test. And will take the form of a
  truth table:

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


path: zk/Hardware_abstraction_and_modularity.md
sortable_path: zkHardware_abstraction_and_modularity.md
title: Hardware abstraction and modularity
checksum: 6d101cf97794548ab23f8a70cf416e70d8642d888d2ba82ecbbe32ac1327bc4a
created: 2024-03-30 09:03:19.732731161+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["nand-to-tetris"]}
lead:
  In computer architecture we deal with complexity by breaking the system into
  **modules**. For each module we distinguish **_abstraction_** from
  **_implementation_**.

body:
  In computer architecture we deal with complexity by breaking the system into
  **modules**. For each module we distinguish **_abstraction_** from
  **_implementation_**.

  <dl>
    <dt>abstraction</dt>
    <dd>what the module does</dd>
    <dt>implementation</dt>
    <dd>how it does it</dd>
  </dl>

  When using a module as a building block you are to focus exclusively on the
  module's abstraction, ignoring completely its implementation details.

  > The abstraction-implementation paradigm helps developers manage complexity and
  > maintain sanity: by dividing an overwhelming system into well-defined modules
  > we create manageable chunks of implementation work and localize error
  > detection and correction.

  [N.Nisan, S.Schoken. 2021. **The Elements of Computing Systems** (Second
  Edition)]

  The design of the diagram below emphasises the role of abstraction and
  modularity in the movement from transistors to chips:

  ![](/img/hardware-abstraction-hierarchy.png)

raw_content:
  ---
  tags: [nand-to-tetris]
  ---

  # Hardware abstraction and modularity

  In computer architecture we deal with complexity by breaking the system into
  **modules**. For each module we distinguish **_abstraction_** from
  **_implementation_**.

  <dl>
    <dt>abstraction</dt>
    <dd>what the module does</dd>
    <dt>implementation</dt>
    <dd>how it does it</dd>
  </dl>

  When using a module as a building block you are to focus exclusively on the
  module's abstraction, ignoring completely its implementation details.

  > The abstraction-implementation paradigm helps developers manage complexity and
  > maintain sanity: by dividing an overwhelming system into well-defined modules
  > we create manageable chunks of implementation work and localize error
  > detection and correction.

  [N.Nisan, S.Schoken. 2021. **The Elements of Computing Systems** (Second
  Edition)]

  The design of the diagram below emphasises the role of abstraction and
  modularity in the movement from transistors to chips:

  ![](/img/hardware-abstraction-hierarchy.png)


path: zk/Hardware_simulation.md
sortable_path: zkHardware_simulation.md
title: Hardware simulation
checksum: 81eaf5ae524f90bc704e6b23c5218acb3c5324da1ad875beec4ae07caba430d7
created: 2024-03-30 09:03:19.735626091+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["nand-to-tetris"]}
lead:
  In order to test our
  [HDL](Hardware_Description_Language.md) files we load
  them into the hardware simulator program. We will demonstrate this with the
  following XOR implementation:

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

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

  There are several simulation options:

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

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

  ## Interactive

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

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

  ## Script-based

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

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

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

  ## Comparison-based

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

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

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

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

raw_content:
  ---
  tags: [nand-to-tetris]
  ---

  # Hardware simulation

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

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

  There are several simulation options:

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

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

  ## Interactive

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

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

  ## Script-based

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

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

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

  ## Comparison-based

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

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

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

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


path: zk/Hexadecimal_number_system.md
sortable_path: zkHexadecimal_number_system.md
title: Hexadecimal number system
checksum: 63bc9d724bdb0cdb6a3ff91608edef7131bf7768ed94dd4c82bfa42487978476
created: 2024-03-30 09:03:19.740848011+00:00
modified: 2024-06-16 17:12:33.068015804+00:00
metadata: {"tags":["number-systems","hardware","computer-architecture"]}
lead:
  Hexadecimal is the other main number system used in computing. It works in
  tandem with the [binary number system](Binary_number_system.md) and provides an
  easier and more accessible means of working with long sequences of binary
  numbers.

body:
  Hexadecimal is the other main number system used in computing. It works in
  tandem with the [binary number system](Binary_number_system.md) and provides an
  easier and more accessible means of working with long sequences of binary
  numbers.

  ## Hexadecimal place value

  Unlike denary which uses base ten and binary which uses base two, hexadecimal
  uses base 16 as its place value.

  > Each place in a hexadecimal number represents a power of 16 and each place can
  > be one of 16 symbols.

  ## Hexadecimal values

  The table below shows the symbols comprising hexadecimal alongside their denary
  and binary equivalents:

  | Hexadecimal | Decimal | Binary |
  | ----------- | ------- | ------ |
  | 0           | 0       | 0000   |
  | 1           | 1       | 0001   |
  | 2           | 2       | 0010   |
  | 3           | 3       | 0011   |
  | 4           | 4       | 0100   |
  | 5           | 5       | 0101   |
  | 6           | 6       | 0110   |
  | 7           | 7       | 0111   |
  | 8           | 8       | 1000   |
  | 9           | 9       | 1001   |
  | A           | 10      | 1010   |
  | B           | 11      | 1011   |
  | C           | 12      | 1100   |
  | D           | 13      | 1101   |
  | E           | 14      | 1110   |
  | F           | 15      | 1111   |

  This table shows the raw value of each hexadecimal place value:

  | $16^{3}$ | $16^{2}$ | $16^{1}$ | $16^{0}$ |
  | -------- | -------- | -------- | -------- |
  | 4096     | 256      | 16       | 1        |

  ## Hexadecimal prefix

  The custom is to prefix a hexadecimal number with `0x` to indicate that the
  number is hexadecimal.

  ## Converting hexadecimal numbers

  Using the previous table we can convert hexadecimal values to decimal.

  For example we can convert `1A5` as follows, working from right to left:

  $(5 \cdot 1 = 5) + (A \cdot 16 = 160) + (1 \cdot 256 = 256) = 421$

  The process is quite easy: we get the n from $16^{n}$ based on the position of
  the digit and then multiply this by the value of the symbol (1,2,...F):

  $$
    16^{n} \cdot 1,2,...F
  $$

  As applied to `0x1A5`:

  | $16^{3}$               | $16^{2}$              | $16^{1}$                   | $16^{0}$            |
  | ---------------------- | --------------------- | -------------------------- | ------------------- |
  | $1\cdot 16^{3} = 4096$ | $1\cdot 16^{2} = 256$ | $A (10)\cdot 16^{1} = 160$ | $5\cdot 16^{0} = 5$ |

  Another example for `0xF00F`:

  $(15 \cdot 4096 = 61440) + (0 \cdot 256 = 0) + (0 \cdot 16 = 0) + (15 \cdot 1 = 15) = 61455$

  ## Using hexadecimal to simplify binary numbers

  Whilst computers themselves do not use the hexadecimal number system (everything
  is binary), hexadecimal offers advantages for humans who must work with binary:

  1. It is much easier to read a hexadecimal number than long sequences of binary
     numbers
  2. It is easier to quickly convert binary numbers to hexadecimal than to convert
     binary numbers to decimal

  Look at the following equivalences

  | Number system   | Example 1           | Example 2           |
  | --------------- | ------------------- | ------------------- |
  | **Binary**      | 1111 0000 0000 1111 | 1000 1000 1000 0001 |
  | **Hexadecimal** | F00F                | 8881                |
  | **Decimal**     | 61,455              | 34,945              |

  It is obvious that a pattern is maintained between the hexadecimal and binary
  numbers and that this pattern is obscured by the decimal conversion. In the
  first example the binary half-byte `1111` is matched by the hexadecimal `F00F`.

  Mathematically comparing hex `F` and binary `1111`:

  $$
    \textsf{1111} = ((1 \cdot 2^{3}) + (1 \cdot 2^{2}) + (1 \cdot 2^{1}) + (1 \cdot 2^{0})) \\
    = 8 + 4 + 2 + 1 \\
    = 15
  $$

  $$
    \textsf{F} = 15 \cdot 16^{0}  \\
    = 15
  $$

  ![](/img/hexadecimal-to-bytes.svg)

  > Every four bits (or half byte) in binary corresponds to one symbol in
  > hexadecimal. Therefore **a byte can be easily represented with two hexadecimal
  > symbols, a 16-bit number can be represented with four hex symbols, a 32-bit
  > number can represented with eight hex symbols and so on.**

raw_content:
  ---
  tags:
    - number-systems
    - hardware
    - computer-architecture
  ---

  # Hexadecimal number system

  Hexadecimal is the other main number system used in computing. It works in
  tandem with the [binary number system](Binary_number_system.md) and provides an
  easier and more accessible means of working with long sequences of binary
  numbers.

  ## Hexadecimal place value

  Unlike denary which uses base ten and binary which uses base two, hexadecimal
  uses base 16 as its place value.

  > Each place in a hexadecimal number represents a power of 16 and each place can
  > be one of 16 symbols.

  ## Hexadecimal values

  The table below shows the symbols comprising hexadecimal alongside their denary
  and binary equivalents:

  | Hexadecimal | Decimal | Binary |
  | ----------- | ------- | ------ |
  | 0           | 0       | 0000   |
  | 1           | 1       | 0001   |
  | 2           | 2       | 0010   |
  | 3           | 3       | 0011   |
  | 4           | 4       | 0100   |
  | 5           | 5       | 0101   |
  | 6           | 6       | 0110   |
  | 7           | 7       | 0111   |
  | 8           | 8       | 1000   |
  | 9           | 9       | 1001   |
  | A           | 10      | 1010   |
  | B           | 11      | 1011   |
  | C           | 12      | 1100   |
  | D           | 13      | 1101   |
  | E           | 14      | 1110   |
  | F           | 15      | 1111   |

  This table shows the raw value of each hexadecimal place value:

  | $16^{3}$ | $16^{2}$ | $16^{1}$ | $16^{0}$ |
  | -------- | -------- | -------- | -------- |
  | 4096     | 256      | 16       | 1        |

  ## Hexadecimal prefix

  The custom is to prefix a hexadecimal number with `0x` to indicate that the
  number is hexadecimal.

  ## Converting hexadecimal numbers

  Using the previous table we can convert hexadecimal values to decimal.

  For example we can convert `1A5` as follows, working from right to left:

  $(5 \cdot 1 = 5) + (A \cdot 16 = 160) + (1 \cdot 256 = 256) = 421$

  The process is quite easy: we get the n from $16^{n}$ based on the position of
  the digit and then multiply this by the value of the symbol (1,2,...F):

  $$
    16^{n} \cdot 1,2,...F
  $$

  As applied to `0x1A5`:

  | $16^{3}$               | $16^{2}$              | $16^{1}$                   | $16^{0}$            |
  | ---------------------- | --------------------- | -------------------------- | ------------------- |
  | $1\cdot 16^{3} = 4096$ | $1\cdot 16^{2} = 256$ | $A (10)\cdot 16^{1} = 160$ | $5\cdot 16^{0} = 5$ |

  Another example for `0xF00F`:

  $(15 \cdot 4096 = 61440) + (0 \cdot 256 = 0) + (0 \cdot 16 = 0) + (15 \cdot 1 = 15) = 61455$

  ## Using hexadecimal to simplify binary numbers

  Whilst computers themselves do not use the hexadecimal number system (everything
  is binary), hexadecimal offers advantages for humans who must work with binary:

  1. It is much easier to read a hexadecimal number than long sequences of binary
     numbers
  2. It is easier to quickly convert binary numbers to hexadecimal than to convert
     binary numbers to decimal

  Look at the following equivalences

  | Number system   | Example 1           | Example 2           |
  | --------------- | ------------------- | ------------------- |
  | **Binary**      | 1111 0000 0000 1111 | 1000 1000 1000 0001 |
  | **Hexadecimal** | F00F                | 8881                |
  | **Decimal**     | 61,455              | 34,945              |

  It is obvious that a pattern is maintained between the hexadecimal and binary
  numbers and that this pattern is obscured by the decimal conversion. In the
  first example the binary half-byte `1111` is matched by the hexadecimal `F00F`.

  Mathematically comparing hex `F` and binary `1111`:

  $$
    \textsf{1111} = ((1 \cdot 2^{3}) + (1 \cdot 2^{2}) + (1 \cdot 2^{1}) + (1 \cdot 2^{0})) \\
    = 8 + 4 + 2 + 1 \\
    = 15
  $$

  $$
    \textsf{F} = 15 \cdot 16^{0}  \\
    = 15
  $$

  ![](/img/hexadecimal-to-bytes.svg)

  > Every four bits (or half byte) in binary corresponds to one symbol in
  > hexadecimal. Therefore **a byte can be easily represented with two hexadecimal
  > symbols, a 16-bit number can be represented with four hex symbols, a 32-bit
  > number can represented with eight hex symbols and so on.**


path: zk/IO_in_Python.md
sortable_path: zkIO_in_Python.md
title: IO in Python
lead: ## The open() object
checksum: 10fe2480c7405bdff032ec32bfa2b332069ce1a05f24246026df7a1841f434fb
created: 2024-03-30 09:03:19.746239686+00:00
modified: 2024-05-31 11:55:02.785274678+00:00
metadata: {"tags":["python"]}
body:
  ## The open() object

  The built-in `open()` function creates a **file object** that allows us to read,
  write and append to files.

  The general syntax is as follows:

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

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

  - `r`
    - read
  - `w`
    - write
  - `a`
    - append

  ### All access modes

  In addition we have the following access modes

  ## Reading files

  Once a file object has been intialised with `open()` there are several ways in
  which the content can be read:

  | Read method | Behaviour                                                                                                                                |
  | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
  | `read`      | Return the entire contents of the file as a single string.                                                                               |
  | `readline`  | Read the contents of a file a line at a time. You would combine this with a loop so that you can do something with each individual line. |
  | `readlines` | Return a list of all the lines in a file. Each line will be an element in the list.                                                      |

  ### Read

  `read` reads the entire contents of a file and returns it as a single string.

  `read()` reads the entire file into memory at once, so it may not be suitable
  for very large files that exceed the available memory of your system. In such
  cases, you can use the `readline()` method to read the file line by line
  instead.

  The basic syntax is as follows:

  ```py
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Read the entire contents of the file
  contents = file.read()

  # We could also limit the read to a number of characters:
  contents = file.read(100)

  # Close the file
  file.close()
  ```

  Once we have the file contents stored, we can then interact with it. The
  standard way of doing this is to parse it line by line. For example, say we were
  reading a CSV:

  ```py
  lines = content.split(',')
  for line in lines:
      if line: # if the line is not empty
          # do something with line
  ```

  ### Readline

  > The readline() method in Python is used to read a single line from a file. It
  > is typically used when you want to process a file line by line, rather than
  > reading the entire file into memory at once.

  `readline()` returns the line including the newline character at the end of the
  line, so you may need to strip this character off using the strip() method
  before processing the line further.

  ```python
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Loop through the file, reading one line at a time
  line = file.readline()
  while line:
      # Strip off the newline character
      line = line.strip()

      # Do something with the line
      print(line)

      # Read the next line
      line = file.readline()

  # Close the file
  file.close()
  ```

  ### Readlines

  The `readlines()` method is used to read all the lines of a file and return them
  as a list of strings, where each element of the list is a line from the file.

  ```py
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Read all the lines of the file and store them in a list
  lines = file.readlines()

  # Loop through the lines and print them to the console
  for line in lines:
      print(line)

  # Close the file
  file.close()
  ```

  ### Error handling

  Obviously file access can raise errors - typically when the file you want to
  access does not exist (i.e. a `FileNotFoundError`
  [exception](Error_handling_in_Python.md)). We can manage this scenario with
  [exception handlers](Error_handling_in_Python.md):

  ```py
  try:
      with open('filename.txt', 'r') as file:
      contents = file.readlines()
      for line in lines:
          print(line)
  except FileNotFoundError as err:
      print("File does not exist")
      print(err)
  ```

  ## Close and "with as"

  You notice that once we have finished with our I/O operation, we must call
  `file.close()` to terminate the process. This removes the reference to the file
  from memory.

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

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

  ## Writing to files

  Again we create a file object with `open()` and this time use the `write`
  method:

  ```py
  # Open file in write mode
  file = open("example.txt", "w")

  # Write some text to the file
  file.write("Hello, this is an example text written using Python.")

  # Close the file
  file.close()
  ```

  > Note that in the above example, if the file does not already exist, it will
  > create it. If it does exist, it will overwrite its contents with the new data.
  > So we use `write` to create new files as well as to write to existing files.

  ## Renaming and deleting files

  We have to use another built-in module to rename and delete files: `os`.

  To rename an existing file:

  ```py
  import os
  os.rename('original-file-name.txt', 'new-file-name.txt')
  ```

  To delete a file:

  ```py
  import os
  os.remove('file-name.txt')
  ```

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

  # IO in Python

  ## The open() object

  The built-in `open()` function creates a **file object** that allows us to read,
  write and append to files.

  The general syntax is as follows:

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

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

  - `r`
    - read
  - `w`
    - write
  - `a`
    - append

  ### All access modes

  In addition we have the following access modes

  ## Reading files

  Once a file object has been intialised with `open()` there are several ways in
  which the content can be read:

  | Read method | Behaviour                                                                                                                                |
  | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
  | `read`      | Return the entire contents of the file as a single string.                                                                               |
  | `readline`  | Read the contents of a file a line at a time. You would combine this with a loop so that you can do something with each individual line. |
  | `readlines` | Return a list of all the lines in a file. Each line will be an element in the list.                                                      |

  ### Read

  `read` reads the entire contents of a file and returns it as a single string.

  `read()` reads the entire file into memory at once, so it may not be suitable
  for very large files that exceed the available memory of your system. In such
  cases, you can use the `readline()` method to read the file line by line
  instead.

  The basic syntax is as follows:

  ```py
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Read the entire contents of the file
  contents = file.read()

  # We could also limit the read to a number of characters:
  contents = file.read(100)

  # Close the file
  file.close()
  ```

  Once we have the file contents stored, we can then interact with it. The
  standard way of doing this is to parse it line by line. For example, say we were
  reading a CSV:

  ```py
  lines = content.split(',')
  for line in lines:
      if line: # if the line is not empty
          # do something with line
  ```

  ### Readline

  > The readline() method in Python is used to read a single line from a file. It
  > is typically used when you want to process a file line by line, rather than
  > reading the entire file into memory at once.

  `readline()` returns the line including the newline character at the end of the
  line, so you may need to strip this character off using the strip() method
  before processing the line further.

  ```python
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Loop through the file, reading one line at a time
  line = file.readline()
  while line:
      # Strip off the newline character
      line = line.strip()

      # Do something with the line
      print(line)

      # Read the next line
      line = file.readline()

  # Close the file
  file.close()
  ```

  ### Readlines

  The `readlines()` method is used to read all the lines of a file and return them
  as a list of strings, where each element of the list is a line from the file.

  ```py
  # Open the file for reading
  file = open('filename.txt', 'r')

  # Read all the lines of the file and store them in a list
  lines = file.readlines()

  # Loop through the lines and print them to the console
  for line in lines:
      print(line)

  # Close the file
  file.close()
  ```

  ### Error handling

  Obviously file access can raise errors - typically when the file you want to
  access does not exist (i.e. a `FileNotFoundError`
  [exception](Error_handling_in_Python.md)). We can manage this scenario with
  [exception handlers](Error_handling_in_Python.md):

  ```py
  try:
      with open('filename.txt', 'r') as file:
      contents = file.readlines()
      for line in lines:
          print(line)
  except FileNotFoundError as err:
      print("File does not exist")
      print(err)
  ```

  ## Close and "with as"

  You notice that once we have finished with our I/O operation, we must call
  `file.close()` to terminate the process. This removes the reference to the file
  from memory.

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

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

  ## Writing to files

  Again we create a file object with `open()` and this time use the `write`
  method:

  ```py
  # Open file in write mode
  file = open("example.txt", "w")

  # Write some text to the file
  file.write("Hello, this is an example text written using Python.")

  # Close the file
  file.close()
  ```

  > Note that in the above example, if the file does not already exist, it will
  > create it. If it does exist, it will overwrite its contents with the new data.
  > So we use `write` to create new files as well as to write to existing files.

  ## Renaming and deleting files

  We have to use another built-in module to rename and delete files: `os`.

  To rename an existing file:

  ```py
  import os
  os.rename('original-file-name.txt', 'new-file-name.txt')
  ```

  To delete a file:

  ```py
  import os
  os.remove('file-name.txt')
  ```


path: zk/Identify_merged_branches.md
sortable_path: zkIdentify_merged_branches.md
title: Identify merged branches
lead:
  ```sh
  # View merged
  git branch --merged

checksum: 4276aeff9c2f164c65c35fb617983aa2739633e1617b080b74ae32b35baacc1a
created: 2024-03-30 09:03:19.756286126+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["git"]}
body:
  ```sh
  # View merged
  git branch --merged

  # View merged remote
  git branch -r --merged

  # View unmerged
  git branch --no-merged

  # View unmerged remote

  git branch -r --no-merged
  ```

  The above cases run the merge check from the point of view of HEAD as this is
  the default.

  But we can also run it from any branch.

  ```
  git branch --merged non_head_branch
  ```

  ## Demonstration

  We have the following branches

  ```
  git branch

  * main
  key_feature
  another_feature
  ```

  The branches other than `main`, have not been merged:

  ```
  git branch --merged

  * main
  ```

  Just to confirm:

  ```
  git branch --no-merged

  key_feature
  another_feature
  ```

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

  # Identify merged branches

  ```sh
  # View merged
  git branch --merged

  # View merged remote
  git branch -r --merged

  # View unmerged
  git branch --no-merged

  # View unmerged remote

  git branch -r --no-merged
  ```

  The above cases run the merge check from the point of view of HEAD as this is
  the default.

  But we can also run it from any branch.

  ```
  git branch --merged non_head_branch
  ```

  ## Demonstration

  We have the following branches

  ```
  git branch

  * main
  key_feature
  another_feature
  ```

  The branches other than `main`, have not been merged:

  ```
  git branch --merged

  * main
  ```

  Just to confirm:

  ```
  git branch --no-merged

  key_feature
  another_feature
  ```


path: zk/Increasing_fractions_to_their_highest_terms.md
sortable_path: zkIncreasing_fractions_to_their_highest_terms.md
title: Demonstration
lead: _Express $\frac{3}{4}$ as an equivalent fraction having the denominator 20_
checksum: 8478dbec4697a42d8a0300f8f8c1b9cd322f9929519fc437c36fe3a875f3f5f3
created: 2024-03-30 09:03:19.762808771+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["prealgebra","fractions"]}
body:
  _Express $\frac{3}{4}$ as an equivalent fraction having the denominator 20_

  $$
  \\frac{3 \cdot 4}{5 \cdot 4} = \frac{12}{20}
  $$

  _Express $\frac{2}{3}$ as an equivalent fraction having the denominator 21_

  $$
  \\frac{2 \cdot 7}{3 \cdot 7} = \frac{14}{21}
  $$

  ## Increasing fractions with variables to higher terms

  _Express $\frac{2}{9}$ as an equivalent fraction having the denominator 18a_

  In these cases, just append the variable to the factor:

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

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

  Given the equivalence between factors and divisors we can increase fractions to
  higher terms in a very similar way to when we reduce fractions. In the latter
  case we are dividing by divisors to reduce. In the former, we are multiplying by
  factors to increase.

  > Whenever we increase a fraction, the resultant fraction will always be
  > [equivalent](Equivalent%20fractions.md) to the fraction we started with.

  ## Demonstration

  _Express $\frac{3}{4}$ as an equivalent fraction having the denominator 20_

  $$
  \\frac{3 \cdot 4}{5 \cdot 4} = \frac{12}{20}
  $$

  _Express $\frac{2}{3}$ as an equivalent fraction having the denominator 21_

  $$
  \\frac{2 \cdot 7}{3 \cdot 7} = \frac{14}{21}
  $$

  ## Increasing fractions with variables to higher terms

  _Express $\frac{2}{9}$ as an equivalent fraction having the denominator 18a_

  In these cases, just append the variable to the factor:

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


path: zk/Insert_data_into_SQL_table.md
sortable_path: zkInsert_data_into_SQL_table.md
title: Insert data into table with SQL INSERT statement
lead: _A table named `sales`:_
checksum: c665728b91472161cc4b0d63c8626aec625bba9226159c46e3f801e7b51242e0
created: 2024-03-30 09:03:19.772063797+00:00
modified: 2024-06-15 09:46:40.662102255+00:00
metadata: {"tags":["SQL","databases"]}
body:
  _A table named `sales`:_

  | saleId | modelId | date       | salesPerson |
  | ------ | ------- | ---------- | ----------- |
  | 223    | 2       | 2020-01-09 | tgomay      |
  | 321    | 3       | 2020-03-14 | snedgamon   |
  | 491    | 4       | 2021-08-06 | tricksahoy  |

  ```sql
  INSERT INTO sales
  VALUES (1, 11, '2020-01-01','mhogan');
  ```

  > If you intend to miss out a value, you shouldn't leave it blank, you should
  > instead use `NULL` :

  ```sql
  INSERT INTO sales
  VALUES (1, 11, '2020-01-01', NULL);
  ```

  > The approach above works well if the order of entry matches the order of the
  > table fields. If this is not the case you can specify the order as part of the
  > `INSERT` statement:

  ```sql
  INSERT INTO sales**(employee_id, sale_id, model_id, sale_date)**
  VALUES ('mhogan', 1, 11, '2020-01-01',);
  ```

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

  # Insert data into table with SQL `INSERT` statement

  _A table named `sales`:_

  | saleId | modelId | date       | salesPerson |
  | ------ | ------- | ---------- | ----------- |
  | 223    | 2       | 2020-01-09 | tgomay      |
  | 321    | 3       | 2020-03-14 | snedgamon   |
  | 491    | 4       | 2021-08-06 | tricksahoy  |

  ```sql
  INSERT INTO sales
  VALUES (1, 11, '2020-01-01','mhogan');
  ```

  > If you intend to miss out a value, you shouldn't leave it blank, you should
  > instead use `NULL` :

  ```sql
  INSERT INTO sales
  VALUES (1, 11, '2020-01-01', NULL);
  ```

  > The approach above works well if the order of entry matches the order of the
  > table fields. If this is not the case you can specify the order as part of the
  > `INSERT` statement:

  ```sql
  INSERT INTO sales**(employee_id, sale_id, model_id, sale_date)**
  VALUES ('mhogan', 1, 11, '2020-01-01',);
  ```


path: zk/Instruction_set_architectures.md
sortable_path: zkInstruction_set_architectures.md
title: Instruction Set Architectures
lead: ## Summary
checksum: 38ccb65b129d4cbcb060765e9e148bd9527299c2ec783167fa140f86880ee1db
created: 2024-03-30 09:03:19.774620306+00:00
modified: 2024-04-09 06:25:57.380871694+00:00
metadata: {"tags":["CPU"]}
body:
  ## Summary

  - Computers with different hardware execute machine code instructions (e.g
    _add_) in different ways. They have different _instruction set architectures_
    (ISAs).

  - A family of CPUs that implement the core instructions of the
    [[Arithmetic_Logic_Unit]] in the same way share an ISA. Any software built for
    that ISA will work on any CPU that implements the ISA

  - The two main CPU architectures are ARM and x86

  ## Architecture is logical not physical

  The "architecture" of an ISA is logical rather than physical, i.e processors
  that share an ISA may work very differently on the hardware level whilst still
  adhering to the same instruction set.

  ## ARM and x86

  The two main processor architectures are ARM and x86. x86 processors were first
  introduced by Intel but are also made by AMD.

  ### x86

  - First introduced by Intel but also made by AMD.

  - Name stems from Intel's internal naming conventions - every new processor they
    introduced would have a letter designation ending with "86". Later they would
    adopt names such as "Pentium", "Celeron" etc. Regardless, these processors
    still fall under the "x86" family.

  - Over time, new instructions have been added to the x86 architecture but they
    all maintain backwards compatibility with preceding generations.

  - There have been different, successive generations of x86 corresponding to
    their [word-size](Signed_and_unsigned_numbers.md): 16-bit, 32-bit, 64-bit.
    Word size here just means how many bits the processor can work with at a time.
    A 34-bit processor can operate on values that are 32 bits in length. This
    means it has 32bit [[CPU_architecture#registers|registers]], a 32-bit data
    [[Bus|bus]], a 32-bit address bus and so on.

  - AMD introduced the first 64-bit processor, not Intel but they later caught up.
    A 64-bit x86 processor is often referred to as _x64_ or _x86-64_.

  ### ARM

  - x86 processors dominate the personal computer and server space

  - On movile devices ARM ISA predominates

  - The ARM company develops the architecture and standard which it licenses it to
    other companies to implement.

  - Preferred on mobile because they have reduced power consumption and lower
    costs compared to x86.

  - The processor is on a single chip that contains the CPU as well as memory and
    other hardware.

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

  # Instruction Set Architectures

  ## Summary

  - Computers with different hardware execute machine code instructions (e.g
    _add_) in different ways. They have different _instruction set architectures_
    (ISAs).

  - A family of CPUs that implement the core instructions of the
    [[Arithmetic_Logic_Unit]] in the same way share an ISA. Any software built for
    that ISA will work on any CPU that implements the ISA

  - The two main CPU architectures are ARM and x86

  ## Architecture is logical not physical

  The "architecture" of an ISA is logical rather than physical, i.e processors
  that share an ISA may work very differently on the hardware level whilst still
  adhering to the same instruction set.

  ## ARM and x86

  The two main processor architectures are ARM and x86. x86 processors were first
  introduced by Intel but are also made by AMD.

  ### x86

  - First introduced by Intel but also made by AMD.

  - Name stems from Intel's internal naming conventions - every new processor they
    introduced would have a letter designation ending with "86". Later they would
    adopt names such as "Pentium", "Celeron" etc. Regardless, these processors
    still fall under the "x86" family.

  - Over time, new instructions have been added to the x86 architecture but they
    all maintain backwards compatibility with preceding generations.

  - There have been different, successive generations of x86 corresponding to
    their [word-size](Signed_and_unsigned_numbers.md): 16-bit, 32-bit, 64-bit.
    Word size here just means how many bits the processor can work with at a time.
    A 34-bit processor can operate on values that are 32 bits in length. This
    means it has 32bit [[CPU_architecture#registers|registers]], a 32-bit data
    [[Bus|bus]], a 32-bit address bus and so on.

  - AMD introduced the first 64-bit processor, not Intel but they later caught up.
    A 64-bit x86 processor is often referred to as _x64_ or _x86-64_.

  ### ARM

  - x86 processors dominate the personal computer and server space

  - On movile devices ARM ISA predominates

  - The ARM company develops the architecture and standard which it licenses it to
    other companies to implement.

  - Preferred on mobile because they have reduced power consumption and lower
    costs compared to x86.

  - The processor is on a single chip that contains the CPU as well as memory and
    other hardware.


path: zk/Integrated_circuits.md
sortable_path: zkIntegrated_circuits.md
title: Integrated circuits
checksum: 2975740b35db4e9dc4a11b6d50c9ec2ef85f76210d3e6ad66356667a3c809c5c
created: 2024-03-30 09:03:19.796542348+00:00
modified: 2024-06-16 17:13:12.521868564+00:00
metadata: {"tags":["logic-gates","electronics"]}
lead:
  An integrated circuit (IC) is a single unit that comprises several logic gates
  designed for the easy construction of
  [digital circuits](Digital_circuits.md).
  The terms "integrated circuit" and "chip" are often used interchangeably.

body:
  An integrated circuit (IC) is a single unit that comprises several logic gates
  designed for the easy construction of
  [digital circuits](Digital_circuits.md).
  The terms "integrated circuit" and "chip" are often used interchangeably.

  An IC puts the gates on a single piece of silicon that has electrical contact
  points called pins. There are two types of pins on an IC: input pins and output
  pins. Input pins are used to receive signals from other components in the
  circuit, while output pins are used to send signals to other components. The
  function of a pin is determined by the connection it has to the rest of the
  circuit and the type of signal it is intended to transmit or receive.

  The type we will look at are called **dual in-line packages** (DIPs). They are
  rectangular wth two parallel rows of pins. The pins make it easy to connect DIPs
  to a breadboard.

  _An integrated circuit and its use on a breadboard:_

  <img align="left" width="200" src="/home/thomas/repos/computer_science/img/integrated-circuit.jpeg">
  <img  width="200" src="/home/thomas/repos/computer_science/img/breadboard-DIP.jpg">

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

  # Integrated circuits

  An integrated circuit (IC) is a single unit that comprises several logic gates
  designed for the easy construction of
  [digital circuits](Digital_circuits.md).
  The terms "integrated circuit" and "chip" are often used interchangeably.

  An IC puts the gates on a single piece of silicon that has electrical contact
  points called pins. There are two types of pins on an IC: input pins and output
  pins. Input pins are used to receive signals from other components in the
  circuit, while output pins are used to send signals to other components. The
  function of a pin is determined by the connection it has to the rest of the
  circuit and the type of signal it is intended to transmit or receive.

  The type we will look at are called **dual in-line packages** (DIPs). They are
  rectangular wth two parallel rows of pins. The pins make it easy to connect DIPs
  to a breadboard.

  _An integrated circuit and its use on a breadboard:_

  <img align="left" width="200" src="/home/thomas/repos/computer_science/img/integrated-circuit.jpeg">
  <img  width="200" src="/home/thomas/repos/computer_science/img/breadboard-DIP.jpg">


path: zk/Interactive_staging.md
sortable_path: zkInteractive_staging.md
title: Interactive staging
checksum: fdef4d4ff9933facb66f0b9ca70df3e69d95d9376ecb3748f72eb15d9a91a064
created: 2024-03-30 09:03:19.801911632+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["git"]}
lead:
  With interactive staging we can stage sub-portions of files rather than the
  whole file. This makes commits much more targetted and atomic.

body:
  With interactive staging we can stage sub-portions of files rather than the
  whole file. This makes commits much more targetted and atomic.

  ## Basic use

  We enter interactive mode with:

  ```
  git add -i
  ```

  This opens an interface:

  ![](/img/git-interactive-mode-2.png)

  We select 2 and it lets us stage by number. If I enter 1, it will stage the
  first change.

  If we then go into 3 for revert, we can undo what we have staged.

  > Note that untracked files are not automatically added to the changes. We have
  > to manually add them with 4 ("add untracked").

  > Interactive mode doesn't commit anything, it is only for staging.

  ## Working with patches and hunks

  There's not much utility in interactive mode if you are commiting whole files.
  You can just use `git add [file]` and not bother with it.

  But it is useful for staging sub-portions of a file, which are called **hunks**.

  We access these via **patch mode**:

  ![](/img/git-patch-mode.png)

  ### Splitting hunks

  Git will try to suggest the best hunks it can but these will sometimes not
  reflect the changes you want. Sometimes they will reflect multiple changes
  rather than a single change.

  So long as there is an unchanged line between the different changes that it has
  selected as a hunk you can split the hunk further. You just hit `s` in patch
  mode, and it will repeatedly split the hunk each time until you get what you
  want.

  This is still an automated process and you may not find that the splitting
  results in a hunk you want to commit. In this case you can use manual splitting
  mode. We enter this with `e`.

  This will oped up Vim for the manual work to be done:

  ![](/img/git-manual-hunk.png)

  We use the diff symbols `+`, `-` and space to do this.

  ### Accessing patch mode directly

  If we just want to go straight to patch mode, without anything else:

  ```
  git add -p
  ```

  We can also work with patches in the following commands:

  ```
  git stash -p
  git reset -p
  git restore -p
  git commit -p
  ```

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

  # Interactive staging

  With interactive staging we can stage sub-portions of files rather than the
  whole file. This makes commits much more targetted and atomic.

  ## Basic use

  We enter interactive mode with:

  ```
  git add -i
  ```

  This opens an interface:

  ![](/img/git-interactive-mode-2.png)

  We select 2 and it lets us stage by number. If I enter 1, it will stage the
  first change.

  If we then go into 3 for revert, we can undo what we have staged.

  > Note that untracked files are not automatically added to the changes. We have
  > to manually add them with 4 ("add untracked").

  > Interactive mode doesn't commit anything, it is only for staging.

  ## Working with patches and hunks

  There's not much utility in interactive mode if you are commiting whole files.
  You can just use `git add [file]` and not bother with it.

  But it is useful for staging sub-portions of a file, which are called **hunks**.

  We access these via **patch mode**:

  ![](/img/git-patch-mode.png)

  ### Splitting hunks

  Git will try to suggest the best hunks it can but these will sometimes not
  reflect the changes you want. Sometimes they will reflect multiple changes
  rather than a single change.

  So long as there is an unchanged line between the different changes that it has
  selected as a hunk you can split the hunk further. You just hit `s` in patch
  mode, and it will repeatedly split the hunk each time until you get what you
  want.

  This is still an automated process and you may not find that the splitting
  results in a hunk you want to commit. In this case you can use manual splitting
  mode. We enter this with `e`.

  This will oped up Vim for the manual work to be done:

  ![](/img/git-manual-hunk.png)

  We use the diff symbols `+`, `-` and space to do this.

  ### Accessing patch mode directly

  If we just want to go straight to patch mode, without anything else:

  ```
  git add -p
  ```

  We can also work with patches in the following commands:

  ```
  git stash -p
  git reset -p
  git restore -p
  git commit -p
  ```


path: zk/Intro_to_the_Unix_shell.md
sortable_path: zkIntro_to_the_Unix_shell.md
title: Unix based systems
checksum: 5ae0adb0aa72338a198a1ace4ecc38eb42cf5515fb938b953f3a447a1497fd93
created: 2024-03-30 09:03:19.808151053+00:00
modified: 2024-03-27 10:45:52.503525368+00:00
metadata: {"tags":["shell"]}
lead:
  Many operating systems are based on the UNIX software architecture. macOS/OSX
  (Darwin) and GNU/Linux are two very popular examples. Most web servers run a
  version of Linux as their native OS thus a knowledge of command line for UNIX
  systems is invaluable for web developers. Windows systems are not UNIX based so
  everything that is written here is not applicable to the Windows command line
  program Command Prompt although there are obvious conceptual overlaps.

body:
  Many operating systems are based on the UNIX software architecture. macOS/OSX
  (Darwin) and GNU/Linux are two very popular examples. Most web servers run a
  version of Linux as their native OS thus a knowledge of command line for UNIX
  systems is invaluable for web developers. Windows systems are not UNIX based so
  everything that is written here is not applicable to the Windows command line
  program Command Prompt although there are obvious conceptual overlaps.

  UNIX was initially developed by AT&T and is still owned by them. GNU (the basis
  for Linux) is a recursive acronym for ’GNU is not UNIX’. Functionally it is the
  same as Unix however, it just doesn’t contain the proprietary code.

  ## Key terms

  ### Kernal

  The kernel is the central part of an operating system. It manages the operations
  of the computer and the hardware; most notably memory and CPU time. There are
  two types of kernels: a **micro-kernel**, which only contains basic
  functionality; and a **monolithic kernel**, which contains many device drivers.

  ### Shell

  A shell is a user interface for access to an operating system’s services. In
  general, operating system shells use either a command-line interface or GUI,
  depending on a computer’s role and particular operation. It is named a shell
  because it is the outermost layer around the operating system kernel

  ### Bash

  Bash is a Unix shell and command language written by Brian Fox for the GNU
  Project as a free software replacement for the Bourne shell. First released in
  1989, it has been distributed widely as the default login shell for most Linux
  distributions and Apple’s macOS.

  ## What is the command line?

  Command line is both a general computing concept and a specific utility. In its
  general sense, engaging with a computer via command line means inputting
  abbreviated written commands into a console or terminal window on a computer.
  The computer then runs these commands on the basis of the instructions you have
  given.

  You can get the computer to ‘do things’ (make files, connect to networks etc)
  via command line and you can use it to check the status of systems within your
  computer.

  This mode of engagement contrasts with the engagement facilitated by modern
  graphical user interfaces (GUIs) where the user navigates via a combination of
  keyboard and mouse inputs and interacts with windows whose functionality is
  usually self-explanatory and therefore more user-friendly. Command line predates
  GUIs but it is considered a more efficient and expansive means of interacting
  with the whole of your computer especially if you are a developer or programmer.

  In the more specific sense, the ‘command line’ refers to the location in the
  console window where you input your commands. The command line consists in a
  user name and the current directory you are in followed by a dollar sign and a
  cursor waiting for input.

  ## Basic orientation

  When you are using command line you are always situated somewhere within a file
  system. Typically this will be your own user files. directory structure By
  default you are always in your home directory, this is represented by the tilda
  symbol: `~`. This is why you see the tilda symbol as part of the command line.
  In command line, we do not use the term ‘folders’, instead they are called
  directories. (I will follow this convention from hereon.) You do not have to
  worry too much about remembering the names of specific folders and files you can
  always ask the computer to display them to you by using `ls` argument or `pwd`.

  ## Command line syntax

  The syntax of the language used to input commands is analogous to the grammar of
  natural languages. We have a verb that is operative on an object/noun and which
  can be modified through adverbs. The syntax of a command sequence is as follows:

  1. Command (verb): what we want to do
  1. Option (adverb): modifying the command - always starts with a hyphen
  1. Argument (noun/object): what we want our command to operate on

  We will see that not all commands require arguments, but this is the general
  structure

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

  ## Unix based systems

  Many operating systems are based on the UNIX software architecture. macOS/OSX
  (Darwin) and GNU/Linux are two very popular examples. Most web servers run a
  version of Linux as their native OS thus a knowledge of command line for UNIX
  systems is invaluable for web developers. Windows systems are not UNIX based so
  everything that is written here is not applicable to the Windows command line
  program Command Prompt although there are obvious conceptual overlaps.

  UNIX was initially developed by AT&T and is still owned by them. GNU (the basis
  for Linux) is a recursive acronym for ’GNU is not UNIX’. Functionally it is the
  same as Unix however, it just doesn’t contain the proprietary code.

  ## Key terms

  ### Kernal

  The kernel is the central part of an operating system. It manages the operations
  of the computer and the hardware; most notably memory and CPU time. There are
  two types of kernels: a **micro-kernel**, which only contains basic
  functionality; and a **monolithic kernel**, which contains many device drivers.

  ### Shell

  A shell is a user interface for access to an operating system’s services. In
  general, operating system shells use either a command-line interface or GUI,
  depending on a computer’s role and particular operation. It is named a shell
  because it is the outermost layer around the operating system kernel

  ### Bash

  Bash is a Unix shell and command language written by Brian Fox for the GNU
  Project as a free software replacement for the Bourne shell. First released in
  1989, it has been distributed widely as the default login shell for most Linux
  distributions and Apple’s macOS.

  ## What is the command line?

  Command line is both a general computing concept and a specific utility. In its
  general sense, engaging with a computer via command line means inputting
  abbreviated written commands into a console or terminal window on a computer.
  The computer then runs these commands on the basis of the instructions you have
  given.

  You can get the computer to ‘do things’ (make files, connect to networks etc)
  via command line and you can use it to check the status of systems within your
  computer.

  This mode of engagement contrasts with the engagement facilitated by modern
  graphical user interfaces (GUIs) where the user navigates via a combination of
  keyboard and mouse inputs and interacts with windows whose functionality is
  usually self-explanatory and therefore more user-friendly. Command line predates
  GUIs but it is considered a more efficient and expansive means of interacting
  with the whole of your computer especially if you are a developer or programmer.

  In the more specific sense, the ‘command line’ refers to the location in the
  console window where you input your commands. The command line consists in a
  user name and the current directory you are in followed by a dollar sign and a
  cursor waiting for input.

  ## Basic orientation

  When you are using command line you are always situated somewhere within a file
  system. Typically this will be your own user files. directory structure By
  default you are always in your home directory, this is represented by the tilda
  symbol: `~`. This is why you see the tilda symbol as part of the command line.
  In command line, we do not use the term ‘folders’, instead they are called
  directories. (I will follow this convention from hereon.) You do not have to
  worry too much about remembering the names of specific folders and files you can
  always ask the computer to display them to you by using `ls` argument or `pwd`.

  ## Command line syntax

  The syntax of the language used to input commands is analogous to the grammar of
  natural languages. We have a verb that is operative on an object/noun and which
  can be modified through adverbs. The syntax of a command sequence is as follows:

  1. Command (verb): what we want to do
  1. Option (adverb): modifying the command - always starts with a hyphen
  1. Argument (noun/object): what we want our command to operate on

  We will see that not all commands require arguments, but this is the general
  structure


