# notes

path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Analogue_and_digital.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeAnalogue_and_digital.md
title: Analogue and digital
checksum: 94c1cd2bdfbc7ff7cf0a43f53ebf0a4a0f855e8bcd860b5f447ceceb32229270
created: 2024-10-22 16:11:43.208406963+00:00
modified: 2024-10-22 15:52:03.343569242+00:00
metadata: {"tags":["analogue"]}
lead:
  Analogue and digital are paradigms for recording information, specifically
  information about an object or state that obtains in the world.

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

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

  ## Analogue

  Here are two examples of analogue measurement/ capture:

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

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

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

  ## Digital

  ### Limitations of analogue for computation

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

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

  Furthermore:

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

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

  ### Why digital is better suited to computational requirements

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

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

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

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

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

  # Analogue and digital

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

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

  ## Analogue

  Here are two examples of analogue measurement/ capture:

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

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

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

  ## Digital

  ### Limitations of analogue for computation

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

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

  Furthermore:

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

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

  ### Why digital is better suited to computational requirements

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

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

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Apollo_Server.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeApollo_Server.md
title: Apollo Server
checksum: c607e86538d0621aa6df8c7db83f041fa728925631e2e7491827db6b090ea41f
created: 2024-10-22 16:11:43.29579027+00:00
modified: 2024-10-22 15:52:03.353569431+00:00
metadata: {"tags":["graphql","REST","APIs"]}
lead:
  > Apollo Server is the part of the Apollo suite that we use to create the
  > backend of a GraphQL project: a GraphQL server.

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

  It is able to do the following:

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

  ## Example schema

  We will use the following schema in the examples.

  ```js
  // schema.js

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

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

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

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

  ## Setting up the server

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

  ```js
  // index.js

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

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

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

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

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

  ### Adding some mock data

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

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

  #### Generic mock

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

  #### Custom mock

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

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

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

  ## Implementing resolvers

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

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

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

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

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

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

  ### Resolver parameters

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

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

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

  ### `RESTDataSource`

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

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

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

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

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

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

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

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

  ### Using our `RESTDataSource` in our resolver

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

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

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

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

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

  Now we can complete our resolver:

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

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

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

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

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

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

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

  ## The `useMutation` hook

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

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

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

  # Apollo Server

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

  It is able to do the following:

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

  ## Example schema

  We will use the following schema in the examples.

  ```js
  // schema.js

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

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

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

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

  ## Setting up the server

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

  ```js
  // index.js

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

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

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

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

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

  ### Adding some mock data

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

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

  #### Generic mock

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

  #### Custom mock

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

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

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

  ## Implementing resolvers

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

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

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

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

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

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

  ### Resolver parameters

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

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

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

  ### `RESTDataSource`

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

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

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

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

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

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

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

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

  ### Using our `RESTDataSource` in our resolver

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

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

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

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

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

  Now we can complete our resolver:

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

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

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

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

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

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

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

  ## The `useMutation` hook

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Application_Layer_of_Internet_Protocol.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeApplication_Layer_of_Internet_Protocol.md
title: Application Layer of the Internet Protocol
lead: The final, topmost layer of the Internet Protocol suite.
checksum: 850f86d855fc24b2fb576dd2de3be8b813b7e43fd9a0820513fa34b2ca7703c2
created: 2024-10-22 16:11:43.422383379+00:00
modified: 2024-10-22 15:52:03.343569242+00:00
metadata: {"created":"Friday, September 06, 2024","tags":["internet","networks"]}
body:
  The final, topmost layer of the Internet Protocol suite.

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

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

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

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

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

  # Application Layer of the Internet Protocol

  The final, topmost layer of the Internet Protocol suite.

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

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

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Awk.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeAwk.md
title: Awk
checksum: 05550463f98eb04837f31018a401baeb4a2bfbe46b41eb22b5df2cc276f040d0
created: 2024-10-22 16:11:43.634729362+00:00
modified: 2024-10-22 15:52:03.340235844+00:00
metadata: {"tags":["shell","awk"]}
lead:
  > Awk is a programming language designed for text processing and data
  > extraction. It was created in the 1970s and remains widely used today for
  > tasks such as filtering and transforming text data, generating reports, and
  > performing basic calculations. Awk is known for its simplicity and
  > versatility, making it a popular tool for Unix system administrators and data
  > analysts.

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

  ## Invocation

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

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

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

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

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

  ## Syntactic structure

  `awk` is a line-oriented language.

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

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

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

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

  ### Patterns and actions

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

  ```
  pattern {action}
  ```

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

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

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

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

  ### Lines, records, fields

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

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

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

  The fields are accessed by special variables:

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

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

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

  ## Basic examples

  **_Match a pattern_**

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

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

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

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

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

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

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

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

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

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

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

  **_Match and string-interpolate the output_**

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

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

  **_Match against a numerical property_**

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

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

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

  **_Match a field against a regular expression_**

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

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

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

  ## Syntactic shorthands

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

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

  ## Built-in variables

  ### `NF`

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

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

  ### `NR`

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

  ### `FS`

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

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

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

  # Awk

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

  ## Invocation

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

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

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

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

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

  ## Syntactic structure

  `awk` is a line-oriented language.

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

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

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

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

  ### Patterns and actions

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

  ```
  pattern {action}
  ```

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

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

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

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

  ### Lines, records, fields

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

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

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

  The fields are accessed by special variables:

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

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

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

  ## Basic examples

  **_Match a pattern_**

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

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

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

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

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

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

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

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

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

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

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

  **_Match and string-interpolate the output_**

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

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

  **_Match against a numerical property_**

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

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

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

  **_Match a field against a regular expression_**

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

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

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

  ## Syntactic shorthands

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

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

  ## Built-in variables

  ### `NF`

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

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

  ### `NR`

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

  ### `FS`

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Biconditional_Elimination.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeBiconditional_Elimination.md
title: Biconditional Elimination
checksum: 7b514de977618e04b9e7c9736386c8ba622cc31b46fa514fa6446ccdf5890a2b
created: 2024-10-22 16:11:43.720413612+00:00
modified: 2024-10-22 15:52:03.333569051+00:00
metadata: {"tags":["logic"]}
lead:
  Give that the biconditional means that if $P$ is the case, $Q$ must be the case
  and if $Q$ is the case, $P$ must be the case, if we have $P \leftrightarrow Q$
  and $P$, we can derive $Q$ and vice versa.

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

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

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

  # Biconditional Elimination

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Biconditional_Introduction.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeBiconditional_Introduction.md
title: Biconditional introduction
checksum: ea64509fe776b582513c7b75da20d346f447a7271c2494b9741ac05486fd2424
created: 2024-10-22 16:11:43.724794752+00:00
modified: 2024-10-22 15:52:03.336902448+00:00
metadata: {"tags":["logic"]}
lead:
  The biconditional means if $P$ is the case, $Q$ must be the case and if $Q$ is
  the case, $P$ must be the case. Thus to introduce this operator we must
  demonstrate both that $Q$ follows from $P$ and that $P$ follows from $Q$. We do
  this via two sub-proofs.

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

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

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

  # Biconditional introduction

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Binary_colour_encoding.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeBinary_colour_encoding.md
title: Binary encoding of colours
checksum: b5d17688aa0a39af00827bdc3e496f0494219865c7a6a153650ead471fa6fc3e
created: 2024-10-22 16:11:43.741152852+00:00
modified: 2024-10-22 15:52:03.343569242+00:00
metadata: {"tags":["binary"]}
lead:
  The approach to encoding binary representations of colour is very similar to the
  approach we explored when looking at the encoding of
  [alphanumeric values](Text_encoding.md).

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

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

  ### Greyscale

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

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

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

  ### Colour encoding

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

  Some examples below

  #### Red

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

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

  #### Yellow

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

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

  ## Binary encoding of images

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

  # Binary encoding of colours

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

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

  ### Greyscale

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

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

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

  ### Colour encoding

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

  Some examples below

  #### Red

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

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

  #### Yellow

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

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

  ## Binary encoding of images


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Binary_number_system.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeBinary_number_system.md
title: Binary number system
lead: ## Decimal (denary) number system
checksum: 01445843a6d4d3c8d39541db0522d6761065460012dafc1eab4c3a3865336b92
created: 2024-10-22 16:11:43.783810012+00:00
modified: 2024-10-22 15:52:03.360236224+00:00
metadata: {"tags":["binary","number-systems"]}
body:
  ## Decimal (denary) number system

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

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

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

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

  The principle of counting in decimal:

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

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

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

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

  Thus 264 is the sum of

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

  ## Binary number system

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

  So instead of:

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

  You have:

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

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

  $$ 1, 2, 3, 4 $$

  is equal to:

  $$ 0001, 0010, 0011, 0100 $$

  Counting in binary:

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

  ## Binary prefix

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

  ## Converting decimal to binary

  Let's convert 6 into binary:

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

  More clearly:

  ![](static/Pasted_image_20220319135558.png)

  And for comparison:

  ![](static/Pasted_image_20220319135805.png)

  Or we can express the binary as:

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

  Or more concisely as:

  $$ 2^1 + 2^2 $$

  ### Another example

  Let's convert 23 into binary:

  ![](static/Pasted_image_20220319135823.png)

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

  # Binary number system

  ## Decimal (denary) number system

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

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

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

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

  The principle of counting in decimal:

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

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

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

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

  Thus 264 is the sum of

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

  ## Binary number system

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

  So instead of:

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

  You have:

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

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

  $$ 1, 2, 3, 4 $$

  is equal to:

  $$ 0001, 0010, 0011, 0100 $$

  Counting in binary:

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

  ## Binary prefix

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

  ## Converting decimal to binary

  Let's convert 6 into binary:

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

  More clearly:

  ![](static/Pasted_image_20220319135558.png)

  And for comparison:

  ![](static/Pasted_image_20220319135805.png)

  Or we can express the binary as:

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

  Or more concisely as:

  $$ 2^1 + 2^2 $$

  ### Another example

  Let's convert 23 into binary:

  ![](static/Pasted_image_20220319135823.png)


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Boot_process.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeBoot_process.md
title: The boot process
checksum: c1333a309daa1b9e5f139a7495bdc10a4fe4d64dc0fd2abc1c8c193ce2900e40
created: 2024-10-22 16:11:44.092870107+00:00
modified: 2024-10-22 15:52:03.343569242+00:00
metadata: {"tags":["disks","operating-systems"]}
lead:
  The primary or boot
  [partition](Disks.md#primary-extended-and-logical-partitions) of a harddisk
  contains a bootloader. It is the job of the bootloader to locate the
  [kernel](The_kernel.md) on the harddrive and inject it into memory so that they
  operating system can start. This is the boot process.

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

  ## Boot loaders

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

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

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

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

  ### BIOS and UEFI

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

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

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

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

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

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

  ### GRUB

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

  ![](static/grub.jpg)

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

  ## The boot sequence

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

  ## Boot diagnostics

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

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

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

  ## Initial kernel actions

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

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

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

  # The boot process

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

  ## Boot loaders

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

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

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

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

  ### BIOS and UEFI

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

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

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

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

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

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

  ### GRUB

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

  ![](static/grub.jpg)

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

  ## The boot sequence

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

  ## Boot diagnostics

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

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

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

  ## Initial kernel actions

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Breadboards.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeBreadboards.md
title: Breadboards
lead: ![](static/breadboard-diagram.jpg)
checksum: de902be16c73f1a553eece9da4757963ca463ad1325580126979f9c593bb7b2a
created: 2024-10-22 16:11:44.203721358+00:00
modified: 2024-10-22 15:52:03.343569242+00:00
metadata: {"created":"Sunday, June 23, 2024","id":"jbf1","tags":["electronics"]}
body:
  ![](static/breadboard-diagram.jpg)

  ## Contact holes

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

  ## Terminal strips

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

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

  ## Bus strips

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

raw_content:
  ---
  id: jbf1
  tags: [electronics]
  created: Sunday, June 23, 2024
  ---

  # Breadboards

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

  ## Contact holes

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

  ## Terminal strips

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

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

  ## Bus strips

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/CPU_architecture.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeCPU_architecture.md
title: CPU architecture
checksum: ff91050088c80d80c167ab1978247a0c4387f555bb0b612fce3619464ac6be3a
created: 2024-10-22 16:11:44.283065959+00:00
modified: 2024-10-22 15:52:03.356902828+00:00
metadata: {"tags":["CPU","computer-architecture"]}
lead:
  At the core of a computer sits the Central Processing Unit. This is the assembly
  of chips that execute all computation. Instructions are passed to the CPU along
  the data bus part of the [system bus](Bus.md) from the
  memory. The [kernel](The_kernel.md), also residing in memory,
  sequences and schedules the sending of data to the CPU and manages requests from
  the CPU for data in memory.

body:
  At the core of a computer sits the Central Processing Unit. This is the assembly
  of chips that execute all computation. Instructions are passed to the CPU along
  the data bus part of the [system bus](Bus.md) from the
  memory. The [kernel](The_kernel.md), also residing in memory,
  sequences and schedules the sending of data to the CPU and manages requests from
  the CPU for data in memory.

  The CPU comprises three core components:

  - Registers (a type of memory positioned on the same chip as the CPU )
  - the Arithmetic Logic Unit (ALU)
  - the Control Unit (CU)

  ![](static/von_neumann_architecture.jpeg)

  > This method of putting together a computer is known as the **Von Neumann
  > Architecture**. It was devised by John von Neumann in about 1945, well before
  > any of the components that would be needed to produce it had actually been
  > invented.

  ## Registers

  This is the part of the CPU that stores data. The memory cells that comprise it
  do not have [capacitors](Memory.md) (unlike RAM)
  so they cannot store very much data but they work faster, which is what is
  important. Because their memory capacity is so small, we measure the size of
  registers in bits rather than bytes.

  In terms of speed, registers sit at the top part of the overall
  [memory hierarchy](Memory.md#the-memory-hierarchy).

  There are five main types of register in the CPU:

  | Register type           | What it stores                                              |
  | ----------------------- | ----------------------------------------------------------- |
  | Accumulator             | The results of calculations                                 |
  | Instruction Register    | The DRAM address of the **instruction** to be processed     |
  | Memory Address Register | The DRAM address of the **data** to be processed            |
  | Memory Data Register    | The store of the data that is currently being processed     |
  | Program Counter         | The RAM address of the **next instruction** to be processed |

  ## Arithmetic Logic Unit

  See [Arithmetic Logic Unit](Arithmetic_Logic_Unit.md)

  ## Control Unit

  The CPU's [controller](Chipset_and_controllers.md). It
  takes the instructions in binary form from RAM memory (separate from the CPU,
  but connected) and then signals to the to ALU and memory registers what it is
  supposed to do to execute the instructions. Think of it as the overseer that
  gets the ALU and registers to work together to run program instructions.

  ## The system clock

  Whilst modern CPUs and multithreading make it appear as though the CPU is
  capable of running multiple processes at once, access to the CPU is in fact
  sequential. The illusion of simultaneous computation is due to the fact the
  processor is so fast that we do not detect the sequential changes. For this to
  happen, the CPU needs to have a means of scheduling and sequencing processes.
  This is made possible through the system clock, hence when talking about the
  speed of the CPU we do so with reference to _clock speeds_ and the _clock
  cycle_.

  The clock's circuitry is based on a quartz crystal system like that used in
  watches. At precisely timed intervals, the clock sends out pulses of electricity
  that cause bits to move from place to place within
  [logic gates](Logic_gates.md) or
  between logic gates and
  [registers](CPU_architecture.md#registers). This is
  covered in greater detail in the discussion of
  [clock signals in digital circuits](Clock_signals.md).

  Simple instructions such as add can often be executed in just one clock cycle,
  whilst complex operations such as divide will require a number of smaller steps,
  each using one cycle.

  We measure the speed of a chip process within the CPU in **Hertz (Hz)**. One
  Hertz is equivalent to _1 cycle per second_ where a "cycle" is equivalent to a
  single clock **tick**. Thus a tick covers a period of 1 second.

  A speed of 2GHz for example means two billion cycles per second. This would mean
  that the clock was completing two billion cycles at each tick. It is clock speed
  that is being referred to when computers are marketed in terms of the number of
  Hz a processor possesses.

  ## Processing cycles

  Each "cycle" is the execution of a process that commences once the
  [kernel](The_kernel.md) hands control to the CPU. Each cycle
  follows a sequence of events known as
  [fetch, decode, and execute](Fetch_decode_execute.md).

  ## Electromagnetism: broader scientific context

  Hertz was the scientist who detected
  [electromagentic waves](Electromagnetism.md).
  We use Hertz as a measure of the frequency of electromatic wave cycles in a
  signal.

  ![](static/hertz_wave_freq.gif)

  As the diagram above shows, a cycle is equal to one ascending and one descending
  crest. The more cycles per time unit, the greater the Hertz. We see the Hz
  increase as the number of cycles increases over time.

  ## Processor cores

  // TODO: add to dedicated file

  // Add diagram The vast majority of general purpose computers are multi-core.
  This means that the CPU contains more than one processing unit. They are best
  thought of as mini-CPUs within the main CPU since they each have the same
  overall Von Neumann architecture.

  With Intel processors the two main consumer processors are the i5 and i7. The
  latter has more cores than the former. Consequently it has faster clock speeds
  and greater concurrency due to increased threads.

  ### Cache access

  // See pp.129

  ### History

  The development of multi-core processors emerged because practical limits began
  to be reached when trying to increase the clock speeds of processors. For a
  period, increased transistor density meant that there were steady increases in
  clock speeds. This meant that more instructions could be executed per second.

  When the 3GHz level was reached it became clear that pushing a CPU beyond this
  led to excessive heat production and the logic gates not being able to keep up
  at those speeds. Instead of maxing out the clock rate, attention turned to
  running multiple instructions in parallel in order to increase performance.

raw_content:
  ---
  tags:
    - CPU
    - computer-architecture
  ---

  # CPU architecture

  At the core of a computer sits the Central Processing Unit. This is the assembly
  of chips that execute all computation. Instructions are passed to the CPU along
  the data bus part of the [system bus](Bus.md) from the
  memory. The [kernel](The_kernel.md), also residing in memory,
  sequences and schedules the sending of data to the CPU and manages requests from
  the CPU for data in memory.

  The CPU comprises three core components:

  - Registers (a type of memory positioned on the same chip as the CPU )
  - the Arithmetic Logic Unit (ALU)
  - the Control Unit (CU)

  ![](static/von_neumann_architecture.jpeg)

  > This method of putting together a computer is known as the **Von Neumann
  > Architecture**. It was devised by John von Neumann in about 1945, well before
  > any of the components that would be needed to produce it had actually been
  > invented.

  ## Registers

  This is the part of the CPU that stores data. The memory cells that comprise it
  do not have [capacitors](Memory.md) (unlike RAM)
  so they cannot store very much data but they work faster, which is what is
  important. Because their memory capacity is so small, we measure the size of
  registers in bits rather than bytes.

  In terms of speed, registers sit at the top part of the overall
  [memory hierarchy](Memory.md#the-memory-hierarchy).

  There are five main types of register in the CPU:

  | Register type           | What it stores                                              |
  | ----------------------- | ----------------------------------------------------------- |
  | Accumulator             | The results of calculations                                 |
  | Instruction Register    | The DRAM address of the **instruction** to be processed     |
  | Memory Address Register | The DRAM address of the **data** to be processed            |
  | Memory Data Register    | The store of the data that is currently being processed     |
  | Program Counter         | The RAM address of the **next instruction** to be processed |

  ## Arithmetic Logic Unit

  See [Arithmetic Logic Unit](Arithmetic_Logic_Unit.md)

  ## Control Unit

  The CPU's [controller](Chipset_and_controllers.md). It
  takes the instructions in binary form from RAM memory (separate from the CPU,
  but connected) and then signals to the to ALU and memory registers what it is
  supposed to do to execute the instructions. Think of it as the overseer that
  gets the ALU and registers to work together to run program instructions.

  ## The system clock

  Whilst modern CPUs and multithreading make it appear as though the CPU is
  capable of running multiple processes at once, access to the CPU is in fact
  sequential. The illusion of simultaneous computation is due to the fact the
  processor is so fast that we do not detect the sequential changes. For this to
  happen, the CPU needs to have a means of scheduling and sequencing processes.
  This is made possible through the system clock, hence when talking about the
  speed of the CPU we do so with reference to _clock speeds_ and the _clock
  cycle_.

  The clock's circuitry is based on a quartz crystal system like that used in
  watches. At precisely timed intervals, the clock sends out pulses of electricity
  that cause bits to move from place to place within
  [logic gates](Logic_gates.md) or
  between logic gates and
  [registers](CPU_architecture.md#registers). This is
  covered in greater detail in the discussion of
  [clock signals in digital circuits](Clock_signals.md).

  Simple instructions such as add can often be executed in just one clock cycle,
  whilst complex operations such as divide will require a number of smaller steps,
  each using one cycle.

  We measure the speed of a chip process within the CPU in **Hertz (Hz)**. One
  Hertz is equivalent to _1 cycle per second_ where a "cycle" is equivalent to a
  single clock **tick**. Thus a tick covers a period of 1 second.

  A speed of 2GHz for example means two billion cycles per second. This would mean
  that the clock was completing two billion cycles at each tick. It is clock speed
  that is being referred to when computers are marketed in terms of the number of
  Hz a processor possesses.

  ## Processing cycles

  Each "cycle" is the execution of a process that commences once the
  [kernel](The_kernel.md) hands control to the CPU. Each cycle
  follows a sequence of events known as
  [fetch, decode, and execute](Fetch_decode_execute.md).

  ## Electromagnetism: broader scientific context

  Hertz was the scientist who detected
  [electromagentic waves](Electromagnetism.md).
  We use Hertz as a measure of the frequency of electromatic wave cycles in a
  signal.

  ![](static/hertz_wave_freq.gif)

  As the diagram above shows, a cycle is equal to one ascending and one descending
  crest. The more cycles per time unit, the greater the Hertz. We see the Hz
  increase as the number of cycles increases over time.

  ## Processor cores

  // TODO: add to dedicated file

  // Add diagram The vast majority of general purpose computers are multi-core.
  This means that the CPU contains more than one processing unit. They are best
  thought of as mini-CPUs within the main CPU since they each have the same
  overall Von Neumann architecture.

  With Intel processors the two main consumer processors are the i5 and i7. The
  latter has more cores than the former. Consequently it has faster clock speeds
  and greater concurrency due to increased threads.

  ### Cache access

  // See pp.129

  ### History

  The development of multi-core processors emerged because practical limits began
  to be reached when trying to increase the clock speeds of processors. For a
  period, increased transistor density meant that there were steady increases in
  clock speeds. This meant that more instructions could be executed per second.

  When the 3GHz level was reached it became clear that pushing a CPU beyond this
  led to excessive heat production and the logic gates not being able to keep up
  at those speeds. Instead of maxing out the clock rate, attention turned to
  running multiple instructions in parallel in order to increase performance.


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Call_stack.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeCall_stack.md
title: The call-stack
checksum: 64042ab628ea55a1f622900877202add523d6d6623ab01ac0e6cb727a82d7c92
created: 2024-10-22 16:11:44.481490427+00:00
modified: 2024-10-22 15:52:03.353569431+00:00
metadata: {"tags":["memory","javascript"]}
lead:
  A [stack](Stacks.md) data structure that holds the information of the functions
  called within a program that allows transfer of the application control from
  these functions to the main process after code inside the functions has been
  executed.

body:
  A [stack](Stacks.md) data structure that holds the information of the functions
  called within a program that allows transfer of the application control from
  these functions to the main process after code inside the functions has been
  executed.

  ## Example

  ```js
  function greet(who) {
    console.log("Hello " + who);
  }

  greet("Harry");

  console.log("Bye");
  ```

  ### Breakdown

  1. Interpreter receives call to `greet()`
  2. Goes to the definition of this function (`function greet(who)...`)
  3. Executes the `console.log` within this function
  4. Returns to the location that called it (`greet("Harry")`)
  5. Finds that there is nothing else to do in this function so moves to next
     function: the `console.log("bye")`
  6. Executes
  7. Returns to line that called it. Finds nothing else to do. Exits program.

  ## Related notes

  ![Stack memory](static/Stack_memory.md)

raw_content:
  ---
  tags: [memory, javascript]
  ---

  # The call-stack

  A [stack](Stacks.md) data structure that holds the information of the functions
  called within a program that allows transfer of the application control from
  these functions to the main process after code inside the functions has been
  executed.

  ## Example

  ```js
  function greet(who) {
    console.log("Hello " + who);
  }

  greet("Harry");

  console.log("Bye");
  ```

  ### Breakdown

  1. Interpreter receives call to `greet()`
  2. Goes to the definition of this function (`function greet(who)...`)
  3. Executes the `console.log` within this function
  4. Returns to the location that called it (`greet("Harry")`)
  5. Finds that there is nothing else to do in this function so moves to next
     function: the `console.log("bye")`
  6. Executes
  7. Returns to line that called it. Finds nothing else to do. Exits program.

  ## Related notes

  ![Stack memory](static/Stack_memory.md)


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Cells_and_batteries.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeCells_and_batteries.md
title: Cells and batteries
checksum: b812985b53da44a1cd05289fabae776374ca85bd543a0ebad5909a3ca17da34f
created: 2024-10-22 16:11:44.562505358+00:00
modified: 2024-10-22 15:52:03.343569242+00:00
metadata: {"tags":["physics","electricity"]}
lead:
  Cells are a
  [voltage source](Voltage.md) that
  generate a difference of potential via a positive and negative electrode
  separated by an electrolytic solution. The electrolytes pull free electrons from
  one of the materials which creates a positive charge. The other material gains
  the free electrons creating a negative charge.

body:
  Cells are a
  [voltage source](Voltage.md) that
  generate a difference of potential via a positive and negative electrode
  separated by an electrolytic solution. The electrolytes pull free electrons from
  one of the materials which creates a positive charge. The other material gains
  the free electrons creating a negative charge.

  > A battery is a combination of two or more cells.

  > Cells which cannot be recharged are called **primary cells**. Cells which can
  > be recharged are called **secondary cells**.

  ## Connecting batteries

  Cells and batteries can be connected to each other in electrical ciruits to
  increase the overall voltage and current that is produced. There are three main
  connection types:

  - series
  - parallel
  - series-parallel

  > The key thing to remember: **cells configured in series increases the overall
  > voltage available** and **cells configured in parallel increases the overall
  > current available**

  The table below summarises the relative differences:

  ![](static/cell-comparison.svg)

  ### Series connections

  With series connections we distinguish **series aiding** and **series opposing**
  configurations.

  In the case of **series aiding**, cells are connected one in front of another
  with the positive terminal connecting to the negative terminal of the other in a
  line.

  In this configuration the same current flows through all the cells; it is not
  cumulative. We represent this as follow> However the voltage is cumulative: it
  is the _sum_ of the individual cell voltages, represented below as
  [electrical field](Voltage.md#distinguishing-voltage-from-electric-field):

  $$
  E_{T} = E_{1} + E_{2} + E_{3} \\
  $$

  Thus series connections increase voltage but keep current constant.

  _Series battery connection:_

  ![](static/series-battery-diagram.svg)

  _Can be represented in a circuit diagram in one of the following two ways: as a
  series of cells or as a single battery:_

  ![](static/series-battcircuit.svg)

  In the case of **series opposing**, negative terminals are connected to each
  other and positive terminals are connected to each other in a series. This
  doesn't have many applications.

  ### Parallel connections

  In parallel connections all positive terminals are connected to each other and
  all negative terminals are connected to each other.

  This time the voltage is the same as each individual cell but the current is the
  sum of the individual cell currents. So the voltage is constant but the current
  is cumulative:

  $$
  E_{T} = E_{1} = E_{2} = E_{3} \\
  $$

  $$
  I_{T} = I_{1} + I_{2} + I_{3}
  $$

  _Parallel battery connection:_

  ![](static/parallel-battery-diagram.svg)

  _Parallel battery circuit diagram:_

  ![](static/circ-batt-final.svg)

  ### Series-parrallel

  If we want both a higher voltage and a higher current we can use series-parallel
  configurations. Connecting cells in series increases the voltage and connecting
  cells in parellel increases the current so doint both boosts the amount of both
  quantities.

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

  # Cells and batteries

  Cells are a
  [voltage source](Voltage.md) that
  generate a difference of potential via a positive and negative electrode
  separated by an electrolytic solution. The electrolytes pull free electrons from
  one of the materials which creates a positive charge. The other material gains
  the free electrons creating a negative charge.

  > A battery is a combination of two or more cells.

  > Cells which cannot be recharged are called **primary cells**. Cells which can
  > be recharged are called **secondary cells**.

  ## Connecting batteries

  Cells and batteries can be connected to each other in electrical ciruits to
  increase the overall voltage and current that is produced. There are three main
  connection types:

  - series
  - parallel
  - series-parallel

  > The key thing to remember: **cells configured in series increases the overall
  > voltage available** and **cells configured in parallel increases the overall
  > current available**

  The table below summarises the relative differences:

  ![](static/cell-comparison.svg)

  ### Series connections

  With series connections we distinguish **series aiding** and **series opposing**
  configurations.

  In the case of **series aiding**, cells are connected one in front of another
  with the positive terminal connecting to the negative terminal of the other in a
  line.

  In this configuration the same current flows through all the cells; it is not
  cumulative. We represent this as follow> However the voltage is cumulative: it
  is the _sum_ of the individual cell voltages, represented below as
  [electrical field](Voltage.md#distinguishing-voltage-from-electric-field):

  $$
  E_{T} = E_{1} + E_{2} + E_{3} \\
  $$

  Thus series connections increase voltage but keep current constant.

  _Series battery connection:_

  ![](static/series-battery-diagram.svg)

  _Can be represented in a circuit diagram in one of the following two ways: as a
  series of cells or as a single battery:_

  ![](static/series-battcircuit.svg)

  In the case of **series opposing**, negative terminals are connected to each
  other and positive terminals are connected to each other in a series. This
  doesn't have many applications.

  ### Parallel connections

  In parallel connections all positive terminals are connected to each other and
  all negative terminals are connected to each other.

  This time the voltage is the same as each individual cell but the current is the
  sum of the individual cell currents. So the voltage is constant but the current
  is cumulative:

  $$
  E_{T} = E_{1} = E_{2} = E_{3} \\
  $$

  $$
  I_{T} = I_{1} + I_{2} + I_{3}
  $$

  _Parallel battery connection:_

  ![](static/parallel-battery-diagram.svg)

  _Parallel battery circuit diagram:_

  ![](static/circ-batt-final.svg)

  ### Series-parrallel

  If we want both a higher voltage and a higher current we can use series-parallel
  configurations. Connecting cells in series increases the voltage and connecting
  cells in parellel increases the current so doint both boosts the amount of both
  quantities.


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Cherry_picking.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeCherry_picking.md
title: Cherry-picking a commit
checksum: 41680bd4b3b2d0046b24d0a94e3037f518669cb5ce93de5243b231621fa0943f
created: 2024-10-22 16:11:44.604813235+00:00
modified: 2024-10-22 15:52:03.343569242+00:00
metadata: {"tags":["git"]}
lead:
  Cherry-picking is the act of selecting a specific commit or set of commits and
  applying them to a different branch. It allows you to extract a change or a
  series of changes from one branch and apply them to another branch without
  merging the entire branch.

body:
  Cherry-picking is the act of selecting a specific commit or set of commits and
  applying them to a different branch. It allows you to extract a change or a
  series of changes from one branch and apply them to another branch without
  merging the entire branch.

  It can also be used to apply a series of commits that are not sequential in a
  branch to another branch in a specific order.

  Cherry-picking can sometimes result in conflicts, especially if the changes
  you're trying to apply conflict with changes in the destination branch. In such
  cases, you'll need to resolve the conflicts manually before completing the
  cherry-pick process.

  ## Syntax

  Suppose you have two branches: `main` and `feature`. You want to apply the
  changes from the commit with hash `xyx` from the `main` branch to the `feature`
  branch.

  First, switch to the `feature` branch:

  ```
  git checkout feature
  ```

  Next, cherry-pick the commit from the `main` branch:

  ```
  git cherry-pick xyz
  ```

  This will apply the changes from the commit with hash `xyz` from thee `main`
  branch to the `feature` branch. This will create a new SHA on `feature` (pqr)
  but the changes will be identical.

  ![](static/cherry-pick.svg)

  The benefit is that you only take the select changes you want, you are not
  merging the whole `main` branch into feature.

  Note that you can also cherry-pick multiple commits by specifying their hashes
  separated by spaces:

  ```
  git cherry-pick abcdefg hijklmn opqrst
  ```

  ## Limitations

  - You don't have to just cherry-pick locally, you can also cherry-pick from a
    [remote tracking branch](Remote_tracking_branches.md).
  - You cannot cherry-pick merge commits since these commits do not implement a
    set of changes, they are connecting commits.

  ## Use case

  The time when I have cherry-picked is when a commit has been reverted via
  GitHub. This typically happens on the `main` branch when breaking changes have
  been merged and we want to undo this by reverting back to the previous commit,
  from before the problematic commit was merged in.

  In this scenario, you will want to start from the reverted `main` branch and
  then cherry-pick the breaking commit. You can then fix the bug and keep an
  accurate record of the whole history.

raw_content:
  ---
  tags:
    - git
  ---

  # Cherry-picking a commit

  Cherry-picking is the act of selecting a specific commit or set of commits and
  applying them to a different branch. It allows you to extract a change or a
  series of changes from one branch and apply them to another branch without
  merging the entire branch.

  It can also be used to apply a series of commits that are not sequential in a
  branch to another branch in a specific order.

  Cherry-picking can sometimes result in conflicts, especially if the changes
  you're trying to apply conflict with changes in the destination branch. In such
  cases, you'll need to resolve the conflicts manually before completing the
  cherry-pick process.

  ## Syntax

  Suppose you have two branches: `main` and `feature`. You want to apply the
  changes from the commit with hash `xyx` from the `main` branch to the `feature`
  branch.

  First, switch to the `feature` branch:

  ```
  git checkout feature
  ```

  Next, cherry-pick the commit from the `main` branch:

  ```
  git cherry-pick xyz
  ```

  This will apply the changes from the commit with hash `xyz` from thee `main`
  branch to the `feature` branch. This will create a new SHA on `feature` (pqr)
  but the changes will be identical.

  ![](static/cherry-pick.svg)

  The benefit is that you only take the select changes you want, you are not
  merging the whole `main` branch into feature.

  Note that you can also cherry-pick multiple commits by specifying their hashes
  separated by spaces:

  ```
  git cherry-pick abcdefg hijklmn opqrst
  ```

  ## Limitations

  - You don't have to just cherry-pick locally, you can also cherry-pick from a
    [remote tracking branch](Remote_tracking_branches.md).
  - You cannot cherry-pick merge commits since these commits do not implement a
    set of changes, they are connecting commits.

  ## Use case

  The time when I have cherry-picked is when a commit has been reverted via
  GitHub. This typically happens on the `main` branch when breaking changes have
  been merged and we want to undo this by reverting back to the previous commit,
  from before the problematic commit was merged in.

  In this scenario, you will want to start from the reverted `main` branch and
  then cherry-pick the breaking commit. You can then fix the bug and keep an
  accurate record of the whole history.


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Clock_signals.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeClock_signals.md
title: Clock signals
checksum: 2a3f168489bef4fce4b50ac909b0d07fa5cffedd13aaed5a48047ad0b1f5aa76
created: 2024-10-22 16:11:44.805807899+00:00
modified: 2024-10-22 15:52:03.340235844+00:00
metadata: {"tags":["binary","memory","electromagnetism","hardware"]}
lead:
  In the examples of digital circuits so far (i.e
  [adders](Half_adder_and_full_adder.md)
  and [latches](Latches.md)) everything
  happens in a single instant or over several repeated instances. This is because
  of how simple the circuits are. In the case of latches only a single bit is
  updated. And even with rippled adders they are just a series of 1-bit updaters
  in a chain.

body:
  In the examples of digital circuits so far (i.e
  [adders](Half_adder_and_full_adder.md)
  and [latches](Latches.md)) everything
  happens in a single instant or over several repeated instances. This is because
  of how simple the circuits are. In the case of latches only a single bit is
  updated. And even with rippled adders they are just a series of 1-bit updaters
  in a chain.

  With more complex circuits that use multiple memory devices which store a series
  of bits at once, we need a way to ensure that the bits are set at the same time.
  We do this by sequencing the execution with the pulses of the system clock.

  A single iteration of the volatage rising and falling is a **pulse**. A complete
  oscillation from low to high and back to low is a **cycle**. As with all
  [electromagnetic](Electromagnetism.md)
  signals we measure the frequency of the wave in Hertz: cylcles per second. We
  also further distinguish the rising and falling edge of a pulse. Rising
  represents the signal passing from ground to its maximum voltage and falling is
  the reverse (the electrons moving from the voltage source to ground).

  The diagram below shows a pulse cycle of 2Hz.

  ![](static/clock_pulses.png)

  ## Linking components to the clock

  - All components that need to be synchronised are connected to the clock
  - State changes in the component occur only when a clock pulse occurs
  - Clock-driven components will typically trigger their state changes on either
    the rising edge or the falling edge of the pulse.
  - Components that trigger state changes on the rising pulse are **positive
    edge-triggered**
  - Components that trigger state changes on the falling pulse are **negative
    edge-triggered**

  The role of the clock is essential in the functioning of the
  [CPU](CPU_architecture.md#the-system-clock). It is
  the system clock that gives CPUs their performance rating: how many processes
  can execute within a given clock cycle.

raw_content:
  ---
  tags:
    - binary
    - memory
    - electromagnetism
    - hardware
  ---

  # Clock signals

  In the examples of digital circuits so far (i.e
  [adders](Half_adder_and_full_adder.md)
  and [latches](Latches.md)) everything
  happens in a single instant or over several repeated instances. This is because
  of how simple the circuits are. In the case of latches only a single bit is
  updated. And even with rippled adders they are just a series of 1-bit updaters
  in a chain.

  With more complex circuits that use multiple memory devices which store a series
  of bits at once, we need a way to ensure that the bits are set at the same time.
  We do this by sequencing the execution with the pulses of the system clock.

  A single iteration of the volatage rising and falling is a **pulse**. A complete
  oscillation from low to high and back to low is a **cycle**. As with all
  [electromagnetic](Electromagnetism.md)
  signals we measure the frequency of the wave in Hertz: cylcles per second. We
  also further distinguish the rising and falling edge of a pulse. Rising
  represents the signal passing from ground to its maximum voltage and falling is
  the reverse (the electrons moving from the voltage source to ground).

  The diagram below shows a pulse cycle of 2Hz.

  ![](static/clock_pulses.png)

  ## Linking components to the clock

  - All components that need to be synchronised are connected to the clock
  - State changes in the component occur only when a clock pulse occurs
  - Clock-driven components will typically trigger their state changes on either
    the rising edge or the falling edge of the pulse.
  - Components that trigger state changes on the rising pulse are **positive
    edge-triggered**
  - Components that trigger state changes on the falling pulse are **negative
    edge-triggered**

  The role of the clock is essential in the functioning of the
  [CPU](CPU_architecture.md#the-system-clock). It is
  the system clock that gives CPUs their performance rating: how many processes
  can execute within a given clock cycle.


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/CloudWatch.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeCloudWatch.md
title: AWS CloudWatch
checksum: bb489d109e8890c3c600c7f0a65e170ddc19063246b7023d1ca1c171bdd0bedc
created: 2024-10-22 16:11:44.881185026+00:00
modified: 2024-10-22 15:52:03.340235844+00:00
metadata: {"tags":["AWS"]}
lead:
  AWS CloudWatch is a monitoring and observability service offered by Amazon Web
  Services (AWS). It is designed to help users collect, track, and analyze various
  metrics, logs, and events related to their AWS resources and applications
  running on the AWS cloud. CloudWatch enables users to gain operational insights
  and take timely actions to maintain the performance, availability, and health of
  their infrastructure and applications.

body:
  AWS CloudWatch is a monitoring and observability service offered by Amazon Web
  Services (AWS). It is designed to help users collect, track, and analyze various
  metrics, logs, and events related to their AWS resources and applications
  running on the AWS cloud. CloudWatch enables users to gain operational insights
  and take timely actions to maintain the performance, availability, and health of
  their infrastructure and applications.

  ## Key features

  - Metrics
    - CloudWatch collects and stores metrics from various AWS services, such as
      EC2 instances, Lambda functions, and RDS databases. Users can monitor these
      metrics in real-time, set alarms based on thresholds, and visualize them
      through customizable dashboards.
  - Logs
    - CloudWatch Logs enables users to collect, store, and analyze log data from
      AWS resources and applications.
  - Events
    - Events allows users to respond to changes in their AWS resources
      automatically by triggering actions such as sending notifications, invoking
      Lambda functions, or starting/stopping EC2 instances.
  - Alarms
    - Users can set alarms based on specified metric thresholds. When an alarm is
      triggered, CloudWatch can send notifications or automatically take
      corrective actions.
  - Dashboards
    - Dashboards provide customizable, at-a-glance visualizations of various
      metrics and logs, making it easier to monitor the health and performance of
      AWS resources and applications.

  ## Accessing CloudWatch

  CloudWatch can usually be accessed directly from within the dashboard for the
  given AWS service under the "Monitor" tab. For example with AWS Lambda:

  ![](static/lambda-cloudwatch.png)

  In addition to graphical metrics, we can also view specific logs for each event
  and execution of the given service. In the case of a Lambda function:

  ![](static/cloudwatch-logs.png)

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

  # AWS CloudWatch

  AWS CloudWatch is a monitoring and observability service offered by Amazon Web
  Services (AWS). It is designed to help users collect, track, and analyze various
  metrics, logs, and events related to their AWS resources and applications
  running on the AWS cloud. CloudWatch enables users to gain operational insights
  and take timely actions to maintain the performance, availability, and health of
  their infrastructure and applications.

  ## Key features

  - Metrics
    - CloudWatch collects and stores metrics from various AWS services, such as
      EC2 instances, Lambda functions, and RDS databases. Users can monitor these
      metrics in real-time, set alarms based on thresholds, and visualize them
      through customizable dashboards.
  - Logs
    - CloudWatch Logs enables users to collect, store, and analyze log data from
      AWS resources and applications.
  - Events
    - Events allows users to respond to changes in their AWS resources
      automatically by triggering actions such as sending notifications, invoking
      Lambda functions, or starting/stopping EC2 instances.
  - Alarms
    - Users can set alarms based on specified metric thresholds. When an alarm is
      triggered, CloudWatch can send notifications or automatically take
      corrective actions.
  - Dashboards
    - Dashboards provide customizable, at-a-glance visualizations of various
      metrics and logs, making it easier to monitor the health and performance of
      AWS resources and applications.

  ## Accessing CloudWatch

  CloudWatch can usually be accessed directly from within the dashboard for the
  given AWS service under the "Monitor" tab. For example with AWS Lambda:

  ![](static/lambda-cloudwatch.png)

  In addition to graphical metrics, we can also view specific logs for each event
  and execution of the given service. In the case of a Lambda function:

  ![](static/cloudwatch-logs.png)


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Conditional_Elimination.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeConditional_Elimination.md
title: Conditional elimination
checksum: 28b7e20881ba13feb9c0e716b326732ac6b802ee875fcfb70659605715d6408c
created: 2024-10-22 16:11:44.970520086+00:00
modified: 2024-10-22 15:52:03.340235844+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  If we have a conditional and we have independently derived its antecedent, we
  may invoke its consequent. This is often referred to as _Modus ponens_
  (affirming the antecedent).

body:
  If we have a conditional and we have independently derived its antecedent, we
  may invoke its consequent. This is often referred to as _Modus ponens_
  (affirming the antecedent).

  ![](static/cond-elim.png)

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

  # Conditional elimination

  If we have a conditional and we have independently derived its antecedent, we
  may invoke its consequent. This is often referred to as _Modus ponens_
  (affirming the antecedent).

  ![](static/cond-elim.png)


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Conditional_Introduction.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeConditional_Introduction.md
title: Conditional Introduction
checksum: fa576a40fdcff608194561b50987012316e28d7f573b4b2a1eb99436646a074f
created: 2024-10-22 16:11:44.976422487+00:00
modified: 2024-10-22 15:52:03.350236035+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  If we can show that $Q$ follows from $P$ (typically via a sub-proof) than we can
  assert that P implies Q. This is also sometimes known as _Conditional Proof_

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

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

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

  # Conditional Introduction

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Conjunction_Elimination.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeConjunction_Elimination.md
title: Conditional Elimination
checksum: 16712666f11456513c8293b5e4b70a3c4bab5975e29e0f93dec1f25495a2be10
created: 2024-10-22 16:11:44.994612829+00:00
modified: 2024-10-22 15:52:03.340235844+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  If a conjunction exists, it means that both conjuncts are the case; therefore we
  can legitimately extract either one of them. Also known as _Simplification_.

body:
  If a conjunction exists, it means that both conjuncts are the case; therefore we
  can legitimately extract either one of them. Also known as _Simplification_.

  ![](static/conjunc-elim.png)

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

  # Conditional Elimination

  If a conjunction exists, it means that both conjuncts are the case; therefore we
  can legitimately extract either one of them. Also known as _Simplification_.

  ![](static/conjunc-elim.png)


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Conjunction_Introduction.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeConjunction_Introduction.md
title: Conjunction Introduction
checksum: c6ea5000fe6787b43110cc4e1c298a69f7f72ded98bb224ed7228301058500c0
created: 2024-10-22 16:11:45.000428205+00:00
modified: 2024-10-22 15:52:03.356902828+00:00
metadata: {"tags":["propositional-logic","logic"]}
lead:
  If two conjuncts have each been independently derived then they can be
  conjoined. Also known more simply as _Conjunction_

body:
  If two conjuncts have each been independently derived then they can be
  conjoined. Also known more simply as _Conjunction_

  ![](static/conjunc-intro.png)

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

  # Conjunction Introduction

  If two conjuncts have each been independently derived then they can be
  conjoined. Also known more simply as _Conjunction_

  ![](static/conjunc-intro.png)


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Containerization.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeContainerization.md
title: Containerizaton
checksum: ff766df8cdfb8758562ebc0c4431b7bb40acd3d09af609e61fa8c9c6cd13120a
created: 2024-10-22 16:11:45.057869387+00:00
modified: 2024-10-22 15:52:03.346902638+00:00
metadata: {"tags":["docker","containerization","Linux"]}
lead:
  > In this entry we look at containerization as a general technology that is
  > distinct from its particular implementation by Docker.

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

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

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

  ## Containers and software

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

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

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

  ## How containers work

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

  > Containerization = resource control + process isolation

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

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

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

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

  _Standard userspace_

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

  _Userspace with containerization_

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

  ## Differences with virtual machines

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

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

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

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

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

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

  ## Why use containers?

  - portability
  - scalability
  - efficiency
  - isolation
  - DevOps agility

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

  # Containerizaton

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

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

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

  ## Containers and software

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

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

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

  ## How containers work

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

  > Containerization = resource control + process isolation

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

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

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

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

  _Standard userspace_

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

  _Userspace with containerization_

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

  ## Differences with virtual machines

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

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

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

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

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

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

  ## Why use containers?

  - portability
  - scalability
  - efficiency
  - isolation
  - DevOps agility


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Create_Mongo_database.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeCreate_Mongo_database.md
title: Creating a MongoDB database
lead: First create a directory for the database and set permissions:
checksum: ff0f1a70e4dcfda459ec3793f906b516c447a342353952cca6b2cb7cfd2eab1d
created: 2024-10-22 16:11:45.121350088+00:00
modified: 2024-10-22 15:52:03.343569242+00:00
metadata: {"tags":["mongo-db","node-js","mongoose","databases"]}
body:
  First create a directory for the database and set permissions:

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

  Then start the Mongo daemon

  ```bash
  mongod
  ```

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

  ## MongoDB Compass

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

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

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

  # Creating a MongoDB database

  First create a directory for the database and set permissions:

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

  Then start the Mongo daemon

  ```bash
  mongod
  ```

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

  ## MongoDB Compass

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Creating_a_Linux_partition_table.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeCreating_a_Linux_partition_table.md
title: Creating a Linux partition table
lead: ## Creating a partition table
checksum: ec1ef4b61b11b58aead6d8c16aca7ce8b82d36d2ef518624651eb0bcbdc89e46
created: 2024-10-22 16:11:45.167447755+00:00
modified: 2024-10-22 15:52:03.340235844+00:00
metadata: {"created":"Monday, May 06, 2024","tags":["disks","Linux"]}
body:
  ## Creating a partition table

  To demonstrate the process of partitioning a harddrive I am going to repartition
  an external SATA drive as if it were being primed for a fresh Linux install.

  Let's take a look at the disk in its current form:

  ```
  $ fdisk -l

    Disk /dev/sda: 465.74 GiB, 500079525888 bytes, 976717824 sectors
    Disk model: My Passport 071Aumount /dev/sda2
    Disklabel type: gpt
    Disk identifier: 9993F1BB-626C-485F-8542-3CC73BB40953

    Device      Start       End   Sectors   Size Type
    /dev/sda1      40    409639    409600   200M EFI System
    /dev/sda2  409640 976455639 976046000 465.4G Apple HFS/HFS+
  ```

  (This disk was previously used as a backup disk for MacOS so in addition to the
  extended partition which has a proprietary file system type (Apple HFS) it has a
  primary partition which would load the recovery OS. In contrast to my main
  harddrive this uses the standard SCSI prototcol and thus the partitions are
  prepended with `sda`.)

  #### 1. Unmount existing partitions

  ```bash
  umount /dev/sda1
  umount /dev/sda2
  ```

  #### 2. Deleting the existing partitions

  ```bash
  # Load the disk into fdisk
  $ sudo fdisk /dev/sda
  Command (m for help): d
  Partition number (1,2, default 2): 1
  Partition 1 has been deleted.
  Command (m for help): d
  Selected partition 2
  Partition 2 has been deleted.

  # Verify deletion with p(rint) command
  Command (m for help): p
  Disk /dev/sda: 465.74 GiB, 500079525888 bytes, 976717824 sectors
  Disk model: My Passport 071A
  Units: sectors of 1 * 512 = 512 bytes
  Sector size (logical/physical): 512 bytes / 512 bytes
  I/O size (minimum/optimal): 512 bytes / 512 bytes
  Disklabel type: gpt
  Disk identifier: 9993F1BB-626C-485F-8542-3CC73BB40953

  # Write the changes to disk with w(rite)
  w

  # Check execution:
  $ lsblk

  NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
  sda           8:0    0 465.7G  0 disk
  ```

  So now the disk has no partitions, just the physical disk space and no
  mountpoints. We are ready to re-partition.

  #### 3. Re-partition the disk

  We are going to create a GPT partition table

  ```bash
  $ sudo fdisk /dev/sda
  Command (m for help): g
  Created a new GPT disklabel (GUID: E316B1A9-6F73-FB41-8CBB-FA4E3C448F2B).

  Command (m for help): n
  Partition number (1-128, default 1):
  First sector (2048-976717790, default 2048):

  # Just press enter here

  # The last sector is where we put the actual size we want for the partition
  Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-976717790, default 976715775): +100G
  Created a new partition 1 of type 'Linux filesystem' and of size 100 GiB.

  # For the second and final partition, we follow the same steps but don't add a partition size, since it will default to what is left.

  Command (m for help): n
  Partition number (2-128, default 2):
  First sector (209717248-976717790, default 209717248):
  Last sector, +/-sectors or +/-size{K,M,G,T,P} (209717248-976717790, default 976715775):

  Created a new partition 2 of type 'Linux filesystem' and of size 365.7 GiB.

  # Check the partitions with p(rint)
  Command (m for help): p
  Disk /dev/sda: 465.74 GiB, 500079525888 bytes, 976717824 sectors
  Disk model: My Passport 071A
  Units: sectors of 1 * 512 = 512 bytes
  Sector size (logical/physical): 512 bytes / 512 bytes
  I/O size (minimum/optimal): 512 bytes / 512 bytes
  Disklabel type: gpt
  Disk identifier: 92739978-F7D2-5042-9758-C1429B5C8E11

  Device         Start       End   Sectors   Size Type
  /dev/sda1       2048 209717247 209715200   100G Linux filesystem
  /dev/sda2  209717248 976715775 766998528 365.7G Linux filesystem

  # Then write with w
  Command (m for help): w
  The partition table has been altered.
  Calling ioctl() to re-read partition table.
  Syncing disks.
  ```

  We can now confirm our new partitions:

  ```bash
  $ lsblk
  NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
  sda           8:0    0 465.7G  0 disk
  ├─sda1        8:1    0   100G  0 part
  └─sda2        8:2    0 365.7G  0 part
  ```

  #### Naming a partition

  By default each partition will have its GUID as its name, but you can add a
  human friendly name with:

  ```bash
  sudo e2label /dev/sda1 my_human_name
  ```

  > Whilst we have created our partitions we cannot yet mount them. This is
  > because we have not yet set up a filesystem on the partitions. This is the
  > next step.

  ## Related notes

  ![Linux disk partitions](static/Linux_disk_partitions.md)

raw_content:
  ---
  tags:
    - disks
    - Linux
  created: Monday, May 06, 2024
  ---

  # Creating a Linux partition table

  ## Creating a partition table

  To demonstrate the process of partitioning a harddrive I am going to repartition
  an external SATA drive as if it were being primed for a fresh Linux install.

  Let's take a look at the disk in its current form:

  ```
  $ fdisk -l

    Disk /dev/sda: 465.74 GiB, 500079525888 bytes, 976717824 sectors
    Disk model: My Passport 071Aumount /dev/sda2
    Disklabel type: gpt
    Disk identifier: 9993F1BB-626C-485F-8542-3CC73BB40953

    Device      Start       End   Sectors   Size Type
    /dev/sda1      40    409639    409600   200M EFI System
    /dev/sda2  409640 976455639 976046000 465.4G Apple HFS/HFS+
  ```

  (This disk was previously used as a backup disk for MacOS so in addition to the
  extended partition which has a proprietary file system type (Apple HFS) it has a
  primary partition which would load the recovery OS. In contrast to my main
  harddrive this uses the standard SCSI prototcol and thus the partitions are
  prepended with `sda`.)

  #### 1. Unmount existing partitions

  ```bash
  umount /dev/sda1
  umount /dev/sda2
  ```

  #### 2. Deleting the existing partitions

  ```bash
  # Load the disk into fdisk
  $ sudo fdisk /dev/sda
  Command (m for help): d
  Partition number (1,2, default 2): 1
  Partition 1 has been deleted.
  Command (m for help): d
  Selected partition 2
  Partition 2 has been deleted.

  # Verify deletion with p(rint) command
  Command (m for help): p
  Disk /dev/sda: 465.74 GiB, 500079525888 bytes, 976717824 sectors
  Disk model: My Passport 071A
  Units: sectors of 1 * 512 = 512 bytes
  Sector size (logical/physical): 512 bytes / 512 bytes
  I/O size (minimum/optimal): 512 bytes / 512 bytes
  Disklabel type: gpt
  Disk identifier: 9993F1BB-626C-485F-8542-3CC73BB40953

  # Write the changes to disk with w(rite)
  w

  # Check execution:
  $ lsblk

  NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
  sda           8:0    0 465.7G  0 disk
  ```

  So now the disk has no partitions, just the physical disk space and no
  mountpoints. We are ready to re-partition.

  #### 3. Re-partition the disk

  We are going to create a GPT partition table

  ```bash
  $ sudo fdisk /dev/sda
  Command (m for help): g
  Created a new GPT disklabel (GUID: E316B1A9-6F73-FB41-8CBB-FA4E3C448F2B).

  Command (m for help): n
  Partition number (1-128, default 1):
  First sector (2048-976717790, default 2048):

  # Just press enter here

  # The last sector is where we put the actual size we want for the partition
  Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-976717790, default 976715775): +100G
  Created a new partition 1 of type 'Linux filesystem' and of size 100 GiB.

  # For the second and final partition, we follow the same steps but don't add a partition size, since it will default to what is left.

  Command (m for help): n
  Partition number (2-128, default 2):
  First sector (209717248-976717790, default 209717248):
  Last sector, +/-sectors or +/-size{K,M,G,T,P} (209717248-976717790, default 976715775):

  Created a new partition 2 of type 'Linux filesystem' and of size 365.7 GiB.

  # Check the partitions with p(rint)
  Command (m for help): p
  Disk /dev/sda: 465.74 GiB, 500079525888 bytes, 976717824 sectors
  Disk model: My Passport 071A
  Units: sectors of 1 * 512 = 512 bytes
  Sector size (logical/physical): 512 bytes / 512 bytes
  I/O size (minimum/optimal): 512 bytes / 512 bytes
  Disklabel type: gpt
  Disk identifier: 92739978-F7D2-5042-9758-C1429B5C8E11

  Device         Start       End   Sectors   Size Type
  /dev/sda1       2048 209717247 209715200   100G Linux filesystem
  /dev/sda2  209717248 976715775 766998528 365.7G Linux filesystem

  # Then write with w
  Command (m for help): w
  The partition table has been altered.
  Calling ioctl() to re-read partition table.
  Syncing disks.
  ```

  We can now confirm our new partitions:

  ```bash
  $ lsblk
  NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
  sda           8:0    0 465.7G  0 disk
  ├─sda1        8:1    0   100G  0 part
  └─sda2        8:2    0 365.7G  0 part
  ```

  #### Naming a partition

  By default each partition will have its GUID as its name, but you can add a
  human friendly name with:

  ```bash
  sudo e2label /dev/sda1 my_human_name
  ```

  > Whilst we have created our partitions we cannot yet mount them. This is
  > because we have not yet set up a filesystem on the partitions. This is the
  > next step.

  ## Related notes

  ![Linux disk partitions](static/Linux_disk_partitions.md)


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Creating_a_Mongo_schema_and_model.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeCreating_a_Mongo_schema_and_model.md
title: Creating a schema and model
lead: ## Schema
checksum: 155bfb3366b9d8fceea14b3b4d16d21630599de9ac37d5a01941fd2f7fa19972
created: 2024-10-22 16:11:45.17351126+00:00
modified: 2024-10-22 15:52:03.350236035+00:00
metadata: {"tags":["mongo-db","node-js","mongoose","databases"]}
body:
  ## Schema

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

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

  ### Creating a schema

  ```js
  const courseSchema = new mongoose.Schema({
    name: { type: String, required: true, minlength: 5, maxlength: 255 },
    author: String,
    tags: [String],
    data: { type: Date, default: Date.now }, // if unspecified, entry will default to current date
    isPublished: Boolean,
  });
  ```

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

  #### Available data types

  The following data types are available:

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

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

  ## Models

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

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

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

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

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

  ## Outcome

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

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

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

  # Creating a schema and model

  ## Schema

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

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

  ### Creating a schema

  ```js
  const courseSchema = new mongoose.Schema({
    name: { type: String, required: true, minlength: 5, maxlength: 255 },
    author: String,
    tags: [String],
    data: { type: Date, default: Date.now }, // if unspecified, entry will default to current date
    isPublished: Boolean,
  });
  ```

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

  #### Available data types

  The following data types are available:

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

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

  ## Models

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

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

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

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

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

  ## Outcome

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Creating_a_RESTful_API_GET.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeCreating_a_RESTful_API_GET.md
title: Creating a RESTful API: GET requests
lead: With our GET request we will simply return the array of course objects.
checksum: a0c81c5033d04ba8a169b69f3a4b1e11906adcf361a38834e517ac34672dca11
created: 2024-10-22 16:11:45.227427465+00:00
modified: 2024-10-22 15:52:03.360236224+00:00
metadata: {"tags":["node-js","REST","APIs"]}
body:
  With our GET request we will simply return the array of course objects.

  We create an [event emitter](Events%20module.md#event-emitters) and listener
  that listens for GET requests on a specified port and sends data in response to
  requests.

  ```js
  // Return a value as response from specified URI
  router.get("/", (req, res) => {
    res.send(courses);
  });
  ```

  Our server is now set up:

  ![](static/server-listening.png)

  > When creating our API this structure of creating handlers for specific routes
  > will be iterated. Every endpoint will be specified with
  > `[app].[http_request_type]` and followed by a callback.

  We can now call the endpoint:

  ```js
  const getAllCourses = async () => {
    try {
      const resp = await axios.get("http://localhost:3000/api/courses");
      console.log(resp.data);
    } catch (err) {
      console.error(err);
    }
  };

  getAllCourses();
  ```

  Returns:

  ```js
  [
    { id: 1, name: "First course" },
    { id: 2, name: "Second course" },
    { id: 3, name: "Third course" },
  ];
  ```

  ## Parameters

  The previous example serves the entire set of our data. But we will also need to
  retrieve specific values, we do this by adapting the GET callback to accept
  parameters. These parameters will correspond to the specific entry in our main
  data array.

  ```js
  router.get("/:id", (req, res) => {
    res.send(req.params.id);
  });
  ```

  We use the `:` symbol in the URI to indicate that we looking to parse for a
  specific value in the data. Now if we call `/api/courses/2`, we will get the
  second item in the array.

  The block above is the most basic format but we would want to add some kind of
  error handling, for example:

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

  ## Queries

  Whereas parameters return specific data points, queries don't get data they
  aggregate or present the data that is returned in a certain way, such as for
  instance applying a search function. We indicate queries with a `?` in our URI.
  For example: `/api/posts/2018/1?sortBy=name`. To facilitate a request like this
  we have to create a GET path that allows for it:

  ```js
  router.get("/:year/:month", (req, res) => {
    res.send(req.query);
  })[]();
  ```

  We would get the following back:

  ```json
  {
    "sortBy": "name"
  }
  ```

  Again a JSON object with key-value pairs is returned.

raw_content:
  ---
  tags:
    - node-js
    - REST
    - APIs
  ---

  # Creating a RESTful API: `GET` requests

  With our GET request we will simply return the array of course objects.

  We create an [event emitter](Events%20module.md#event-emitters) and listener
  that listens for GET requests on a specified port and sends data in response to
  requests.

  ```js
  // Return a value as response from specified URI
  router.get("/", (req, res) => {
    res.send(courses);
  });
  ```

  Our server is now set up:

  ![](static/server-listening.png)

  > When creating our API this structure of creating handlers for specific routes
  > will be iterated. Every endpoint will be specified with
  > `[app].[http_request_type]` and followed by a callback.

  We can now call the endpoint:

  ```js
  const getAllCourses = async () => {
    try {
      const resp = await axios.get("http://localhost:3000/api/courses");
      console.log(resp.data);
    } catch (err) {
      console.error(err);
    }
  };

  getAllCourses();
  ```

  Returns:

  ```js
  [
    { id: 1, name: "First course" },
    { id: 2, name: "Second course" },
    { id: 3, name: "Third course" },
  ];
  ```

  ## Parameters

  The previous example serves the entire set of our data. But we will also need to
  retrieve specific values, we do this by adapting the GET callback to accept
  parameters. These parameters will correspond to the specific entry in our main
  data array.

  ```js
  router.get("/:id", (req, res) => {
    res.send(req.params.id);
  });
  ```

  We use the `:` symbol in the URI to indicate that we looking to parse for a
  specific value in the data. Now if we call `/api/courses/2`, we will get the
  second item in the array.

  The block above is the most basic format but we would want to add some kind of
  error handling, for example:

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

  ## Queries

  Whereas parameters return specific data points, queries don't get data they
  aggregate or present the data that is returned in a certain way, such as for
  instance applying a search function. We indicate queries with a `?` in our URI.
  For example: `/api/posts/2018/1?sortBy=name`. To facilitate a request like this
  we have to create a GET path that allows for it:

  ```js
  router.get("/:year/:month", (req, res) => {
    res.send(req.query);
  })[]();
  ```

  We would get the following back:

  ```json
  {
    "sortBy": "name"
  }
  ```

  Again a JSON object with key-value pairs is returned.


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Creating_memory_with_NAND.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeCreating_memory_with_NAND.md
title: Creating memory with NAND gates
checksum: aaa127ad46ff760c97e0b04ced5781bb247f47c9b847c8d30d7535e0c0f47d3f
created: 2024-10-22 16:11:45.560515675+00:00
modified: 2024-10-22 15:52:03.360236224+00:00
metadata: {"tags":["logic-gates","binary","memory"]}
lead:
  The
  [logic circuit](Digital_circuits.md)
  below demonstrates how memory can be created using
  [NAND](Logic_gates.md#nand-gate)
  gates. A single bit is stored in memory.

body:
  The
  [logic circuit](Digital_circuits.md)
  below demonstrates how memory can be created using
  [NAND](Logic_gates.md#nand-gate)
  gates. A single bit is stored in memory.

  ![](static/nand_latch_logic_circuit.png) Interactive version of circuit:

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

  ## Components

  - **I** is where we input the bit that we want to remember.
  - **O** is the output of the remembered bit
  - **S** is an input that tells these gates when to set the memory.
  - **A, B, C** are internal wires that represent the individual bit values which
    channel into each gate

  ## State changes

  ### First state: S `ON`, I `OFF`

  - Start with **S** `ON` and **I** `OFF`.
  - Given the logic of NAND, **A** will be on.
  - This means **Gate 2** is receiving `ON` from **A** and `ON` from **S**
    therefore `B` is `OFF`
  - Moving to **Gate 4** it is receiving `OFF` from **B** and it's other input is
    not activated so is therefore also `OFF`. Consequently **C** is `ON`
  - `C` is fed as the second input into **Gate 3**, thus we have both inputs
    (**A** and **C**) as `ON`, therefore the output will make **O** `OFF`

  > Upshot: With **S** `ON`, output is the same as input

  ![](static/nand-mem-first.gif)

  ### Second state: both S and I `ON`

  - With **S** and **I** both on, Gate A will be `OFF`
  - Gate 2 is receiving `OFF` from **A** and `ON` from **S** therefore **B** is
    `ON`
  - **A** feeds into Gate 2 which is receiving `ON` from **S**, therefore the
    output (**B**) will be `ON`
  - **A** feeds into Gate 3 and the other input is `OFF`, therefore Gate 3 is
    outputting `ON`. Thus **O** is `ON`
  - **O** feeds back to Gate 4 which is receiving `ON` from **B** thus we have two
    `ON` inputs going into Gate 4, turning **C** off
  - This means the two inputs going into Gate 3 are both `OFF`, keeping **O** `ON`

  > Upshot: With **S** on, the output is again the same as the input

  ![](static/nand-mem-second.gif)

  > So far we have seen that when **S** is `ON` you can change **I** on and off
  > and **O** will change with it.

  ### Memory

  When **S** is `ON`, **O** will mirror whatever state **I** is in. However if you
  turn **S** `OFF`, **O** will remain in whatever state it was when **S** was
  turned `OFF`. You can toggle **I** as much as you like, **O** will remain in its
  previous state. Hence creating a memory store of the past value of **I**.

  The specific reason for this is that, if **S** is `OFF`, both **A** and **B**
  are `ON` since at Gate 1: `ON (I) + OFF (S) = ON` and `OFF (I) + OFF (S) = OFF`
  and at Gate 2: `OFF (Gate 1) + OFF (S) = OFF`

  This is illustrated in the diagram below. The space occupied by **A** and **B**
  remains on (note it is illuminated) regardless of the state of **I**.

  ![](static/nand-mem-demonstrated.gif)

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

  # Creating memory with NAND gates

  The
  [logic circuit](Digital_circuits.md)
  below demonstrates how memory can be created using
  [NAND](Logic_gates.md#nand-gate)
  gates. A single bit is stored in memory.

  ![](static/nand_latch_logic_circuit.png) Interactive version of circuit:

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

  ## Components

  - **I** is where we input the bit that we want to remember.
  - **O** is the output of the remembered bit
  - **S** is an input that tells these gates when to set the memory.
  - **A, B, C** are internal wires that represent the individual bit values which
    channel into each gate

  ## State changes

  ### First state: S `ON`, I `OFF`

  - Start with **S** `ON` and **I** `OFF`.
  - Given the logic of NAND, **A** will be on.
  - This means **Gate 2** is receiving `ON` from **A** and `ON` from **S**
    therefore `B` is `OFF`
  - Moving to **Gate 4** it is receiving `OFF` from **B** and it's other input is
    not activated so is therefore also `OFF`. Consequently **C** is `ON`
  - `C` is fed as the second input into **Gate 3**, thus we have both inputs
    (**A** and **C**) as `ON`, therefore the output will make **O** `OFF`

  > Upshot: With **S** `ON`, output is the same as input

  ![](static/nand-mem-first.gif)

  ### Second state: both S and I `ON`

  - With **S** and **I** both on, Gate A will be `OFF`
  - Gate 2 is receiving `OFF` from **A** and `ON` from **S** therefore **B** is
    `ON`
  - **A** feeds into Gate 2 which is receiving `ON` from **S**, therefore the
    output (**B**) will be `ON`
  - **A** feeds into Gate 3 and the other input is `OFF`, therefore Gate 3 is
    outputting `ON`. Thus **O** is `ON`
  - **O** feeds back to Gate 4 which is receiving `ON` from **B** thus we have two
    `ON` inputs going into Gate 4, turning **C** off
  - This means the two inputs going into Gate 3 are both `OFF`, keeping **O** `ON`

  > Upshot: With **S** on, the output is again the same as the input

  ![](static/nand-mem-second.gif)

  > So far we have seen that when **S** is `ON` you can change **I** on and off
  > and **O** will change with it.

  ### Memory

  When **S** is `ON`, **O** will mirror whatever state **I** is in. However if you
  turn **S** `OFF`, **O** will remain in whatever state it was when **S** was
  turned `OFF`. You can toggle **I** as much as you like, **O** will remain in its
  previous state. Hence creating a memory store of the past value of **I**.

  The specific reason for this is that, if **S** is `OFF`, both **A** and **B**
  are `ON` since at Gate 1: `ON (I) + OFF (S) = ON` and `OFF (I) + OFF (S) = OFF`
  and at Gate 2: `OFF (Gate 1) + OFF (S) = OFF`

  This is illustrated in the diagram below. The space occupied by **A** and **B**
  remains on (note it is illuminated) regardless of the state of **I**.

  ![](static/nand-mem-demonstrated.gif)


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Current.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeCurrent.md
title: Current
checksum: 73dc61a8a608d53055b3489430283ae054ed307614002e3c95b09926a374d5fe
created: 2024-10-22 16:11:45.615180116+00:00
modified: 2024-10-22 15:52:03.336902448+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:_

  ![](static/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:_

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Defining_a_computer.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeDefining_a_computer.md
title: Defining a computer
checksum: e513193f874e3e894db5c14021c1d1623f84730cf04266ec40e9a737a1cc9c1a
created: 2024-10-22 16:11:45.838735186+00:00
modified: 2024-10-22 15:52:03.353569431+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](static/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](static/Screenshot_2020-08-09_at_21.32.54%201.png)

  ![Screenshot_2020-08-09_at_21.34.48.png](static/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](static/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](static/Screenshot_2020-08-09_at_21.32.54%201.png)

  ![Screenshot_2020-08-09_at_21.34.48.png](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Disjunction_Elimination.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeDisjunction_Elimination.md
title: Disjunction Elimination
checksum: d66515e89469ee439562a13e307bf310280fea45fabb03bb1229bd5591e026aa
created: 2024-10-22 16:11:45.998237839+00:00
modified: 2024-10-22 15:52:03.336902448+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.

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

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

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

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

  ![](static/ORelim1.png) ![](static/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.

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

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

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

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Disjunction_Introduction.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeDisjunction_Introduction.md
title: Disjunction Introduction
checksum: f894dcc0d8cd79bda3e1e0ebf01e7c6e0185eb1690f73263d92b5728c75af91e
created: 2024-10-22 16:11:46.007892115+00:00
modified: 2024-10-22 15:52:03.350236035+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_.

  ![](static/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_.

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Dividing_fractions.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeDividing_fractions.md
title: Dividing fractions
lead: Suppose you have the following shape:
checksum: 8a424ff691568e972a3c5c165f4e040743c4256b9f99902a5b708a7ab6f6de48
created: 2024-10-22 16:11:46.052979553+00:00
modified: 2024-10-22 15:52:03.353569431+00:00
metadata: {"tags":["prealgebra","fractions"]}
body:
  Suppose you have the following shape:

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

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

  ![one-eighth-a.png](static/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](static/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](static/draw.io-Page-9.drawio.png)

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

  ![one-eighth-a.png](static/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](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Docker_architecture.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeDocker_architecture.md
title: Docker architecture
lead: ## Overview
checksum: 4feee81a708f3bf3ceb5fddfda31cc4b687e315dd45b19b048691514ae5ec4eb
created: 2024-10-22 16:11:46.124637441+00:00
modified: 2024-10-22 15:52:03.346902638+00:00
metadata: {"tags":["docker","containerization"]}
body:
  ## Overview

  ![](static/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

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Docker_containers.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeDocker_containers.md
title: Docker containers
lead: ## Launch a container
checksum: 083a66243f94ead94583708ce19502772c92940a1f2b6d8c4a06cacf09f7ecd1
created: 2024-10-22 16:11:46.161600218+00:00
modified: 2024-10-22 15:52:03.353569431+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

  ![](static/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

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/DynamoDB_data_structure.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeDynamoDB_data_structure.md
title: DynamoDB
lead: ## Data structure
checksum: 5746bdc8d7aab050de01242bea076050c7d4e6ebfc1580210a66696e5e312ca7
created: 2024-10-22 16:11:46.218210574+00:00
modified: 2024-10-22 15:52:03.356902828+00:00
metadata: {"created":"Sunday, June 09, 2024","id":"xowl","tags":["AWS","databases","dynamodb"]}
body:
  ## Data structure

  ### Non-relational tables

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

  ![](static/dynamodb.png)

  ### Primary key

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

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

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

  There are two types of primary key available:

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

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

  ### Secondary index

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

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

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

  ## Real example

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

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

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

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

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

  ## Related notes

raw_content:
  ---
  id: xowl
  tags:
    - AWS
    - databases
    - dynamodb
  created: Sunday, June 09, 2024
  ---

  # DynamoDB

  ## Data structure

  ### Non-relational tables

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

  ![](static/dynamodb.png)

  ### Primary key

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

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

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

  There are two types of primary key available:

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

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

  ### Secondary index

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

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

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

  ## Real example

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

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

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

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

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

  ## Related notes


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Electromagnetism.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeElectromagnetism.md
title: Electromagnetism
checksum: 272baec9abf29003f2b7bfb3f988e31f4b16996b76e8fd8d4c777bce7f20354a
created: 2024-10-22 16:11:46.278010189+00:00
modified: 2024-10-22 15:52:03.346902638+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.

  ![](static/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:

  ![](static/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

  ![](static/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.

  ![](static/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:

  ![](static/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

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Electrons.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeElectrons.md
title: Electrons
lead: ## Shells
checksum: 57dfe3bcfdd98784e73e56cd2c68c035623f09c5cbd93d5f0aac7974258c2bfa
created: 2024-10-22 16:11:46.296385074+00:00
modified: 2024-10-22 15:52:03.346902638+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._

  ![](static/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._

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Equivalent_fractions.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeEquivalent_fractions.md
title: Equivalent fractions
checksum: 3518ba05220bbc03eb58cbc66286613fac05ddcf153a21201c02b14068dc319d
created: 2024-10-22 16:11:46.495803273+00:00
modified: 2024-10-22 15:52:03.356902828+00:00
metadata: {"tags":["prealgebra","fractions"]}
lead:
  Two fractions are equivalent if they represent the same value. To begin with we
  can represent this visually:

body:
  Two fractions are equivalent if they represent the same value. To begin with we
  can represent this visually:

  ![equiv-fractions.png](static/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
  ---

  # Equivalent fractions

  Two fractions are equivalent if they represent the same value. To begin with we
  can represent this visually:

  ![equiv-fractions.png](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Error_handling_in_Python.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeError_handling_in_Python.md
title: Error handling and exceptions in Python
lead: Errors can typically occur in the following scenarios:
checksum: 5194c22bf3888dbe4da2c40964629d60943b41f52b1f8476135919e9193eb884
created: 2024-10-22 16:11:46.503647483+00:00
modified: 2024-10-22 15:52:03.356902828+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:

  ![](static/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:

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Event_loop.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeEvent_loop.md
title: The Event Loop
checksum: 47166c1bacb54b516c7e3d83a0f1c66223f0273e2c926607094993811ad0ee57
created: 2024-10-22 16:11:46.515132807+00:00
modified: 2024-10-22 15:52:03.356902828+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](static/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](static/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).

  ![](static/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](static/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](static/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).

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Example_scenario_internet_data_transfer.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeExample_scenario_internet_data_transfer.md
title: Example scenario of data transfer accross the internet
lead: ![Path of a network request accross the internet](static/network-request-layers.png)
checksum: 475ff443ca78ca39fcde25ff473cda8e09123251f9628e26cb9a7be5a4fcaed3
created: 2024-10-22 16:11:46.568456202+00:00
modified: 2024-10-22 15:52:03.346902638+00:00
metadata: {"created":"Friday, September 06, 2024","tags":["networks","internet"]}
body:
  ![Path of a network request accross the internet](static/network-request-layers.png)

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

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

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

  # Example scenario of data transfer accross the internet

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

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Filesystems.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeFilesystems.md
title: Filesystems
checksum: d758b348d188addc9ec310d93a12064a00782d012b377495df935458c9a45187
created: 2024-10-22 16:11:47.047438613+00:00
modified: 2024-10-22 15:52:03.356902828+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`.

  ![](static/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`.

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Flip_flops.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeFlip_flops.md
title: Flip-Flops
checksum: 1e2fe545b182468bc78453f1b094ed8b7b07bc3e677f462d380d5ed0c4623165
created: 2024-10-22 16:11:47.111083112+00:00
modified: 2024-10-22 15:52:03.353569431+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:

  ![](static/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    |

  ![](static/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:

  ![](static/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    |

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Formal_proofs_in_propositional_logic.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeFormal_proofs_in_propositional_logic.md
title: Formal proofs in propositional logic
checksum: 2195112247fb535d7ff5cd2a1de489fdf8be9d59fc4d80a91a12ac5dd1d0deda
created: 2024-10-22 16:11:47.189093658+00:00
modified: 2024-10-22 15:52:03.356902828+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_:

  ![](static/proofs-drawio-Page-5.drawio.png)

  _Applied example_:

  ![](static/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_:

  ![](static/proofs-drawio-Page-5.drawio.png)

  _Applied example_:

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Formatting_output_text_in_Bash.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeFormatting_output_text_in_Bash.md
title: Formatting output text in Bash
checksum: 7c23f69186ecad1c35b34dd3692c23cfbb5a1ad5c409939cd71d39764283aeb8
created: 2024-10-22 16:11:47.415338296+00:00
modified: 2024-10-22 15:52:03.336902448+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

  ![](static/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

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Git_bisect.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeGit_bisect.md
title: Bisect
lead:
  Bisect can be used to identify commits that introduce a bug or regression in our
  code.

checksum: 89f9edff37a970ad5929de39f8386c813b77cd041864d4dc43db759ae0c7146c
created: 2024-10-22 16:11:47.618598901+00:00
modified: 2024-10-22 15:52:03.340235844+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.

  ![](static/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.

  ![](static/git-bisect.svg)

  ## Procedure

  ```
  git bisect start

  git bisect good <SHA, tag, branch>

  git bisect bad <SHA, tag, branch>
  ```


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Git_rebasing.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeGit_rebasing.md
title: Rebasing
checksum: 5a4c8546e0ab7fa3fd63366e98d77f7dbcfc1603ccf722200b6904b3e7959ca2
created: 2024-10-22 16:11:47.62683988+00:00
modified: 2024-10-22 15:52:03.340235844+00:00
metadata: {"tags":["git"]}
lead:
  Rebasing is a way to integrate changes from one branch into another. In this
  regarding it is like merging a branch B into another branch A. However rebasing
  differs from normal merging in the way in which it modifies the Git history.

body:
  Rebasing is a way to integrate changes from one branch into another. In this
  regarding it is like merging a branch B into another branch A. However rebasing
  differs from normal merging in the way in which it modifies the Git history.

  In a normal merge of branch B into branch A, Git creates a new commit that
  combines the changes of branches B and A. This is known as the merge commit and
  is evident from the following automatic commit message that is generated:

  ```
  Merge branch B of github.com:thomasabishop/remote-repository into A
  ```

  ![](static/normal-merge-again.svg)

  In this scenario the merge commit has two or more parent commits each
  representing the history of the merged branches. The resulting history of A will
  include the commits of B. Basically the two histories are combined.

  This would give us a history that looks like the following, with different
  colours for the separate SHAs of each merged branch:

  ![](static/combined-merge-hist.svg)

  If we were to create a rebase branch of A from B, there would be a new singular
  history without distinguishing multiple parents that combines the commits of A
  and B. The rebased branch's commits are recreated with new commit IDs and new
  timestamps, based on the current state of the branch that you are rebasing onto.
  This is obviously potentially destructive because it does not preserve the
  respective branch's separate history. It rewrites history as a continuous stream
  of commits in a single branch.

  When a rebase is applied, it will put the diverging B commits at the tip of A
  like so:

  ![](static/rebase-tip-chage.svg)

  And then rebrand the previous A commits to be continuous with B presenting a
  flat and linear Git history like the following:

  ![](static/single-git-history-rebase.svg)

  ## Benefits, use-cases

  > The purpose of rebasing is to ensure that the commit history of a project is
  > as linear and simple as possible, by incorporating changes from one branch
  > into another, as if the changes had been made on the other branch all along.

  A common use-case is with feature branches as it makes the features fit more
  seamlessly with the `main` or `develop` branch.

  It is also a technique that can be used to integrate recent commits without
  merging.

  When to use one over the other:

  - **Merge** to allow commits to stand out or to be clearly grouped
  - **Merge** to bring large feature branches back into main
  - **Rebase** to add minor commits in a main branch into a feature branch
  - **Rebase** when you need to move commits from one branch to another

  So in terms of my personal workflow, I should be using

  - Merge when I want to bring a feature into develop
  - Merge when I want to bring develop into main for a release
  - Merge any time the feature branch is already public and being used by others.
  - Rebase to capture small changes in the parent branch
  - Rebase when I have branched from a branch because a refactor or something has
    become more complex than originally anticipated.

  ## Syntax

  For example if we are working on a feature branch off of `main`, we would run
  the following from this feature branch:

  ```
  git rebase main
  ```

  This will move the feature branch to the tip of `main`.

  If this completes successfully, we would then go on to merge the feature with
  main.

  If conflicts occur, the rebase will halt. You fix the conflicts and then run:

  ```
  git rebase --continue
  ```

  Or if you don't want to resolve the conflict (not advised):

  ```
  git rebase --skip
  ```

  Or stop the whole process:

  ```
  git rebase --abort
  ```

  ### Isolate the point at which one branch diverges from another

  This can be useful to check before you create a rebase:

  ```
  git merge-base main feature/some-feature

  c33acc84f06fcb94e0e87d9adb240c038da6d71c
  ```

  ## Golden rule of rebasing

  When we rebase we remove the additional merge commit but we are changing
  history. The commits are still there but the SHAs are changed. For this reason
  **you should not rebase a public branch**, otherwise you will mess up your
  collaborators history. You should do your rebasing locally and then make a pull
  request.

  ## Difference from cherry-picking

  The main difference between the two approaches is that
  [cherry-picking](Cherry_picking_a_branch.md) is a more selective
  process, where you can pick and choose specific commits that you want to include
  in another branch. This can be useful when you only want to apply specific
  changes or fixes from one branch to another, without including all the changes
  made in the original branch.

  On the other hand, rebasing is a more comprehensive process that can include all
  the changes from one branch to another, but it modifies the commit history,
  making it more linear and easier to understand. Unlike rebasing, cherry-picking
  does not modify the commit history of either branch.

  ## Squashing commits

  This is a handy feature of rebasing. It allows you to combine multiple commits
  into one. This is useful when you have been doing a lot of trial and error to
  resolve a bug and you have lots of small commits that are not that important in
  themselves.

  We use the interactive rebase tool for this.

  Checkout the branch where the commit you want to squash is located.

  Then run:

  ```
  git rebase -i HEAD~n
  ```

  Where `n` is the number of commits you want to squash starting from the most
  recent. This will open an interactive rebase window, listing the commits. You
  can then use the keywords to decide what you want to do with them. In our case
  this will be `s` for squash.

  ![](static/git-interactive-rebase.png)

  ### Example

  I made a small fix commit because something went wrong during build. I do not
  want this commit to be in the history because it is so minor. Instead I want to
  incorporate these changes into the last public commit.

  Trivial commit is `xy` Previous commit is `aa`

  ```
  git rebase -i HEAD~2
  ```

  This displays:

  ```
  pick xy
  pick aa
  ```

  Change this to:

  ```
  pick xy
  squash aa
  ```

  Now `xy == aa`

  ### Another example

  Here we will rebase the interim commits into the last feature commit

  ```sh
  git rebase -i HEAD~10
  ```

  ```
  pick a691a7667 feature (shared-nav) add active app check
  pick 74a07fdb1 interim: add logging
  pick 9af4e2652 interim: more logging
  pick 51b36f667 interim: use href not origin
  pick 33f161198 interim : try catch handler
  pick 41c144ddf interim: remove didLoadCheck
  pick ebd1c9f24 interim: fix flash star
  pick 77af452e7 interim: rm redundant active app check
  pick 79a9b737a iterim: test ternary
  pick a90d23223 interim: further tidying
  ```

  ```
  pick a691a7667 feature (shared-nav) add active app check
  squash 74a07fdb1 interim: add logging
  squash 9af4e2652 interim: more logging
  squash 51b36f667 interim: use href not origin
  squash 33f161198 interim : try catch handler
  squash 41c144ddf interim: remove didLoadCheck
  squash ebd1c9f24 interim: fix flash star
  squash 77af452e7 interim: rm redundant active app check
  squash 79a9b737a iterim: test ternary
  squash a90d23223 interim: further tidying
  ```

  ## `git pull rebase`

  We can use this command to combine a rebase with a pull. This way we silently
  update our local version of a branch with the remote, without adding a merge
  commit.

  ```
  git pull --rebase
  ```

  Add `--rebase=preserve` to keep merges that have been done _locally_. It is
  generally a good idea to do this.

raw_content:
  ---
  tags:
    - git
  ---

  # Rebasing

  Rebasing is a way to integrate changes from one branch into another. In this
  regarding it is like merging a branch B into another branch A. However rebasing
  differs from normal merging in the way in which it modifies the Git history.

  In a normal merge of branch B into branch A, Git creates a new commit that
  combines the changes of branches B and A. This is known as the merge commit and
  is evident from the following automatic commit message that is generated:

  ```
  Merge branch B of github.com:thomasabishop/remote-repository into A
  ```

  ![](static/normal-merge-again.svg)

  In this scenario the merge commit has two or more parent commits each
  representing the history of the merged branches. The resulting history of A will
  include the commits of B. Basically the two histories are combined.

  This would give us a history that looks like the following, with different
  colours for the separate SHAs of each merged branch:

  ![](static/combined-merge-hist.svg)

  If we were to create a rebase branch of A from B, there would be a new singular
  history without distinguishing multiple parents that combines the commits of A
  and B. The rebased branch's commits are recreated with new commit IDs and new
  timestamps, based on the current state of the branch that you are rebasing onto.
  This is obviously potentially destructive because it does not preserve the
  respective branch's separate history. It rewrites history as a continuous stream
  of commits in a single branch.

  When a rebase is applied, it will put the diverging B commits at the tip of A
  like so:

  ![](static/rebase-tip-chage.svg)

  And then rebrand the previous A commits to be continuous with B presenting a
  flat and linear Git history like the following:

  ![](static/single-git-history-rebase.svg)

  ## Benefits, use-cases

  > The purpose of rebasing is to ensure that the commit history of a project is
  > as linear and simple as possible, by incorporating changes from one branch
  > into another, as if the changes had been made on the other branch all along.

  A common use-case is with feature branches as it makes the features fit more
  seamlessly with the `main` or `develop` branch.

  It is also a technique that can be used to integrate recent commits without
  merging.

  When to use one over the other:

  - **Merge** to allow commits to stand out or to be clearly grouped
  - **Merge** to bring large feature branches back into main
  - **Rebase** to add minor commits in a main branch into a feature branch
  - **Rebase** when you need to move commits from one branch to another

  So in terms of my personal workflow, I should be using

  - Merge when I want to bring a feature into develop
  - Merge when I want to bring develop into main for a release
  - Merge any time the feature branch is already public and being used by others.
  - Rebase to capture small changes in the parent branch
  - Rebase when I have branched from a branch because a refactor or something has
    become more complex than originally anticipated.

  ## Syntax

  For example if we are working on a feature branch off of `main`, we would run
  the following from this feature branch:

  ```
  git rebase main
  ```

  This will move the feature branch to the tip of `main`.

  If this completes successfully, we would then go on to merge the feature with
  main.

  If conflicts occur, the rebase will halt. You fix the conflicts and then run:

  ```
  git rebase --continue
  ```

  Or if you don't want to resolve the conflict (not advised):

  ```
  git rebase --skip
  ```

  Or stop the whole process:

  ```
  git rebase --abort
  ```

  ### Isolate the point at which one branch diverges from another

  This can be useful to check before you create a rebase:

  ```
  git merge-base main feature/some-feature

  c33acc84f06fcb94e0e87d9adb240c038da6d71c
  ```

  ## Golden rule of rebasing

  When we rebase we remove the additional merge commit but we are changing
  history. The commits are still there but the SHAs are changed. For this reason
  **you should not rebase a public branch**, otherwise you will mess up your
  collaborators history. You should do your rebasing locally and then make a pull
  request.

  ## Difference from cherry-picking

  The main difference between the two approaches is that
  [cherry-picking](Cherry_picking_a_branch.md) is a more selective
  process, where you can pick and choose specific commits that you want to include
  in another branch. This can be useful when you only want to apply specific
  changes or fixes from one branch to another, without including all the changes
  made in the original branch.

  On the other hand, rebasing is a more comprehensive process that can include all
  the changes from one branch to another, but it modifies the commit history,
  making it more linear and easier to understand. Unlike rebasing, cherry-picking
  does not modify the commit history of either branch.

  ## Squashing commits

  This is a handy feature of rebasing. It allows you to combine multiple commits
  into one. This is useful when you have been doing a lot of trial and error to
  resolve a bug and you have lots of small commits that are not that important in
  themselves.

  We use the interactive rebase tool for this.

  Checkout the branch where the commit you want to squash is located.

  Then run:

  ```
  git rebase -i HEAD~n
  ```

  Where `n` is the number of commits you want to squash starting from the most
  recent. This will open an interactive rebase window, listing the commits. You
  can then use the keywords to decide what you want to do with them. In our case
  this will be `s` for squash.

  ![](static/git-interactive-rebase.png)

  ### Example

  I made a small fix commit because something went wrong during build. I do not
  want this commit to be in the history because it is so minor. Instead I want to
  incorporate these changes into the last public commit.

  Trivial commit is `xy` Previous commit is `aa`

  ```
  git rebase -i HEAD~2
  ```

  This displays:

  ```
  pick xy
  pick aa
  ```

  Change this to:

  ```
  pick xy
  squash aa
  ```

  Now `xy == aa`

  ### Another example

  Here we will rebase the interim commits into the last feature commit

  ```sh
  git rebase -i HEAD~10
  ```

  ```
  pick a691a7667 feature (shared-nav) add active app check
  pick 74a07fdb1 interim: add logging
  pick 9af4e2652 interim: more logging
  pick 51b36f667 interim: use href not origin
  pick 33f161198 interim : try catch handler
  pick 41c144ddf interim: remove didLoadCheck
  pick ebd1c9f24 interim: fix flash star
  pick 77af452e7 interim: rm redundant active app check
  pick 79a9b737a iterim: test ternary
  pick a90d23223 interim: further tidying
  ```

  ```
  pick a691a7667 feature (shared-nav) add active app check
  squash 74a07fdb1 interim: add logging
  squash 9af4e2652 interim: more logging
  squash 51b36f667 interim: use href not origin
  squash 33f161198 interim : try catch handler
  squash 41c144ddf interim: remove didLoadCheck
  squash ebd1c9f24 interim: fix flash star
  squash 77af452e7 interim: rm redundant active app check
  squash 79a9b737a iterim: test ternary
  squash a90d23223 interim: further tidying
  ```

  ## `git pull rebase`

  We can use this command to combine a rebase with a pull. This way we silently
  update our local version of a branch with the remote, without adding a merge
  commit.

  ```
  git pull --rebase
  ```

  Add `--rebase=preserve` to keep merges that have been done _locally_. It is
  generally a good idea to do this.


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Half_adder_and_full_adder.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeHalf_adder_and_full_adder.md
title: The half adder and full adder
lead: ## Binary arithmetic
checksum: 13d8f7ae64ae0a72f954e9a0ac2ba39ae638d56f6cceee46274447d28557e6eb
created: 2024-10-22 16:11:47.705595912+00:00
modified: 2024-10-22 15:52:03.343569242+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$.

  ![](static/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:

  ![](static/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) |

  ![](static/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$.

  ![](static/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:

  ![](static/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) |

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Hardware_Description_Language.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeHardware_Description_Language.md
title: Hardware Description Language
checksum: a968ccbe68dba4d47428dbe4041d6ada5e5f25f01d960761a2e0db6f97719d7a
created: 2024-10-22 16:11:47.85373974+00:00
modified: 2024-10-22 15:52:03.343569242+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:

  ![](static/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:

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Hardware_abstraction_and_modularity.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeHardware_abstraction_and_modularity.md
title: Hardware abstraction and modularity
checksum: 72f02d79df7fc7c9de35d4a5c0b5b94e54b4e0362b5493683c3d9634e407e3d7
created: 2024-10-22 16:11:47.947169654+00:00
modified: 2024-10-22 15:52:03.340235844+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:

  ![](static/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:

  ![](static/hardware-abstraction-hierarchy.png)


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Hardware_simulation.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeHardware_simulation.md
title: Hardware simulation
checksum: 6e30ca00ff63ffd7a83f9e4d45f178fb9ac324810e68b67965d7df926f07df3c
created: 2024-10-22 16:11:47.955489877+00:00
modified: 2024-10-22 15:52:03.356902828+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:

  ![](static/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.

  ![](static/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:

  ![](static/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.

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Hexadecimal_number_system.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeHexadecimal_number_system.md
title: Hexadecimal number system
checksum: 94de85fd8695c0b54756ea2a7ac4a5287256eecc56a81fd24cfd7fe573af82af
created: 2024-10-22 16:11:48.020905457+00:00
modified: 2024-10-22 15:52:03.356902828+00:00
metadata: {"tags":["number-systems","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
  $$

  ![](static/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
    - 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
  $$

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/IP_addresses.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeIP_addresses.md
title: IP addresses
lead: ## IPv4 vs IPv6
checksum: 9358d6ba3acd9009c0a5fef77f817796d815124777616a49d9e4d02882b29d0b
created: 2024-10-22 16:11:48.079278042+00:00
modified: 2024-10-22 15:52:03.353569431+00:00
metadata: {"created":"Friday, August 16, 2024","tags":["networks","internet"]}
body:
  ## IPv4 vs IPv6

  IP addresses come in two formats and bit-lengths depending on which version of
  the [Internet Protocol](Internet_Layer_of_Internet_Protocol.md) is being used.

  - IPv4 addresses are 32-bits in length
  - IPv6 addresses are 128-bits in length

  The larger the bit-length, the more unique addresses that can be generated. Thus
  IPv6 can create more addresses that IPv4.

  This entry focuses on IPv4 addresses.

  ## Anatomy of an IPv4 address

  Each of the 32 bits are divided into four groups which are each 8 bits in
  length. Each group therefore comprises an octet and is separated by dots.

  The decimal number system, rather than binary is used to represent the values.
  This means each binary octet is three decimal digits in length.

  The 32 bits comprise two major groupings

  - the **network prefix**
  - the **host**

  ![Different sections of IP address](static/ip-address-anatomy.png)

  Devices connected to the same network share the same network prefix. They are
  said to be on the same **subnet**. However each device will have a unique value
  for the host octet.

  (This account is somewhat idealised. The length of the prefix does not always
  align with the octet boundary. For instance, the prefix could be 25 bits in
  length, rather than 24. In this case it would "steal" one bit from the host
  section making it 23 bits in length.)

  Any changes to the length of the network prefix change the number of hosts
  available on the subnet. If the network prefix is longer, the number of unique
  hosts is reduced. If the network prefix is shorted, the number of unique hosts
  is increased.

  ## Identifying the network and host groupings

  Given that the network prefix and host identifier do not always stick to set
  boundaries, we require a way of distinguishing the two values. There are two
  dominant methods.

  ### CIDR notation

  This stands for _Classless Inter-Domain Routing_. An example:

  > 192.168.1.23/24

  The value after the forward slash is the number of bits that specify the network
  address. In the example, it is the standard 24-bits. Thus 192.168.1.23 stands
  for the network.

  ### Subnet masking

  The subnet mask is another number _in addition to_ the network address. It is a
  kind of superimposed map on top of the address.

  Here is an example of a subnet mask:

  > 11111111.11111111.11111111.00000000

  The denary form would be as follows (as 255 is the maximal decimal number that
  can be represented with a single 8-bit number and 0 is the smallest):

  > 255.255.255.0

  In the binary form, the 1 values represent the bits which designate the network
  address and the 0 values represent the bits that designate the host.

  In the example above this corresponds to the idealised 32-bit:8-bit ratio of
  CIDR /24.

  There is a clever consequence of the subnet mask: if you apply a bitwise AND
  operator against the IP address and mask (both in their binary form) you can
  determine whether two addresses are on the same network.

  To compare the IP address 192.168.1.23 against 192.168.1.100 to demonstrate:

  ```
  192.168.1.23:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ___________________________________

  192.168.1.100:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ```

  After applying the bitwise AND logic we see that the result is identical for
  both IPs indicating they are on the same network (share the same network
  prefix), whilst the host value is "masked".

  Here is a scenario where the subnet mask indicates that two IPs are not on the
  shared network:

  ```
  192.168.1.23:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ___________________________________

  192.168.2.1
  IP:     11000000.10101000.00000010.00000001
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000010.00000000

  ```

  255.255.255.0 is not the only possible subnet mask. There are masks
  corresponding to ratios other than CIDR /24. For example:

  - 255.0.0.0 (/8)
  - 255.255.0.0 (/8)
  - 255.255.255.192 (/26)
  - 255.255.255.240 (/28)

  As noted previously, the different ratios will obviously affect the number of
  unique hosts available on the subnet.

  ## Determining the range of hosts: practical example

  Let's say we have the following IP address expressed in CIDR: 192.168.0.133/27.
  In binary this would be:

  ```
  110000000.10101000.00000000.100--00101
  ```

  The `--` indicates the demarcation point between the network prefix and the bits
  designated for the host. 32 - 27 leaves us 5 bits for our range of hosts. This
  gives us 2^5 = 32 unique host values.

  In fact it will actually be 30 values since the first value (`00000`) will be
  used to identify the network itself and the last value (`11111`) will be the
  broadcast address - the address used when a message needs to be sent to all
  hosts on the network.

  An actual host address in this range is included in the example: `00101`.

raw_content:
  ---
  tags: [networks, internet]
  created: Friday, August 16, 2024
  ---

  # IP addresses

  ## IPv4 vs IPv6

  IP addresses come in two formats and bit-lengths depending on which version of
  the [Internet Protocol](Internet_Layer_of_Internet_Protocol.md) is being used.

  - IPv4 addresses are 32-bits in length
  - IPv6 addresses are 128-bits in length

  The larger the bit-length, the more unique addresses that can be generated. Thus
  IPv6 can create more addresses that IPv4.

  This entry focuses on IPv4 addresses.

  ## Anatomy of an IPv4 address

  Each of the 32 bits are divided into four groups which are each 8 bits in
  length. Each group therefore comprises an octet and is separated by dots.

  The decimal number system, rather than binary is used to represent the values.
  This means each binary octet is three decimal digits in length.

  The 32 bits comprise two major groupings

  - the **network prefix**
  - the **host**

  ![Different sections of IP address](static/ip-address-anatomy.png)

  Devices connected to the same network share the same network prefix. They are
  said to be on the same **subnet**. However each device will have a unique value
  for the host octet.

  (This account is somewhat idealised. The length of the prefix does not always
  align with the octet boundary. For instance, the prefix could be 25 bits in
  length, rather than 24. In this case it would "steal" one bit from the host
  section making it 23 bits in length.)

  Any changes to the length of the network prefix change the number of hosts
  available on the subnet. If the network prefix is longer, the number of unique
  hosts is reduced. If the network prefix is shorted, the number of unique hosts
  is increased.

  ## Identifying the network and host groupings

  Given that the network prefix and host identifier do not always stick to set
  boundaries, we require a way of distinguishing the two values. There are two
  dominant methods.

  ### CIDR notation

  This stands for _Classless Inter-Domain Routing_. An example:

  > 192.168.1.23/24

  The value after the forward slash is the number of bits that specify the network
  address. In the example, it is the standard 24-bits. Thus 192.168.1.23 stands
  for the network.

  ### Subnet masking

  The subnet mask is another number _in addition to_ the network address. It is a
  kind of superimposed map on top of the address.

  Here is an example of a subnet mask:

  > 11111111.11111111.11111111.00000000

  The denary form would be as follows (as 255 is the maximal decimal number that
  can be represented with a single 8-bit number and 0 is the smallest):

  > 255.255.255.0

  In the binary form, the 1 values represent the bits which designate the network
  address and the 0 values represent the bits that designate the host.

  In the example above this corresponds to the idealised 32-bit:8-bit ratio of
  CIDR /24.

  There is a clever consequence of the subnet mask: if you apply a bitwise AND
  operator against the IP address and mask (both in their binary form) you can
  determine whether two addresses are on the same network.

  To compare the IP address 192.168.1.23 against 192.168.1.100 to demonstrate:

  ```
  192.168.1.23:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ___________________________________

  192.168.1.100:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ```

  After applying the bitwise AND logic we see that the result is identical for
  both IPs indicating they are on the same network (share the same network
  prefix), whilst the host value is "masked".

  Here is a scenario where the subnet mask indicates that two IPs are not on the
  shared network:

  ```
  192.168.1.23:
  IP:     11000000.10101000.00000001.00010111
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000001.00000000
  ___________________________________

  192.168.2.1
  IP:     11000000.10101000.00000010.00000001
  Mask:   11111111.11111111.11111111.00000000
  Result: 11000000.10101000.00000010.00000000

  ```

  255.255.255.0 is not the only possible subnet mask. There are masks
  corresponding to ratios other than CIDR /24. For example:

  - 255.0.0.0 (/8)
  - 255.255.0.0 (/8)
  - 255.255.255.192 (/26)
  - 255.255.255.240 (/28)

  As noted previously, the different ratios will obviously affect the number of
  unique hosts available on the subnet.

  ## Determining the range of hosts: practical example

  Let's say we have the following IP address expressed in CIDR: 192.168.0.133/27.
  In binary this would be:

  ```
  110000000.10101000.00000000.100--00101
  ```

  The `--` indicates the demarcation point between the network prefix and the bits
  designated for the host. 32 - 27 leaves us 5 bits for our range of hosts. This
  gives us 2^5 = 32 unique host values.

  In fact it will actually be 30 values since the first value (`00000`) will be
  used to identify the network itself and the last value (`11111`) will be the
  broadcast address - the address used when a message needs to be sent to all
  hosts on the network.

  An actual host address in this range is included in the example: `00101`.


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Integrated_circuits.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeIntegrated_circuits.md
title: Integrated circuits
checksum: feed3e04a6ab218035c0ff18af275382f5d44fc2d3a0f628bd4dd5c8f0158b32
created: 2024-10-22 16:11:48.255716605+00:00
modified: 2024-10-22 15:52:03.356902828+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:_

  ![](static/integrated-circuit.jpeg)

  ![](static/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:_

  ![](static/integrated-circuit.jpeg)

  ![](static/breadboard-DIP.jpg)


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Interactive_staging.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeInteractive_staging.md
title: Interactive staging
checksum: 0cbe46991d07bd3035c3f59ab1f9dfc5c92027c65dd64766ec4a42d8484bc2d9
created: 2024-10-22 16:11:48.296490248+00:00
modified: 2024-10-22 15:52:03.340235844+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:

  ![](static/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**:

  ![](static/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:

  ![](static/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:

  ![](static/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**:

  ![](static/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:

  ![](static/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: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Internet_Layer_of_Internet_Protocol.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeInternet_Layer_of_Internet_Protocol.md
title: Internet Layer of the Internet Protocol Suite
checksum: ca40842bab0f318379c40effeb846507a8b38fa6500bc0b44ef3d2a0edd936a0
created: 2024-10-22 16:11:48.304851754+00:00
modified: 2024-10-22 15:52:03.353569431+00:00
metadata: {"created":"Thursday, August 15, 2024","tags":["internet","networks"]}
lead:
  The Internet Layer is concerned with establishing the basis for the connection
  and communication between hosts accross the internet.

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

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

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

  ## Packets

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

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

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

  // Need to find out more about packets work.

raw_content:
  ---
  tags:
    - internet
    - networks
  created: Thursday, August 15, 2024
  ---

  # Internet Layer of the Internet Protocol Suite

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

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

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

  ## Packets

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

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

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

  // Need to find out more about packets work.


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Internet_fundamentals.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeInternet_fundamentals.md
title: Internet fundamentals
checksum: 79388f349654a61644c023f81ef1d750025ddd45be630a2a8d63275e5eac4176
created: 2024-10-22 16:11:48.354343436+00:00
modified: 2024-10-22 15:52:03.336902448+00:00
metadata: {"created":"Saturday, August 03, 2024","tags":["networks"]}
lead:
  The internet is a _[network](Network_fundamentals.md) of networks_. A globally
  connected set of computer networks that all use a suite of common
  [protocols](Network_protocols.md).

body:
  The internet is a _[network](Network_fundamentals.md) of networks_. A globally
  connected set of computer networks that all use a suite of common
  [protocols](Network_protocols.md).

  The protocols governing the communication between networks on the internet are
  specified in the **Internet Protocol Suite**.

  This combines two protocols:

  - **TCP**
    - The Transmission Control Protocol
  - **IP**
    - The Internet Protocol

  Hence the Internet Protocol Suite is also known as "TCP/IP".

  The TCP/IP is a network stack comprising four layers, each with its own
  protocols. From the top:

  ![TCP/IP stack diagram](static/TCP_IP.png)

  - The [Link Layer](Link_Layer_of_Internet_Protocol.md) concerns communication
    between devices on the same local network. The transfer of information once it
    has been received from the internet or its transfer between devices before it
    sent out into the internet. Ethernet and WiFi are two core protocols utilised
    at this layer.

  - The [Internet Layer](Internet_Layer_of_Internet_Protocol.md) is concerned with
    communication accross networks. It is responsible for identifying hosts on the
    inter-network by their addresses and routing data from network to network.

  - The [Transport Layer](Transport_Layer_of_Internet_Protocol.md) creates a
    commmunication channel between two hosts using ports and sockets.

  - The [Application Layer](Application_Layer_of_Internet_Protocol.md) concerned
    with software that utilises information received and sent over the internet,
    for instance web pages and emails. This is the highest level of abstraction
    for which all the lower levels serve as "plumbing". Examples of protocols
    operative at the Application Layer include HTTP and SSH.

  See:
  [Example_scenario_internet_data_transfer](Example_scenario_internet_data_transfer.md)

raw_content:
  ---
  tags: [networks]
  created: Saturday, August 03, 2024
  ---

  # Internet fundamentals

  The internet is a _[network](Network_fundamentals.md) of networks_. A globally
  connected set of computer networks that all use a suite of common
  [protocols](Network_protocols.md).

  The protocols governing the communication between networks on the internet are
  specified in the **Internet Protocol Suite**.

  This combines two protocols:

  - **TCP**
    - The Transmission Control Protocol
  - **IP**
    - The Internet Protocol

  Hence the Internet Protocol Suite is also known as "TCP/IP".

  The TCP/IP is a network stack comprising four layers, each with its own
  protocols. From the top:

  ![TCP/IP stack diagram](static/TCP_IP.png)

  - The [Link Layer](Link_Layer_of_Internet_Protocol.md) concerns communication
    between devices on the same local network. The transfer of information once it
    has been received from the internet or its transfer between devices before it
    sent out into the internet. Ethernet and WiFi are two core protocols utilised
    at this layer.

  - The [Internet Layer](Internet_Layer_of_Internet_Protocol.md) is concerned with
    communication accross networks. It is responsible for identifying hosts on the
    inter-network by their addresses and routing data from network to network.

  - The [Transport Layer](Transport_Layer_of_Internet_Protocol.md) creates a
    commmunication channel between two hosts using ports and sockets.

  - The [Application Layer](Application_Layer_of_Internet_Protocol.md) concerned
    with software that utilises information received and sent over the internet,
    for instance web pages and emails. This is the highest level of abstraction
    for which all the lower levels serve as "plumbing". Examples of protocols
    operative at the Application Layer include HTTP and SSH.

  See:
  [Example_scenario_internet_data_transfer](Example_scenario_internet_data_transfer.md)


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Joins_in_SQL.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeJoins_in_SQL.md
title: Joins in SQL
checksum: 85d8c8784eeb88c0e98bb5addae500854e64e560e73890ffffb3fbd0afda16c3
created: 2024-10-22 16:11:48.742547865+00:00
modified: 2024-10-22 15:52:03.350236035+00:00
metadata: {"tags":["SQL","databases"]}
lead:
  <!-- Once a relationship has been created using primary and foreign keys (as detailed in the previous section), you are able to combine and integrate data from the different tables. This is known as performing **joins.** -->

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

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

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

  ## Inner joins

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

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

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

  We will be working from the following tables.

  _The `sales` table_:

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

  _The `model` table_:

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

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

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

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

  ### Implementation

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

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

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

  Applied to our example:

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

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

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

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

  ## Outer joins

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

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

  _The `sales` table_:

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

  _The `model` table_:

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

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

  ### Left outer join

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

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

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

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

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

  #### Implementation

  The schematic form:

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

  Applied to our data set:

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

  ### Right outer join

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

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

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

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

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

  #### Implementation

  The schematic form:

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

  Applied to our data set:

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

  ### Full outer join

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

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

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

  Represented by the following diagram:

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

  #### Implementation

  The schematic form:

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

  Applied to our data set:

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

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

  In SQL this would be achieved with:

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

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

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

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

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

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

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

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

  # Joins in SQL

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

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

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

  ## Inner joins

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

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

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

  We will be working from the following tables.

  _The `sales` table_:

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

  _The `model` table_:

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

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

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

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

  ### Implementation

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

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

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

  Applied to our example:

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

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

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

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

  ## Outer joins

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

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

  _The `sales` table_:

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

  _The `model` table_:

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

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

  ### Left outer join

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

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

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

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

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

  #### Implementation

  The schematic form:

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

  Applied to our data set:

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

  ### Right outer join

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

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

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

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

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

  #### Implementation

  The schematic form:

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

  Applied to our data set:

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

  ### Full outer join

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

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

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

  Represented by the following diagram:

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

  #### Implementation

  The schematic form:

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

  Applied to our data set:

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

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

  In SQL this would be achieved with:

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

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

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

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

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

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

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


path: neuron/df0a3e21-43c6-4da3-ae9f-a026972034ee/Key_characteristics_of_GraphQL.md
sortable_path: neurondf0a3e21-43c6-4da3-ae9f-a026972034eeKey_characteristics_of_GraphQL.md
title: Key characteristics of GraphQL
checksum: 1015bdf47cc2532d0fe3f9a9bbea28d8d04224d76c182fb328b5e4c5d45e7474
created: 2024-10-22 16:11:48.774371082+00:00
modified: 2024-10-22 15:52:03.343569242+00:00
metadata: {"tags":["graphql","APIs"]}
lead:
  > In a nutshell, GraphQL is all about optimizing data communication between a
  > client and a server. This includes the client asking for needed data and
  > communicating that need to the server, and the server preparing a fulfillment
  > for that need and communicating the fulfillment back to the client. GraphQL
  > allows clients to ask for the exact data they need and makes it easier for
  > servers to aggregate data from multiple data storage resources.

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

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

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

  ## Architecture

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

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

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

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

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

  ## Implementation overview

  ### Queries, mutations, subscriptions

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

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

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

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

  ### Frontend

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

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

  ### Backend: structure and behaviour

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

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

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

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

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

  ### GraphQL endpoint

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

  ## Benefits

  ### Single requests

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

  The REST scenario:

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

  The GraphQL scenario:

  ![](static/graphQL_request_load.png)

  ### Abstraction of multiple services

  ![](static/graphql_multiple_resources.png)

  ### Stops overfetching

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

  ### Single endpoint

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

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

  # Key characteristics of GraphQL

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

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

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

  ## Architecture

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

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

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

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

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

  ## Implementation overview

  ### Queries, mutations, subscriptions

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

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

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

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

  ### Frontend

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

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

  ### Backend: structure and behaviour

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

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

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

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

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

  ### GraphQL endpoint

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

  ## Benefits

  ### Single requests

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

  The REST scenario:

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

  The GraphQL scenario:

  ![](static/graphQL_request_load.png)

  ### Abstraction of multiple services

  ![](static/graphql_multiple_resources.png)

  ### Stops overfetching

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

  ### Single endpoint

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


