1
0

Add repository files

This commit is contained in:
ami_sc 2023-04-10 00:28:20 -05:00
parent 585e6e560f
commit e47be76167
345 changed files with 30383 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/.obsidian/

View File

@ -0,0 +1,36 @@
> ### 001 - Naming of Plants
> Class Notes - August 31, 2018
> Emilio Soriano Chávez
> ***
> <span style="color:#2ecc71">Biology of Plants</span>
> Fall 2018
- Linnaeus came up with the <u>binomial nomenclature</u> for species and a higher order classification system.
- Domain, Kingdom, Phylum, Class, Order, Species, Subspecies... Variety.
- Progenitor cell that branched of down to every possible species.
- Most species are based on visual comparisons, structures, architectures, and in some cases, chemical compounds.
- Linnaeus began with an “artificial” system of grouping based on a number of stamens and floral characteristics.
- Later “natural” systems based on broader characteristics (leaf types and characteristics) where thought to reflect evolutionary relationships.
- Bacteria - Protista (Unicellular) - Plantae - Fungi - Animalia
- **Basic Domains:**
- **Archaea:** Ancient and relatively rare species that often live in extreme environments. Geysers, some produce methane as a source of energy.
- **Bacteria:** Far more common and live in nearly all environments. Unicellular with no nucleus.
- **Eukarya:** Contain a separate membrane-bound nucleus, that may or may not be multicellular:
- Protista (Algae)
- Fungi
- Plantae
- Animalia
- **Branch Points:** Where one phyla branches with the next is largely based on accumulation of changes over time, and the recognition that many blended organisms and died out.
- Nearby branches can be hard to group because it is impossible to know which one was first.
- **Species:**
- **Morphological:** Species that look similar.
- **Interbreeding:** Whether or not the species can produce kids and their kid can have kids.
- **Ecological:** You look similar but live in a different neighborhood. Species able to interbreed but have different morphology.
- **Cladistic:** Based on DNA sequencing. Using a matching comparison we are in some percentage alike.
- **Electic:** We can mix and match as long as my thing is a different species.
- **Nominalistic:** Whats the point? We cant really tell where one species ends and another begins. (Very true in bacteria)
- The ability to actually show genetic relationships with the idea of genetic clocks is cleaning up the actual relations between the categories.
- **Genetic Clock:** Number of changes divided by time. Average changes per unit of time. Useful for determining how far species are. Cleaning up relationships. $3.2 \times 10^{9}$ (human genome).

View File

@ -0,0 +1,55 @@
> ### 002 - Cells
> Class Notes
> Emilio Soriano Chávez
> ***
> <span style="color:#2ecc71">Biology of Plants</span>
> Fall 2018
- **Cells:** Fundamental unit of life. Every living thing is made of a cell.
- Xylem (water), Phloem (food), Meristem (becomes other things) are tissues.
- **Basic Tissues (from which all others are made):**
- Parenchyma (slim cell wall)
- Collenchyma (thick cell wall)
- Sclerenchyma (thickest cell wall)
- Also there are special cells (Tracheids, guard cells, root hairs... ).
- Nearly all plant cells are surrounded by a cell wall (except eggs pollen, etc.).
- Photosynthesis takes place within the parenchyma of the leaf, in a sub-cellular organelle called the chloroplast.
- Water and soil (xylem-dead cells, just a pipe) substances travels through the dead skeletons of cells.
- Cells provide a discrete barriers and sub-barriers for chemistry and function.
- This matters because it allows for incompatible and specialized chemistry to occur.
- Plants are made of cells.
- **Robert Hooke:**
- First drawing of cells.
- Primitive microscope in mid-1600.
- After looking at cork cells, he introduces the term cell.
- Two basic types of cells:
- Prokaryotes (before nucleus)
- Eukaryotes (after nucleus)
- **Prokaryotes:** Lack visible nucleus and tend to be small in size.
- **Microscopes:** Dissecting and Transmission Light. Machining has improved light microscopes to a magnification of about 1500x.
- Light is the limitation. If the wavelength goes around we are not able to see. Parts have to be larger than the wavelength of light. In microscope we are able to see 500 nm. DNA is 10 nm.
- **Electron Microscope:**
- Uses electron beams.
- Condenser lenses use magnets to shape the electrons.
- The specimen must be dense (coat the specimen with something like a metal).
- Not able to see cellular structures.
- 2 types (transmission and scanning - electrons being reflected, obtaining picture of the surface).
- 100 nm.
- More detail.
- Proteins, organelles.
- Anything that is black and white normally is transmission.
- **Limitations:**
- In light microscopes we are able to see live samples, but at limited resolution.
- Electron microscopes can be used at higher magnification to look at proteins, but structures are dead.
- The microscope has been useful to humans.
- **Staining:** Stain reacts to biomolecules and attach to the cell:
- Some are dyes (100 years ago, man way).
- Some are antibodies. They bind to proteins. Break cell membrane. Cells are dead.
- Some are proteins that fluoresce.
- Stains provide contrast.
- They may or may not require death.
- Histologists and Pathologists (structure and shape of cells)

View File

@ -0,0 +1,84 @@
> ### 003 - Organelles
> Class Notes
> Emilio Soriano Chávez
> ***
> <span style="color:#2ecc71">Biology of Plants</span>
> Fall 2018
- **Specific to Plants:** Cell wall, chloroplasts and central vacuole.
- **Cell Wall:**
- Plants and fungi have them.
- Made of cellulose, hemicellulose and pectin.
- Composed of a middle laminate of pectin, followed by primary and secondary walls (between the 2 walls).
- **Primary Walls:** Cellulose and hemicellulose.
- **Secondary Walls:** Pectin. Addition of Lignin (can be seen in wood, holds together cellulose. Brown color).
- **Plasmodesma / Plasmodesmata:**
- Proteinaeous structures that allow the passage of cytoplasmic constitutions between cells (share resources) through the cell wall.
- **Plasma Membrane:**
- Made of phospholipids.
- Phosphate (charged) and sugar.
- Tail made of lipids, made of carbon chains (neutrally charged).
- Polar side and nonpolar side.
- The outside and inside of the membrane is charged.
- All kinds of proteins in the membrane.
- **Phospholipid Bilayer:**
- Hydrophobic fatty acid and chains associate and hydrophilic phosphate sugar heads associate with the aqueous environment.
- Proteins can be anchored or integral (different whether they penetrate the membrane).
- **Nucleus:**
- Houses DNA.
- Double membrane organelle.
- Anything that contains DNA has a double membrane.
- Regulation of what enters the membrane through nuclear pores (protoplasm).
- 100s of proteins inside.
- The nuclear membrane is continuous and the same as the Endoplasmic Reticulum.
- Proteins can go to the ER (outside the cell or in a membrane) or to the cytoplasm (inside the cell).
- **Dictyosomes (Golgi Apparatus):**
- Flattened sacs of membranes.
- Highly polarized.
- Process proteins from the nuclear side to the extra cellular side (plasma membrane or outside the cell.
- Associated to protein modification (2 types - glycosylation, with sugars and lipidation, with lipids)
- **Plastids:**
- Membrane encased organelles, many of which are plant specific.
- Basic organelle that changes.
- **Chloroplasts:** Capture light energy and make energy.
- **Chromoplasts:** Stores pigments.
- **Leucoplasts:** Amyloplasts and Elaioplasts. Starch and fat storage.
- It is unclear if these are not instances of the same basic structure, only specialized according to the cell type.
- Proplastid receiving signaling from DNA and changing.
- **Mitochondria:**
- Release or conversion of energy from sugars. Glucose to ATP.
- Significant role in biochemical construction of molecules.
- Outside membrane and inner membrane (inside-outside = crista) (inside-inside = matrix)
- **Vacuole:**
- Not empty, mostly water. Has storage molecules.
- Home to anthrocyanins (water soluble pigments with purple color) - Beets.
- Cytoplasm affects the pressure on the vacuole and can change the structure of the plant.
- **Cytoskeleton:**
- System of scaffold (*andamio*) and structure for the cell.
- Provides structure of the cell.
- Composed of microtubules, microfilaments (actin) and intermediate filaments. (Poles)
- Numerous organizing proteins that tie it together, and motor proteins that give both shape and change shape.
- Main way that something moves.
![[A001 - Cell Organelles.png | 500]]
- **Centrifugation:**
- Came out from radioactivity, by the isolation of Uranium. $200,000$ rpm.
- Centripetal force and centrifugal force.
- Centrifugation & Microscopy
- **Differential Centrifugation:**
- The heavier it is, the more mass it has, the faster it moves, the heavier it is, the faster its going to go.
- **Buoyancy Centrifugation:**
- Change density of the medium by changing the medium, commonly sucrose or cesium chloride.
- Forms a gradient of density and objects move towards the same density.
- Put it in the centrifuge, spin it for long time and the components separate in layers according to their density.

View File

@ -0,0 +1,76 @@
> ### 004 - Cell Division Cycle
> Class Notes
> Emilio Soriano Chávez
> ***
> <span style="color:#2ecc71">Biology of Plants</span>
> Fall 2018
- Three parts in order:
- **DNA Synthesis:** Create copy of DNA.
- **Mitosis:** Separating the 2 copies of different parts and have potentially 2 nuclei.
- **Cytokinesis:** The cell divides.
- Parts do not overlap.
- **`G0`:**
- No growth nor gap.
- Most cells exist in this phase.
- Most of cancer drugs will specifically target cells that are dividing (lose hair, problems with their gut, skin dry, fingernails stop growing).
- **`G1`:**
- Gap between mitosis.
- G stands for growth.
- The cell is growing in `G1`.
- The cell knows how big it is.
- Once it is on a certain size, they enter DNA synthesis.
- We have 2 copies of every chromosome.
- **`S`:**
- Divide chromosomes.
- **`G2`:**
- The cell has 2 copies of the chromosomes and about to enter Mitosis.
- **`M`:**
- Separate one copy of the chromosomes and put them in 2 different cells.
- Dividing cells.
- **Interphase"**
- Not dividing, `G1`, `G2`, `S` and Mitosis.
- 24 hours to go through a cell cycle.
- **Length of Time in the Cycle:**
- How long does it take to pass from `G2` to `S`.
- Feed the cells radioactive thymidine ($\text{H}^3 \text{T}$).
- The only cells that take this are the cells that are dividing (`S` phase).
- When they put the thymidine it attaches.
- Wash the thymidine.
- The cells that have radioactivity are the ones that are in `S` phase.
- Plate of cells.
- Look for cells that have chromosomes that can be seen (only in mitosis).
- Count the ones that are in mitosis and the total number of cells and compare to the time it takes for the cell cycle and obtain the time of mitosis.
- *Dye* that stains DNA (fluorescent dye).
- Look for number of cells that have twice the amount of DNA.
- **Mitosis (80 minutes):**
- Separate four chromosomes for two copies in every cell.
- Continuous process. When it starts it keeps going.
- **Interphase / Early Prophase:**
- Condensing the chromosomes.
- They start to become visible.
- Inside the nucleus.
- The nuclear membrane disappears and dissolves.
- **Metaphase:**
- Lining of the chromosome.
- Each chromosome has a centromere and microtubules attached to them.
- Some of them push them others pull them.
- They end in the middle and you end up with a disc of chromosomes.
- **Anaphase:**
- Decondensing.
- **Telophase:**
- The nuclear membrane reforms and you get 2 nuclei.
- We get a cell with 2 nuclei and each one with the DNA copy.
- Mitosis is very controlled.
- **Cytokinesis:**
- Actual division of the cell, cytoplasm, organelles and plasma membrane.
- Contractal ring.
- Not very controlled.
- One cell might get more organelles that the other.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 MiB

View File

@ -0,0 +1,49 @@
> ### 001 - Atoms, Molecules, and Ions
> Class Notes - August 29, 2018
> Emilio Soriano Chávez
> ***
> <span style="color:#2e86c1">General Chemistry I</span>
> Fall 2018
- **Democritus:**
- Greek Era
- **Daltons Atomic Theory (1808):**
- <u>Elements are composed of extremely small particles called atoms</u>.
- <u>All atoms of a given element are identical</u>, having the same size, mass and chemical properties. The atoms of one element are different from the ones of other elements.
- <u>Compounds are composed of atoms of more than one element</u>. In any compound, the ratio of the numbers of atoms of any two of the elements present is either an integer or a simple fraction.
- <u>A chemical reaction</u> involves only the separation, combination or rearrangement of atoms; it <u>doesnt result in their creation or destruction</u>.
- **J.J. Thompson (1890):**
- Started thinking that the atom wasnt the smallest particle.
- **Cathode Ray Tube Experiment:**
- Ray passing through a cathode tube.
- He placed a positively charged metal near the ray and it was attracted, proving the existence of the electron.
- Also with a magnet.
![[A001 - Thompson's Model A.jpeg | 450]]
![[A002 - Thompson's Model B.jpeg | 450]]
- **Millikan (1910):**
- Determined the charge of the electron as $1.60 \times 10^{-19}$.
![[A003 - Millikan's Experiment.jpeg | 350]]
- **Rutherfords Experiment:**
- 1908 Nobel Prize in Chemistry.
- Thought that positive charges were in the middle.
- Idea that the nucleus is positively charged and the electrons are around it.
![[A004 - Rutherford's Experiment.jpeg | 450]]
- **Chadwicks Experiment (1932):**
- 1935 Nobel Prize in Physics.
- Hydrogen Atoms (1 Proton); Helium Atoms (2 Protons)
- Mass Helium / Mass Hydrogen should be 2.
- When measured, it was 4.
| Particle | Location | Charge |
|:--------:|:------------------:|:-----------------------:|
| Proton | Nucleus | $+1.60 \times 10^{-19}$ |
| Neutron | Nucleus | No Charge |
| Electron | Around the Nucleus | $-1.60 \times 10^{-19}$ |

View File

@ -0,0 +1,24 @@
> ### 002 - Atomic Number, Mass Number, and Isotopes
> Class Notes
> Emilio Soriano Chávez
> ***
> <span style="color:#2e86c1">General Chemistry I</span>
> Fall 2018
- **Atomic Number ($Z$):** Number of protons in nucleus.
- **Mass Number ($A$):** $\text{Number of Protons} + \text{Number of Neutrons}$
- **Number of Electrons:** Same as number of protons.
- **Isotopes:** Atoms of the same element with <u>different numbers of neutrons</u> in their nuclei. To separate isotopes, the methods used are:
- **Diffusion:** Manhattan Project - Separation of the Uranium.
- **Centrifuge:** Vortex Tubes - Helikon Vortex Separation Project (South Africa). <u>The ones heavier go away faster that the ones that are lighter</u>.
- **Laser:** CALVIS - Atomic Vapor Laser Isotope Separation.
- **Chemical Method:** ($\text{D}_2 \text{O}$ - Heavy Water) Girdler Sulfide Process - Evaporation to separate.
- **Gravity:** Cryogenic Distillation - Take gases ($\text{O}$, $\text{N}$) and make them fall down, condense them and isotopes that are heavier fall faster.
- **Dimitri Mendeleev (1834 - 1907):** Started organizing the periodic table by the number of protons.
- 2 Elements = Diatomic Molecules
- Homonuclear (Same Atom) = $\text{O}_2 , \: \text{H}_2$
- Heteronuclear (Different Atoms) = $\text{HCl} , \: \text{NaCl} , \: \text{CO}$
- Polyatomic Molecules = $\text{NH}_3 , \: \text{H}_2 \text{O} , \: \text{O}_3$

View File

@ -0,0 +1,10 @@
> ### 003 - Formulas and Models
> Class Notes
> Emilio Soriano Chávez
> ***
> <span style="color:#2e86c1">General Chemistry I</span>
> Fall 2018
- **Lewis Dot Structure:** Number of electrons to share.
![[A005 - Formulas and Models.jpeg | 500]]

View File

@ -0,0 +1,96 @@
> ### 004 - Nomenclature
> Class Notes
> Emilio Soriano Chávez
> ***
> <span style="color:#2e86c1">General Chemistry I</span>
> Fall 2018
- $\text{CO}$ - Carbon Monoxide
- Replace Oxy<u>gen</u> for Ox<u>ide</u>
- Add prefix
- $\text{HF}$ - Hydrogen Fluoride
- $\text{SO}_2$ - Sulfur Dioxide
- $\text{B}_2 \text{H}_6$ - Diborum Hexahydride
- **Acid:** Compound that when dissolved in water forms ions $\text{H}^+$ (Add `-ic` termination + acid)
- $\text{HF}$ - Hydrofluoric Acid
- $\text{HCl}$ - Hydrochloric Acid
- $\text{HCN}$ - Hydrocyanic Acid
- $\text{H}_2 \text{SO}_3$ - Dihydrosulfuric Acid
- **Net Charge:** Difference between number of protons and electrons. Typically, it is zero.
- **Ion:** Atom or group of atoms that has a net charge different to zero.
- **Cation:** Ion with positive charge $+$. $\text{Ca}^{2+}$
- **Anion:** Ion with a negative charge $-$. $\text{Cl}^{-}$
- **Polyatomic Ions:** Group of different atoms that form an ion.
- $\text{SO}_{4}^{3-}$
- $\text{OH}^{-}$
- $\text{CO}_{3}^{2-}$ - Carbonate
- $\text{NH}_{4}^{+}$ - Ammonia
- $\text{NO}_{3}^{-}$ - Nitrate
- $\text{NO}_{2}^{-}$ - Nitrite
- $\text{PO}_{4}^{3-}$ - Phosphate
- $\text{SO}_{3}^{2-}$ - Sulfite
- **Ionic Compounds:** Metal + Nonmetal (Separate in water)
- $\text{HF}$ - Hydrogen Fluoride
- Take the first atom -> Hydrogen
- Fluor -> Add the `-ide`
- $\text{HCl}$ - Hydrogen Chloride
- **Molecular Compounds:** Nonmetal + Nonmetal (Stay together in water and do not separate in two atoms)
- $\text{CO}$ - Carbon Monoxide
- Take first element, write the name, take the second element, remove termination and add `-ide` at the end.
- In the middle, add prefix indicating the number of atoms (Mono, Bi, Tri, Tetra, Penta, Hexa, Hepta, Octa, Nona, Deca).
- **Ions:**
- $\text{O}_2$ - Oxide
- $\text{F}^{-}$ - Fluoride
- $\text{Cl}^{-}$ - Chloride
- $\text{Br}^{-}$ - Bromide
- $\text{S}^{-}$ - Sulfite
- If we have a `mono` in the front its not expressed.
- If we have `ao`, `eo`, just leave last letter `o`.
- $\text{SO}_2$ - Sulfuric Dioxide
- $\text{N}_2 \text{O}_3$ - Dinitrogen Trioxide
- $\text{CF}_4$ - Carbon Tetrafluoride
- $\text{Cl}_2 \text{O}_7$ - Dichlorine Heptoxide
- **Ionic Compounds:** Transition Metal + Nonmetal
- $\text{Fe}^{2+}$
- $\text{Fe}^{3+}$
- $\text{FeCl}_3$ - Iron (III) Chloride
- $\text{VO}_2$ - Vanadium (IV) Oxide
- $\text{Ag}$ always $+$
- $\text{Zn}$ always $2+$
- $\text{ZnCl}_2$ - Zinc Chloride
- **Acids:** With Oxygen and Without Oxygen
- **Without Oxygen:**
- $\text{HF}$ - Hydrofluoric Acid
- Remove `-gen` from first element.
- Add `-ic` Acid to last element.
- **With Oxygen:**
- $\text{H}_2 \text{CO}_3$ - Carbonic Acid
- $\text{HNO}_3$ - Nitric Acid
- $\text{HNO}_2$ - Nitrous Acid
- `-ate` becomes `-ic`.
- `-ite` becomes `-ous`.
- **Exceptions:**
- $\text{H}_3 \text{PO}_4$ - Phosphoric Acid
- $\text{H}_3 \text{PO}_3$ - Phosphorous Acid
- $\text{H}_2 \text{SO}_4$ - Sulfuric Acid
- $\text{H}_2 \text{SO}_3$ - Sulfurous Acid
- **Bases:**
- $\text{NaOH}$ - Sodium Hydroxide
- $\text{KOH}$ - Potassium Hydroxide
- $\text{CaOH}$ - Calcium Hydroxide
- **Hydrates:**
- $\text{CuSO}_4$ - Copper Sulfate Pentahydrate

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -0,0 +1,6 @@
- **Biological Perspective:**
- Seek relationships between the brain, hormones, heredity and evolution.
- Behavior and mental processes.
- Nervous system.
- Role of heredity in behavior and mental processes.
- Evolution, adaptation. How has the brain has changed through time.

View File

@ -0,0 +1,43 @@
***
## Critical Thinking
- **Be Skeptical:** Keep an open mind. <u>Accept nothing as the truth until you have examined the evidence</u>.
- **Insist on Evidence:** An opinion is not sufficient.
- Examine Definition of Terms
- Examine Assumptions of Premises of Arguments
- Be cautious in drawing conclusions from evidence. Consider alternative interpretations on research evidence.
- Do not oversimplify
- Do not overgeneralize
- Apply critical thinking to all areas of life.
***
## Scientific Method
> Organized way of using experience and testing ideas to expand and refine knowledge.
1. **Psychological Theory:** Experiences and Beliefs
2. **Research Questions:** Hypothesis
3. **Examining Research Questions:** Hypothesis Testing
4. **Evidence:** Observations
5. **Drawing Conclusions**
6. **Theory Construction or Modification**
7. **New Research Questions or Hypothesis**
***
## Methods of Observation
- **Case Study:**
- Collect information about individuals and small groups.
- Usually clinical, used to describe a person's psychological problems and how psychology treats them.
- They are subject to inaccuracies (gaps and factual errors in people's memories).
- **Survey:**
- Useful to learn about behavior and mental processes that cannot be observed in the natural setting or studied experimentally.
- Questionnaires and interviews.
- They allow a large amount of data.
- Inaccuracies (on memories or lies).
- **Naturalistic Observation:**
- Observe people in their natural habitats.
- Allows to observe behavior where it happens.
- They use unobtrusive measures to avoid interfering with the behaviors they are observing.

View File

@ -0,0 +1,28 @@
***
# The Nervous System
- Scientists agree that <u>mind is a function of the brain</u>.
- Research has focused on pinpoint exactly what happens in certain parts of the brain where we engage in different activities.
- Clinical Psychology mainly studies the mind.
- From injuries to the brain, we have learned that brain damage can impair consciousness, perception, memory, and abilities to make plans and decisions.
- Loss of large portions of the brain may result in loss of function.
- Loss of smaller portion in certain locations may affect memory, language, or even result death.
***
# Neuroanatomy
- ** <p style="color:#1d3aa1;">Hindbrain:</p> ** Where the spinal cord rises to meet the brain. Composed of three main structures:
- ** <p style="color:#3a67ee;">Medulla:</p> **
- Many pathways pass through the medulla to connect the spinal cord to higher levels of the brain.
- <u>Regulates basic functions such as heartbeat, blood pressure, movement, and respiration</u>.
- Sleeping, sneezing, and coughing.
- ** <p style="color:#3a67ee;">Pons:</p> **
- Latin word for *bridge*.
- Bundles of nerves pass through it.
- Transmits information about body movement.
- <u>Involved in attention, sleep-arousal, and respiration</u>.
- ** <p style="color:#3a67ee;">Cerebellum:</p> **
- Has two hemispheres that <u>maintain and control motor (muscle) behavior</u>.
- Involved in muscle coordination and balance.
- It's the key to organizing information that gets us to do things.
- Injury to the cerebellum may impair motor coordination.

View File

@ -0,0 +1,121 @@
> ### 001 - Arguments
> Class Notes
> Emilio Soriano Chávez
> ***
> <span style="color:#9b59b6">Introduction to Philosophy</span>
> Summer 2020
- All philosophy attempts to give arguments.
- *We ought to have reasons for the things we believe*.
- Beliefs that are supported by good reasons are worth holding to.
- Belief = Think = Opinion - Something you think.
- Good opinions are those that are supported by reasons.
- Some of the things we believe are false.
- Mistakes in reasoning.
- In the past we may have had false believes.
- To be a good critical thinker, we need to figure out which are the true beliefs and which ones are the false beliefs that we have.
- False beliefs can also be harmful.
- **Reasons:**
- **Personal Experiences:** Can serve as a reason. Perception, vision, and memory.
- **Testimony from Reliable Sources**: Experts. People that have more knowledge. This does not mean that an expert is never wrong.
- **Reliable Sources of Information:** Books, newspapers. 
- **Good Arguments**
- **Kinds of Reasons:**
- Reasons that provide **conclusive or deductive support**. They settle an issue. Rare. Usually in mathematics.
- Pythagorean Theorem
- Gives certainty.
- Reasons that provide **probable or inductive support**. High probability. Almost always there may be new evidence that leads us to reject a previous hypothesis.
- Does not give certainty.
- Found in almost all science.
- **Arguments:**
- All arguments have at least one premise and only one conclusion.
- They collect our reasons for thinking that something is true.
- **Premises:** Reasons for thinking that something is true.
- Good arguments are the ones that have premises that support the conclusion.
- Bad arguments have premises that do not support the conclusion.
- **Kinds of Support:**
- Conclusive Support - Deductive
- Probabilistic Support - Inductive
- **Aim:** Arguments where the premises support the conclusion, either deductively or inductively, and also have true premises.
- **Ways to Reject an Argument:**
- Argument has at least one false premise.
- The premises do not support the conclusion.
- **Example Argument #1:** Inductive Argument
- It looks like there is a palm tree outside. (Premise 1)
- My visual system seems to be working correctly. (Premise 2)
- Therefore,
- There is a palm tree outside. (Conclusion)
- This is an **inductive (probabilistic)** argument. It is possible for the premises to be true, but the conclusion may be false. These type of arguments only give a high probability.
- For example, it may be a hologram.
- **Example Argument #2:** Good Argument
- All ASU students are human.
- Ralph goes to ASU.
- Therefore,
- Ralph is human.
- This argument is deductively valid. If the premises are true, then the conclusion must be true.
- **Example Argument #3:** Bad Argument
- All ASU students are human.
- All men are human.
- Therefore,
- All ASU students are men.
- To show why this fails we would have to use categorical logic, but intuitively we can say it's wrong.
- Giving good arguments is not simply saying true things. We also need to have our premises fit together.
- **Example Argument #4:**
- Arkansas is south of Missouri.
- The capital of Texas is Austin.
- Therefore,
- All ASU students are human.
- Both premises are true, but the conclusion does not fit the premises. We would say 10 and 11 do not support the conclusion.
- **Deductive Arguments:** Provide conclusive reasons for the conclusion.
- **Inductive Arguments:** Provide probable reasons for the conclusions.
- **Example Argument #5:** Deductive Argument
- All cats have four legs.
- Ralph is a cat.
- Therefore,
- Ralph has four legs.
- Premise 13 is false, so it is not the best kind of argument but at least the premises are true and support the conclusion.
- **Example Argument #6:** Inductive Argument
- Most cats have four legs.
- Ralph is a cat.
- Therefore,
- Ralph has four legs.
- It is **highly probable** that the conclusion is true.
- A *sound argument* is a deductively valid argument.
- A *cogent argument* is a strong argument. An inductively successful argument with all true premises.
- **Example Argument #7:**
- If ASU wins the tennis match then everyone will be happy.
- ASU won the tennis match.
- Therefore,
- Everyone will be happy.
- This argument is deductively valid. It is not sound, because probably the first premise is false.
- **Example Argument #8:**
- If ASU wins the tennis match then everyone will be happy.
- Everyone is happy.
- Therefore,
- ASU won the tennis match.
- Failed deductive argument. Premises does not support the conclusion. Not logically valid argument.
- **Example Argument #9:**
- Experiment #1 and #2 indicate that the compound is acidic.
- Professor Smith says that her experiments have indicated that the compound is acidic.
- The handbook of chemistry says that the compound is acidic.
- Therefore,
- The compound is acidic.
- Good argument. It is an inductive argument.

View File

@ -0,0 +1,82 @@
> ### 002 - Apology: Defence of Socrates
> Class Notes
> Emilio Soriano Chávez
> ***
> <span style="color:#9b59b6">Introduction to Philosophy</span>
> Summer 2020
- **Socrates:**
- Lived about 25,000 years ago.
- Did not write anything during his lifetime.
- Some people think that he had a particular view of how to do philosophy.
- He thought that philosophy is something that you did with other people.
- It is about a conversation that you have, sharing reasons and arguments
- Not something you write, but something you do.
- Plato was his student.
- The apology is Plato's account of the trial of Socrates.
- Plato also had a student, Aristotle.
- Plato's version is fairly reliable.
- Socrates does not come as the most likable person.
- Other versions are not as reliable.
- It is not a transcript. It was written years after the trial from memory.
- Some is reconstructed.
- Apology comes from the ancient Greek word *apologia*, meaning defense.
- We see Socrates committed to the relentless pursue of truth.
- Socrates only cares for the truth.
- He does not care about money. He did not have a job at the time.
- He spent all of his time doing philosophy, talking to people.
- Socrates wanted answers to his questions.
- He wanted to know what it was to live a good life, in a moral dimension, human nourishment, know what goodness, justice, and knowledge was.
- This is why he wandered asking questions.
- Why it is good to do the things that we do.
- What it is to have a good life?
- Socrates is on trial.
- Socrates discusses each of the charges and responds (defense).
- Guilty of being a busybody.
- He would bother people by asking them questions.
- He often sough rich and powerful people, politicians. He asked them why they believed what they did.
- He asked them to give their reasons for thinking the way they do.
- What do you believe?
- Stating what you believe is easy, but Socrates would as **why**?
- He asks for reasons.
- Premises that support your position.
- People almost always did not have good answers and they looked foolish.
- In Athens, all of this happens in public.
- All life was in public.
- Everyone would be paying attention.
- This was damaging for the individuals that look foolish.
- He asked questions about sensitive topics and he taught other people to do the same.
- He exposed influential people by showing that they did not have good reasons for what they believe.
- Socrates does not provide well-formed answers.
- He points out that in most cases, people that claim to have the answers don't.
- Socrates guilty of corrupting the young.
- Young people thought this was cool and exciting.
- Young people tried the same.
- They were being jerks about it (not good).
- They emulated Socrates without understanding what they were doing.
- Socrates replied that he would never intentionally corrupt the young.
- He does not know what goodness is, and so he can't intentionally make them bad.
- Socrates acts a bit like a jerk.
- He is arrogant even to his accusers.
- He wanted the jurors to understand and act like good critical thinkers.
- He cared for Athens.
- He asked this questions to make Athenians critical thinkers.
- Challenge the things you believe to have true beliefs.
- He was declared guilty to death by drinking hemlock.
- He voluntarily drank the hemlock.
- He respected the law and drank the hemlock himself.
- Before he died, Plato and his friends tried to sneak him out, to which he replied:
- An unexamined life is no life for a human being to live.
- An examined life is where you question everything.
- A life without questions has no value. A life without being a critical thinker.
- An unexamined life is where you do what everyone wants you to do.
- Anyone could do that.
- A person would just be acting like a robot and following orders.
- There is value in exercising reason. If you don't, your life has no worth.

View File

@ -0,0 +1,107 @@
> ### 003 - Existence of God I
> Class Notes
> Emilio Soriano Chávez
> ***
> <span style="color:#9b59b6">Introduction to Philosophy</span>
> Summer 2020
- Question - Whether **X** exists (Here, **X** is God)
- There are three possible answers:
- Yes (Theism)
- No (Atheism)
- We can't know (Agnosticism)
- **Faith:**
- "I believe because of faith". But, what does this mean?
- Is faith "reason-based":
- Yes - Then what is the reason?
- Could be personal experience, which people think it suggests the existence of God,
- Expert testimony. Someone that the person thinks is smart or has good evidence.
- No - Faith is not "reason-based".
- Holding a belief on the basis of no reason is irrational.
- Unlikely to be true because it is not supported by evidence.
- Could have bad consequences.
- Leads to do unreasonable things.
- If your belief is not based on reason, then why believe in one thing rather than another.
- Belief should be supported by reason.
- Typically, appeals to faith are unclear or under-specified.
- **Attributes of God:**
- We need to have a basic concept in order to investigate.
- All good, all knowing, all powerful.
- **Cosmological Argument:**
- Working backwards. Where do things come from? (Starting point)
- Aristotle and Aquinas gave well-known versions.
- Inductive argument:
- *If the premises are true, the conclusion gives a high probability*.
- **Premise 1:** Everything is caused by something prior in the causal chain.
- **Premise 2:** It is absurd to think that the causal chain can go back infinitely.
- **Premise 3:** There had to be some uncaused thing at the beginning that started the whole chain of causation.
- **Conclusion:** Therefore, this uncaused thing is God (so, God exists).
- Should we believe the argument? Why is this plausible?
- Premise 1 is true, from our experience.
- For Premise 2, it is harder to give evidence. But going back infinitely would require infinite energy, and currently, physics and metaphysics suggest otherwise. 
- There had to be an uncaused thing from the beginning.
- We should think this uncaused thing is God.
- **Objections:** Worries about the argument:
- **Internal Inconsistency:**
- Premise 1 says that everything is caused by something prior, but Premise 3 suggests there is something that is not.
- They both cannot be true at the same time, so one is false.
- If we have a false premised we should not be convinced by the conclusion.
- **Problem of the Attributes:**
- We do not get any details of the attributes of God.
- The argument does not prove that God is all good, all knowing, and all powerful.
- The argument is not powerful enough to give all of the attributes.
- **Alternative Scientific Explanation:**
- The premises could be true bu the conclusion false.
- Big Bang model suggests the first uncaused thing is not God. Evidence:
- Cosmic microwave background - Energy dispersed thorough the universe.
- Space is expanding.
- This shows that the premises are true, but not enough to argue that God exists.
- **Ontological Argument:**
- First given by St. Anselm of Canterbury (1000 years ago).
- Tries to prove God's existence form the concept/definition of God.
- Deductive argument.
- **Premise 1:** The concept of God is that of the most perfect being imaginable.
- God has every perfection that is possible.
- **Premise 2:** Either God is purely imaginary (does not exist, but we still have the idea) or God is real. 
- Either God exists or not.
- **Premise 3:** It is more perfect to exist than to not exist.
- Existence is a great making property.
- God has every great property.
- If God did not have the property of existence, God would be less than perfect, but our concept tells us that this is not possible, so God has to exist.
- **Premise 4:** Therefore, a purely imaginary God is less perfect than a real God.
- **Premise 5:** Therefore, a purely imaginary God does not correspond to the concept of God, which is that of the most perfect being.
- **Conclusion:** God is real.
- **Objections:**
- **Fool's Response:**
- From the monk Ganilo.
- It seems like the argument would prove too much.
- The perfect version of everything must then exist.
- The argument could have a flaw.
- **Reverse Parody:**
- Existence is a great making property, but what if it worked the other way.
- Maybe it is more impressive for something not to exist.
- Maybe existence is not a great making property, but non-existence may be better.
- **Existence is not a Property:**
- Immanuel Kant argued that existence is not a property at all.
- Existence is not a property, it is a precondition to having property at all.
- This would mean that the claim that existence is a great making property is false. Premise 3 would then be false.
- **Argument from Scripture:**
- Attempts to show that God exists because a certain religious text is reliable.
- Whether you think that the religious text is reliable or not.
- Different religious scriptures. Which one is the most reliable?
- **Teleological Argument (Design Argument):**
- Comes from William Paley.
- The universe must have had a designer because it was set in just the right ways.
- The Big Bang model serves as a kind of objection.
- Evolution proves that you can get complexity without a designer.
- **Pascal's Wager:**
- Does not claim that you have good evidence for thinking God exists.
- Convinces you that you have practical reasons to believe in God even though you do not have the evidence.
- In terms of expected payout, it is in rational interest to believe that God exists.
- If God exists, he would punish you if you do not believe.
- No believing has no penalty if God does not exist, but if God does exist, not believing has a significant penalty.

View File

@ -0,0 +1,81 @@
> ### 004 - Existence of God II
> Class Notes
> Emilio Soriano Chávez
> ***
> <span style="color:#9b59b6">Introduction to Philosophy</span>
> Summer 2020
- Arguments either for God exists or we cannot know.
- **Argument for Religious Pluralism:**
- There are many different religious traditions.
- This also includes all the past religious traditions.
- People did believe them and they could be possibly true.
- Do we have good reason for rejecting these alternatives?
- Religious belief may not be based in good evidence.
- Could this be an argument for atheism or agnosticism?
- This argument is aimed at a theist.
- It is given to someone who already believe in the existence of God.
- **Premise 1:** If you had been born in another country, you would have very different religious beliefs.
- If you were placed in different regions or time periods, you would believe differently.
- People in India believe in Hinduism.
- People in ancient Rome believed in Roman mythology.
- This can also apply sub-regionally.
- **Premise 2:** So, your current religious belief is not based on evidence.
- More based on social pressure, tradition.
- Sensitive to non-evidential factors.
- **Premise 3:** You do not know anything about alternative religious traditions.
- No good reason to think that yours is the one worth choosing.
- You do not know anything significant.
- Who should we think is right?
- **Premise 4:** You have no reason to prefer your religious tradition to the others.
- **Premise 5:** Therefore, you do not have good reason for your religious beliefs (and should stop believing them).
- The conclusion is more of: you should not have a religious belief.
- **Objections:**
- I know all of the religious alternatives.
- Premise 1 and 3 are false then.
- The evidence for all the religious alternatives is not the same.
- You do not need that much evidence to disprove.
- Hard to assess.
- **The Problem of Evil:**
- Most influential argument for atheism (at least, among philosophers).
- A better name would be "the problem of bad stuff".
- Basic idea of God - All good, all powerful, and knows everything. Suppose also God exists.
- The world has arguably bad things.
- **Premise 1:** Suppose that there is a God who is omnibenevolent, omniscient, and omnipotent.
- Supposition.
- Cannot really be challenged.
- **Premise 2:** The world is filled with suffering and misery.
- Do not confuse your experience with the one of a typical human.
- Most humans did not have access to water, shelter, medicine, etc.
- Many women die during childbirth.
- No clean water.
- Some of these are still present today.
- Also natural disasters. Pandemics, etc.
- Animals suffering. Forest fires that kill many animals, simply because they cannot escape.
- **Premise 3:** Since God is omniscient, he knows about human and animal suffering and misery.
- **Premise 4:** Since God is omnipotent, he could effortlessly prevent such suffering if he wanted to.
- If God is all powerful, there is nothing he cannot do.
- **Premise 5:** Anyone who knows about suffering and could effortlessly prevent it, but does not do it, is not so perfectly good.
- If God can prevent this and does not, then God is not perfectly good.
- **Premise 6:** Therefore God is not perfectly good.
- **Premise 7:** This contradicts Premise 1 - therefore there is no God.
- **Objections:**
- **Give up an attribute:**
- God is not good or does not know everything.
- **Part of God's greater plan:**
- The stuff is not really bad stuff.
- Whether you think this is true or not.
- The Bible shows that it is a good thing to relieve suffering.
- Religious inconsistency.
- **Free Will Objection:**
- Free will is so important that God gave it to us.
- The bad stuff is the fault of humans.
- Explains genocide, war, rape, torture, etc.
- Does not explain tornadoes, disease, aging. (natural evils- not the fault of humans)
- Some people claim that this are the fault of humans.
- **Worries:**
- Moral and Natural Evil
- It can also be possible to have free will and always do the right thing, because God is all powerful.
- They may not be compelled, but they are better reasoners.
- God could have made both.

View File

@ -0,0 +1,126 @@
> ### 005 - Ethics I
> Class Notes
> Emilio Soriano Chávez
> ***
> <span style="color:#9b59b6">Introduction to Philosophy</span>
> Summer 2020
- **Ethics:**
- Which actions are moral? Which actions are immoral?
- Why are they moral or immoral?
- Developing a <u>moral theory</u> helps us understand <u>what moral actions have in common</u>.
- Physical theories help us understand how things behave and why they behave that way.
- We also want to know why things are moral.
- A moral theory helps us figure out how to think about difficult moral cases.
- We all agree that gratuitous killing is immoral.
- There are many cases that are not so obvious (difficult cases).
- Moral =  Ethical
- Immoral = Unethical
- **Morally Permissible:** Something that is permitted, that morally you **can** do.
- **Morally Obligatory:** Something that you **must** do. If you do not, you break the core rules of morality.
- **The "Big Three" Ethical Theories:**
- Utilitarianism
- Kantian Ethics (Deontology)
- Virtue Ethics
- This does not mean that the correct theory is between these three.
- Every ethical theory has some flaws.
- We should pick the ethical theory with the fewest objections.
- Every ethical theory has some worries associated.
- We do not need to find the perfect theory.
- They help guide our behavior.
- Pick the ethical theory that has the fewest serious objections and work on small changes that you can make.
- This does not mean that anything you pick has to be correct.
- Through careful considerations.
- Morality is valuable.
- Know what it takes to be a good person.
- **Utilitarianism:**
- It is a *consequentialist* moral theory - all that is morally relevant are the consequences of an action.
- When trying to figure out whether an action is moral or immoral, all you have to do is look at the consequences of the action.
- An action **X** is moral if it produces the most happiness/pleasure for the group, and **x** is immoral if it does not produce the most happiness/pleasure for the group.
- Happiness/pleasure not for yourself, but for everyone that is affected.
- Utilitarianism is not a selfish ethical theory.
- Ethical Egoism - An action is moral if it produces the most happiness/pleasure for you.
- Utilitarianism is not Ethical Egoism.
- It only agrees if the action only affects yourself, but this usually never happens.
- The opposite of happiness/pleasure is suffering/pain.
- An action is moral if it reduces suffering for the group.
- Produce happiness or reduce suffering.
- To be moral, we need to reduce suffering.
- We must calculate the happiness/pleasure and suffering/pain of an action.
- Can this be calculated?
- Use money:
- When we think of buying something.
- If we think something is overpriced, we have more value in the money than the item.
- **Economics:**
- Q - How much happiness would **x** bring you?
- A - The highest amount of money you would pay for **x**.
- This is the "measure of happiness".
- Example - Happiness of buying a new t-shirt. The most money you would pay for it.
- Example - Suffering of getting a cold. The most you would pay to not get it.
- Happiness or suffering of **x** can vary between individuals.
- Different people suffer differently.
- **Examples of Utilitarianism:**
- **The Bar Case:** You're in a bar and there is an annoying bar patron battering everyone. Is it moral to kill this person?
- Utilitarianism - Do a calculation on group happiness to see.
- Two Options - Kill the person or not kill the person.
- #1 - Kill the Person:
- How much would you pay to not get stabbed? -$20,000 (does not have to be perfectly accurate) [Suffering of killed person]
- Suffering of family and friends: -$60,000
- Suffering because loss of productivity at work: -$20,000
- Psychological trauma of people in the bar: -$20,000
- Happiness obtained for no annoying person anymore: +$50
- Result: -$119,950 (Loss of happiness)
- #2 - Not Kill the Person
- Loss of happiness for annoying person: -$50
- Happiness of annoying person: +$5
- Result: -$45 (Loss of happiness)
- Whatever you do, the world suffers a loss of happiness.
- According to Utilitarianism, you should pick the action that causes the less suffering.
- Verdict - It is immoral to kill the person.
- This is because it would cause more unhappiness/suffering in the word.
- It leads to more group unhappiness.
- Your happiness matters, but so does the others' happiness.
- **Breaking and Entering Case:** Suppose someone breaks into your house to steal your valuables, and you know that if you do nothing, you and your 4 other family members will be killed. However, you can prevent this by killing the intruder.
- Two Options - Kill the intruder or do nothing.
- #1 - Kill the Intruder:
- One person and their family suffers. (They morally matter)
- Your family continues to live happy lives. We also need to factor our the psychological trauma of the event.
- #2 - Do Nothing:
- Intruder gets modest happiness from stealing valuables.
- 5 people suffer in their death. Countless friends and family suffer.
- Neighborhood scared because of the intruder.
- The utilitarian perspective would say it is morally permissible to kill the intruder.
- Utilitarianism will not always say it is wrong to kill a person.
- Robin Hood Case -> Utilitarians would say it is morally permissible to steal from the rich.
- **Objections to Utilitarianism:**
- **Practicality:**
- It is too hard or impossible to do the calculations.
- Responses:
- No one said morality was going to be easy.
- It is to be expected the difficulty.
- Rough heuristics, numbers do not have to be exact.
- **Invasiveness:**
- Everything you do is potentially moral or immoral.
- Responses:
- Who said that morality was going to be easy.
- **Supererogation:**
- Acts that are good but you do not have to do.
- In Utilitarianism, this do not exist, because you always have to do the thing that produces more happiness.
- Nothing is absolutely wrong:
- There are cases where it is moral to kill someone.
- **Organ Robber Case:**
- Someone visits their doctor.
- There is a car accident and 5 people are harmed in the accident.
- Each person needs an organ.
- The person that visited the doctor is a perfect match.
- Is it morally permissible for the doctor to kill the healthy patient without its consent to save the life of the 5 people.
- If this person is not killed, then the 5 people die.
- Utilitarians would say that it is morally permissible to kill the patient and save the 5 people.
- Gives a reason to think that the theory is wrong.

View File

@ -0,0 +1,112 @@
> ### 001 - Computer Abstractions and Technology
> Class Notes - August 26, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#8B4513">Computer Organization</span>
> Fall 2021
- **Moore's Law:**
- Number of transistors would double every year.
- Now, it is harder to increase computing power.
- **Classes of Computers:**
- Desktop
- Server
- Embedded:
- Depends on the application.
- Mainly extra memory is the difference between desktop and server.
- A server has more extra space for when drive sectors do not work.
- More expensive.
- More processors for phones than PC's (2007).
- **Performance:**
- Parallelization
- Algorithms
- Programming Language, Compiler, Architecture
- Sometimes no control over this.
- Processor and Memory System
- I/O System (including OS)
- How fast operations are executed.
- **Abstraction Layers:**
- Application Software
- High-Level Languages
- C++
- Systems Software:
- Compiler
- Operating System
- Hardware
- **Levels of Program Code**
- **Components of a Computer:**
- User-Interface Devices
- Storage Devices
- Network Adapters
- Not always present in all computers.
- LCD Screen
- **CPU:**
- **Datapath:** Where data comes from.
- **Control:** Sequences, memory, etc.
- **Cache Memory:** Small fast SRAM memory for immediate access to data.
- Takes more space in CPU.
- More expensive.
- Larger in servers.
- **Abstractions:**
- Instruction Set Architecture -> Hardware
- Application Binary Interface:
- Between Hardware and Operating System
- Translates the two layers.
- **Volatile Main Memory:**
- Loses instructions and data when powered off.
- FRAM: Works like regular RAM but keeps data when turned off.
- Very big and expensive.
- Used in calculators.
- Non-Volatile
- **Networking:** LAN, Wi-Fi
- **Defining Performance:**
- **Response Time:** How long it takes to do a task. (Ping Response Time)
- **Throughput:** Total work done per time. (DOWN)
- **Measuring Execution Time:**
- **Elapsed Time:**
- Total response time, including all aspects.
- Determines system performance.
- **CPU Time:**
- Time spent processing a given job.
- Ignores I/O time.
- Every processor has a clock speed.
- 3.0 GHz -> 3 billion cycles/sec
- But each instruction does not necessarily run in one cycle.
- Some instructions take more than one clock cycle.
- **Power Wall:**
- Before 2003, every year computers would get 50% faster.
- About every 3 years, computers needed to be replaced.
- This is not the case anymore.
- After 2015, computers have not got any faster at all (for single-core performance).
- A faster processor needs more power, and will generate more heat.
- We have the issue of being able to dissipate this heat.
- To fix this, we can use multiple cores. There can be issues with this:
- Parallel programming for multiple cores is hard.
- A lot of things will run on only one core unless explicitly programmed to do differently.
- Parallel programming has limitations. Many different ways to parallelize something.
- **CPU Benchmarks:**
- How much work can be done using a certain amount of energy.
- If we have a bottleneck that we cannot paralellize, then we will not be able to improve the speed of our program.
$T_{\text{improved}} = \dfrac{T_{\text{affected}}}{\text{Improvement Factor}} + T_{\text{unnafected}}$
- The amount of work being done is not necessarily proportional to the amount of power being used.
- Computers can be rated in millions of instructions per second (MIPS).
- This does not account for different instruction sets.

View File

@ -0,0 +1,517 @@
> ### 002 - Instructions - Language of the Computer
> Class Notes - August 31, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#8B4513">Computer Organization</span>
> Fall 2021
```c
f = (g + h) - (i + j)
```
- `t` is a temporary variable.
- `add` and `sub` are in the instruction set.
- Different types of processors have different instruction sets.
```py
add $t0, $s1, $s2
add $t1, $s3, $s4
sub $s0, $t0, $t1
```
- `t` is a temporary register.
- RISC - Reduced Instruction Set Computer
- Apple A12 - ARM
- RISC-V
- CISC - Complex Instruction Set Computer
- x86 / AMD
- MIPS Instruction Set:
- Used a lot for embedded design and special hardware.
- It is not used a lot anymore.
- Simple instruction set.
- Simple - PIC, ARMega
- MIPS are in between.
- ARM, x86
- Number Bases
- **Register Operands:**
- Arithmetic instructions use register operands.
- Register is simply memory inside of a processor.
- Storage Hierarchy:
- CPU (Has a bunch of registers). They are fast.
- To put things in registers, we use RAM.
- We usually store things in RAM from an external storage.
- Octal came from 8-bit values.
- In MIPS architecture, 32-bit data is called a "word". (or 4 bytes is a word)
- 8 bits is one byte.
- Half is a nibble.
```c
/*
We have spots in memory (bytes).
For a 32-bit integer, we can take 4 bytes to store g.
g is assigned the value 12.
The memory address would be 0C.
*/
int g = 12;
int h = 21;
int i = 24;
int j = -8;
int f = (g + h) - (i + j);
```
```py
# We have to get the value in the register somehow.
# We can put it in memory first, use immediate instructions...
# Consider g is s1 and h is s2.
# Consider f is s0.
# This will add 0 to a register.
ADD $s0, $zero, $zero
# We need to get them in registers somehow.
# There is a register zero, we cannot change it.
# ADD expects register (destination), register (source), register (source).
# ADDI expects register (destination), register (source), value.
# We need a second register before the value.
# We can use the zero register to simply add our value to the destination register.
# The other registers will have garbage values until initialized.
ADDI $s1, $zero, 12
ADDI $s2, $zero, 21
ADDI $s1, $zero, 12
ADDI $s2, $zero, 21
ADDI $s3, $zero, 24
ADDI $s4, $zero, -8
ADD $t0, $s1, $s2
ADD $t1, $s3, $s4
SUB $s0, $t0, $t1
```
- MIPS register $0$ `$zero` is the constant $0$.
- Cannot be overwritten.
- It can be useful for multiple operations.
- Install QtSpim.
- We have 2 types of instructions, I (Intermediates) and R (Registers).
- `ADD` and `ADDI`
- `SUB`
- There is no `SUBI`, we have to add a negative value instead.
- `MOVE` instruction just uses `ADDU`, where `U` stands for unsigned.
- Review Binary to Decimal Conversion.
- Complement Signed Integers
- Consider 8-bit representation.
- Signed Negation
$ 00011001 = 25 $
- Convert to negative $25$:
- Invert all numbers and add $1$.
$ 11100110 + 1 = 11100111 = -25 $
$ 00011001 $
- If it us Unsigned:
$ 1111 = 15 $
- If it is Signed:
$ 1111 = 0000 + 1 = 0001 = -1 $
- For `R` Instructions, the first 6 bits are the operation code (opcode).
- `ADD`, `ADDU`, `AND`, `OR`
- Specific locations.
- The second field is the first source register, the third is the second source register, and the fourth is the destination register.
```py
ADD $t0, $s1, $s2
# Is actually:
# op rs rt rd shamt funct
# 0 17 18 0 (...)
# These are R format instructions.
```
- With `I` format instructions:
- We can only use 16-bit constants.
- Runs quicker and requires less instructions, sacrificing address space.
- To load 32-bit, we can load half of it into one register and the other in another register, add them, and put them into a new register.
- We would need minimum 3 instructions to do this.
- **Stored Program Computers:**
- von Newman architecture.
- All data and the program itself run from memory.
- All computers actually use this.
- **Logical Operators:**
- Shift Operators:
- Shift Left (If we are in base 2, it is the same as multiplying by 2). `sll`
- Shift Right (In this case, we would be dividing by 2). `srl`
- Bitwise AND, OR, NOT:
- Bitwise means just one value.
- `and, andi` (Useful to mask bits in a word)
- `or, ori` (Useful to include bits in a word)
- `not` (Useful to invert bits in a word)
- To flip one register: `nor $t0, $t1, $zero` (First do OR with zero register and then not on the result. This gives the negative number stored in the register.)
```py
# We have an array of 6 elements -> A[6].
# If we want to load them into registers, we can take the address.
# 20 is the offset.
# We are working with byte values.
lw 20($s3)
```
- **Conditional Operations:**
- Branch to a labeled instruction if a condition is true.
- We have 3 different branch instructions:
- `beq` - Branch with Equal (In C, this would be something like `if (x == y)`)
- `bne` - Branch with Not Equal (`if (x != y)`)
- `j` - Return
```c
int f;
int g = 2;
/*
*/
if (i == j)
f = g + h;
else
f = g - h;
```
```py
# We will only jump to this address if the statement i == j is not equal.
# == BNE
# != BEQ
bne R, R, A
# No operation. Give time for operation to finish.
nop
# If the statement is true, then we get to f = g + h.
# Go to memory and grab the value for g = 3.
lw v1, 4(s8)
# Grab the value for h = 2.
lw v0, 8(s8)
# Add both values and place them in the v0 register.
addu v0, v1, v0
# Now, we can assign it to f, which is at 20 in memory.
sw v0, 20(s8)
# To make sure we do not execute the else...
# Jump / Branch
b 64
# If they are not equal, then we do another part of the code.
# Our instructions are all in memory.
# 0x54 is a memory address. We are telling the compiler what instruction to execute.
bne v1, v0, 54
# If the statement is false, then we get to f = g - h.
# Go to memory and grab the value for g = 3.
lw v1, 4(s8)
# Grab the value for h = 2.
lw v0, 8(s8)
# Subtract both values and place them in the v0 register.
subu v0, v1, v0
# We should get a negative 1.
```
- **Additional Conditional Operations:**
```py
# Set result to 1 if a condition is true.
slt rd, rs, rt
```
- **Compiling Loop Statements:**
- We have an array called `save`.
```c
int save[];
int i = 0;
int k = 123;
while (save[i] == k)
{
i += 1;
}
```
- **Compiled MIPS Code:**
- Page 121, 98
- We are going to refer to each element of the array by their memory address.
- `i` will go in register `$s3`.
- `k` will go in register `$s5`.
- Address of save is in `$s6`. A 32-bit address pointing to the base (element 0) of the array.
```py
Main:
# Shift Left Logical
# We have a number we want to shift left by some value.
# In math terms, we multiplied this number by 4.
# We take what is in s3, multiply it by 4, and storing it in t1.
# 0b (0 binary) by 4 is 0b0000.
# Now, t1 is 0b0.
sll $t1, $s3, 2
# Consider that s6 is 0x0 (zero). This is the stat of the array.
# We are adding 0b0 (t1), which is still 0.
# We are storing this value in t0.
add $t1, $t1, $s6
# Load Word
# Take something from a memory address and place it in a register.
# We are taking what is in t1 and storing it in t0.
# 0() allows taking memory addresses.
# We are taking 123 (first element of array) and storing it in t0.
lw $t0, 0($t1)
# We are comparing whatever is in s5 to t0.
# If they are not equal, we just jump to the exit.
# If they are equal we go to the next instruction.
# In this case, they are equal.
bne $t0, $s5, Exit
# We add 1 to s3 and store it in s3 again.
# This would be 0 + 1 = 1.
# Now we repeat the loop.
# This would be the equivalent of i += 1.
addi $s3, $s3, 1
# Repeat the loop.
j Loop
Exit:
```
- For the second loop:
```py
Main:
# s3 is now a binary 1 (0b0001)
# We are gong to shift left by 2.
# We get 0b0100 (same as multiplying 1 times 4, which gives 4).
# We now store this in t1.
sll $t1, $s3, 2
# We add t1 to s6, which is the memory address of our array.
# t1 is now a memory address.
# This means t1 is now pointing to the second element of the array.
add $t1, $t1, $s6
# We retrieve the second element of the array, which is 123.
lw $t0, 0($t1)
# Now, compare 123 to 123, and they are equal.
bne $t0, $s5, Exit
# We add 1 to s3 and store it in s3 again.
# This would be 1 + 1 = 2.
addi $s3, $s3, 1
# Repeat the loop.
j Loop
Exit:
```
- Third Loop:
```py
Main:
# s3 is now a binary 1 (0b0100)
# We are gong to shift left by 2.
# We get 0b0100 (same as multiplying 2 times 4, which gives 8).
# We now store this in t1.
sll $t1, $s3, 2
# We add t1 to s6, which is the memory address of our array.
# t1 is now a memory address.
# This means t1 is now pointing to the third element of the array.
add $t1, $t1, $s6
# We retrieve the third element of the array, which is 5.
lw $t0, 0($t1)
# Now, compare 123 to 5, and they are not equal.
# We jump to exit.
bne $t0, $s5, Exit
# This is NOT executed.
addi $s3, $s3, 1
# This is NOT executed.
j Loop
Exit:
```
- **Basic Blocks:**
- Sequence of instructions with no branches.
- **More Conditional Operators:**
```c
x = 5;
if (x > 3)
{
x += 1;
}
```
```py
# $t0 is 5
# $t1 is 3
# $t2 will contain 1 if true or 0 if false
# Set less than
slt rd, rs, rt
```
- Faster than having `blt` or `bge`.
- **Procedure Calling**:
- A procedure is a chunk of code that we can call, similar to a function. It has parameters.
- Instruction Pointer (IP) is the memory address of an instruction.
- **Jump and Link:**
- `jal ProcedureLabel`
- Equivalent to a function call.
- We can put the instruction address in `$ra` and jump to that target address.
- **Procedure Return:** Jump Register
- `jr $ra`
- Equivalent to `return`.
- Can be used for computed jumps.
- **Leaf Procedure:**
- A procedure that does not leap into or call another procedure.
- Procedures that do not call anything else.
- Output is system call 4. Takes one argument (a string).
- It will print the string.
- Input is system call 8.
- We take a string and shift by 13 characters (ROT13).
- **Frame Pointer:** Where the procedure starts.
- **Stack Pointer:**
- We can pad a code with `nop`.
- No Operation
- We simply execute the next operation.
- **Memory Layout:**
- **Dynamic Data / Heap:**
- Normally used for local variables.
- Grows and shrinks as needed for function calls.
- **Static Data:**
- Usually includes constants.
- **Character Data:**
- Nowadays is primarily unicode.
- ASCII
- Has 128 characters.
- Latin-1 (Also called extended ASCII).
- Unicode gets to 32-bit (4 billion different characters).
- **Byte/Halfword Operations:**
- `lw` Loads a 32-bit value.
- `lb` Loads half of `lw` and places it in the lower part of a register.
- `lbu` Loads into the upper part of the register.
- **32-bit Constants:**
- Instructions are 32-bit long.
- Intermediate instructions can only load 16-bits.
- We can use `lhi` to load intro the front half into a register and then `ori` with the left half.
- Largest number we can store in 16-bits is 65,535.
- **Branch Addressing:**
- When we branch using `bne` or `beq`, we are doing an `r` instruction.
- Address of the Instruction we are doing to the left (in simulator).
- We only have 16-bits left for an address to branch to.
- We can branch to some offset from where we are now.
```py
# PC 0x40000000 bne
# Loop at address 0x40000100 ADDI
# All we have to do is add 100 to get here.
# Since we are not jumping directly to a memory address, we can now jump 65,535 now.
```
- One solution can be using the `j` instruction, which does accept a longer address.
- Another solution is that the assembler will flip a `bne` into a `beq`, flip the logic and move the code closer.
- Another solution is to put the address into a register.
- **Synchronization:**
- Communication between processes.
- May not be possible or may be slow.
- Hardware support may be needed.
- No other access to the location allowed between a read and write operation.
- Atomic read/write.
- Atomic swap.
- Loan Liked `ll` and Store Conditional `sc`.
- Only store if the original data has not changed since `ll`.
- We could then branch and load the value again.
- **Translation and Startup:**
- Assembler takes assembly code.
- Compiler takes a high level language and converts it into assembly.
- Turn off Kernel Text and Instruction Value, then Reinitialize.
- Text Segment > Kernel Text
- Text Segment > Instruction Value
- File > Reinitialize and Load File
- We can also use "Clear Registers".
- Turn on console for seeing output.
- Window > Console
- All MIPS Instructions are 32-bit.
- We now just have the instruction address, assembly code, and comments.
- Pseudo-instructions will be "translated" to actual assembly.
- **Object File:**
- Machine instructions.
- To run the code, we need to link it.
- Two types of linking:
- Static Linking
- Dynamic Linking
- C++ > Compiler > Assembly Code > Assembler > Object File > Linker > Executable File > Loader
- We can have two programs calling the same function.
- We can link to the same address and make it easier to use.
- We do not have to recompile every time.
- Reduces file size because we do not need to actually include it every time, we just have a link to it.
- **Loader:**
- Initializes registers.
- Copies and pastes text.
- Compiler optimizes code by rearranging it and modifying some things.
- May result in code different than the one that was written.
- Cuts down the number of instructions.
- In x86, instructions vary in length.
- Smaller is faster.
- Make the common case fast.

View File

@ -0,0 +1,53 @@
> ### 003 - Arithmetic for Computers
> Class Notes - October 14, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#8B4513">Computer Organization</span>
> Fall 2021
- **Normalized:**
- We have a number with some digits. ($x.dddd$)
- x needs to be between 1 and 9 to be normalized.
- Example: $-2.34 \times 10^{56}$.
- **Not Normalized:**
- $0.0002 \times 10^{-4}$
- $987.02 \times 10^{-9}$
- **Floating Point:** Representation for non-integral numbers, including very large or small numbers.
- In C, use `float` and `double`.
- The floating point standard is defined by IEEE Std 754-1985.
- **Format:**
- For `float` or single precision (32-bits).
- Signed Bit ($1$ means the number is negative)
- 8 Bits for Exponent
- 23 Bits for Fraction (also called *mantissa* or *significand*) = 1 + F
- Precision is ~$10^{-23}$, meaning everything after is not accurate.
- The standard makes sure the exponent is always positive by using a bias value.
- The range is $\pm 1.0 \times10 ^{-38}$.
- Range for `double` (64-bit) is bigger.
- Signed Bit
- 11 Bits for Exponent
- 52 Bits for Fraction
- Smallest Value = $\pm 2.2 \times 10 ^{-308}$
- Largest Value = $\pm 1.8 \times 10^{308}$
- Bias value also changes.
- Precision is ~$10^{-52}$, meaning everything after is not accurate.
- **Reserved Values:**
- `00000000`, which is equal to *infinity* and `11111111` means *not a number*.
- If we add anything to infinity we are going to overflow.
- There are rules in the standard for rounding up to a certain point.
- MIPS will not throw an exception when dividing by 0.
- x86 handles it differently.
- Bits do not mean anything unless we give them some context.
- Interpretation of bits depend on the instructions that are applied.
- An example is signed vs unsigned, where meaning changes.
- **Dynamic Memory Allocation:**
- `syscall 9`
- OS returns a pointer.
- MIPS Procedure Call Convention

View File

@ -0,0 +1,505 @@
> ### 004 - The Processor
> Class Notes - October 21, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#8B4513">Computer Organization</span>
> Fall 2021
- **Clock Cycles:**
- Clock signal is *squared* wave.
- Program counter gets incremented when we see the rising edge of the clock cycle.
- Each instruction is 4 bytes (32 bits).
- We add 4 each time to the program counter to jump to the next instruction.
- Dedicated adder to do this.
- If we jump to some address, there is a different adder to do this.
- Instruction Memory -> `.text`
- Data Memory -> `.static`
- Registers are separate from memory.
- They *feed* values into the ALU.
- **Multiplexers:**
- Needed *hardware-wise*.
- We cannot just join wires together.
- Multiple Inputs, One Output
- Abbreviated MUX.
- We can also have Demultiplexers, where we take one signal and output different lines.
- We can connect multiple multiplexers and *chain* them together.
- Binary Encoding of Information:
- We only have one wire per bit.
- Low Voltage = $0$
- High Voltage = $1$
- This is why we need multiplexers.
- Multi-bit data is encoded on multi-wire buses.
- **Combinational Element:**
- We have an input, we move through different logic steps, and get an output.
- Combining logical elements.
- If we make this too complicated, the delay will be too long and we have to increase the clock speed.
- **Example:**
- **CPU 1:**
- Input -> AND -> Output (1 ms)
- Input -> AND -> OR -> Output
- Input -> AND -> OR -> NOT -> Output (3 ms)
- **CPU 2:**
- Input -> AND -> Output
- Input -> AND -> OR -> Output
- Input -> AND -> OR -> NOT -> Output
- Input -> XOR -> AND -> NOT -> AND -> Output (4 ms)
- The clock speed for CPU 2 needs to be 4 ms long to make the last instruction work.
- We had to set our clock speed according to the longest instruction.
- A shorter period means a higher clock speed.
- CPU 1 will have a faster clock speed of 3 ms, because that is the length of the slowest instruction.
- Even though both are running the same instruction, our clock speed has to be slowed down in CPU 2 to compensate for the longest instruction.
- Simpler is Better
- If we make the instructions too complicated, we have to slow our processor.
- A solution could be dividing into multiple instructions.
- Combinational instructions act on data in a register, and produce some output.
- **Example:**
- `li $t0, 0` is a pseudoinstruction.
- What is actually doing is `ori $t0, $t0, 0`.
- It would be faster to do `xor $0, $t0, $t0`, because it is a single operation, we do not have to load $0$ in some place before doing the operation.
- Less parts of the processor are involved.
- We have to store outputs somewhere, which is usually a register.
- **Registers:**
- They store data in a circuit at the beginning of the clock cycle.
- They use a clock signal to determine when to update the stored value.
- When writing into a register, it
- <u>We read from a register when 0 changes to 1 (rising edge of the clock)</u>.
- Writing happens before the peak falls again.
- If this cannot happen fast enough, we have to slow the clock speed.
- We need to move slower or break into multiple instructions.
- As soon as we see the rise, we take the value from the registers and move them to the logical circuit.
- Before the peak falls, we need to be able to write the value into the destination.
- **Homework:**
- Read floats instead of integers..
- If we read an integer, we have to convert it.
- `syscall`:
- $2$ - Prints a float. [`$f12`]
- $9$ - Prints a string. [`$a0`]
- $6$ - Reads a float. [`$f0`]
- The `syscall` needs to be placed in `$v0`.
- Prompt (3), read first float (2), prompt (3), read second float (2), math (~4), print result (2).
- `add.s`, `div.s` (floats)
- `add.d`, `div.d` (doubles)
- Before reading second number, we need to place the value somewhere.
- This is because the `syscall` reads always into `$f0` and the value will get overwritten.
- We can use `swc1` to store floats / doubles in memory.
- Use `lwc1 $f1,` to load the value from memory.
- We can also use `mv.s`.
- To allocate dynamic memory, we need the `sbrk` syscall, which will return a memory address.
- `cvt.s.w $f12, $f12` to convert integer to floating-point.
- There are times where we use half-cycles.
- Rising Edge -> Writes
- Cycles between rising edges.
- **Combinational Element:**
- Logic (AND), adders, multiplexers, ALU.
- Inputs and Outputs.
- Does not actually store anything.
- **State / Sequential Element:**
- Registers.
- Store elements.
- We have a clock and a write signal (input), and an output.
- Flip-flops.
- First, send a signal to the register.
```py
and $s2, $s1, $s2
# We want to write back to s2.
# Send a signal to write.
# Once we have a rising edge, we write to the register.
```
- We only write at the moment we have a rising edge in the clock.
- We add 4 to the program counter to get to the next instruction (because MIPS is 32-bit).
- Longest delay determines how fast we can run our clock.
- Not a limitation for modern processors.
- **Homework:**
- Chapter 2 for the sort function.
- Section 2.13 (P 145).
- If we are in a leaf procedure, do `jr $ra`.
- Every time we do `jal`, we need to put `$ra` in the stack.
```py
.data
array # One single value, the memory address.
sbrk # Allocate memory needed for the array.
# Ask user.
# Bonus for floating input.
```
- **Jumps:**
- Control signal.
- An `R`-Instruction has an opcode, registers...
- Each of these registers (instructions) is 5 bits.
- Load and store are the simplest instructions.
- Add the data memory at the end.
- Our address can only be 16 bits.
- To get the actual address we have to do the sign extend.
- **Branching:**
- We need the registers and the circuit to manipulate the program counter.
- `beq $s1, $s2, address`
- Sign extend because we only have 16 bits and we want to jump to an address.
- Each instruction takes a set time to run.
- Whatever is the instruction that takes the longest time to run sets our clock cycle.
- **ALU Control:**
- We need to give 4 bits to the ALU to *tell it* what operation we want to do.
- These 4 bits tell the ALU what to do with the two registers.
- `ALUop` - What type of instruction the ALU is working with.
- It comes from the `opcode`. (6 bits)
- This decides what parts of the circuit we work with.
- Part of the 6 bits of the `opcode` contain the `ALUop`.
- Main control unit decodes the instruction and feeds the `ALUop` to the ALU control unit, which feeds it to the ALU itself.
- They all come from the Main Control Unit to begin with.
- `sbrk $a0 $v0`
- `$a0` is the amount (in bytes).
- `$v0` will contain the address.
- We need to use `syscall`
```py
.data
# Initializes a word to '0'.
# Consider a word is 4 bytes.
array: .word 0
size: .word 0
# syscall for sbrk.
li $v0, 9
# The amount of bytes we want to allocate.
# If the user specifies we need 10 integers...
mov $a0, $t0
# Consider an integer is 4 bytes.
multi $a0, 4
# Can also be done as:
sll $a0, $a0, 2
syscall
# Depends on the operating system how memory is allocated.
# v0 will contain the address.
# We want to store the address of v0 into the array.
# A zero-offset from the array.
sw $t0, 0(size)
sw $v0, 0(array)
# -----------
# To access first element.
lw $t1, 0(array)
# If we are doing bubble sort, this position will change.
# We need an additional register to keep track of where we are in the array.
# We can load the address in $t2.
lw $t2, 0(array)
# To move to the next index, we can add 4.
# 4 bytes / integer
addi $t2, $t2, 4
# Now, we can load the second element.
lw $t1, 0($t2)
```
- Zero index stored in `.array`.
- First 6 bits are the opcode, which turn on/off different parts of the circuit.
- Early decoding for ALU control.
- ALUop tells ALU Control what operation we want to perform.
- At the rising edge of the clock cycle, whatever comes out of the ALU gets stored in the register.
- **Branch-on-Equal:**
- Result of subtraction comes out of ALU and into multiplexer.
- AND needs both inputs to be $1$.
- If we have $x-y = 0$, then $EQ = 1$.
- If we have $x-y \neq 0$, then $EQ=0$.
- The control unit feeds the address for the jump into the multiplexer.
- 3 ways to change the program counter.
- Anything that is not a jump or branch simply adds 4 to the program counter.
- If we want to do a jump, the address goes into the multiplexer and into the program counter.
- `j` leaves 26 bits for the address.
- 6 bits are taken by the `opcode`.
- Addresses are 32 bits.
- What happens is we use 4 bits from the program counter (the most significant bits).
- We pad with some $0$s.
- We place the address.
- This prevents us from jumping to any memory address we want.
- The very last two bits are $00$, which allow us to *move up* by 4 each time.
- This is why they stay as $00$.
- We cannot jump out from whatever address range is specified by the first 4 significant bits.
- Limited by 26 bits of the space we are inside of.
- If we wanted to jump to a higher address, we use `jr $ra`.
- Slower because we need to place an address in a register and use additional parts of the circuit.
- Clock speed is as fast as we can load from memory.
- We cannot jump any faster than this.
- Slowest instruction determines this.
- **Pipelining:**
- Overlapping execution.
- **MIPS Pipeline:**
- Five stages that we can stagger:
1. Fetch
2. Decode
3. Execute operation.
4. Read
5. Store
- All of these components are doing different operations and we can pipeline.
- We can pipeline so that no part of the circuit remains idle.
- Fetch takes more time because we need to grab the instruction from memory.
- Single cycle would take 800 ps, 4 times as long compared to overlapping stages.
- If we can pipeline, theoretically we can get it down to 200 ps by using parts of the circuits that would otherwise be idle.
- If all stages took the same amount of time, we have the maximum speedup.
- If not, the speedup is less.
- <u>To get the maximum speedup, all steps would have to take the same time</u>.
- **Throughput and Latency:**
- Throughput - 200 Mbit/s (How much you can move at a given time)
- Latency - 150 ms (How long it takes for the first packet)
- Depending on what we want to do we might pick one over the other.
- In MIPS, all instructions are 32 bits, which are easier to fetch and decode in one cycle.
- Generates less heat and consumes less power.
- **Hazards:** Situations that prevent starting the next instruction in the next cycle.
- **Structural Hazards:**
- *Pipeline Stall* or *Bubble*
- One instruction may take longer than we expect it to.
- The next instruction (in pipelining) will not be able to run.
- This affects all things that are being pipelined.
- Loading and storing would cause a stall.
- Pipelined datapaths require separate instruction/data memories or separate instruction/data caches.
- One way to fix this is having multiple channels that we can read from.
- There has to be a controller that communicates with each memory cell.
- The CPU connects with the memory controller.
- What we could do is have multiple connections between the controller and the CPU.
- The issue now is if the controller can access different cells at the same time.
- **Data Hazards:**
- An instruction depends on competition of data access by a previous instruction.
``` py
# Consider s0 = 0, t0 = 5, t1 = 3.
# | Fetch | Decode | Execute | Read Memory | Write Memory
add $s0, $t0, $t1
# We want to start this instruction after fetching the previous one.
# | | Fetch | Decode | Execute | Read Memory | Write Memory
# We have a problem because the previous instruction is writing the value
# almost at the same time we are trying to read it.
# If we have not written the value, t0 is still 0.
# If we try to pipeline without considering this, we would get the wrong result.
sub $t2, $s0, $t3
```
- **Possible Solutions:**
- Force it to **stall** if this is going to happen.
- **Forwarding** or bypassing the value to the next instruction instead of writing and reading it.
- Use result when it is computed.
- Do not wait for the value to be stored in a register.
- Needs extra connections in the datapath.
- We cannot forward backwards.
- Instruction reordering. Move the instruction further down the pipeline so that the value will have already been written.
```text
| Load | Fetch | Execute | Read | Write |
| | Load | Fetch | Execute | Read | Write
```
- We are trying to read a value that has not been written yet.
- We can move the instruction down the pipeline.
- Without adding extra circuits this is possible.
- Both processors trying to read and write the same place in memory.
- Data hazards and control hazards cannot be avoided.
- **Data Hazard:**
- We need to wait for the previous instruction to complete its data read / write.
- Stall, forward, re-order the instructions.
- **Control Hazard:**
```c
while (i != 3)
{
// Do something...
i++;
}
```
```py
# s0 = i
# s1 = 3
beq $s0, $s1, done # Branch if s0 == s1.
addi $s0, $s0, 1 # i++
```
- When doing this instruction: `beq FETCH | DECODE | EXECUTE | (...)`.
- Whenever we start decoding, we can start fetching the next instruction.
- But this depends on running the next instruction.
- When we branch, we do not know what the next instruction might be, because it depends on the condition being true or false.
- Is our next instruction `add` or `sub`?
- One thing we can do is simply **stall**.
- Once that happens, we can figure out what the branch will do.
- But this means stall for 3 cycles.
- Another solution is doing **branch prediction**.
- If we run the loop 100 times and we assume the next instruction every time, we would be correct 100 times and wrong just one time.
- This would still speed up our program.
- Static Branch Prediction:
- *We know we will only be wrong one time.*
- Based on typical branch behavior.
- Examples include loop and if-statement branches.
- Dynamic Branch Prediction:
- *Values change over time.*
- Dynamic uses a table to predict how likely is that we branch or not.
- Assume future behavior will continue the trend.
- When prediction is wrong, stall while re-fetching and update history.
- Pipelines make programs faster because they allow running multiple instructions at the same time.
- Data Hazards can be resolved using forwarding.
- We cannot forward backwards.
- Forwarding from register to register cannot be done at the same time.
- Detecting the need to forward:
- Forward from EX/MEM pipeline register and forward from MEM/WB pipeline register.
- We can try to write to the zero register to stall, as `$zero` is read-only.
- Hazards as a result of pipelining.
- To implement pipelines, we add stage registers between each step.
- Forwarding requires additional hardware.
- **Forwarding Unit:**
- Checks conditions and sends data to the appropriate destination.
- Check for `!= $zero` so that we are not writing to the `$zero` register.
- **Forwarding Conditions:**
- Control signals for the multiplexers.
- Not having a `$zero` register makes the circuit more complicated.
- **Load-Use Data Hazards:**
- We cannot forward backwards.
- First Clock Cycle `lw`
- Second Clock Cycle `and`:
- Cannot move forward until we read the data from the register.
- When decoding, we have not even reached the point of reading the memory for `lw`.
- We cannot possibly forward the result of the previous instruction to the `and`.
- We have to stall for a clock cycle and then we can do the forward.
- The CPU has to know that this will not work.
- If it tried to move on, the values would not be correct.
- We need to add a *Hazard Detection Unit*:
- It looks for a load instruction.
- Checks to see if the write register signal is active.
- It inserts an artificial `nop`.
- Sets all control signals to $0$.
- It also stops the program counter from moving forward so we do not delete an instruction and then load $0$ into the next instruction.
- Compiler arranges code to avoid hazards and stalls.
```py
lw $s2, 20($s1)
and $s4, $s2, $s5
```
- **Branch Hazards:**
- When branching, we may load instructions we do not need.
- The solution is to assume that we are not going to take the branch.
- If we do take the branch, then we have to flush the instructions.
- This would slow the pipeline only for this particular point.
- We could also stall, but then we slow down the pipeline.
- Cannot compare a value that has not been written yet.
- We can also do forwarding, except if the instructions are too close together
- Hardware to detect when we will have this issues and forward / stall.
```py
lw $t1, address
add $t4, $t5, $t6
# If we do this without the stall, at the point we want to grab
# the value from $t4 will not have been written yet.
# We have to stall for at least one clock cycle.
beq stalled
# We could forward, but the ALU has not finished writing the value yet.
# So, we must stall for at least one clock cycle.
beq $t1, $t4, target
```
- In static prediction, if we are wrong, it will cost as much as stalling.
- **Dynamic Branch Prediction:**
- If we assume we are not going to branch, then 81% of the time we are wrong.
- 1-bit predictor.
- First time the predictor is 0. We are going to be wrong the first time.
- We change the predictor to 1.
- We would only be wrong 2 times (first and last).
- We are only wrong 20% of the time.
```c
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10; j++)
{
}
}
```
- We can use a 1-bit predictor.
- When we have a branch, we have a $1$ for taken, and a $0$ for not taken.
- To improve, we can use a 2-bit predictor.
- Add an extra bit.
- Assume the very first time we are not going to take the branch.
- Only change prediction on two successive mispredictions.
- Predict we are not going to loop the first time (wrong).
- **Calculating the Branch Target:**
- Branch target buffer that can pre-load some instructions.
- 1-Bit Predictor with Nested Loop:
- We would be wrong every time we go back to the outer loop.
- 2-Bit Predictor with Nested Loop:
- We would only be wrong two times at the beginning.
- Only wrong once for the outer loop.
- **Branch Prediction:**
1. Assume we are not going to branch.
2. We have a 1-bit predictor.
3. We have a 2-bit predictor. (Good for Nested Loops)
4. Lookup Tables
5. Tournament Predictor
- `osdev.org`
- **Exceptions and Interrupts:**
- Different architectures use both terms interchangeably.
- In MIPS, everything is treated as an exception.
- Interrupt only when externally caused.
- **Interrupt:**
- Save the old program counter.
- CPU stops.
- Update the program counter with an appropriate *interrupt handler*. It is invoked by the operating system.
- Once done, it will restore the original program counter and continue.
- The next instructions are flushed out of the pipeline when handling the interrupt and restored after.
- We have an Exception Program Counter (EPC).
- **Hazard Detection Unit:** Used to detect issues in the pipeline.
- Programmable Interrupt Controller (PIC):
- When writing in the keyboard. the PIC handles it to the CPU.
- Sends a signal to the processor from #.
- The operating system has a table that lists all interrupt numbers and a memory address for each to the code that handles them.
- This code goes on the reserved memory addresses.
- Saves everything and jumps to the interrupt handler. [`j HANDLER`]
- When coming back from an exception... `j $ra` similar special instruction.

View File

@ -0,0 +1,85 @@
> ### 005 - Exploiting Memory Hierarchy
> Class Notes - December 07, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#8B4513">Computer Organization</span>
> Fall 2021
- SRAM
- DRAM
- Locality:
- Temporal
- Spatial
- Cache Hit/Miss
- Cache Write - Dirty
- Normal memory uses DRAM.
- **Static RAM (SRAM):**
- 0.5 to 2.5 nanoseconds to read.
- Registers commonly use SRAM.
- More expensive.
- More transistors, more parts, and takes more room.
- This is why we cannot physically fit a lot of SRAM into he processor.
- <u>Cache uses SRAM</u>.
- Smaller chips are cheaper to produce.
- **Dynamic RAM (DRAM):**
- 10 times lower than SRAM.
- ~20 clock cycles.
- Just uses one transistor.
- **Magnetic Disks:**
- 5 to 20 ms to read.
- ~100 times slower.
- We can use all of these technologies in a hierarchy.
```mermaid
graph LR
Register --> Cache
Cache --> Memory
Memory --> Disk
Disk --> Network
```
- **Temporal Locality:**
- A `for` loop.
- **Spatial Locality:**
- Reading from an array. (Values are in a space close together)
- **Cache Misses:**
- We store first 5 elements of an array in the cache.
- When we want to read element 6 it will not be on cache.
- We need to flush the previous elements and load the next ones.
- We have to stop and wait for this to happen.
- Complicated hardware.
- If we want to write into memory...
- Store it in a register, which communicates with the cache, which communicates with memory.
- Different architectures work in different ways.
- Some implementations are better than others.
- We need to ark memory somehow (Dirty).
- **Multi-Level Caches:**
- L1 (fastest), L2 (5 ns), L3 (20 ns, RAM)
- L1 more expensive.
- Physical space increases with each.
- **Virtual Memory:**
- Each program *sees* the same setup.
- The operating system *translates* in between.
- This allows running multiple things at the same time.
- Address memory we do not have.
- **Paging:**
- Allocate space in a hard drive (page file).
- If there is something in RAM that has not been used, it gets written to the hard drive and frees memory so it is available.
- Operating system makes this work.
- This is slower.
- If this is not done that often, it goes unnoticed.
- Data cache that read from memory.
- **Instruction Cache:**
- Complicates pipelining.
- Caching instructions -> Branch -> Stall CPU -> Re-read into cache

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,110 @@
> ### E001 - Midterm Review
> Extra Notes - October 10, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#8B4513">Computer Organization</span>
> Fall 2021
```py
# syscall number in $v0
# syscall 4 is equivalent to print.
li $v0, 4
# Argument in $a0
la $a0, str1
# We also need to put the maximum length into $a1
```
- **Chapter 1:**
- Progression of computers getting better. [Moore's Law]
- Number of transistors doubles.
- Nowadays, there are issues with keeping up with it.
- **Parallel Processing:**
- Limits on how fast we can make a program because there are sequential aspects that cannot be parallelized.
- Amdahl's Law
- **Concluding Remarks:**
- Cost per Performance (Computers are getting cheaper and better).
- We are limited by power.
- **Chapter 2:**
- Page 205 (Everything, except division)
- **Data Formats:**
- Translate small numbers between bases
- 0xF, where x means HEX.
- If there is not anything, then it is decimal.
- 0b is binary.
- Each hexadecimal value takes 4 bits.
- 0xFFFF is 16 bits.
- 0xFFFFFFFF is 32 bits.
- Every address in SPIM is a 32-bit hexadecimal value.
- **Complement:**
- 0xF000 is a 16-bit value.
- If we are told this is a signed integer and we see an F at the beginning, we automatically know is negative.
- In binary, it would be 1111 0000 0000 0000
- Consider 0xF
- If it is unsigned, it is 15.
- If it is signed, it is -1.
- 1111 -> Complement -> 0001, which is 1, and add the negative makes it -1.
- If we are told 0xF is 8-bit:
- 0000 1111, so the complement would be different.
- Instruction Sets & Instructions
- Adding and Subtracting, Logical Operators
- **3 Different Kind of Instructions:** All instructions are 32-bit:
- `R` Instructions: Instruction, Destination, Source, Source, Amount... [Use Registers]
- `I` Instructions: Instruction, Register, Register, Immediate Value [Use Immediate Values]
- `J` Instructions: Instruction (op code), Address [Use addresses]
- There are only so many bits that we can put in here.
- We cannot put a full 32-bit address.
- We can jump to a register instead.
- Stack grows downward, heap goes upward.
- There is no line dividing them.
```text
RESERVED [0xFFFFFFFF]
USER SPACE:
{
STACK
HEAP / DYNAMIC DATA
STATIC -> .data
.text [0x4000] -> Our program is in here.
}
RESERVED [0x00000000]
```
- **Chapter 3:**
- Know how to add, subtract, and multiply numbers.
```py
# The first argument is a destination register.
# The second argument is an offset.
lb $t0, 0($t1)
# Assuming t1 is the start of an array.
# The offset is how far
```
- **Character Arrays:**
- Each character occupies 1 byte, which is 8 bits.
- **Integer Arrays:**
- If we have 32-bit integers.
- Each integer occupies 4 bytes.
- `lb` is useful for reading character arrays.
- When we add binary numbers, we have a carry.
- Know when we can overflow.
- If the signs are different when we are adding, then we do not get an overflow.
- if we multiply 2 32-bit numbers we can get a 64-bit result.
- All registers in MIPS are 32-bit.
- It gets stored in `HI` and `LO`.
- `slt` shifts left.
- **Program Counter:**
- Shows the address of the instruction that is about to be executed.
- `jal` would set the program counter to the instruction we give it.

View File

@ -0,0 +1,89 @@
> ### E002 - Final Review
> Extra Notes - December 09, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#8B4513">Computer Organization</span>
> Fall 2021
- **Division:**
- How to do division in MIPS.
- In what registers does everything go.
- `HI` (remainder), `LO` (quotient).
- Both 32-bit.
- `div` and `divu`.
- How it works in the processor.
- **Floating Point:**
- What it is.
- How it works.
- We work on the co-processor.
- `add.s` - Single Precision
- `add.d` - Double Precision
- **Chapter 4:**
- 1 Clock Cycle = 1 Instruction
- Whatever instruction s takes the longest, we have to match the clock cycle to it.
- Anything that would run faster would not due to this.
- Pipelining - in what order are things done?
- Slide 49 MIPS Pipelined Datapath
- **Pipeline Stages:**
- 1 - Instruction Fetch:
- Instruction Memory
- Program Counter
- Adder
- After each instruction, we increment the program counter by 4.
- 2 - Decode:
- Involves registers.
- Sign extend.
- 3 - Execute:
- Use of the ALU
- Adder that can modify the program counter.
- 4 - Memory Access:
- *Data memory* used during memory access.
- 5 - Write Back
- Feed result in multiplexer (multiple inputs and one output, we tell which input we want output from) which will write it to a register file.
- **Trade-offs and Drawbacks of Pipelining:**
- Makes things faster but can cause issues.
- Writing back something that has not been calculated.
- Fixes:
- **Data Hazards:**
- **Forward:**
- Needs additional hardware.
- Not always possible because there may not be enough cycles in between.
- **Stall:**
- `nop` operation.
- Always works, but slows our pipeline.
- Instruction Re-Order (No additional hardware needed):
- Easiest but not always helpful.
- **Control Hazards:**
- Caused when branching.
- Fixed using prediction, which needs at least 1-bit. Better when using 2-bits. Even better using a prediction table.
- **Static and Dynamic Prediction:**
- Mostly dynamic. We need additional storage and works better.
- Static does not need any additional storage. Assumptions.
- Needs more hardware to detect the hazard and to store the prediction.
- If we need to stall we have to flush out the pipeline.
- Each pipeline has a register in between (Pipeline Register).
- Control at the top about where data is going.
- **Exceptions:**
- **Exceptions**
- **Interrupts:** I/O. Exception $0$.
- Both use the same hardware.
- Differentiated based on what caused them.
- Jump to the **exception handler**.
- Exception Program Counter (EPC).
- Jump back to the program.
- **Structural Hazards:**
- Two instructions reading / writing same location.
- Different parts of the processor.
- **Chapter 5:**
- Hierarchy of memory.
- Registers are the fastest memory. -> SRAM
- L1
- L2
- L3
- System RAM -> DRAM
- Storage
- GPU follows the same scheme.
- It also has Divide RAM.

View File

@ -0,0 +1,401 @@
> ### SG001 - Midterm Study Guide
> Study Guide
> Emilio Soriano Chávez
> ***
> <span style="color:#8B4513">Computer Organization</span>
> Fall 2021
***
### Chapter 1
- **Moore's Law:**
- States that <u>integrated circuit resources double every 18-24 months (~2 years)</u>.
- Because computer design can take years, the resources (*transistors*) per year would double or triple during this time.
- Moore's Law is no longer accurate today. In recent years, the rate has been doubling every 3 or more years.
- **Transistor:** An on/off switch controlled by an electric signal.
- **Integrated Circuit (Chip):** Device combining dozens to millions of transistors.
- **Systems Software:** Provides useful services, like operating systems, compilers, loaders, and assemblers. *Located* between the hardware and applications software.
- **Binary Digit (Bit):** One of the two numbers in base 2, $0$ or $1$.
- **Instruction:** Command that computer hardware understands and obeys.
- **Assembler:** Program that translates a symbolic version of instructions into the binary version.
- Essentially, it translates *Assembly Language* into *Machine Language*.
- **Example:** An assembler translates something like `add A, B` into `1000110010100000`.
- **Levels of Abstraction:**
- **Assembly Language:** Symbolic representation of machine instructions.
- **Machine Language:** Binary representation of machine instructions.
- **High-Level Programming Language:** Portable language composed of words and algebraic notation that <u>can be translated by a compiler into assembly language</u>.
![[A001 - Abstraction.png | 300]]
- **Central Processor Unit (CPU) / Processor:**
- Active part of the computer.
- Contains the datapath and control.
- **Datapath:** Component of a processor that performs arithmetic operations.
- **Control:** Component of a processor that commands the datapath, memory, and I/O devices.
- Adds numbers, tests numbers, and signals i/O devices to activate.
- **Memory:** Storage area where programs are kept when they are running.
- <u>It also contains the data needed by the running programs</u>.
- **Dynamic Random Access Memory (DRAM):** Memory built as an integrated circuit. Provides random access to any location.
- **Cache Memory:** Small, fast memory that acts as a buffer for a slower, larger memory, like DRAM. Built using SRAM.
- **Static Random Access Memory (SRAM):** Memory built as an integrated circuit. Faster and less dense than DRAM.
- **Instruction Set Architecture (Architecture):**
- Abstract interface between the hardware and the lowest-level software.
- Includes all information necessary to write a machine language program that will run correctly.
- Usually taken care of by the Operating System.
- **Main / Primary Memory:** Volatile memory used to hold programs wile they are running. Usually consists of DRAM.
- **Secondary Memory:** Nonvolatile memory used to store programs and data. Usually consists of magnetic disks or flash memory.
- **Performance:** There are multiple ways to define/measure performance:
- **Response Time:** Also called execution time. <u>Total time required for a computer to perform a task</u>. Usually of interest to individual computer users.
$\text{Performance}_{x} = \dfrac{1}{\text{Execution Time}_x}$
- **Throughput:** Also called bandwidth. <u>Number of tasks completed per unit time</u>.
- **CPU Time:** Time the CPU spends computing for a specific task.
- **User CPU Time:** CPU time spent in a program.
- **System CPU Time:** CPU time spent in the operating system performing tasks on behalf of a program.
- **The Power Wall:**
- Clock rate and power in microprocessors increased over 30 years, but recently this has flattened.
- We have a power limit for cooling microprocessors. Power is dissipated as heat and needs to be removed.
- Increasing the number of transistors increases power dissipation.
- Lowering voltage reduces power, but there is a limit to this.
- We cannot keep lowering the voltage and we cannot remove any more heat, so to optimize performance we can use parallelization.
- Previously, innovations in hardware doubled performance every 2 years. Today, to increase performance, we need to rewrite programs to take advantage of multiple processors.
- **Parallelism:**
- Done by using multiple processors.
- Parallel programming increases difficulty of programming.
- The programmer must divide an application so each processor has the same amount of work at the same time.
- There are limits on how fast we can make a program because there are sequential aspects that cannot be parallelized.
- **Amdahl's Law:**
- States that the performance enhancement possible with a given improvement is limited by the amount that the improved feature is used.
- We cannot expect the improvement of one aspect of a computer to increase the overall performance by an amount proportional to the size of the improvement.
- The cost per performance of computers is and will keep improving. Computers are getting better and cheaper.
***
### Chapter 2
- **Instruction Set:** Vocabulary of commands understood by a given architecture.
- In MIPS, each line of a program can only contain one instruction.
- **Registers:** Primitives used in hardware design that are also visible to the programmer.
- In MIPS, <u>the size of a register is 32 bits</u>.
- **Word:** Natural unit of access in a computer. <u>Group of 32 bits</u>.
- MIPS computers have a limited number of registers, which is 32 registers.
- To represent a register, use a dollar sign (`$`) followed by the two-character name of the register. (`$s0`)
- **Data Transfer Instructions:** Commands that move data between memory and registers.
- Because arithmetic operators can only occur on registers in MIPS, we need to be able to transfer data between memory and registers.
- To access *words* in memory, we can supply a memory address to the instruction.
- **Address:** Value used to delineate the location of a specific data element within memory.
- **Byte:** 8-bits.
- A 32-bit *word* is 4 bytes.
- The memory address of a *word* would match the address of the first byte.
- Addresses of sequential words differ by 4 bytes.
- **Alignment Restriction:**
- <u>In MIPS, words must start at addresses that are multiples of 4</u>.
- MIPS is in the *big-endian* camp, meaning it uses the leftmost byte as the word address.
![[A002 - MIPS Addresses.png | 250]]
- **Constant and Immediate Operands:**
- Many programs use a constant in their operations.
- Instead of loading a constant from memory into a register, we can use an immediate operand.
- **Signed and Unsigned Numbers:**
- Numbers in computer hardware are kept as a series of high and low electronic signals (base 2 numbers or *binary numbers*).
- **Overflow:** When a number that is the proper result of an arithmetic operation cannot be represented by the rightmost bits.
- **Two's Complement Representation:** Convention for representing signed binary numbers where <u>leading $0$s mean a positive number, and leading $1$s mean a negative number</u>.
- **Sign Bit:**
- This representation has the advantage that <u>all negative numbers have a $1$ in their most significant bit</u>.
- The number $0$ in the most significant bit is then considered positive.
- **Example:**
- `0111 1111 1111 1111 1111 1111 1111 1111 = 2,147,483,647`
- `1000 0000 0000 0000 0000 0000 0000 0000 = -2,147,483,648`
- `0000 0000 0000 0000 0000 0000 0000 0000 = 1`
- `1111 1111 1111 1111 1111 1111 1111 1111 = -1`
- **Instruction Format:**
- <u>All MIPS instructions are 32 bits long, the size of a *word*</u>.
![[A003 - MIPS Instruction Format.png | 600]]
- **R Instructions (`R-Type`):** `R` stands for *register*:
![[A004 - R Instructions.png | 500]]
- **`op` (Opcode):** Basic operation of the instruction.
- **`rs`:** First register source operand.
- **`rt`:** Second register source operand.
- **`rd`:** Register destination operand. Gets the result of the operation,
- **`shamt`:** Shift amount.
- **`funct` (Function Code):** Selects the specific variant of the operation in the `op` field.
- **I Instructions (`I-Type`):** `I` stands for *immediate*:
![[A005 - I Instructions.png | 500]]
- **`op` (Opcode):** Basic operation of the instruction.
- **`rs`:** First register source operand.
- **`rt`:** Destination register.
- **`constant or address`:** The 16-bit address allows loading from any of the 32 registers.
- **J Instructions (`J-Type`):**
- Has 6 bits for the operation field, and the rest for the address field.
- **Logical Operations:**
| Logical Operation | C++ Operator | MIPS Instruction |
|:-----------------:|:------------:|:----------------:|
| Shift Left | `<<` | `sll` |
| Shift Right | `>>` | `srl` |
| Bitwise AND | `&` | `and, andi` |
| Bitwise OR | `|` | `or, ori` |
| Bitwise NOT | `~` | `nor` |
- **Shifts:**
- Move all the bits in a word to the left or right.
- **Shift Left Logical `sll`:**
- *R Instruction* with the form `sll rd, rt, shamt`
- `shamt` refers to the *shift amount*.
- <u>Shifting left by $i$ bits is the same as multiplying the number by $2^i$</u>.
- **Example:**
- `$s0 = 0000 1001 = 9`
- `$t2 = 1001 0000 = 144` [Result]
```py
sll $t2, $s0, 4
```
- **Shift Right Logical `srl`**
- **`AND`:**
- Bitwise operation that gives $1$ in the result only if both bits of the operands are $1$.
- It can be used to *mask* a set of bits by applying a bit pattern and forcing $0$s when there is a $0$ in the pattern.
- **Example:**
- `$t2 = 0000 1101 1100 0000`
- `$t1 = 0011 1100 0000 0000`
- `$t0 = 0000 1100 0000 0000` [Result]
```py
and $t0, $t1, $t2
```
- **`OR`:**
- Bitwise operation that gives $1$ in the result if either operand bit is a $1$.
- **Example:**
- `$t2 = 0000 1101 1100 0000`
- `$t1 = 0011 1100 0000 0000`
- `$t0 = 0011 1101 1100 0000` [Result]
```py
or $t0, $t1, $t2
```
- **`NOT` and `NOR`:**
- `NOT` takes one operand and places a $1$ in the result if one operand bit is a $0$, and vice versa.
- <u>To keep the *three-operand format*, MIPS does not include a `NOT` operation. Instead, it uses `NOR`</u>.
- `NOR` is a bitwise operation with two operands that calculated the `NOT` of the `OR` of the two operands. This means it gives a $1$ only if there is a $0$ in both operands.
- This means that <u>we can use `NOT` by having one of the operand be $0$</u>.
- **Example:**
- `$t1 = 0011 1100 0000 0000`
- `$t3 = 0`
- `$t0 = 1100 0011 1111 1111` [Result]
```py
nor $t0, $t1, $t3
```
- For arithmetic operations, MIPS provides *immediate* versions of `AND` and `OR`, which can be used for constants; but not for `NOR`.
- **Immediate AND (`andi`)**
- **Immediate OR (`ori`)**
- **Conditional Branches:**
- These instructions require the comparison of two values.
- They allow the transfer of control to a new address in the program based on the outcome of the comparison.
- **Branch If Equal (`beq`):**
- It can be translated as:
> Go to the statement labeled `L1` if the value in `$t0` **is equal to** the value in `$t1`.
```py
beq $t0, $t1, L1
```
- **Branch If Not Equal (`bne`):**
- It can be translated as:
> Go to the statement labeled `L1` if the value in `$t0` **does not equal** the value in `$t1`.
```py
bne $t0, $t1, L1
```
- **Set On Less Than (`slt`):**
- Sometimes, it is useful to see if a variable is less than another variable.
- It can be translated as:
> Set the value in $t0 to $1$ if the value in $s1 is **less than** the value in $s2.
```py
slt $t0, $s1, $s2
```
- Because constant operands are also commonly used, there is an immediate version of the set on less than instruction called (`slti`).
```py
slti $t0, $s1, 10
```
- <u>MIPS does not include *branch on less than*</u> because it would be too complicated; it would stretch the clock cycle time. Having two faster instructions is more useful.
- **Comparing Signed and Unsigned Integers:**
- **Signed Integers:** A $1$ in the most significant bit would represent a negative number, which is less than any positive number, which would have a $0$ in the most significant bit.
- **Unsigned Integers:** A $1$ in the most significant bit would represent a number that is larger than any that has a $0$ in the most significant bit.
- To handle the alternatives, MIPS has additional versions of the *set on less than* command:
- **Signed Integers:**
- `slt` - Set On Less Than
- `slti` - Set On Less Than Immediate
- **Unsigned Integers:**
- `sltu` - Set On Less Than Unsigned
- `sltiu` - Set On Less Than Immediate Unsigned
- **Procedures:**
- Registers are the fastest place to hold data in a computer.
- In MIPS, the following conventions are used:
- `PC`: Program counter. Shows the address of the instruction that is about to be executed.
- `$t0-$t9`: Temporary registers that are not preserved by the callee on a procedure call.
- `$s0-$s7`: Saved registers that must be preserved on a procedure call.
- `$a0 - $a3`: Four argument registers in which to pass parameters.
- `$v0 - $v1`: Two value registers in which to return values.
- `$ra`: One return address register to return to the point of origin.
- `$sp`: Stores the pointer to the last element that was added to the stack.
![[A006 - MIPS Register Conventions.png | 500]]
- **Jump-and-Link (`jal`):**
- Can be translated as:
> Jump to an address and save the address fo the following instruction in register `$ra`.
```py
jal MyProcedure
```
- **Return Address:** Address that points to the calling site to return to the proper address, which gets stored in `$ra`.
- **Jump Register (`$ra`):**
- Copies the given address to the program counter.
- Can be translated as:
> Jump to the address stored in `$ra`.
```py
jr $ra
```
- To create a procedure, we can do the following instructions:
1. The **caller**, or *calling program* places the parameter values in registers `$a0-$a3`.
2. Caller uses `jal X` to jump to procedure `X` (the **callee**).
3. Callee performs calculations and places results in `$v0`.
4. Callee returns control to the caller using `jr $ra`.
- **Program Counter (`PC`):** Register that holds the address of the current instruction being executed.
- This would mean that to store the next instruction in `$ra`, we would do `PC + 4`.
- If a program needs more registers for a procedure than the four argument and two return value registers, it must make use of a *stack*.
- **Stack:** Data structure for spilling registers.
- **Stack Pointer:** Pointer to the most recently allocated address in the stack. Adjusted by one word for each register that is saved or stored.
- In MIPS, the stack pointer is stored in register `$sp`.
- Stacks grow from higher to lower addresses. Therefore, to add values we subtract from the stack pointer, and to remove values we add to the stack pointer.
- **Leaf Procedures:**
- Procedures that do not call other procedures.
- If a procedure calls another procedure that requires the registers it uses, we have a conflict.
- A solution is to push the registers that must be preserved onto the stack.
- **Allocating Space for New Data:**
- **Stack:** Starts in the high end of memory and grows down.
- **Text Segment:** Contains the machine language code for routines in the source file. It has the MIPS machine code.
- **Static Data:** Contains constants and static variables.
- **Heap:** Contains Dynamic Data.
- This means that the heap and the stack grow in the direction of each other.
![[A007 - MIPS Memory Allocation.png | 250]]
- **ASCII Representation of Characters:**
![[A008 - ASCII Table.png | 550]]
- **Load Byte (`lb`):**
- Reads a byte from a source.
> Loads a byte from memory and stores it in the rightmost 8 bits of a register.
```py
lb $t0, 0($sp)
```
- **Store Byte(`sb`):**
- Writes a byte to a destination.
> Takes the rightmost 8 bits of a register and writes them to memory.
```py
sb $t0, 0($gp)
```
- **Unicode Representation of Characters:**
- Uses 16 bits to represent a character.
- **Halfword:** A 16 bit quantity.
- To load and store 16 bit quantities, MIPS has additional instructions:
- **Load Half (`lh`):**
> Loads a halfword from memory and places it in the rightmost 16 bits of a register.
- **Load Half Unsigned (`lhu`):**
- Works with unsigned integers.
> Reads a halfword (16 bits) from a source.
```py
lhu $t0, 0($sp)
```
- **Store Half (`sh`):**
> Takes a halfword from the rightmost 16 bits of a register and writes it to memory.
```py
sh $t0, 0($gp)
```
- **Loading 32-bit Immediates and Addresses:**
- Sometimes, constants may be bigger than the 16-bit field.
- To allow 32-bit immediates, we can load the upper and lower 16 bits of the immediate in a register, respectively.
- **Load Upper Immediate (`lui`):** Sets the upper 16 bits of a constant in a register.
- We can then use `ori` to add the lower 16 bits to the register.
- **Translation Hierarchy:**
![[A009 - Translation Hierarchy.png | 400]]
***
### Additional Information
- **`$at`:** Register $1$ in MIPS. Used as a temporary register by the assembler.
- <u>MIPS is Big Endian, meaning the most significant byte is at the least address of a word</u>.
- **`$zero`:** Register $0$ in MIPS. Contains the constant $0$. <u>Cannot be overwritten</u>.
- **`j`:** Unconditional jump to an instruction label.
```py
j Label
```
- <u>The assembler will automatically calculate the addresses of labels</u>.
- `t` registers can be overwritten by callee, but `s` registers must be saved or restored by callee.
- To add something to the stack, use `sw $ra, 4($sp)`
- To adjust the number of items in the stack, use `addi $sp, $sp, -8`.

View File

@ -0,0 +1,589 @@
> ### SG002 - Final Study Guide
> Study Guide
> Emilio Soriano Chávez
> ***
> <span style="color:#8B4513">Computer Organization</span>
> Fall 2021
***
### Chapter 3 - Arithmetic for Computers
- **Binary Division:**
![[A010 - Binary Division.png | 200]]
- **Dividend** The number being divided.
- **Divisor:** Number that the *dividend* is divided by.
- **Quotient:** Primary result of a division.
- **Remainder:** Secondary result of a division.
- These components are related by the following:
$\text{Dividend} = \text{Quotient} \times \text{Divisor} + \text{Remainder}$
- **Division Hardware:**
![[A011 - Division Hardware.png | 400]]
1. The 32-bit quotient register is set to $0$.
2. The 32-bit divisor is placed in the left half of a 64-bit register.
3. Subtract the divisor register from the remainder register.
- If the result is **positive**, the divisor was smaller or equal to the dividend. This generates a $1$ in the quotient.
- If the result is **negative**, we need to restore the original value by adding the divisor back to the remainder. This generates a $0$ in the quotient.
4. The divisor is *shifted right* by $1$ bit to align it with the dividend.
5. Repeat the algorithm 32 times (every bit).
- **Signed Division:**
- We need to *remember* the signs of the divisor and the dividend.
- Then, divide the *absolute values*.
- <u>If the signs of the divisor and the dividend are opposite, negate the quotient</u>.
- <u>The sign of a nonzero remainder must match the sign of the dividend</u>.
- **Division in MIPS:**
- Uses the same hardware as multiplication.
- <u>The `LO` register stores the 32-bit **quotient**</u>. Can be retrieved using `mflo`.
- <u>The `HI` register stores the 32-bit **remainder**</u>. Can be retrieved using `mfhi`.
- **Division for Signed Integers `div`:**
```py
div $s2, $s1 # LO = $s2 / $s1
# HI = $s2 % $s1
mflo $t1 # $t1 = LO (Quotient)
mfhi $t2 # $t2 = HI (Remainder)
```
- **Division for Unsigned Integers `divu`:**
```py
divu $s2, $s1 # LO = $s2 / $s1
# HI = $s2 % $s1
mflo $t1 # $t1 = LO (Quotient)
mfhi $t2 # $t2 = HI (Remainder)
```
- <u>There is no overflow checking</u>.
- <u>There is no *divide-by-zero* checking</u>.
- **Faster Division:**
- It is <u>not possible to use parallel hardware</u> because we need to know the sign when subtracting the divisor and the remainder before proceeding to the next step.
- **SRT Division:** Predicts $1$s in the quotient by using a lookup table.
- **Floating-Point:**
> Computer arithmetic that represents non-integral numbers, in which the binary point is not fixed.
- Includes very small and very large numbers, like **scientific notation**.
- <u>The floating-point standard is defined by IEEE Std 754-1985</u>. It is used by almost all modern computers.
- **Floating-Point Representation:**
- Assumed **normalized** numbers, with no leading $0$s.
$1.xxxx_{two} \times 2^{yyyy} = (-1)^{\text{S}} \times (1 + \text{Fraction}) \times 2^{\text{(Exponent - Bias)}}$
- **$\text{S}$:** Signed bit. $0$ for non-negative and $1$ for negative.
- **$\text{Fraction}$:** Actual number. Also called the *mantissa*.
- **$\text{Exponent}$:** Value of the exponent field, including its sign. Because number is normalized, first bit is assumed to be $1$.
- **$\text{Bias}$:** Ensures exponent is unsigned.
- **Single Precision (32-bit):**
![[A012 - Single Precision.png | 650]]
- **Bias:** $127$
- **Smallest Value:** $\pm 1.0 \times 2^{-126} \approx \pm 1.2 \times 10^{-38}$
- **Largest Value** $\pm 2.0 \times 2^{+127} \approx \pm 3.4 \times 10^{+38}$
- **Double Precision (64-bit):**
![[A013 - Double Precision.png | 650]]
- **Bias:** $1203$
- **Smallest Value:** $\pm 1.0 \times 2^{-1022} \approx \pm 2.2 \times 10^{-308}$
- **Largest Value** $\pm 2.0 \times 2^{+1023} \approx \pm 1.8 \times 10^{+308}$
- An exponent of $1111...11$ and a fraction of $0000...00$ are reserved for representing $\pm \infty$.
- An exponent of $1111...11$ and a nonzero fraction are reserved for representing `NaN` (Not A Number).
- An exponent $0000...00$ and a fraction of $0000...00$ are reserved for representing $0$.
- **Overflow (Floating-Point):** When a positive exponent becomes to large to fit in the exponent field.
- **Underflow (Floating-Point):** When a negative exponent becomes too large to fit in the exponent field.
- **Floating-Point Operations in MIPS:**
- Floating point operations take part in a different part of the processor, the **coprocessor 1**.
- There are separate **floating-point registers**:
- **Single Precision:** `$f0`, `$f1`, ... , `$f31`.
- **Double Precision:** Uses the single precision registers in pairs, `$f0/$f1`, `$f2/$f3`...
- <u>Floating-point instructions only operate on floating-point registers</u>.
- We need to move values between the processor and the coprocessor, which means <u>floating-point requires additional circuits and is slower</u>.
- **Load Instructions:**
```py
lwc1 $f2, 32($a1) # Load 32-bit data into $f2.
ldc1 $f2, 64($a1) # Load 64-bit data into $f2/$f3.
```
- **Store Instructions:**
```py
swc1 $f2, 32($a1) # Store 32-bit data from $f2.
sdc1 $f2, 64($a1) # Store 64-bit data from $f2/$f3.
```
- **Floating-Point Arithmetic:**
```py
add.s $f4, $f4, $f6 # Addition (Single Precision)
add.d $f4, $f4, $f6 # Addition (Double Precision)
sub.s $f4, $f4, $f6 # Subtraction (Single Precision)
sub.d $f4, $f4, $f6 # Subtraction (Double Precision)
mul.s $f4, $f4, $f6 # Multiplication (Single Precision)
mul.d $f4, $f4, $f6 # Multiplication (Double Precision)
div.s $f4, $f4, $f6 # Division (Single Precision)
div.d $f4, $f4, $f6 # Division (Double Precision)
```
- **Floating-Point Conditionals:**
```py
c.eq.s $f4, $f6 # Equal (Single Precision)
c.lt.s $f4, $f6 # Less-Than (Single Precision)
c.le.s $f4, $f6 # Less-Than or Equal (Single Precision)
c.eq.d $f4, $f6 # Equal (Double Precision)
c.lt.d $f4, $f6 # Less-Than (Double Precision)
c.le.d $f4, $f6 # Less-Than or Equal (Double Precision)
bc1t label # Branch if condition is true.
bc1f label # Branch if condition is false.
```
- Bits have no inherent meaning.
- <u>Interpretation of bits depends on the instruction being applied</u>.
***
### Chapter 4 - The Processor
- Information inside computers is encoded in binary.
- Electronics in a modern computer are digital, meaning they operate with only two voltage levels of interest:
- **High Voltage:** $1$
- **Low Voltage:** $0$
- The MIPS implementation consists of two types of logic elements:
- **Combinational Elements:**
- Elements that **operate on data values**.
- The output depends on the input.
- **Examples:** Logical gates, adders, multiplexers, ALU.
- **Multiplexers:** They take multiple inputs and give one output.
- **State / Sequential Elements:**
- Elements that **store information**.
- They have some internal storage.
- <u>They require at least two inputs and one output</u>:
- **Inputs:**
- **Data:** Value to be written.
- **Clock:** Determines when the data is written.
- **Output:**
- Value that was written earlier.
- **Examples:** Registers, memory.
- **Registers:**
![[A014 - Registers.png | 350]]
- Sequential elements that store data in a circuit.
- They use a clock signal to determine when to update a stored value.
- <u>An update is triggered when the clock changes from $0$ to $1$</u>.
- Registers are separate from memory.
- Registers can also have write control. This means the register will only be updated when the write control input is $1$, and during a clock edge.
- **Clocking Methodology:**
- **Edge-Triggered Clocking:** <u>All state changes occur on a clock edge, which is a quick transition from low to high, or high to low</u>.
- Any value stored in a sequential element is only updated on a clock edge.
- Any set of combinational elements must have its inputs coming from a set of state elements, and its outputs written to a set of state elements.
- <u>Every instruction begins execution on one clock edge and completes execution before the next clock edge</u>.
- Reading inputs, sending them to a combinational element, and writing outputs must all happen in the same clock cycle.
- $\text{1 Clock Cycle} = \text{1 Instruction}$
- <u>This means that the longest instruction determines the clock period</u>. Shorter periods mean higher clock speeds.
- Any instruction that would run faster would be slowed due to this.
- Simple is better. Complicated instructions take longer.
- Performance can be improved using pipelining or by dividing into multiple instructions.
- **MIPS Datapath:**
![[A015 - MIPS Datapath.png | 700]]
- **I. Instruction Fetch (IF):**
> To execute any instruction:
> 1. Fetch the instruction from memory.
> 2. Increment the program counter to point at the next instruction.
- **Instruction Memory:**
- Stores the instructions of a program.
- Given an address (`Read Address`), it supplies an instruction (`Instruction`).
- **Program Counter (`PC`):**
- 32-bit register that holds the address of the instruction being executed.
- **Adder (`ADD`):**
- Increments the program counter to the address of the next instruction.
- Each instruction is 32 bits, or 4 bytes.
- The adder increments the address by $4$ every time, except when jumping.
- **Branch Instructions:**
- The program counter will be given a target address (`Add Result`) that becomes the new program counter **if the branch is taken** (`1`).
- If the branch **is not taken**, the program counter is simply incremented by 4 (`0`).
- **II. Instruction Decode (ID):**
- **Register File:**
- Collection of registers that can be read or written by specifying the register number.
- **R-Format Instructions:**
```py
add $t1, $t2, $t3
```
- They read from two registers and write into one register.
- For each read:
- We need the number of the register from which to read from (`Read Register`).
- We get an output with the data read from the register (`Read Data`).
- For each write:
- We need the number of the register to write to (`Write Register`).
- We need data to write to the register (`Write Data`).
- Writes are controlled by a write control signal (`RegWrite`), which must be $1$ for a write to occur at a clock edge.
- **Load / Store Instructions:**
```py
lw $t1, offset($a1)
```
- These instructions compute a memory address by adding the base register to a 16-bit signed offset.
- We need to sign-extend the 16-bit offset field to a 32-bit signed value (`Sign-Extend`).
- The address to read or write to is then calculated by the ALU using the sign-extended offset.
- **Branch Instructions:**
```py
beq $t1, $t2, offset
```
- The 16-bit offset is used to compute a **branch target address** relative to the address of the branch instruction.
- The branch target address is calculated by adding the sign-extended offset (`Sign-Extend`) to the Program Counter (`PC`).
- **III. Execute (EX):**
- **R-Format Instructions:**
- The Arithmetic Logical Unit (`ALU`) takes two 32-bit inputs (`Read Data 1` and `Read Data 2`) and produces a 32-bit result (`ALU Result`).
- The operation that the ALU will perform is specified by a 4-bit control signal (`ALU Operation`).
- **Load / Store Instructions:**
- A value needs to be read from or written to a memory address.
- The Arithmetic Logical Unit (`ALU`) calculates the address by using the sign-extended, 32-bit offset (`Sign-Extend`).
- It then returns the address (`ALU Result`) to be used by the `Data Memory` component.
- **Branch Instructions:**
- **Calculating the Target Address:**
- The base for the branch address is the address of the instruction that comes after the branch, which is simply $\text{PC} + 4$ (`Add`).
- Additionally, the offset field must be shifted by 2-bits, so that it is a word offset, which increases the range of the offset.
- Therefore, we must first shift the sign-extended offset (`Sign-Extend`) by 2 bits (`Shift Left 2`), and add the $\text{PC} + 4$ (`Add`) to get the target address to branch to (`Add Result`).
- The target address (`Add Result`) is then returned to the program counter.
- **Comparing the Values:**
- The data read from the registers (`Read Data 1` and `Read Data 2`) is given to the Arithmetic Logical Unit (`ALU`) for comparison.
- The values will be subtracted. If the result of the subtraction is zero, then the two values were equal and the `ALU` produces a 1-bit signal (`Zero`).
- **IV. Memory Access (MEM):**
- **`Data Memory`:**
- **Store Instructions:**
- A value needs to be written into memory.
- The address to which we need to write the data was calculated by the ALU (`Address`) and is given to the `Data Memory` component.
- The data to write was read from a register (`Read Data 2`) and is given to the `Data Memory` as `Write Data`.
- The `Data Memory` is given a `MemWrite` signal from the control unit.
- **Load Instructions:**
- A value needs to be read from memory and written to a register.
- The address from which we read the data was calculated by the ALU (`Address`) and is given to the `Data Memory` component.
- The `Data Memory` component will return the data (`Read Data`) that is on the given memory location (`Address`).
- The `Data Memory` is given a `MemRead` signal from the control unit.
- **V. Write Back (WB):**
- **R-Format Instructions:**
- The `ALU Result` is given back to the register file (`Register`) as `Write Data` to be written to a register.
- This is signaled by a `0` in the multiplexer (`Mux`).
- **Load Instructions:**
- The value read from memory (`Read Data`) is given back to the register file (`Register`) as `Write Data` to be written to a register.
- This is signaled by a `1` in the multiplexer (`Mux`).
- Whether we are writing from memory to a register or writing a result to a register is determined by the `MemtoReg` signal from the control unit, which is given to the multiplexer (`Mux`).
- **Pipelining:**
> Implementation technique in which multiple instructions are overlapped in execution.
- **Stage:** Each step in pipelining. The MIPS pipeline has 5 stages:
1. Instruction Fetch (IF)
2. Instruction Decode (ID)
3. Execute (EX)
4. Memory Access (MEM)
5. Write Back (WB)
- We can pipeline as long as we have separate resources for each stage.
- <u>Pipelining does not decrease the time (latency) that it takes for a single instruction to execute. But if we have multiple instructions to execute, pipelining decreases the total time it takes to complete them (throughput)</u>.
- <u>To get the **maximum speedup**, all stages need to be **balanced**</u>, that is, they need to take the same time. If not, speedup will be less.
- **Total Speedup:**
$\text{Instruction Time}_{\text{ Pipelined}} = \dfrac{\text{Instruction Time}_{\text{ Non-Pipelined}}}{\text{Number of Stages}}$
- **Example:**
- **Non-Pipelined Execution:**
![[A016 - Non-Pipelined Execution.png | 500]]
- **Pipelined Execution:**
![[A017 - Pipelined Execution.png | 300]]
- MIPS instructions are designed to be pipelined:
- All instructions are 32-bits, which makes them easy to fetch and decode in a single cycle.
- There are few instruction formats.
- Memory operands only appear in load or store instructions.
- **Pipeline Registers:**
- They separate each pipeline stage.
- They retain values of individual instructions for the remaining stages.
![[A018 - Pipeline Registers.png | 500]]
- **Pipeline Hazards:**
> Situations that prevent starting the next instruction in the next clock cycle.
- **Hazard Detection Unit:** Used to detect issues in the pipeline.
- **Structural Hazards:**
> When an instruction cannot execute because the required resource is busy.
- **Example:**
```mermaid
gantt
dateFormat HH
axisFormat %H
section A
IF : a1, 01, 1hour
ID : a2, after a1, 1hour
EX : a3, after a2, 1hour
MEM : crit, a4, after a3, 1hour
WB : a5, after a4, 1hour
section B
IF : b1, after a1, 1hour
ID : b2, after b1, 1hour
EX : b3, after b2, 1hour
MEM : b4, after b3, 1hour
WB : b5, after b4, 1hour
section C
IF : c1, after b1, 1hour
ID : c2, after c1, 1hour
EX : c3, after c2, 1hour
MEM : c4, after c3, 1hour
WB : c5, after c4, 1hour
section D
IF : crit, d1, after c1, 1hour
ID : d2, after d1, 1hour
EX : d3, after d2, 1hour
MEM : d4, after d3, 1hour
WB : d5, after d4, 1hour
```
- Loading and storing requires data access.
- In this case, we are trying to fetch an instruction from memory (`IF`) in **D**, but at the same time, we are trying to access memory (`MEM`) in **A**.
- This means there are two instructions trying to access the same memory, causing a structural hazard.
- **Fixes for Structural Hazards:**
- MIPS uses <u>separate instruction and data memories</u> and <u>separate instruction and data caches</u> to avoid structural hazards.
- **Data Hazards:**
> Also called pipeline hazards. When an instruction cannot execute because it needs to read data that has not been written yet.
- One step must wait for a previous step to be completed.
- **Example:**
```py
add $s0, $t0, $t1
sub $t2, $s0, $t3
```
```mermaid
gantt
dateFormat HH
axisFormat %H
section add
IF : a1, 01, 1hour
ID : a2, after a1, 1hour
EX : a3, after a2, 1hour
MEM : a4, after a3, 1hour
WB : crit, a5, after a4, 1hour
section sub
IF : b1, after a1, 1hour
ID : crit, b2, after b1, 1hour
EX : b3, after b2, 1hour
MEM : b4, after b3, 1hour
WB : b5, after b4, 1hour
```
- In this example, we are trying to subtract `$s0` from `$t3`, but `$s0` has not been written yet.
- The instruction decode (`ID`) of the `sub` instruction happens before the write back (`WB`) of the `add` instruction.
- This means the value of `$s0` will not have been written yet, causing a data hazard.
- **Fixes for Data Hazards:**
- **Pipeline Stall:**
> Also called a *bubble*. Simply waiting for an instruction to be completed before running the next one.
- <u>Stalling will slow the pipeline</u>, but will always fix a hazard.
- Implemented using `nop` (No Operation).
- **Forwarding / Bypassing:**
> Retrieving a value just after it is computed rather than waiting for it to be written to a register.
![[A019 - Forwarding.png | 400]]
- Instead of waiting for a value to be stored in a register by a previous instruction, we can forward the result directly from the `EX` stage to the next instruction.
- <u>We cannot forward *backwards in time*</u>.
- Forwarding <u>requires additional hardware connections</u> in the datapath.
- **Instruction Reordering:**
> Moving an instruction further down the pipeline so that required values will be written before an instruction needs them.
- Easy to implement.
- Does not require additional hardware.
- Not always helpful.
- **Control Hazards:**
> Also called branch hazards. Caused when fetching the next instruction depends on the outcome of a previous branch instruction.
- **Example:**
```py
beq $t1, $t2, label
or $t2, $t1, $t2
```
```mermaid
gantt
dateFormat HH
axisFormat %H
section beq
IF : a1, 01, 1hour
ID : a2, after a1, 1hour
EX : crit, a3, after a2, 1hour
MEM : a4, after a3, 1hour
WB : a5, after a4, 1hour
section or
IF : crit, b1, after a1, 1hour
ID : b2, after b1, 1hour
EX : b3, after b2, 1hour
MEM : b4, after b3, 1hour
WB : b5, after b4, 1hour
```
- In this example, the next instruction to be executed is determined by the outcome of the `beq`.
- The problem is, we start fetching the next instruction before the outcome of the `beq` is calculated.
- The pipeline cannot always fetch the correct instruction.
- If the instruction fetched is not the one that is needed, we have a control hazard.
- **Fixes for Control Hazards:**
- **Pipeline Stall:**
> Also called a *bubble*. Simply waiting for an instruction to be completed before running the next one.
![[A020 - Pipeline Stall.png | 500]]
- This means whenever we see a branch instruction, we introduce a pipeline stall.
- <u>We wait until the branch outcome is determined before fetching the next instruction</u>.
- For long pipelines, introducing a stall causes large slowdowns.
- **Branch Prediction:**
> Assuming a given outcome for a branch rather than waiting the calculation of the actual outcome.
- When the prediction is correct, this does not slow down the pipeline.
- When the prediction is incorrect, we must flush the pipeline and load the correct instructions.
- **Static Branch Prediction:**
- Based on typical branch behavior.
- Examples include loops and if-statements.
- Involves assumptions. For example, assuming that after executing the code in a loop, we will always return to the beginning of the loop for another iteration.
- This means *the prediction would only be wrong one time*.
- Does not require additional hardware to be implemented.
- **Dynamic Branch Prediction:**
- Requires additional hardware to measure branch behavior.
- Examples involve recording the recent history of a branch.
- Assume that future behavior will continue the trend of the branch.
- Uses a **branch prediction buffer** (history table).
- Whenever we execute a branch, it checks the table to see what outcome is most likely to expect.
- If the prediction is wrong, flush the pipeline, stall and invert the prediction.
- **1-Bit Predictor:** Change the prediction every misprediction.
- **2-Bit Predictor:** Only change the prediction every 2 successive mispredictions.
- **Exceptions and Interrupts:**
> Unscheduled event that disrupts program execution.
- **Exceptions** occur within the **CPU**.
- Overflows
- `syscall`
- Undefined Instructions
- **Interrupts** come from **outside** of the processor:
- Input/Output Controllers
- <u>When an exception occurs, the processor saves the address of the instruction that caused it in the **Exception Program Counter** (EPC)</u>.
- The processor then transfers control to the operating system by updating the program counter with an appropriate **interrupt handler** (`j HANDLER`).
- The operating system has a table that lists all interrupt handlers and a memory address of code used to handle them.
- After the operating system handles the exception, it can either terminate the program or continue its execution at the instruction that was saved by the EPC (`j $ra`).
***
### Chapter 5
- **Static RAM (SRAM):**
- Fastest memory.
- Expensive. Requires more transistors, more parts, and takes more room.
- <u>Cache and registers use SRAM</u>.
- **Dynamic RAM (DRAM):**
- 10 times lower than SRAM.
- Just uses one transistor.
- <u>System RAM uses DRAM</u>.
- **Cache:**
> Level of memory hierarchy closest to the CPU.
- **Cache Hierarchy:** L1 (Fastest), L2, L3
- Physical space increases with each.
- **Memory Hierarchy (Fastest to Slowest):**
```mermaid
graph TD
style a fill: #ebf5fb, stroke: #000000
style b fill: #ebf5fb, stroke: #000000
style c fill: #ebf5fb, stroke: #000000
style d fill: #ebf5fb, stroke: #000000
style e fill: #ebf5fb, stroke: #000000
style f fill: #ebf5fb, stroke: #000000
style g fill: #ebf5fb, stroke: #000000
a[Registers]
a-->b
b[L1 Memory]
b-->c
c[L2 Memory]
c-->d
d[L3 Memory]
d-->e
e[System RAM]
e-->f
f[Drives]
f-->g
g[Network]
```
- **Virtual Memory:**
- Each program *sees* the same setup.
- The operating system *translates* in between.
- This allows running multiple things at the same time and to address memory we do not have.
- **Paging:**
- Allocating space in a hard drive (page file).
- If there is something in RAM that has not been used, it gets written to the hard drive, freeing memory so it is available.
- Slow, but if it is not done often, it goes unnoticed.

View File

@ -0,0 +1,345 @@
> ### 001 - Classes and Objects
> Class Notes - August 25, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Structured Programming:**
- Deals with processes that occur in a program.
- Basic unit of modularity is the *function*.
- Input (data), processing, output.
- Performing a set of procedures to produce a result.
- **Object-Oriented Programming:**
- Basic unit of modularity is the *class* (or structure). They are almost the same.
- Classes allow for the creation of objects (bundles of code and functionality).
- Focuses on the data and the functions operating on it.
- **OOP Central Concepts:**
- **Encapsulation**
- Bundling information and functionality related to the same thing.
- Variables and functions bundled together into one entity (the class / structure).
- We are able to *hide* the data / implementation.
- We should not know how it works, but simply use it.
- Everything inside the class is self-contained.
- We do not want other things in the code to affect our object.
- **Principle of Less Privilege:** For any object, the user of the object should not be able to access what is inside of that object.
- **Class Hierarchies / Inheritance**
- Breaking things down into hierarchies.
- Class vs Object
- The class is the idea of defining a group of things in common.
- Avoid writing the code again.
- Allows us to factor out common data or behavior.
- Allows standardizing a common interface.
- **Terminology:**
- **Class:**
- Basically a structure.
- Encapsulates data and functions that are related.
- The class is a blueprint that describes a new thing.
- **Object:**
- An instance of a class.
- The real thing that is built by the blueprint (class).
- Has to follow the blueprint of the class but has its own unique properties.
- Can be interacted with.
- **Attributes:**
- A variable that belongs to a class.
- A class's member data.
- things that describe the thing.
- **Methods:**
- A function that belongs to a class.
- What the thing can do.
- `class` and `struct` often use interchangeably.
- **Format of a Class:**
```cpp
class ClassName
{
// Declarations can be a variable or a function.
declaration;
declaration;
};
```
- Differences between `struct` and `class`:
```cpp
struct Rectangle
{
double length;
double width;
double area;
};
// When creating a struct, the name will become a data type.
// We can use the name as a data type.
Rectangle r1;
// To assign a value.
// In here, area is an attribute.
r1.length = 3;
r1.width = 4;
r1.area = 12;
cout << "Length: " << r1.length
<< "Width: " << r1.width
<< "Area: " << r1.width
<< "\n";
r1.length = 3;
// Would still say area is 12.
// As of this moment, we have an inconsistent state (anything we do that changes the state of an object in a way in makes it inconsistent | some values disagree with others), we changed the length but forgot to recalculate the area.
cout << "Length: " << r1.length
<< "Width: " << r1.width
<< "Area: " << r1.width
<< "\n";
```
```cpp
class Rectangle
{
public:
double length;
double width;
double area();
};
// We can use the same with a class.
Rectangle r2;
r2.length = 3;
r2.width = 4;
// The class rectangle does not have an attribute area, in this case it is a method.
// If we call that method, we want it to give what the area is.
// It is a function, we cannot assign a value to it.
// Notice that because area is a method, we have to call it.
cout << "Length: " << r1.length
<< "Width: " << r1.width
<< "Area: " << r1.area()
<< "\n";
// In this case, the area will always be correct.
r2.length = 2;
cout << "Length: " << r1.length
<< "Width: " << r1.width
<< "Area: " << r1.area()
<< "\n";
// We did not have to recalculate the area.
// This makes it harder to make a mistake.
```
- `()` is called an **accessor**.
- The only way to access the information.
- We cannot tell if the value is being stored or calculated.
- Lets you ask for a value.
- **Access Specifiers:**
- `public` means we can directly access one of the members of a class by using the `.`.
- In order for this to work it has to be public.
- In a `struct` everything is public by default.
- In a `class` by default everything is private.
- `private` means only available to the class.
- `protected` is similar to `private`, but deals with inheritance.
- Order does not matter.
- **Methods**
- A method is a function that is a member of a class.
- Once we have the function prototype in the class we call it a method (make sit less ambiguous).
- It is a thing that an object can do.
- We put the prototype in the class declaration.
```cpp
// Contents of rectangle.h
class Rectangle
{
public:
double length;
double width;
double area(); // This is a method.
};
// Contents of rectangle.cpp
// The compiler needs to link the function to the prototype inside the class.
// All members of a class have local scope for any code belonging to the class.
// For the compiler to understand this we need to link it.
// This says that area() definition is a method on the rectangle class.
// This shares the scope of the class.
double Rectangle::area()
{
return length * width;
}
```
- To establish ownership of an identifier, use the scope resolution operator. `::`
- We usually put the definition in a separate implementation file.
- **Private Methods:**
- Cannot get to them from outside the class.
- Why?
- This is to restrict access so it cannot be changed.
- Utility method for the class itself.
- **Accessors and Mutators:**
- Related to the principle of least privilege.
```cpp
class Rectangle
{
// We replaced the attributes with methods that allow to get the value of the attributes.
public:
// These are accessors.
double get_length();
double get_width();
double get_area();
// These are mutators.
void set_length(double l);
void set_width(double w);
// This is a constructor.
// It just initializes the values of this class.
Rectangle();
// Now they cannot be accessed with the dot operator.
// They are hidden from external users.
// Protected but not usable.
// We need to create an accessor for them.
private:
double length;
double width;
};
// The advantage of having mutators is that we get to choose what can and cannot happen.
// Gives control to programmers.
void Rectangle::set_length(double l)
{
// Now, we cannot have a rectangle with negative length.
if(l >= 0)
{
length = l;
}
}
// ---------------------
// Contents of main.cpp
Rectange r1;
r1.length = 12; // This causes an error.
r1.set_length(12);
cout << r1.get_area() << '\n'; // This would give a garbage number because width was not assigned any value.
```
- Object types define their own initial behavior.
- Data Types: Primitive, Object, Reference
- **Constructors:**
- Used to initialize the class's attributes.
```cpp
class Rectangle
{
private:
int length;
int width;
public:
Rectangle (int length, int width)
{
// 'this' is a reference to the class itself.
// We cannot use the dot operator because it has to be used on an object itself.
this -> length = length;
this -> width = width;
}
}
```
- Do not include a `.cpp` file.
- **Inline Methods:**
- Implemented directly in the class specification.
- Substituted during compilation.
- Compiler basically copies and pastes the body of the function instead of actually calling the function.
- Size of binary increases.
- Good for one-liners.
- `inline` keyword has to be used.
- **Constructors:**
- Do not have any return type.
- A default constructor has no parameters and creates a "default" or "blank" object.
- Classes do not always have a default constructor. This is when you explicitly override them.
- **Constructor Initialization Lists:**
- Compact syntax for placing values into attributes.
- Guarantees initialization prior to the body being executed.
```cpp
// length and width inside the braces are the parameters.
Rectangle (int length, int width) : length{length}, width{width}
{
// This has to still have a body inside the function.
}
```
- **Destructors:**
- Called when the object is destroyed (by deletion or going out of scope).
- Named the same as the class, but name starts with `~`.
- Cannot be overloaded.
- Because they need to have different parameters and a destructor takes no parameters.
```cpp
// Takes no parameter.
// Never called itself, the compiler takes care of it.
// Does not return anything.
~Rectangle ()
{
// Body...
}
```
- **Overloading:**
- Constructors can be overloaded, but this affects the default constructor.
- **Pointers to Objects:**
- Dot notation becomes arrow notation.
- Use arrow-notation `->`.
- **`this` Keyword:**
- Represents a pointer to the current object.
- Used to disambiguate naming within method bodies.
- Not the object itself, but a reference or pointer to the object.
- **Uniform Initialization Syntax:**
```cpp
// Equivalent to using the assignment operator.
Rectangle r1{5, 5};
```
- **Timing of Constructors and Destructors:**
- Destructor gets called when object goes out of scope.
- Artificial Scope can be created using curly braces `{}`.
- **`const` Methods:**
- A method promised not to modify.
- Constructors should not be marked with `const`.
- Try to declare methods as `const` by default.
- Accessors should be marked as `const`.
- Setters should not be marked as `const`.
```cpp
int get_width () const
{
return width;
}
```

View File

@ -0,0 +1,202 @@
> ### 002 - Arrays
> Class Notes - September 03, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **The C++ Array:**
- Arrays allow programmers to store multiple values of the same type.
- It is the most fundamental / simple data structure.
- Computer memory is like an infinite series of boxes.
- The computer identifies different boxes with a number.
- Memory Address: Location in memory.
- The smallest boxes that we can work with directly are called bytes (8 bits).
- Each box is one byte.
- A lot of things require multiple bytes.
- **Syntax for Arrays:**
- We have to say how many *boxes* do we want.
```cpp
// Declares an array of 100 integers.
// The square brackets usually mean we are working in arrays.
// Inside the square brackets we have a Size Declarator.
// '100' is the Size Declarator.
// The brackets '[]' are the Array Type Modifier.
// This is the way the compiler knows this is not just an integer, but an array of integers.
int a[100];
// Declares an array of 50 doubles.
double b[50];
```
- Most symbols are either an operator or a type modifier.
- Arrays are not dynamic, they are static.
- The declaration `int tests[5];` allocates 5 *boxes* that are contiguous in memory.
- We refer to each box as an element.
- Arrays are a **Reference Type**:
- The array name does not name the entire array.
- The compiler just uses the array name to remember *where* in memory the array begins.
- It refers to the starting point of the array.
- By default, everything was passed by value. Arrays are passed by reference by default.
- There is no way to pass an array by value.
- To know how much memory the array is going to consume:
- $\text{Storage Size of Array} = \text{Number of Elements} \times \text{Size of each Element}$
- <u>The size declarator must be a literal or a constant. It cannot be a variable.</u> This is because the size of an array has to be defined at compile time, not at runtime.
- **Size Declarators:**
- Named constants are used as size declarators.
```cpp
const int CLASS_SIZE = 5;
int tests[CLASS_SIZE];
```
- Makes maintenance easier if size needs to change.
- Avoid creating *magic numbers*. These are literal numeric values that appear repeatedly in the code.
- **Indexes and Elements:**
- Most numbering in machines start counting from 0.
- Elements are identified by an integer index, starting from 0.
- Each individual element in an array is referred to as an array element.
- Example: <u>The third element in an array would have an index of 2</u>.
- This is how far we need to move to get to the first element of the array. This is why the first number is zero.
- **Initial Values:**
- Array elements start out *uninitialized*. At least for primitive types.
- Array start out in whatever the default is for their type.
- For object types like strings, they start initialized according to whatever the default constructor for that type is.
- An array of strings would start out as empty strings.
- To access individual elements:
```cpp
// In this case, [] are the Array Index Operator.
// Same symbol but different context.
// "Test sub zero / Test at index zero" is how this is read.
tests[0] = 88;
```
- **Generate Indices with a Loop:**
```cpp
for (int i = 0; i < 5; i++)
{
cout << tests[i] << "\n";
}
```
- **Example:**
- We can index the individual elements to access them.
```cpp
tests[0] = 88;
tests[1] = 92;
// (...)
// It is better to use a for loop to generate the indexes.
// 5 is the size of the array.
// We cannot let i be equal to 5, or we get an off-by-one error.
// The array goes from 0 to 4, so 5 is out of the array.
// Try to use strictly 'less than' (<).
for (int i = 0; i < 5; i++)
{
cout << tests[i] << "\n";
}
```
- Initializing an array with a loop:
```cpp
// Declare an array of 100 integers.
int a[100];
// Initialize all elements to 0.
for (int i = 0; i < 100; i++)
{
a[i] = 0;
}
```
- <u>Size declarators must be a literal or a constant</u>.
- We **cannot** use a variable as a size declarator.
- We can use it in the index operator, though. (This is how the loops work)
- C++ does not do bounds checking for arrays.
- Undefined Behavior: Anything that we can do that the language standard does not specify how to handle.
- The compiler will able to do anything. Sometimes it can make sense, sometimes it is not.
- *Anything can happen.* It does not guarantee to generate a compiler error.
- Never trust information that comes from outside the program. Never trust a user-entered value for calculating an index. Check it first.
- **Array Initializer Syntax:**
```cpp
// Initializes an array with these 5 values.
// The size declarator in here is optional.
// The compiler will count the elements inside the braces.
int tests[5] = { 88, 92, 76, 85, 63 };
// This would produce the same result.
int tests[] = { 88, 92, 76, 85, 63 };
```
- This is only useful when we know all of the values that will be stored in the array.
- **Uniform Initialization Syntax:**
```cpp
int tests[] { 88, 92, 76, 85, 63 }
// Previously, we initialized like this:
// Primitives
int x = 4;
// Object Types - Providing parameters to the constructor.
Rectangle r(2,4);
// Arrays
int x[5] = {2, 4, 6, 8, 10};
// The 'new' way of initialization is Uniform Initialization Syntax:
// Primitives
int x {4};
// Objects
Rectangle r{2, 4};
// Arrays:
int x[5] {1, 2, 3, 4, 5};
```
- **Array Assignment:**
- We cannot directly assign one array to another.
- The right way to do this is with an array, element by element.
```cpp
// This cannot be done. It will not work.
copy_of_tests = tests;
for (int i = 0; i < 5; i++)
{
copy_of_tests[i] = tests[i];
}
```
- **Arrays as Parameters:**
- The array size must also be passed.
- Arrays "do not know their own size".
- Arrays are always passed by reference.
```cpp
// We need to add the [] to indicate this is an array in the formal parameters.
// Only for 1D arrays.
void print_tests (int tests[], int size)
{
// Some Loop...
}
```
- **Key points about 1D Arrays:**
- Size declarator has to be a literal or a constant.
- No bounds checking.
- Array elements initialize depending on their data types.
- Arrays cannot be directly assigned.
- They are always passed by reference.
- Arrays do not know their own size.

View File

@ -0,0 +1,78 @@
> ### 003 - Characters and C-Style Strings
> Class Notes - September 10, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **ASCII Encoding:**
- All characters must be encoded into a numeric representation (something the computer can store).
- There are non-printable characters.
- ASCII codes do not correspond to the numbers.
- Uppercase and lowercase characters are not easily comparable, they are always less.
- **C-Style Strings:**
- C++ natively represents strings the same way C does it (it inherited it).
- This is done as a null-terminated array of type *char*.
- If we had a character that does not serve any other purpose but to show the termination of a string, we can use it for a sentinel-controlled loop.
- This looks for a particular value in the data that it iterates across that signals the end of the string.
- We have an extra character at the end `\0`, which is the null-termination character.
- This character cannot do anything else other than indicating the end of the string.
- Now, instead of counting the characters, we can use it to stop printing.
- **C-String Variables:**
- They are only arrays of characters with the `\0` in the end.
- For an array to be a string, we need to consider the space for the `\0`.
- We can do `char name[6] = "SMITH";`
- The trailing `\0` is added automatically during the initialization.
- <u>Do not forget to add the additional space or we can get a buffer overflow.</u>
- <u>This will only work in the initialization statement. We cannot assign c-strings with `=`</u>.
- **C-String Input:**
- We can enter c-strings using the stream extraction operator `<<`.
- What C-Strings cannot do...
- C-strings are really just arrays.
- We cannot directly assign them or compare them.
- **C-String Functions:**
- Use `<cstring>` header file.
```cpp
strlen(str) // Returns the length of the string.
strcmp(str1, str2) // Compares string 1 to string 2 alphabetically.
// Case-sensitive comparison.
// If the result is less than 0, str1 < str2
strcpy(dst, src) // Copies src into dst.
// Similar to assignment, which is why the destination appear first.
```
- We should not use `strcpy()`:
- Does not do bounds-checking.
- Can cause a buffer overflow.
- We should use `strncpy()`:
- Has third argument to give the maximum size.
- Does not guarantee a valid string.
- Example: `strcat()`
- Concatenates one string with another.
- Does not check the length of the buffer an can be dangerous.
- Use `strncat()`:
```cpp
// We have to keep track of the size of the string.
// It will null terminate the string by default.
```
- **Conversion Functions:**
- Requires `<cstdlib>`.
- `atoi(str)` converts to int.
- `atol(str)` converts to a long.
- `atof(str)` converts to a double.
- **Working with Characters:**
- `isalpha` will give true if the argument is a letter, and false otherwise.
- `isalnum`
- `isdigit`
- `islower`
- `isprint`
- `ispunct`
- Many more...
- Case conversion with `<cctype>`.

View File

@ -0,0 +1,290 @@
> ### 004 - Pointers
> Class Notes - September 13, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- Pointers and arrays have some similarities.
- **Addresses and Data:**
- Iterators *act* like pointers.
- Iterators are tied to specific data structures, but pointers can point to anything.
- Every variable in a program is stored in a unique memory address.
- Pointers store memory addresses. They store the address of something in memory.
- **Address Operator `&`**
```cpp
// There is a place in memory named x.
int x = 42;
// If we want to know where x is in memory, we can use &.
// This will actually print a memory address.
cout << &x << endl:
// This would print a hexadecimal (base-16) address like 0xfffc480a.
```
- **Pointers:**
- If we can take an address of something, we should be able to store it somewhere.
- <u>Pointers are variables that store a memory address</u>.
- Pointers do not have a keyword. The compiler does care what type it is, so instead of a keyword for every data type, we have a type modifier `*` (Pointer Type Modifier).
- Anything we can declare a type for can have a corresponding pointer.
- Pointers are a primitive type. When not initialized they point to an undefined location.
```cpp
// A normal integer.
int x = 75;
// Declare a pointer to an int.
int *xPtr;
// Use the address operator and assign the address to the pointer.
// This can also be done in one line.
xPtr = &x;
```
- **Why?**
- Not all programming languages support pointers.
- They give direct, low-level access to the machine.
- Explicit access to any place in memory.
- Allow doing iteration:
- Can be used to iterate through data structures.
- Dynamic Memory Allocation
- **Observation and Modification:**
- **Observing Pointers:** A pointer used to observe the contents of memory and potentially modify the values under observation.
- **Ownership:**
- **Owning Pointers:** Maintains ownership of dynamically-allocated memory.
- When we allocate memory, the operating system gives us a memory address.
- **Physical View:**
- Every byte in memory has an address.
- Integers take 4 bytes on a system.
- It will have a "box" attached. We care about the first byte (address).
- The pointer will give the address of the first byte of storage of x.
```cpp
// A normal integer.
int x = 75;
// Declare a pointer to an int.
int *xPtr;
// Use the address operator and assign the address to the pointer.
// This can also be done in one line.
// xPtr points to x.
xPtr = &x;
```
- Every time we run the program, the memory addresses will be different.
- We know the number exists, but we do not really care about what the number actually is.
- Except when we are debugging.
- *The pointer `xPtr` points to the variable `x`*.
- **Indirection:**
- Pointers give us direct access to something.
- Pointers allow us to communicate with other parts of the program where the original name is not in scope anymore.
- Pointers indirectly refer to an object.
- We can dereference the pointer and modify it (`*xPtr = 100`).
```cpp
int* x; // Pointer type modifier.
*x // Indirection/Dereference operator.
x * y // Multiplication operator.
int& r; // Reference Type Modifier (Pass by Reference)
&r // Address Operator
```
```cpp
int x = 75;
int* xPtr;
// xPtr is now pointing to x.
xPtr = &x;
// We can use the dereference operator to assign x the value 100.
// xPtr did not change. It still points to x.
*xPtr = 100;
// Both statements will print 100.
cout << *xPtr << endl;
cout << x << endl;
```
- **Null Pointers:**
- Having uninitialized pointers is not good.
- It is a primitive type. It has an unknown address.
- We can use a null pointer, which is a pointer that does not point to any *legal* memory address.
- They can be *treated* as zero for comparisons, but it is not an integer.
- *It points to nowhere*. It points to *null*.
- When we assign a pointer `nullptr`, we are guaranteeing that it cannot be dereferenced.
- If we dereference it, the program will crash.
- A null pointer cannot really be used for anything.
```cpp
// Declare and initialize to nullptr.
int *x = nullptr;
```
- Pointers are similar to *pass-by-reference*:
- **Pass-By-Reference:**
- Actually passes an address and not a value.
- This is done implicitly.
- **Arrays:**
- We are explicitly assigning an address and moving it.
- We cannot
- The array can be automatically downcasted to an address when the `[]` is not used, so we do not have to use `&` to assign an address of an array to a pointer.
```cpp
// Normal array.
int a[5] = {1, 2, 3, 4, 5};
// There is no need for '&' because the name will be automatically degraded to a pointer.
int* aPtr = a;
```
- **Pointer Mathematics:**
- The compiler allows us to move by element and not by bytes.
- This is why we need to specify a type for the pointer to get the ability to think in elements instead of bytes.
- We cannot add 2 pointers, but we can "subtract", which would give us the offset distance between two pointers.
- `xPtr - yPtr`
```cpp
int a[5] = {10, 20, 30, 40, 50};
// This would print 30.
cout << a[2];
// This offsets the pointer by 2.
// This would print 30.
cout << *(aPtr + 2);
// We can increment and decrement a pointer.
aPtr++;
// This would print 20.
cout << *aPtr;
aPtr += 2;
// This would print 40.
cout << *aPtr;
aPtr--;
// This would print 30.
cout << *aPtr;
```
- **Array and Pointer Notation:**
```cpp
// The pointer and array notation is interchangeable.
// Its cleaner. If we are using a pointer as an array, this is preferred.
cout << aPtr[2];
```
- **Pointers as Iterators:**
- Do not use `<` or `>` for iterators.
```cpp
int a[5] = {1, 2, 3, 4, 5};
int* begin = a;
int* end = a + 5;
for (int* current = begin; current != end; current++)
{
// Something...
}
```
- **`const` Pointers and Pointers to Constants**:
- A pointer can be declared constant.
- We may want to declare `const` the pointer (address) and the target it points to.
- We can have a `const` pointer or we can have a pointer to a `const`. We can do both at the same time.
```cpp
// The address stored in the pointer cannot be changed.
// Can be used for when we get the beginning or ending address of an array and we do not want to change it.
int* const prt;
// The pointer will not be able to modify the target data.
// Useful for read-only values.
const int* ptr;
// Neither the pointer nor its target may be changed.
const int* const ptr;
```
- **Passing Pointers to Functions:**
- Pointers can be used as an alternative way for passing a 1D array to a function.
- This is commonly used for c-strings.
```cpp
void foo (int a[], int size)
// Can also be done as...
void foo (int* a, int size)
```
- <u>Pointers are primitive types, so they are passed-by-value by default</u>, meaning we are passing a copy of the memory address to the function:
- If we change the address somehow, it will only change the copy of the function.
- To pass-by-reference, we need to use `&`.
- **Memory Creation at Runtime:**
- `new`: Allocates new memory at runtime.
- `delete`: Deallocates memory at runtime.
- We can allocate or deallocate an object.
- We can also create a dynamic array.
- We must use them in pairs.
```cpp
// The new operator always gives back a pointer.
// This allocates new memory for a double.
double* rPtr = new double;
// We have to dereference our pointer to access the value inside.
// This does not do anything to the value in the pointer.
// 'delete' only marks the memory as free. It does not overwrite anything and
// does not change the pointer.
// The OS will only "think" that the memory is free.
delete rPtr;
// We wantt to remove the connection by setting the pointer to null.
rPtr = nullptr;
```
- **Stack Memory:**
- Contains the operating system's memory.
- Also some memory for user programs.
- Ordered and "pre-planned".
- Variables are closed together.
- **Heap / Free Store:**
- Pool of free memory. It is a limited resource.
- The OS starts filling from the other end of the memory.
- "Grows backward".
- When we ask for additional memory with `new`, we get memory from here, not in the stack.
- We do not know if our variables are close to another program.
- Only ask for what is needed, and release as we are finished.
- If we lose the pointer, we have a memory leak.
- We can transfer ownership to another part of the program by passing the pointer.
- The owner of the pointer has the responsibility to make sure memory is deleted eventually.
```cpp
// Notation for deleting arrays.
delete [] array;
```
- If we ask for memory the OS cannot give, the program will crash.
```cpp
// This will not throw an exception.
// If it fails, it will give a null pointer.
int *xPtr = new(std::nothrow) int;
// We can now check if this worked.
if (xPtr == nullptr)
{
// Additional code...
}
```
- If we allocate something with `new`, we need to use `delete` later.
- They do not need to be in the same scope.
- Failure to `delete` leads to memory leaks.
- **Smart Pointers:**
- Objects used to wrap raw pointers and provide more automatic management of resources "owned" by the pointer.
- Part of the `<memory>` header file.
- They delete the object for you.

View File

@ -0,0 +1,178 @@
> ### 005 - Copy Constructors
> Class Notes - September 17, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Destructors:**
- They run when an object is about to go out of memory.
- They are executed at the end of an object's lifetime:
- An object is deleted.
- An object goes out of scope.
- <u>They cannot accept parameters</u>. This also means they cannot be overloaded.
- They cannot be called, this is automatic.
- Naming is the same as the class, but with a leading `~`.
```cpp
class Foo
{
// This would be the destructor for the class.
~Foo();
};
// The implementation could look like this.
Foo::~Foo()
{
// Additional code here...
}
```
- Destructors are responsible for cleaning any dynamic state the object is managing.
- Cleaning up dynamic memory.
- We can guarantee that the destructor will run at the end of an object's lifetime.
- If something is holding a dynamic structure, it probably needs a destructor.
- **Timing:**
```cpp
{
// Default constructor is invoked.
Foo f1;
// Do some stuff with the object...
} // The destructor will be called at this moment; when the object is going out of scope.
```
- **Constructor Initialization Lists:**
- Inline implementation.
- `:` followed by a list of initializations.
- `{}` is the body of the constructor. in this case, it is empty. <u>Cannot be omitted</u>.
- The initialization list takes effect before the body of the constructor starts running. We get a strong guarantee about the timing.
- <u>We can guarantee anything in the initialization list happens before the constructor</u>.
- There is no scope resolution ambiguity.
- <u>Only works with constructors</u>.
```cpp
class Rectangle
{
public:
// Initialize length from l.
// Drops the value automatically.
// We can only fill attributes, and as many as needed.
// The empty set of curly braces at the end have to be there.
// They are the body of a function with no statements.
Rectangle (int l, int w) : length{l}, width{w} {}
// This can also be done...
// There is no need to disambiguate.
// We are initializing our attribute length from the parameter lengt.
Rectangle (int length, int width) : length{length}, width{width} {}
}
```
- **Memberwise Assignment:**
```cpp
// The compiler treats this as an initialization (a construction), not really an assignment operation.
// We are building r2 as a copy of r1.
// Because we have not defined any constructor
// We do a member-wise assignment. It will go down the list of attributes and do an assignment operation for each.
// Copy the values of r1 into the corresponding location of r2.
Rectangle r2 = r1;
```
- **Elastic Array:**
- It has a `_size` and an `_array` attribute.
- `_array` is just a pointer, not the actual array.
- The array is somewhere in the heap (memory).
- The physical object is the exact memory needed to store the attributes, in this case, it does not include the array.
- Logical object. From the point of view of the user, it includes the physical object and the dynamic memory that has the array.
- The compiler knows how to clean up the physical object, but not the logical object.
- We will need a destructor because the physical object differs from the logical object.
- If we try to create a copy of `a1`:
```cpp
ElasticArray a1 {5};
// Make a copy of a1 into a2.
// _array will point to the same array as a1.
// We get a Shallow Copy. It copied the physical object, but it ends up sharing things outside the physical object.
// This is usually an error.
// We do not want to change a1 if we want to only change a2.
ElasticArray a2 {a1};
```
- **Deep Copy:** Both physical and logical objects are copied.
- We want to have completely independent copies.
- When to do Deep Copy:
- We have a pointer pointing outside of the physical object, like objects in the heap.
- If we have system resources that we are holding open and cannot be copied.
- We can solve this by:
- Copy Constructor (Constructor that knows how to copy our object).
- Overloaded assignment operator.
- **Copy Constructor:**
- Called when an object is:
- Assigning one thing to another.
- Returned by value.
- Passed by value as a parameter.
- Thrown (like an exception) and catch by value.
- Placed in a brace-enclosed initialized list.
- Constructor that receives a constant reference of an object of the same type.
- <u>It has to be by reference</u>, or we end in infinite recursion.
- If we have a working overloaded assignment operator, this should be a one-liner.
```cpp
MyClass
{
public:
// Copy Constructor
MyClass(const MyClass& orig);
}
// The implementation can be...
MyClass::MyClass(const MyClass& orig)
{
*this = orig;
}
```
- **Overloaded Operators:**
- Operators are represented by functions with a special naming scheme.
- Almost all operators can be overloaded. Some can only be overloaded as class methods, like `=`.
- **Operator Arity:**
- Unary Operators
- Binary Operators ($+ - \times$)
- Ternary Operators (Cannot be overloaded)
- Operators are actually functions:
```cpp
operator+
operator*
operator/
operator-
operator<<
```
- **Overloaded Assignment Operator:**
- Left-hand and right-hand operands.
- The left-hand operand will always be the one being assigned to. (`this`).
- The right-hand operand is then a parameter. Should be by reference, and probably by `const`.
- It should return something:
- Example: `x = y = z;`
- `z = 4;`.
- y will be assigned 4.
- x will be assigned 4.
```cpp
class ElasticArray
{
public:
// Overloaded Assignment Operator
const ElasticArray& operator=(const ElasticArray& rhs)
}
ElasticArray(const ElasticArray rhs)
{
*this = rhs;
}
```

View File

@ -0,0 +1,70 @@
> ### 006 - Parallel and Multi-Dimensional Arrays
> Class Notes - September 22, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Parallel Arrays:**
- We cannot store different types of data in the same array.
- What we can do is create two arrays of the same size, and arrange to have those arrays in parallel.
- The index number is being used to select related things from all the arrays.
```cpp
const int N_CONTESTANTS = 5;
string names[N_CONTESTANTS];
int votes[N_CONTESTANTS];
for (int i = 0; i < N_CONTESTANTS; i++)
{
// Additional code...
}
```
- Because they have the same size, when passed to a function we can use a single size parameter.
- **2-Dimensional Arrays:**
- Can be done by adding additional size declarators.
- The size declarators have to be a constant of a literal.
```cpp
// Create an array with 5 rows and 3 columns.
int votes[5][3];
```
- The logical view would be a table.
- In reality, all memory is one-dimensional. The physical view would be where rows are sequential:
```text
[0][0], [0][1], [0][2], [1][0], ...
```
- By convention, in C++, the row goes first and the column after. `votes[2][1]` would access row index 2, column index 1.
$(2 \times 3) + 1$
- $2$ is the row index. $3$ is the number of columns (the value for the second dimension). $1$ is the column index.
- The compiler needs to know the number of columns to locate an element.
- **2D Arrays as Formal Parameters:**
- Use nested for loops for accessing 2D arrays.
- We need to specify is
```cpp
void my_function(int my_array[][4], int n_rows)
{
for (int i = 0; i < n_rows; i++)
{
for (int j = 0; j < 4; j++)
{
// Some code...
}
}
}
```
- We can have arrays of higher dimension, there is no practical limit.
- Higher dimensional arrays can require substantial memory.
- The more dimensions, the more nested loops that will be required to access the array.
- The more dimensions, the more time needed.

View File

@ -0,0 +1,228 @@
> ### 007 - Operator Overloading
> Class Notes - September 22, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Friends of Classes:**
- `private` attributes can only be accessed by other members of the class.
- The type modifier `friend` gives a function or class access to the private and protected data in the class being defined.
- It is added to the front of a declaration or prototype.
- We are allowing another function or class to have access to private attributes.
- *Use sparingly!*
- When we are defining a class, we need a destructor, a copy-constructor, and an assignment operator.
- When we are using dynamic memory, the Physical and Logical objects end up being different.
- **Rule of Three (C++):**
- If you need any one of:
- Destructor
- Copy Constructor
- Overloaded Assignment Operator
- Then we probably need all 3.
- Almost all operators in C++ can be overloaded.
- Many can be overloaded as methods in a class.
- This gives them access to the data in the class.
- Some of them (like `=` and `[]`) *must* be in a class.
- The name for the functions of each operator is just the word `operator` followed by the symbol for the operator being overloaded.
```cpp
// Mathematical vector with x and y components.
struct Vector2d
{
// Allows us to add two vectors.
int x;
int y;
};
// We can overload operators to make vector operations.
Vector2D v1 = {1, 2};
Vector2D v2 = {3, 4};
// Both operands are of type Vector2D.
// The operation should return a Vector2D.
// Needs to have two parameters of type Vector2D.
// The method needs to be const so the original parameters do not change.
// In this case, this is a standalone function.
// It has to rely on the attributes being public.
// If not, we need to create accessors.
Vector2D operator+ (const Vector2d& left,
const Vector2D& right);
```
- **Unary `-`:**
- Only has one operand involved.
```cpp
Vector2D operator- (const Vector2D& right);
// Allows us to do...
result = -v1;
```
- If we want to make it a class:
- We cannot make standalone functions if the attributes are `private`, unless we have accessors for them.
- Instead, we can make them methods in our class so they can access the `private` attributes.
- The left-hand side is represented by `this` (the current object).
- The right-hand side needs to be passed as a parameter to the method.
```cpp
class Vector2D
{
public:
Vector2D() : x{0}, y{0} {}
// Now it is a method in the class and not a standalone function.
// The current object will always be the left-hand side of the operation.
// The right-hand side is our only parameter.
// We always "lose" one of our parameters when we add the overloaded operator as a method.
Vector2D operator+ (const Vector2D& right) const;
// The negation operator only takes one operand.
// We do not need any parameters at all.
// This does not actually change the vector, but returns a negated object.
Vector2D operator- () const;
private:
int x;
int y;
};
```
- How to implement the `operator+`:
- Vector addition involves adding the $x$ and $y$ components.
```cpp
Vector2D operator+ (const Vector2D& right) const;
{
int r_x = x + right.x;
int r_y = y + right.y;
Vector2D result {r_x, r_y};
return result;
}
// We can also do this...
Vector2D operator+ (const Vector2D& right) const;
{
return Vector2D {x + right.x, y + right.y};
// This is called "Creating a Temporary".
// Shorter, but we sacrifice readability.
}
```
- **Tips for Overloading:**
- Most operators do not change the operands. Pass by reference and use `const`.
- Only exceptions are `<<, >>, ++, --, =`.
- Figure out what the return type is.
- Remember that the current object is the left (binary) or the only (unary).
- **Follow the Pattern:**
- Figure the return type.
- Figure the parameters (which one is left and right), and the types for the parameters.
- Most operators do not change the operands.
- What happens if we have a non-class item on the left of a binary operation?
- It cannot be implemented as a method because it needs an object on the left.
$\dfrac{1}{2} + 3 = 3 + \dfrac{1}{2}$
- We would need to create a different method to implement this operation.
- Fraction + Fraction
- Fraction + `int`
- `int` + Fraction
- This becomes a problem because every new thing that needs to interact using an operator needs a new method.
- **Operators that do not Follow the Pattern:**
- Binary operators when there is an object that is not a member of the class on the left.
- Example: `cout << x;`. `cout` is not a method in the class, it is a stream object.
- We cannot create a copy of a stream. <u>We must pass the stream by reference</u>.
```cpp
// It must return a stream.
std::ostream& operator<<(std::ostream& strm, const Foo& obj);
// This is evaluated from left to right.
// First, x is thrown into the stream.
// In order for the rest of the statement to be evaluated, this needs to return a stream.
cout << x << y << z;
```
- If we want to do stream extraction:
```cpp
// In this case, the right side cannot be const, because we want to actually modify it to write the value.
std::istream& operator>>(std::istream& strm, Food& obj);
```
- `istream` and `ostream` can also handle file streams like `fstream` and additional streams, making overloading easier.
- Increment and Decrement (`++` and `--`):
- We have both a prefix and postfix version of the operators (`x++` and `++x`).
- If the add them to a class as a method, they take no parameters.
- This means we would not be able to overload them.
- **Prefix (`++x`):**
- First it increments the element and returns the new value.
```cpp
class ComplexNumber
{
public:
ComplexNumber(double real_part, double imaginary_part);
// Overloaded Operator
// Cannot be const, because we need to change the object.
ComplexNumber operator++();
private:
double real_part = 0;
double imaginary_part = 0;
};
```
- **Postfix (`x++`):**
- The only difference is that it first returns the value and then increments it.
```cpp
// The problem is that the prototype is the same as the prefix one.
ComplexNumber operator++();
// We add a "phantom" dummy parameter to allow the compiler to differentiate.
// No name needed, because it will not be used.
ComplexNumber operator++(int);
```
- Implementation:
```cpp
ComplexNumber operator++()
{
++real_part;
return *this;
}
ComplexNumber operator++(int)
{
// This means the method cannot be by reference.
// This involves creating a temporary.
// More time and storage expensive.
ComplexNumber orig_value {*this};
++real_part;
return orig_value;
}
```
- **Array Operator (`[]`):**
- `a[4]`, where `a` is the left-hand side, and `5` is the right-hand side.
- `[]` is a binary operator.
- Commonly used to be able to do array indexing with dynamic arrays.
```cpp
int& operator[] (int index);
{
// If we have a working at() method, this is a one-liner.
// Return value as reference allows also to assign.
}
```

View File

@ -0,0 +1,70 @@
> ### 008 - Aggregation of Objects
> Class Notes - October 04, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Aggregation:** An object of one class is used as an attribute in another class.
- Also called *composition of objects*.
- *An object using another object as an attribute*.
- Creates the <ins>has a</ins> relationship.
- Allows us to build bigger things without rewriting code (code reuse).
- Reuse previous code and composing better things. Aggregation makes it easier to reuse code.
- <ins>Simplest way to reuse code</ins>. Another way of doing it is called *inheritance*.
```cpp
class Item
{
public:
// Additional code...
private:
// This is aggregation of objects.
// A string is an object from another class.
// We can say that Item "has a" string.
string name;
};
class ThreePack
{
public:
// Additional code...
private:
// Object based on an existing object (Item class).
// We can say that ThreePack "has a" Item.
Item items[3];
};
```
- To initialize the `ToolBox`, we first have to build all 1024 Tools.
- In order to build each `Tool`, we need to build the string first.
- The order in which this happens is from the deepest point to the upper one.
1. Create string.
2. Create all names.
3. Create all the tools.
- $((\text{string ctor, double [primitive]}) \rightarrow \text{Tool ctor}) \times 1024 \rightarrow \text{ToolBox ctor}$
- The destructor "works backward", first the destructor of ToolBox gets fired, then it fires the destructor of Tool, which fires the destructor for string...
```cpp
class Tool
{
public:
// Additional code...
private:
string name;
double weight;
};
class ToolBox
{
public:
// Additional code...
private:
// An array of tools.
Tool inventory[1024];
};
```

View File

@ -0,0 +1,199 @@
> ### 009 - Linked Lists
> Class Notes - October 06, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Linked List (ADT):**
- Simple data structure.
- ADT -> Abstract Data Type
- It is linear.
- We have a set of data structures (called nodes), and each of these contain references (pointers) to other data structures.
- Every node references the next node (or sometimes the previous). Always a 1D relationship, and linear.
- These references can be pointers or array indices.
- The advantage is that data can be added or removed during execution.
- "Always fits in". We can always add new values and the data structure will grow. It is very dynamic.
- We no longer require elements to be in a contiguous order.
- There is still a need to find where the first element (node) is.
- Every node has the responsibility to locate the next element. It has to store a pointer to the next node.
- We cannot calculate an offset, we have to go node by node to locate an element.
- <u>Linked lists can grow or shrink as needed, unlike arrays</u>.
- <u>We can insert data between other nodes easily</u>.
- Same things apply for removal.
- Linked Lists are very directional.
- We cannot go back unless we have another pointer going backwards. [Double Linked Lists].
- A Linked List technically involves two separate data structures:
- **Node:**
- Holds the data itself.
- Simple data structure with *at least* two attributes.
- Contains:
- **Payload:** An object or value.
- **Next:** Pointer (or array index to the next node.
```cpp
class IntNode
{
public:
// We want the node to be as efficient as possible so we do inline methods.
IntNode (int value) : payload {value} {}
// Accessor for the payload.
int get_value () const {return payload;}
// Accessor for the "next" pointer.
IntNode* get_next () const {return next;}
// Mutator for the "next" pointer.
void set_next (IntNode* newptr) {next = newptr;}
private:
int payload;
IntNode* next = nullptr;
};
```
- **List:**
- Algorithms to put the nodes together.
- Lists are made of nodes. They can be empty.
- We have to know how to get to the first node of the list.
- We typically have a pointer to the first node.
- We call the list *head* (or front).
- We could also have a pointer to the last element of the list, called *tail* (or end). Usually points to `nullptr`.
- **Empty List:** List that contains zero nodes. The *head* pointer will point to `nullptr`.
```cpp
class IntList
{
private:
// This will point to the first element of the list.
IntNode* head = nullptr;
IntNode* tail = nullptr;
};
```
- **Basic Operations of the List:**
- Adding nodes at the front or the end.
- Add a node in-order in a sorted list.
- Traverse the linked list (going through all the nodes from beginning to end).
- Searching the list.
- Delete a node anywhere in the list.
- Empty the list.
- The List class understands how the nodes work.
- Someone using the class should only have to think about values.
- **Add Node at the Front:**
- The method cannot be `const` because we want to change the list.
- Steps (Works for both the case where we have an empty list and when we already have elements):
1. Create a new node containing value (`new_node`):
- This needs to be dynamic memory allocation, because if not, when the method ends, it would go out of scope. We need its storage lifetime to be more than just the function, so the variable cannot be local.
- <u>Has to be dynamically allocated</u>.
2. Point `new_node`s `next` to `head`.
3. If `tail == nullptr`, point `tail` to `new_node`.
4. Point `head` to `new_node`.
- **Add Node at the End:**
- This requires a loop, which takes more steps and more time than adding at the front:
1. Create a new node containing `value` and call it `new_node`.
2. Create `current` pointer pointing to `head`.
- While `current != nullptr` and `current`s `next` is not equal to `nullptr`... point `current` to `current`s next.
3. If `current != nullptr`, point `current`s `next` to `nullptr`. Else, point `head` to `new_node`.
- A more efficient way would be to have a `tail` pointer:
1. Create a new node containing `value` and call it `new_node`.
2. If `tail != nullptr` point `tail`s `next` to `new_node`. Else, point `head` to `new_node`.
3. Point `ŧail` to `new_node`.
```cpp
void IntList::add_front (int value) {...}
void IntList::add_back (int value) {...}
```
- How much runtime or how much memory?
- **Time Complexity:**
- "Big Oh" Notation > $O(...)$
- How many steps it takes to do something.
- If there is a loop, we probably do not have a constant number of steps.
- In the case of `add_front`, it is constant in terms of time. We can write it as $O(1)$. We do not need to count the number of steps, we just know it is constant, so we give it $1$.
- In the case of `add_back` with no tail, we can have a number of $n$ steps, so we write is as $O(n)$, which we define as *linear time*. Linear time is more than $1$.
- In the case of `add_back` with tail, we are back to $O(1)$.
- **Removing Items from the List (Deleting a Node):**
- **Delete a Node from the Head/Front:**
- Takes no parameters.
- Cannot be `const` because we are modifying the list,
- It should probably return the value being removed. Prevents retrieving and removing in two different steps.
- It should return an `int`.
```cpp
int IntList::remove_front () {}
```
- We may or may not have a `tail` pointer in a list.
- **For an Empty List:**
1. Check that the list is not empty.
2. Throw an exception.
- **For a List with Multiple Values:**
1. Check that the list is not empty.
2. Store the value at the front in a temporary called `value`.
3. Create a `current` pointer to the first node.
4. Now, move head to point to head's `next`.
5. De-allocate the `current` node.
6. Return `value`.
- **For a List with One Value:**
1. Check that the list is not empty.
2. Store the value at the front in a temporary called `value`.
3. Create a `current` pointer to the first node.
4. Now, move head to point to head's `next`.
5. Check if `head` is `nullptr`. If so, set `tail` to `nullptr`.
6. De-allocate the `current` node.
7. Return `value`.
- **Delete a Node from the Back/End:**
- Take the last element, retrieve its value and remove it.
```cpp
int IntList::remove_back () {}
```
- Assuming we have a tail pointer.
- **For an Empty List:**
1. Check that the list is not empty.
2. Throw an exception.
- **For a List with Multiple Values or with One Value:**
- We actually care about the penultimate node.
- *If we do not have a `ŧail` pointer:*
- `current`s next and `current`s `next`s `next` not equal to `nullptr`.
- *If we have a `tail` pointer:*
- `current`s next and `tail`s `next` not equal to `nullptr`.
- In this case, having or not having a `tail` pointer does not help in any way.
- We still have to run a loop to get to the penultimate value.
1. Create a pointer `current` pointing to `head`.
2. While `current`s `next` is not `nullptr` AND `current`s `next` is not `ŧail`:
a. Move `current` to `current`s next.
b. This step would be different if there is no `tail`.
3. Store the value from `tail` in `value`.
4. If `tail` is not `current`
a. Point `tail` to `current`.
b. De-allocate `current`s `next`.
c. Set `current`s `next` to `nullptr`.
5. Else:
a. Point `tail` to `nullptr` and point `head` to `nullptr`.
b. De-allocate `current`.
6. Return `value`.
- **Adding at the Middle of the List (Ordered List):**
1. *Look before you leave...*
2. Use a *follow* pointer that will always be one node behind `current`.
- **Destructor:**
```cpp
IntList::~IntList () {...}
```
- Set `current` pointing to `head`.
- Move `head` forward (assign it to `head`s `next`).
- De-allocate `current`.
- Loop until `head` is assigned `nullptr` (end of the list).
- Null `tail` and `current` pointers. `head` will already be `nullptr`.

View File

@ -0,0 +1,35 @@
> ### 010 - Doubly-Linked Lists
> Class Notes - October 13, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- The main difference is the way the node is built.
- It is a linked list in which the nodes have a `next` and a `previous` pointer.
- <u>The nodes now support access to both the next and the previous items in the list</u>.
- This allows bidirectional traversal.
- We now have to consider updating both the `next` and `previous` pointers every time we change the list.
- $O(1)$ removal for both beginning and end, which is an advantage over the singly-linked list.
- **Storage Overhead:** It will be $O(N)$ for both cases:
- **Singly-Linked Lists:**
- **From List:**
- 2 Pointers (`head` and `tail`) $=$ 16 Bytes [On 64-bit]
- **From Nodes:**
- 1 Pointers per Node $\times$ N Nodes $=$ 8N Bytes
- **Total:**
- 16 + 8N Bytes (Overhead)
- **Doubly-Linked Lists:**
- **From List:**
- 2 Pointers (`head` and `tail`) $=$ 16 Bytes [On 64-bit]
- **From Nodes:**
- 2 Pointers per Node $\times$ N Nodes $=$ 16N Bytes
- **Total:**
- 16 + 16N Bytes (Overhead)
- Doubly-linked lists occupy more overhead. Depending on the payload, they may not be necessary.
- **Advantages:**
- Reverse traversal is possible.
- Removal at tail becomes $O(1)$.
- **Drawbacks:**
- Added complexity to code.
- Storage requirements.

View File

@ -0,0 +1,233 @@
> ### 011 - Templates, Stacks, and Queues
> Class Notes - October 13, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- Can be implemented with a linked list or an array.
- Algorithms for accessing underlying data structures.
- **Stacks:**
- Data structure optimized for addition and removal only at one end.
- We typically mean the *top* of the stack.
- *Last-in, first-out* pattern.
- For example, a browser *back* button uses a stack.
- We are always operating on the same "side".
- **Stack Operations:**
- **push:** Put something on the stack. Adding a value at the *top*.
- **pop:** Removing the value at the *top* of the stack.
- **top:** Accessor for the value at the *top* of the stack. Allows viewing but not removing it.
- Operations like write, clear, etc.
- Any data structure with linear access can become a stack.
- It can be implemented as a linked list.
- We map *front* to *top*. This means `head` would be `top` (just an accessor).
- `add_front()` becomes `push()`.
- `remove_front()` becomes `pop()`. It returns the element being removed.
- **Queues:**
- *Queuing in a line*.
- We add at one end and remove at the other end.
- Focuses on the *first.in, first-out* access pattern, the first item we add is the first we remove.
- We would want a `tail` pointer if we used a singly-linked list.
- We are always adding at the beginning of the queue.
- We are always removing at the end of the queue.
- **Queue Operations:**
- *enqueue* adds a value at the *end* of the queue.
- *dequeue* removes a value at the *front* of the queue.
- *front* accesses the value at the *front* of the queue.
- `enqueue()` becomes `add_back()`.
- `dequeue()` becomes `remove_front()`.
- Stacks and queues map really well to singly-linked lists.
- It is simply a matter of restricting how the user interacts with it.
- **Templates:**
- Allow making code more reusable.
- C++ cares deeply about the types.
- To use a function we have to declare its type. To use parameters, we have to declare types.
- *Generic Programming*:
- The implementation of an algorithm should not depend much on the type is operating on.
- Separating algorithm from underlying type.
- In C++, this is done using templates.
- Example: Not writing a sorting function for every different type we have.
- Using templates, this is possible.
- Requires some assumptions on the data type.
- C++ compiler requires the data type to create variables.
- Templates are useful for filling at compile time.
- Allows us to create variables that represent a specified type name.
- *A template variable can contain a type; any type.* It takes the place of a typename.
- We create a type specifier, a *template type specifier*.
- Whenever we use the typename, the compiler will have to figure out what is the actual typename being used, and it will replace the typename with the respective type.
```cpp
// "Numeric" can be an int, or a double...
// Creating a template.
// <> always hint that we are doing with a template.
// "typename" called "Numeric" used in this template.
// Before C++11, the keyword "class" was used instead of "typename".
// We can use the word numeric anywhere needed.
// It will be replaced with the appropriate type at compile time.
// A variable will bind to one typename.
template<typename Numeric>
// We can take any numeric variable.
// Anything that can be multiplied by 10.
// The compiler will replace Numeric with the appropriate type.
// It will write the function with he appropriate type.
Numeric times10 (Numeric num)
{
return 10 * num;
}
```
- Multiple type parameters are possible, we simply *comma-separate* them.
- We can have as many as we need, but we need to use them all.
- We can overload function templates, as long as each overload has a unique signature.
- Usually different numbers of parameters.
- All type parameters specified in the prefix must be used in the definition.
```cpp
// Two independent types.
template<typename ValueType, typename KeyType>;
```
- **Function Template Notes:**
- They do not const anything.
- Does not make the code more complicated.
- Compilation time will be longer.
- The template itself is a pattern. No actual code is generated until the function is called.
- The template will not do anything until it is used.
- When we call the function, it will *re-write* it with the type that is being used to call it, and compile it.
- Template do not take any memory.
- The compiler will no fully syntax check, because it cannot find semantic errors.
- Templates are harder to debug because the compiler is less helpful.
- When passing an object to a function template, that thing has to be able to behave in the way expected by the function.
- When we pass something to a template function, can the type of thing we are trying to pass work in the context on the function?
- Only way to see this is to look at each line of the function and see if the object will behave there.
- Does the object support every single operation?
- **Class Templates:**
- Used to generate generic classes and abstract data types.
- Often used for container classes.
- We can create containers tat will work with any type.
- With classes, we need to always specify the template argument list.
```cpp
template<typename Numeric>
Numeric times10 (Numeric num)
{
return 10 * num;
}
// To use the function:
// There is no way for the user to know we are using a template.
int ans = times10(42);
// Which is the same as using a template argument list:
int ans = times10<int> (42);
```
```cpp
// With templates, everything has to be in the .h file.
// We cannot split into two separate files.
// Sometimes a grade might be an integer, a floating-point, a letter...
// Create a template where we are allowing GradeType to be anything.
// Template Headers only apply to one thing, which is the next declaration.
// In this case, it is modifying class Grade.
template<typename GradeType>
class Grade
{
public:
Grade (const GradeType&);
void setGrade (const GradeType&);
GradeType getGrade () const;
private:
// The type will "depend" on whatever it needs to be.
GradeType grade;
};
// In the implementation file, we have to put the template header for each method.
// Headers get larger...
template<typename GradeType>
void Grade<GradeType>::setGrade (const GradeType& newgrade)
{
// Something here...
}
```
- **Normal Libraries:**
- We can place the class definition in the .h file and the implementation in the .cpp file.
- This is because the .cpp file can be compiled into an object file (binary representation).
- **Templates:**
- We only have the .h file.
- Within that file we have the class definition and below the implementations.
- Single file (.h).
- This is because *we cannot compile a template directly*.
- Every line will be inside the include guards.
```cpp
// Takes two values and bundles them into a single object.
#ifndef PAIR_H
#define PAIR_H
#include <iostream>
using std::cout;
using std::ostream;
// We need two type parameters, for the name and the age respectively.
// Two template parameters that can be an independent type.
template<typename TypeA, typename TypeB>
class Pair
{
public:
// Constructor
Pair (const TypeA a, const TypeB b) : a{a}, b{b} {}
ostream& write (ostream& strm) const;
TypeA get_a () const {return a;}
TypeB get_b () const {return b;}
private:
TypeA a;
TypeB b;
};
// This should also be a template.
// Because we are outside the class, we need to add the template header again.
template<typename TypeA, typename TypeB>
ostream& operator<< (ostream& strm, const Pair<Type A, Type B>& p)
{
return p.write(strm);
}
template<typename TypeA, typename TypeB>
ostream& Pair<TypeA, TypeB>::write (ostream& strm) const
{
strm << '(' << a << ',' << b << ')';
return strm;
}
#endif
#include <string>
using std::string;
#include "Pair.h"
int main()
{
Pair<string, int> name_age {"Alice", 23};
cout << name_age;
// name_age and second_pair are independent.
// All methods will work.
Pair<double, string> second_pair {3.14, "Hello"};
return 0;
}
```

View File

@ -0,0 +1,182 @@
> ### 012 - Trees
> Class Notes - October 20, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- Linked lists do not allow direct access.
- Arrays cannot easily shrink or grow at runtime.
- **Arrays:**
- Linear search always works, no assumptions needed.
- We have a loop that is looking at every element.
- The loop takes $O(n)$ time.
- Binary search requires a sorted array.
- We end up cutting the array.
- Related to the power of 2.
- Logarithmic relationship.
- We can only cut the array $n$ times.
- $O(log_n (N))$
- **Linked Lists:**
- Only linear search, even if it is ordered, because we do not have random access.
- **Binary Search Tree:**
- Designed for a specific purpose, *searching*.
- We want to search as fast as possible.
- High efficiency search time.
- **Trees:** Hierarchical data structures.
- They are non-linear by nature, linked structures.
- We cannot traverse it from beginning to end.
- <u>They are hierarchical</u>.
- **Root:** The entry point for a tree. Drawn at the top.
- We draw the *upside down*.
- Each node will have a payload.
- We do not draw the null pointers, but we must remember they are there.
- **Interior Nodes:** Any node that has at least one child node.
- **Leaf Node:** A node that has no child node.
- **Edge:** Non-null outgoing link from a node to its child node.
- Pointers.
- They always have a *downward* direction, deeper into the data structure.
- **Height:** Longest path along edges, from root to any leaf, plus one.
- *Number of nodes that you visit.*
- Height of an empty tree is 0.
- A single node is height 1.
- **Subtree:**
- Trees are recursive.
- Any node in the tree and all of its descendants.
- **Binary Tree:**
- <u>Any tree in which a node has up to two children</u>.
- Has a payload and two nodes (left and right).
- We can have $0$, $1$ or $2$ children.
- Simplest hierarchical data structures. Very common.
- Easy to design, conceptualize, and visualize.
- *Relatively easy to code*.
- Useful for modeling real word scenarios.
- <u>The order of the values may or may not matter</u>. Almost always *it matters*.
- We write a class to represent the node and a class to represents the tree (actual container).
- **Binary Tree Node:**
- Stores a payload value.
- Maintains pointers to the left and right subtree.
- The pointers point to the next node, *not the payload type*.
- **Binary Tree Class:**
- Links nodes together.
- We need an entry point (pointer to tell us where the first node in the tree), which is the root.
- We do not have any pointers back up.
- <u>We just have a pointer to the root</u>.
- The tree is allocating new nodes and has ownership of all of the nodes.
- The root pointer points to a tree node.
- *Root is not a node, it is a pointer*. Similar to `head` in a linked list.
- Doing something to a node means *visiting* the node. Either print, delete, change...
- **Binary Search Tree:**
- Binary tree with additional properties (rules).
- <u>For any node in the tree, the left child (if not null) must have a smaller value than the parent</u>.
- <u>For any node in the tree, the right child (if not null) must have a larger value than the parent</u>.
- **Definition of Binary Search Tree:**
- It is a binary tree.
- The empty tree **is** *trivially* a Binary Search Tree (does not break any rules).
- For any non-empty tree it is a Binary Search tree if and only if:
- The left subtree is a binary search tree.
- The right subtree is a binary search tree.
- The value of the left child, if any, is < the current value.
- The value of the right child, if any, is > the current value.
- We are always adding values at the leaves.
- *Binary trees always grow at the leaf level*.
- *We are always adding a leaf* on an existing interior node.
- Shape of binary trees is determined in the way data is added to the tree.
- It affects the efficiency.
- **Complete Tree:** Every non-leaf node has all possible children.
- Every node has all possible children or is a leaf node.
- From any point in the tree we can go right or left.
- The relationship is *in order*. Everything to the left is less than the current value, and everything to the right is more than the current value.
- Binary Trees are optimized for searching.
- **Balanced Tree:**
- <u>At any node, the height of the left and right subtrees can differ by at most 1.</u>
- If the height differs by more than one, we have an unbalanced tree.
- A complete tree is balanced by default.
- Same amount of weight no matter where you look.
- **Binary Search Tree:**
- Order of data addition affects efficiency.
- The shape of the trees is determined by the order we add values to it.
- We want a binary tree to be $O(lg(N))$, where $lg$ is $log_2$.
- If the tree is balanced, we can guarantee it.
- Every time we choose either left or fight, we are *eliminating* half of the remaining values.
- Same as the binary search.
- <u>Only if we can assume the tree is balanced</u>.
- **Worst Case:**
- $O(n)$
- Just as bad as a linked list.
- We have to look at all of the nodes.
- For binary trees we do not want our data to already come with an order.
- <u>Truly random addition eventually ends in a balanced tree</u>.
- **Code Examples:**
```cpp
void Tree::insert (int value)
{
/*
* #1 - Create a new TreeNode containing value. [new_node]
* - This is via dynamic allocation.
*
* #2 - Check to see if the tree is empty.
* - If root == nullptr, point root -> new_node.
*
* #3 - Create a "current" pointer at root.
*
* #4 - Set a flag (bool) to indicate we are finished. (false by default)
*
* #5 - while (!finished)
* - if (new_node -> value < current -> value) go left.
* - if (current -> left == nullptr), set current's left to new_node (add it).
* - Set finished = true.
* - else [this means we can go left]
* - Move current to the left.
*
* - else [this means we are going right]
* - if (current -> right == nullptr), set current's right to new_node (add it).
* - Set finished = true.
* - else [this means we can go right]
* - Move current to the right.
*/
}
```
- There is more than one way to traverse a tree:
- **In-Order Traversal:**
- If we want to print all the values in sorted order.
- We can print everything to the left, then the head, and finally the right part. [Recursion]
- Left - L, Visit - V, Right - R
- Traverse left subtree (recursively). [L]
- Visit the current node. [V]
- Traverse the right subtree (recursively). [R]
- **Pre-Order Traversal:**
- V L R
- **Post-Order Traversal:**
- L R V
- Useful for *destructing* the tree.
```cpp
int x = 4;
int* xPtr = &x;
null_the_ptr(xPtr);
// Pointers are passed-by-value by default.
void null_the_ptr (int* ptr)
{
// This would null the local variable, not the pointer in main().
// We would need to explicitly pass-by-reference.
ptr = nullptr;
}
```

View File

@ -0,0 +1,265 @@
> ### 013 - Inheritance
> Class Notes - October 29, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- Establishes the *is a* relationship.
- Classify things into hierarchies.
- Re-using code, *encapsulation*.
- Two important concepts in OOP: Encapsulation and Inheritance.
- **Is A Relationship:**
- Going from most general to most specific.
- *A flower is a plant.*
- *A mammal is an animal.*
- *A dog is a mammal.*
- *A poodle is a dog.*
- Allows us to re-use code.
- The real world works this way.
- **Terminology and Notation:**
- **Base Class (or Parent):**
- More general class.
- *Starting point*.
- **Derived Class (or Child):**
- More specialized.
- Derived from the base class.
- <u>The derived class *is a* child of the base class</u>.
```cpp
class Student // base
{
};
// Class Undergrad inherits from Student in a public interface.
// The Student class needs to exist to be able to inherit from it.
class Undergrad : public Student // derived
{
};
```
- The derived object is an object of the base class.
- It has all of its characteristics.
- All of the members defined in the base class are instantly part of the derived class. (Code Reuse)
- Plus, all of the members defined in the derived class. (Extra Code)
- Anything that applies to all, do it in the base class.
- We just need to write the code particular to the derived classes.
- A derived class can access many things from the base class.
- We can control this.
- Derived classes can use all `public` and `protected` members in the base class.
- Additionally, it can use all of its own class.
- `protected` allows access only to derived classes, not to external methods.
- **`protected`:**
- Essentially works like `private`, except `protected` members can be accessed by derived class.
- Useful for when we are writing a base class.
- Extends the ability for derived classes.
- It is convenient, but derived classes can always use accessors and mutators.
- `private` data from the base class is part of the derived class, but cannot be accessed.
```text
Base Class Foo ()
int x
Derived Class Bar : public Foo
string name
- If we declare an instance of Foo, we would simply have a container with an integer (x).
- If we create an instance of Bar, part of it is the Foo object, because Bar "is a" Foo.
- We would have a container for name.
- We would also have a container of Foo, which has a container for x.
```
- **Class Access Specifiers:** We can inherit with certain level of access. Allows us to control how much level of access will classes down in the hierarchy will have:
- How much of the base class *flows* to the derived class.
- **`public`:**
- The "true" *is a* relationship.
- **`private`:**
- Does not give the *has a* relationship .
- `public` and `protected` items from the base class become `private` in derived class.
- Derived class cannot be treated as an object from the base class.
- **`protected`:**
- The `public` items from the base class become `protected` in the derived class.
- We might consider using aggregation instead. It will be a closer mapping.
- *Essentially, everything that was public from the base class becomes protected to the derived class.*
```cpp
class Poodle : public Dog {}
```
- **Building and Tearing Down:**
```cpp
Square::Square (int s) : Rectangle {s, s} {}
```
- *To build a square, we first need to build a rectangle*.
- Any class that we are inheriting from needs to be constructed before our derived class gets built.
- <u>Base classes must be constructed before derived classes</u>.
- <u>Derived classes must be destructed before the base classes</u>.
- A derived class can pass arguments to the base class constructor:
- We must use the constructor's member initialization list for this.
- It is the only way to get things into the base class.
- This is how we guarantee that the base class gets constructed before the derived class.
```cpp
class Rectangle
{
public:
Rectangle (int w, int h);
int get_width () const;
protected:
int width;
int height;
};
class Square : public Rectangle
{
public:
Square (int i) : Rectangle {s, s} {}
void write () const
{
// The square has a width attribute, but is private and cannot be
// accessed directly.
// We need an accessor available and we can just call it here.
// get_width() is inherited from the base class.
cout << "s=" << get_width() << endl;
// We could have also made the width a protected attribute.
cout << "s=" << width << endl;
}
private:
// Literally nothing here...
};
Square s1 {5};
// Square has a get_width() method inherited from public inheritance.
// If it was by protected inheritance, this would cause an error.
cout << "s1 side length is: "
<< s1,get_width();
```
- Redefining a method.
- We get the closest method that is defined within the scope.
- Can lead to unintended behavior.
- Local identifiers *shadow* the base identifiers.
- We can use scope resolution.
- We cannot use the dot operator, because it is the member access operator for objects.
- It only works for objects, not typenames.
- Any time we use a typename, use the scope resolution operator `::`.
- **Redefining a Method:**
- A derived class method has an identical interface to a base class method.
- Used to specialize the derived class behavior.
- Allows adding additional functionality (customizing) to a method.
- **Static Binding:**
- <u>Function (or method) calls are bound to their implementation at compile time</u> by default in C++, not at runtime.
- The compiler will use the implementation whose identifier is the closest available(by scoping rules).
```cpp
class Foo
{
public:
Foo (const string = "");
~Foo;
int serial () const;
string tag () const;
ostream& write (ostream&) const;
private:
static int _count;
int _serial;
string _tag;
};
/*********************/
#ifndef SUPERFOO_H
#define SUPERFOO_H
#include <iostream>
#include <string>
using std::string;
#include "Foo.h"
// Public inheritance from Foo class.
// Because we are inheriting from Foo, SuperFoo can already do
// everything that Foo already did.
class SuperFoo : public Foo
{
public:
// We need to initialize Foo before SuperFoo.
SuperFoo (string tag, string secret)
: Foo {tag}, _secret {secret}
{
cout << " * c-tor - SuperFoo secret=\"" << _secret << "\"\n";
}
~SuperFoo()
{
cout << " * d-tor - SuperFoo #\"" << serial() << "\"\n";
}
string get_secret () const {return _secret;}
// Here, we are redefining the write() method.
ostream& write (ostream&) const
{
cout << "[SuperFoo instance #"
<< serial() << "]";
}
private:
string _secret;
};
#endif
/*********************/
#include <iostream>
using std::cout;
using std::endl;
#include "Foo.h"
int main()
{
Foo f1 {"f1"};
cout << f1 << endl;
// Lets say we also want a password.
// Foo constructor starts before SuperFoo constructor.
SuperFoo sf1 {"sf1", "password"};
// Call the write method from SuperFoo instead of the one in Foo.
sf1.write(cout);
// But we want to use the stream insertion operator.
// This does not quite work correctly.
// It would use the stream insertion operator for the Foo class.
// It would call the write method for the object.
// But which write method?
// At compile time, it will "grab" the closest method, which is the one in Foo.
//
cout << sf1;
// Things are destructed in the opposite way they are constructed.
return 0;
}
```

View File

@ -0,0 +1,153 @@
> ### 014 - Virtual Methods and Polymorphism
> Class Notes - November 05, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Redefining Base Class Methods:**
- We create a method in the derived class with the same name and parameters.
- This is not the same as overloading.
- Base class objects would use the base class method and derived classes use the derived class method.
- It causes a problem when we use pointers.
- **Problems with Redefining:**
- Consider `BaseClass()` with methods `x()` and `y()`. `x()` calls `y()`.
```cpp
class BaseClass
{
public:
void x();
void y();
};
class DerivedClass : public BaseClass
{
public:
void y();
};
int main()
{
DerivedClass d1;
/*
* We want our derived class to use the y() method in it.
* But this x() will call the method in the base class.
* The x() method of the base class will call the y() method of
* the base class.
* Our derived class' y() method will not get called.
* All of these decisions get made at compile time.
* This is called "Static Binding".
* It did not matter that we redefined the method in the derived
* class.
* At compile time, the only available method for the base class
* is its own y().
* Not what we want but what we expect.
*/
d1.x();
return 0;
}
```
- **Class Hierarchies:**
- A derived class can also be used as a base class.
- More complicated to redefine methods.
- **Virtual Methods:**
- We can create a virtual method.
- Change the prototype in the **base class**.
- By using the `virtual` keyword, we prevent the compiler from doing static binding, and instead, do dynamic binding.
- This means that method name binding will occur at runtime, instead of compile time.
- Compiler creates a lookup table that manages a list of possible targets for a function call. It will match the function with the closest match, by looking at the type of the object that initiated the call.
- Ability to inspect the object that makes the function call.
```cpp
// This has to happen in the base class.
virtual void y();
```
```cpp
class BaseClass
{
public:
void x();
virtual void y();
};
class DerivedClass : public BaseClass
{
public:
// Good programming practice to also put the virtual here.
// But this is virtual anyway.
virtual void y();
};
int main()
{
DerivedClass d1;
/*
* Now, we call the x() method from the base class.
* The x() method will then call the y() method from the
* derived class.
* Decision now made at runtime.
*/
d1.x();
return 0;
}
```
- Virtual lookup tables are not fast and efficient.
- Every time we call the virtual method, the computer has to stop what is doing and find the method in the lookup table.
- We do not want this to happen all the time.
- `virtual` is not good for efficiency.
- **Polymorphism:**
- A pointer (or reference) of the base class type may be *pointed* to a derived class object.
- <ins>Base class pointers can only access members defined in the base class</ins>.
- When the base class using dynamic binding, the base class pointer to derived class object will behave appropriately. (this is polymorphism)
- Base class pointers change behavior according to the object.
- <ins>Polymorphism requires a pointer or a reference</ins>.
```cpp
// Without virtual, these calls would only call the methods
// from the base class.
// With virtual, the lookup table will be used and the y()
// method called will be from the derived class.
BaseClass* ptr = new DerivedClass;
ptr -> y();
ptr -> x();
```
- **Redefining:** Refers to statically-bound methods. (No `virtual`)
- **Do not** exhibit polymorphic behavior.
- **Overriding:** Refers to dynamically-bound methods. (`virtual`)
- **Exhibit** polymorphic behavior.
- **When to Use `virtual`:**
- Whenever we want a derived class to override a method, we should consider using `virtual`.
- There is a performance cost, because a lookup table (vtable) is required at runtime.
- **Destructors:**
- Almost always a good idea to make destructors `virtual`.
- They are only called once.
- Probably the right thing to do.
- We are limited to methods that exist in the base class.
- The base class defines the *interface* for all of the derived classes.
- **Pure `virtual` Methods:**
- All derived classes will have the method. (Common Interface in all Derived Classes)
- The base class will not have the method defined. (No Default Behavior)
- Done by assigning $0$ to the virtual method.
- Not implemented in the base class but defined in the derived classes.
```cpp
virtual void y() = 0;
```
- **Abstract Base Class:**
- Base class that contains at least one pure virtual method.
- Cannot be instantiated. No object will be created.
- We can create pointers that use the common interface.

View File

@ -0,0 +1,180 @@
> ### 015 - Smart Pointers
> Class Notes - November 12, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- *PurpleMath*
- Consider the points on the edges as *inside*.
- `std::unique_ptr` is defined in `<memory>`.
- Unique pointers can also point to arrays.
```cpp
// Create a pointer to an array of integers.
std::unique_ptr<int []> a {new int[N]};
// Now, the poiner will behave like an array.
for (int i = 0; i < N; i++)
{
cout << a[i] << endl;
}
// This can cause problems if we do...
Foo* f = new Foo;
std::unique_ptr<Foo> ptr {f};
// This allows us to make more mistakes.
// unique_ptr design could be better.
// We would want to do this.
UniquePtr<Foo> ptr;
// We would assume that the UniquePtr allocates and deletes everything.
// If we cannot get to it, we cannot damage it.
// We would also want...
UniquePtr<Foo> ptr2 {"This is an example"};
// The pointer should be able to take the appropriate parameters.
// No opportunity for mistakes.
```
- Example:
- Unique pointer to a dynamically-allocated integer.
```cpp
class UniquePtrInt
{
public:
// Uninitialized value.
UniquePtrInt () : raw_ptr {new int} {}
UniquePtrInt (int value) : raw_ptr {new int {value}} {}
// Just de-allocate the managed object.
// Nothing can access our pointer after the destructor.
~UniquePtrInt () {delete raw_ptr;}
private:
// We need an attribute to store the actual pointer.
int* raw_ptr = nullptr;
};
// This would just create an uninitialized integer value that p1 points to.
UniquePtrInt p1;
// This means we must support de-reference.
// We have to write the overload.
*p1 = 42;
// We also want ot be able to initialize it to a value.
UniquePtrInt p2 {47};
```
- **Shared Pointer:**
- Only one instance of a unique pointer can exist.
- One-to-one ownership situation.
- No other pointer has ownership of the object.
- When one of the shared pointers goes out of scope, it should not delete that managed object.
- Shared pointers have to have some sense of how many pointers there are to an object.
- We do that by using **reference counting**.
- The shared object will not be deleted as long as at least one reference to it still exist.
- During construction, assume ownership fo the resource.
- If it is a copy, we increase the reference count.
- Every time we destruct, the reference count goes down.
- When the reference count gets to $0$, we delete the object.
- **Reference Counting:**
- We need to have a dynamically-allocated integer that keeps count.
- All share the address of the counter and are able to see the count.
- When the last one is going out of scope, it has to also delete the reference counter.
```cpp
// Both pointers share the ownership of the integer.
SharedPtrInt p3 {52};
SharedPtrInt p4 = p3;
```
- **Overloading Dereference Operator `*`:**
- Just returns the dereference of the actual raw pointer.
- We want to get the value.
- We also want to be able to change it.
- It has to make the value writable.
- We need to return by reference.
- `const` means that the pointer address cannot be changed.
- This cannot be overloaded as a standalone function, it has to be a method.
- Unary operator.
```cpp
int& UniquePtrInt::operator*() const
{
return *raw_ptr;
}
```
- **Overloading the Arrow Operator `->`:**
- Member access operator when applied to a pointer.
- The alternative is the dot operator, but it cannot be overloaded.
- Arrow returns a result that can then be returned by the arrow.
- We are returning a pointer to an object, which is something that the arrow can be applied on.
- Implemented as a unary operator, it is only acting on the thing on its left.
- Transforms the left side into something that the arrow can be applied to, which needs to be legal.
```cpp
Foo* UniquePtr::operator-> () const
{
return raw_ptr;
}
// The compiler will then re-apply the arrow to the pointer.
(myPtr.operator->()) -> doSomething();
```
- Now, we can make the UniquePtr generic using a template:
- Making the constructor can be challenging, because we do not know the amount of parameters needed for the underlying type.
```cpp
template <class DType>
class UniquePtr
{
};
```
- **Variadic Templates:**
- Having a function with any number of parameters.
```cpp
// ... means as many ArgTypes as we need.
// Allows us to create any number of template types.
template <typename... ArgType>
UniquePtr(const ArgType&... args)
{
// All we want to do is sink the parameters into the constructor.
_ptr = new DType {args...};
}
```
- **Template Specialization:**
- Overloaded templates.
- We can end up having two classes doing different things.
- This allows us to have a different version of our class that works with arrays.
```cpp
template <class DType>
class UniquePtr <DType[]>
{
};
```
- **Shared Pointer:**
- It is a reference-counting pointer.
- It can be copied.
- We can have more than one shared pointer at a time that is managing the same object as another shared pointer.
- We need to make sure that the last shared pointer de-allocates the object.
- We can keep track of this by using a reference count, which is also a dynamically allocated object.
- The actual attribute is a pointer to the count.
- The copy constructor and the assignment operator need to increment the counter.
- Now, all of the other objects can see the updated counter.
- The destructor needs to decrement the reference count.
- When the pointer goes out of scope.
- Check if the counter is zero. If it is, de-allocate the counter AND the object.
- We have two managed objects to clear, the actual object and the counter.

View File

@ -0,0 +1,334 @@
> ### 016 - Random Access Files
> Class Notes - November 17, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- Accessing binary files.
- **File:** A set of data stored on a computer, often on secondary storage, like a disk drive.
- There has to be a way to access the file and know where it starts / ends.
- Can be read or written, depending on permissions.
- To interact with files we need the `fstream` header file.
- `ifstream` specialized for input.
- `ofstream` specialized for output.
- Operators `<<` and `>>` work with file streams, unless we open the file in binary mode.
- `get()` and `getline()` also work.
- **`fstream` Object:**
- Handy to pen binary files for reading / writing.
- Can do it in both directions at the same time.
- We must specify in what mode we want to open the file:
- `ios::in`
- `ios::out`
- `ios::app` - Append Mode
- `ios::ate`:
- If the file exists, the initial position markers are moved to the end.
- *Starting at the end*.
- Done by default for append mode.
- `ios::trunc` - If the file exists, its contents will be deleted. Done by default by `ios::out`.
- `ios::binary`:
- Binary mode.
- We *turn off* the translation layer, where the bits are translated into a text representation.
- The bits are not translated. the physical blob is placed into the file with no translation.
- <u>Data is read / written in pure binary format</u>.
- We can combine them by using the bitwise OR (`|`).
- Advantages of a Binary Formats:
- Some files do not have any meaningful translation to an ASCII representation, like audio and video.
- Also, some files would end up being larger if encoded to ASCII. The binary representation is usually more efficient.
- This is not always the case.
- $4$ as an `int` is $4$ bytes, but as a `char` it is only $1$ byte.
- **Binary Files:**
- Opening in binary mode means no encoding scheme is used.
- Stream insertion or extraction do not work anymore.
- We need to use `read()` and `write()`,
- C++ does not have any `byte` type, so we use a `char` (which is one byte).
```cpp
// Open the file for input in binary mode.
// This assumes that the file exists in the current directory.
// Assuming that the first thing in it can be decoded as a character.
fstream fin {"my_file", ios::in | ios::binary};
// No characters anymore, just bytes.
// Lets say that the file contains the character 'a'.
char ch;
// Read one character.
// (Pointer to store read data, Amount of bytes to read)
// The 1 byte that was read gets stored in "ch".
fin.read(&ch, sizeof(ch));
```
- Given a stream, the `read()` method takes two parameters:
```cpp
strm.read(address_of_source, size);
```
- Address of the destination.
- The type of this parameter is expected to be a `char*`.
- Size.
- The number of bytes to read.
- Its type is expected to be a `size_t`.
- The `write()` looks exactly the same:
```cpp
strm.write(address_of_source, size);
```
- The only difference is the flow of information.
- To read / write non-character data, we must cast using:
- This is because the read and write methods are designed to input / output raw bytes of data.
- `char` is a convenient package for raw bytes.
- `reinterpret_cast` forces the compiler to treat a pointer / address of one type as if it were another.
```cpp
reinterpret_cast<char*> (address_of_object)
```
- Both methods expect a source / destination type `char*`.
```cpp
strm.read(destination_ptr, size_in_bytes);
strm.write(source_ptr, size_in_bytes);
```
- Binary files are not ASCII encoded, so they cannot be opened in a text editor.
- We have to know what is contained in the file.
```cpp
/**
* @file main.cpp
*/
#include <iostream>
using std::cout;
#include <fstream>
using std::fstream;
using std::ios;
int main()
{
int day_of_month = 19;
double temperature = 28.4;
// Open, or create, a file, in binary mode, for output.
fstream binfile ("data_file", ios::out | ios::binary);
// We want to write the values into our binary file.
// We cant use stream insertion or extraction.
// We have to use the write() method.
// We need to do "reinterpret_cast".
binfile.write(reinterpret_cast<char*> (&day_of_month), sizeof(int));
binfile.write(reinterpret_cast<char*> (&temperature) , sizeof(double));
binfile.close();
// "erase" the values, so that we can read them back from the file.
day_of_month = 0;
temperature = 0;
// Open the file for input and read the values back.
binfile.open("data_file", ios::in | ios::binary);
binfile.read(reinterpret_cast<char*> (&day_of_month), sizeof(int));
binfile.read(reinterpret_cast<char*> (&temperature) , sizeof(double));
cout << "day: " << day_of_month << endl;
cout << "day: " << temperature << endl;
return 0;
}
```
- **Random Access:**
- *Access anywhere, at any time*.
- Arrays are a random access data structure.
- *Indexed / Arbitrary Access*
- File that contains records.
- *Array-in-a-file*.
- Arrays are always contiguous and with *boxes* of the same size.
- They are indexed. The index always starts at 0.
- Mapping this idea to a file gives us a random access file.
- Every item in the file must be of the same size.
- Structures and classes are both perfect for this, provided they are constant sized.
- **What we Do:**
- Open a file for both input and output.
- Open the file in binary mode.
- Move to the desired location.
- In array terminology -> Indexing
- In file terminology -> Seeking
- We can seek the read and write position independently and move separately.
- Read or write a record.
- Must be done as a series of bytes (chars).
- **Positioning:**
- Not actual memory positions, but rather cursors.
- Two methods for seeking:
- `seekg()`: *seek get*. Moves the "read pointer".
- `seekp()`: *seek put*. Moves the "write cursor".
- We can use them to go to any location in a file.
- They always work in terms of bytes.
- We have to know what is the logical index and multiply by the size of each record.
- **Syntax:**
- `stream.seekg(offset, mode)`
- `stream.seekp(offset, mode)`
- `offset` is the distance to seek, in bytes.
- `mode` is where to start from. To start at the beginning, use `ios::beg`, which is also the default.
- **Useful `ios` Methods:**
```cpp
stream.seekg() // Seek "get" to an offset in stream. (For reading...)
stream.seekp() // Seek "put" to an offset in stream. (For writing...)
stream.read () // Read raw bytes from a stream.
stream.write() // Write raw bytes to a stream.
stream.clear() // Clears the stream's state flags.
stream.tellg() // Where is the "get" cursor? (In bytes...)
stream.tellp() // Where is the "put" cursor? (In bytes...)
```
- Keep records simple:
- Every record needs to be the same size (in bytes).
- This is because we need to do math to know how many records we need to skip to get to a target record.
- We need to make sure that whatever we put in the file is good after.
- Something not part of the physical object, like on the heap, should not be written to the file.
- <u>Avoid using any attributes that are pointers</u>.
- <u>Avoid using any attribute type that is not trivially copyable.</u>.
- `std::string` is dynamic so it cannot be used in random access files.
- We want to create an *array-in-a-file*.
- Open a file in binary mode.
- Read and write in binary mode.
- `reinterpret_cast`
- We are working in bytes.
- The beginning of the file is treated as byte $0$ (`ios::beg`).
- To read or write to a particular record, we use `seekg()` [Get] and `seekp()` [Put].
- The offset is specified in bytes.
- Calculated as: $\text{Logical Index} \times \text{Size of Record in Bytes}$.
- Example:
```cpp
stream.seekg(offset, ios::beg);
```
- Avoid having pointers or dynamic memory in binary files.
```cpp
/**
* @file PersonRecord.h
*/
#ifndef PERSON_RECORD_H
#define PERSON_RECORD_H
#include <string>
using std::string;
#include <cstring>
struct PersonRecord
{
// Default construct the record.
PersonRecord (string name = "", int age = 0) : age {age}
{
// Converts the standard string to a c-string.
// We need to save room for the null-terminator, so we use "- 1".
strncpy(this -> name, name.c_str(), NAME_SIZE - 1);
// Not actually needed since we initialized all characters to '\0'.
// Last character is guaranteed to be '\0'.
// this -> name[NAME_SIZE - 1] = '\0';
}
static const int NAME_SIZE {256};
int age {0};
// We cannot use std::string.
// We use c-strings as they are fixed-size.
// The {} causes all characters to be initialized to the default.
// In this case, the default is '\0'.
char name[NAME_SIZE] {};
};
#endif
/**
* @file main.cpp
*/
const int N_RECORDS = 10;
// ios::in does not try to create the file.
// This would fail on first run.
// We have to know if the file is not there.
fstream fs{"records.dat", ios::in | ios::out | ios::binary};
// Check if the stream is in a failed state.
// If it is, then the file failed to open.
if (!fs)
{
// This should create the file.
// Initially, the file size is 0.
fs.open("records.dat", ios::out | ios::binary);
fs.close();
fstream fs{"records.dat", ios::in | ios::out | ios::binary};
// If this still fails...
if (!fs)
{
std::cerr << "Failed to create file..." << std::endl;
exit(1);
}
PersonRecord r;
// Fill the file with blank records.
// Reserve space so the file will have same size for now on.
for (int i = 0; i < N_RECORDS; i++)
{
fs.write(reinterpret_cast<char*>(&r), sizeof(PersonRecord));
}
// Return the cursor to the beginning.
fs.seekp(0, ios::beg);
}
// The file is now open for input and output.
// Now, we can create a record at index 4.
// This creates a record with the age 42.
PersonRecord r1 {"Alice", 42};
// To write it to the file, we need to seek to the right location.
// We need to skip 4 records.
// This places us in the 5th record (index 4).
fs.seekp(4 * sizeof(PersonRecord), ios::beg);
// Now, we have to actually write the record to the file.
// This is always a two step process, first seek and then write.
fs.write(reinterpret_cast<char*> (&r1), sizeof(PersonRecord));
// File size should be 10 * 4.
// Now, lets try to read the record back and print the age.
// Just to prove that we are really reading the record...
// We can create a new record variable.
// In practice, we can re-use r1.
PersonRecord r2;
// To see this working, comment the write part.
// Again a two-step process, seek and read.
// We are moving the get cursor.
fs.seekg(4 * sizeof(PersonRecord), ios::beg);
fs.read(reinterpret_cast<char*> (&r2), sizeof(PersonRecord));
std::cout << "The name of the record we retrieved is: " << r2.name << std::endl;
std::cout << "The age of the record we retrieved is: " << r2.age << std::endl;
```
- When we construct the record, use `memset()`.
- It fills every byte in a data file with the value we want.
```cpp
// Zero all bytes.
memset(&myObj, 0, sizeof(myObj));
```

View File

@ -0,0 +1,85 @@
> ### 017 - Quicksort
> Class Notes - December 01, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Simple Sorting Methods:**
- Bubble Sort
- Selection Sort
- Insertion Sort
- They are fairly slow in practice.
- <u>They are all $O(n²)$</u>.
- If we have large datasets, we care about these times.
- **Quicksort:**
- It is a divide-and-conquer algorithm developed in 1960 by Tony Hoare.
- Reduce the array into smaller arrays. (Less Work)
- Splits the array into two halves and tries to find an element (pivot element) around which to sort.
- Place the elements larger than the pivot to the right and the smaller to the left.
- Sort halves recursively.
- How to do Quicksort:
- Divide the array into two halves.
- Sort hales recursively.
- Choose a pivot element. All elements greater than the pivot are moved to its right, and all elements lower than the pivot are moved to the left.
```cpp
// This is our base condition.
if n > 1:
choose pivot element;
for each element in the array:
if element < pivot:
place in left subarray;
else:
place in right subarray;
quicksort(left_subarray, size_leftsubarray);
quicksort(right_subarray, size_rightsubarray);
```
1. Pick Pivot
2. Partition
3. Quicksort Left Subarray
4. Quicksort Right Subarray
```cpp
// Index of leftmost element -> left
// Index of rightmost element -> right
function partition (data[], left, right, pivotIndex)
{
// pivotValue so we can easily compare.
pivotValue = array[pivotIndex];
swap(array[pivotIndex], array[right]);
// Boundary between small and bigger stuff.
// Where the pivot will be.
storeIndex = left;
for i from left to right - 1
{
if array[i] < pivotValue
{
swap(array[i], array[storeIndex]);
storeIndex = storeIndex + 1;
}
}
swap(array[storeIndex], array[right]);
return storeIndex;
}
```
- Number of times we can cut the array in half is $lg(n)$,
```text
Index:
0 1 2 3 4 5 6 7
----------------------------------------------------------
2 34 8 12 77 56 0 27
- Now, 12 is in the right place.
2 8 0 12 77 56 24 27
```
- Partitioning takes $O(n)$.
- Quicksort can split $O(lg(n))$ times.
- Each time we split we have to partition.
- Assuming partitioning always results in splitting the array in half, we have $O((N) \times lg(N))$, on average.
- Worst case is $O(N²)$.

View File

@ -0,0 +1,86 @@
> ### 018 - Heaps and Heapsort
> Class Notes - December 01, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Heap:** A kind of (usually binary) tree. They normally come in two varieties:
- Min-Heaps: Smallest value at the root.
- Max-Heaps: Largest value at the root.
- Useful for a sorting algorithm.
- We can make a guarantee about what value is at the root.
- In order to be a heap:
- The empty tree is a heap.
- A non-empty tree is a heap if and only if the heap property is satisfied for all nodes in the tree.
- **Heap Property:**
- A node with nonchildren trivially satifies the heap property.
- For min-heap:
- The subtree rooted at node R is a min-heap tree if each of R's child subtrees are min-heaps and the values of each of R's direct descendants are greater than or equal to the value at R.
- For max-heap:
- The subtree rooted at node R is a max-heap tree if each of R's child subtrees are max-heaps and the values of each of R's direct descendants are less than or equal to the value at R.
- **Heap Convention:**
- All leaves are on the tee at level h or h-1, where h is the tree height.
- This means the tree is balanced.
- Leaves are added at the level h from left-to-right.
- Swap so heap property holds.
- For something to be a heap:
- Parent to children relationship.
- The parent must always be bigger than any of its children. (Recursively)
- The root must be the biggest element.
- The only guarantee we can make is the location of the biggest element.
- We are always going to maintain our heaps in a balanced state.
- Tree is always balanced.
- We add new leaves from left to right from the lowest level.
- We map heaps to an array,
- **Heap Convention:**
- The first item of the array (index $0$) is the root.
- Breath-first traversal. The root ends at the first index.
- Right and left children are next.
- From any node, to get either its right or left children...
- Get the index of the node.
- The index of the left child is: $\text{Index} \times 2 + 1$
- The index of the right child is: $\text{Index} \times 2 + 1$
- This works no matter how big the tree is.
- If we generate an index:
- Bounds-check with the logical size.
- Adding a new value to a heap is adding at the end of the array.
- We can also travel *backwards*:
- To get the parent: $\dfrac{i - 1}{2}$ (<u>Integer Division</u>)
- **Inserting into a Heap:**
- Restore Heap - *Percolate Up* Algorithm:
- Given a sub-heap rooted at index $i$, let $p$ be the parent of $i$, and $i < 0$.
- If `heap[p] < heap[i]`, then this violates the heap property.
- We need to swap elements $p$ and $i$.
- Continue to percolate up from index $p$ until we do not make a swap.
- **Removing from a Heap:**
- <u>The only place that we care about removing is at the root</u>.
- If we remove the root, we leave a gap.
- Instead of removing the root, swap with the last element (because we now the array size will be reduced by $1$).
- *Sift-Down* Algorithm:
- *Sift-down* from index $i$.
- We have to figure out which child we want to promote.
- Let $l$ be the left-child index.
- Let $r$ be the right-child index.
- Let $m$ be the index of the child element (either $l$, $r$) that contains the largest value.
- If `heap[m] > heap[i]`, the heap property is broken.
- We need to swap elements at indexes $m$ and $i$.
- Continue *sifting down* from index $m$ until we do not have to do a swap.
- Edge case requires doing bounds-checking.
- **Heapify:**
- We already have an array that we want to convert into a heap.
- Two ways to make a heap:
- ***Top-Down* Method:** $O(N \times lg(N)$
- Start by *pretending* the heap is empty.
- For each element in the array:
- Insert that element into the heap.
- *Inserting* is actually adding at the end.
- ***Bottom-Up* Method:** $O(N)$
- Start with the last parent; the parent of the last element. $\dfrac{i - 1}{2}$
- Now move to the next parent. ($p = p - 1$)
- Remember to sift all the way down every time we do a swap.
- Do this until we get to the root.
- At worst, percolating can take $O(lg(n))$
- Heapsort means making an array into a heap. Overall $O(N \times lg(N)$.
- Sometimes **quicksort** might be faster, but **heapsort** is consistently $O(N \times lg(N)$.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="963px" height="165px" version="1.1"><defs/><g transform="translate(0.5,0.5)"><rect x="1" y="1" width="960" height="150" fill="none" stroke="none" pointer-events="none"/><rect x="931" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="901" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="871" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="841" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="811" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="781" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="91" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="61" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="31" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="1" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="121" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="151" y="61" width="30" height="60" fill="#e6e6e6" stroke="#999999" pointer-events="none"/><rect x="186" y="1" width="110" height="40" fill="none" stroke="none" pointer-events="none"/><g fill="#000000" font-family="Courier New" font-weight="bold" text-anchor="middle" font-size="36px"><text x="241" y="37">tests</text></g><rect x="181" y="61" width="600" height="60" fill="none" stroke="none" pointer-events="none"/><rect x="181" y="61" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="none"/><rect x="301" y="61" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="none"/><rect x="421" y="61" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="none"/><rect x="541" y="61" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="none"/><rect x="661" y="61" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="none"/><path d="M 186 21 L 171 21 Q 161 21 161 26 L 161 29 Q 161 31 161 41 L 161 81 Q 161 91 166 91 L 171 91" fill="none" stroke="#000000" stroke-width="3" stroke-miterlimit="10" pointer-events="none"/><path d="M 178 91 L 169 96 L 171 91 L 169 87 Z" fill="#000000" stroke="#000000" stroke-width="3" stroke-miterlimit="10" pointer-events="none"/><rect x="201" y="121" width="80" height="30" fill="none" stroke="none" pointer-events="none"/><g fill="#000000" font-family="Courier New" font-weight="bold" text-anchor="middle" font-size="36px"><text x="241" y="152">0</text></g><rect x="321" y="121" width="80" height="30" fill="none" stroke="none" pointer-events="none"/><g fill="#000000" font-family="Courier New" font-weight="bold" text-anchor="middle" font-size="36px"><text x="361" y="152">1</text></g><rect x="441" y="121" width="80" height="30" fill="none" stroke="none" pointer-events="none"/><g fill="#000000" font-family="Courier New" font-weight="bold" text-anchor="middle" font-size="36px"><text x="481" y="152">2</text></g><rect x="561" y="121" width="80" height="30" fill="none" stroke="none" pointer-events="none"/><g fill="#000000" font-family="Courier New" font-weight="bold" text-anchor="middle" font-size="36px"><text x="601" y="152">3</text></g><rect x="681" y="121" width="80" height="30" fill="none" stroke="none" pointer-events="none"/><g fill="#000000" font-family="Courier New" font-weight="bold" text-anchor="middle" font-size="36px"><text x="721" y="152">4</text></g></g></svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="397px" height="62px" viewBox="-0.5 -0.5 397 62"><defs/><g><rect x="1" y="20" width="40" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><ellipse cx="386" cy="40" rx="10" ry="10" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 24 40 L 72.76 40" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="21" cy="40" rx="3" ry="3" fill="#000000" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 78.76 40 L 70.76 44 L 72.76 40 L 70.76 36 Z" fill="#000000" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><rect x="1" y="0" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 10px; margin-left: 2px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">head</div></div></div></foreignObject><text x="21" y="15" fill="#000000" font-family="Helvetica" font-size="16px" text-anchor="middle">head</text></switch></g><rect x="81" y="20" width="40" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><rect x="121" y="20" width="20" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><rect x="281" y="20" width="40" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><rect x="321" y="20" width="20" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 134 40 L 172.76 40" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="131" cy="40" rx="3" ry="3" fill="#000000" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 178.76 40 L 170.76 44 L 172.76 40 L 170.76 36 Z" fill="#000000" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><rect x="181" y="20" width="40" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><rect x="221" y="20" width="20" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 234 40 L 272.76 40" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="231" cy="40" rx="3" ry="3" fill="#000000" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 278.76 40 L 270.76 44 L 272.76 40 L 270.76 36 Z" fill="#000000" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><path d="M 334 40 L 367.76 40" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="331" cy="40" rx="3" ry="3" fill="#000000" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 373.76 40 L 365.76 44 L 367.76 40 L 365.76 36 Z" fill="#000000" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="317px" height="122px" viewBox="-0.5 -0.5 317 122"><defs/><g><rect x="1" y="20" width="40" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><ellipse cx="306" cy="40" rx="10" ry="10" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 24 40 L 72.76 40" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="21" cy="40" rx="3" ry="3" fill="#000000" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 78.76 40 L 70.76 44 L 72.76 40 L 70.76 36 Z" fill="#000000" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><rect x="1" y="0" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 10px; margin-left: 2px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 16px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">head</div></div></div></foreignObject><text x="21" y="15" fill="#000000" font-family="Helvetica" font-size="16px" text-anchor="middle">head</text></switch></g><rect x="81" y="20" width="40" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><rect x="121" y="20" width="20" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><rect x="201" y="20" width="40" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><rect x="241" y="20" width="20" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 134 40 L 151 40 L 151 70 L 121 70 L 121 100 L 132.76 100" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="131" cy="40" rx="3" ry="3" fill="#000000" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 138.76 100 L 130.76 104 L 132.76 100 L 130.76 96 Z" fill="#000000" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><path d="M 254 40 L 287.76 40" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="251" cy="40" rx="3" ry="3" fill="#000000" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 293.76 40 L 285.76 44 L 287.76 40 L 285.76 36 Z" fill="#000000" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><rect x="141" y="80" width="40" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><rect x="181" y="80" width="20" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 194 100 L 211 100 L 211 70 L 181 70 L 181 40 L 192.76 40" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/><ellipse cx="191" cy="100" rx="3" ry="3" fill="#000000" stroke="#000000" stroke-width="2" pointer-events="all"/><path d="M 198.76 40 L 190.76 44 L 192.76 40 L 190.76 36 Z" fill="#000000" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="112px" height="43px" viewBox="-0.5 -0.5 112 43"><defs/><g><rect x="1" y="2" width="40" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><rect x="41" y="2" width="20" height="40" fill="#ffffff" stroke="#000000" stroke-width="2" pointer-events="all"/><ellipse cx="51" cy="22" rx="5" ry="5" fill="#000000" stroke="none" pointer-events="all"/><rect x="1" y="2" width="40" height="10" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 7px; margin-left: 2px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><font style="font-size: 8px">payload</font></div></div></div></foreignObject><text x="21" y="11" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">payload</text></switch></g><rect x="41" y="2" width="20" height="10" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 18px; height: 1px; padding-top: 7px; margin-left: 42px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><font style="font-size: 8px">next</font></div></div></div></foreignObject><text x="51" y="11" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">next</text></switch></g><path d="M 56 22 L 94.63 22" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 99.88 22 L 92.88 25.5 L 94.63 22 L 92.88 18.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><ellipse cx="106" cy="22" rx="5" ry="5" fill="#ffffff" stroke="#000000" pointer-events="all"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="102" height="62" viewBox="0, 0, 102, 62">
<g id="Layer_1">
<g>
<g>
<path d="M1.5,20.5 L41.5,20.5 L41.5,60.5 L1.5,60.5 z" fill="#FFFFFF"/>
<path d="M1.5,20.5 L41.5,20.5 L41.5,60.5 L1.5,60.5 z" fill-opacity="0" stroke="#000000" stroke-width="2"/>
</g>
<g>
<path d="M101.5,40.5 C101.5,46.023 97.023,50.5 91.5,50.5 C85.977,50.5 81.5,46.023 81.5,40.5 C81.5,34.977 85.977,30.5 91.5,30.5 C97.023,30.5 101.5,34.977 101.5,40.5 z" fill="#FFFFFF"/>
<path d="M101.5,40.5 C101.5,46.023 97.023,50.5 91.5,50.5 C85.977,50.5 81.5,46.023 81.5,40.5 C81.5,34.977 85.977,30.5 91.5,30.5 C97.023,30.5 101.5,34.977 101.5,40.5 z" fill-opacity="0" stroke="#000000" stroke-width="2"/>
</g>
<path d="M24.5,40.5 L73.26,40.5" fill-opacity="0" stroke="#000000" stroke-width="2" stroke-miterlimit="10"/>
<g>
<path d="M24.5,40.5 C24.5,42.157 23.157,43.5 21.5,43.5 C19.843,43.5 18.5,42.157 18.5,40.5 C18.5,38.843 19.843,37.5 21.5,37.5 C23.157,37.5 24.5,38.843 24.5,40.5 z" fill="#000000"/>
<path d="M24.5,40.5 C24.5,42.157 23.157,43.5 21.5,43.5 C19.843,43.5 18.5,42.157 18.5,40.5 C18.5,38.843 19.843,37.5 21.5,37.5 C23.157,37.5 24.5,38.843 24.5,40.5 z" fill-opacity="0" stroke="#000000" stroke-width="2"/>
</g>
<g>
<path d="M79.26,40.5 L71.26,44.5 L73.26,40.5 L71.26,36.5 z" fill="#000000"/>
<path d="M79.26,40.5 L71.26,44.5 L73.26,40.5 L71.26,36.5 z" fill-opacity="0" stroke="#000000" stroke-width="2" stroke-miterlimit="10"/>
</g>
<text transform="matrix(1, 0, 0, 1, 21, 9.5)">
<tspan x="-17.797" y="5.5" font-family="Helvetica" font-size="16" fill="#000000">head</tspan>
</text>
</g>
<text transform="matrix(1, 0, 0, 1, 1, -6)">
<tspan x="-75.969" y="2.5" font-family="GillSans" font-size="10" fill="#000000">Viewer does not support full SVG 1.1</tspan>
</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="246px" height="290px" viewBox="-0.5 -0.5 246 290"><defs/><g><rect x="0.5" y="12" width="245" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 243px; height: 1px; padding-top: 27px; margin-left: 2px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><pre><font style="font-size: 25px">Binary Tree Node</font></pre></div></div></div></foreignObject><text x="123" y="31" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Binary Tree Node</text></switch></g><ellipse cx="123" cy="168" rx="120" ry="120" fill="#ffffff" stroke="#000000" stroke-width="3" pointer-events="all"/><path d="M 3 168 L 243 168" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 123 168 L 123 288" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><rect x="60.5" y="108" width="125" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 123px; height: 1px; padding-top: 123px; margin-left: 62px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><pre><font style="font-size: 23px">payload</font></pre></div></div></div></foreignObject><text x="123" y="127" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">payload</text></switch></g><rect x="20" y="198" width="95" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 93px; height: 1px; padding-top: 213px; margin-left: 21px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><pre><font style="font-size: 23px">left</font></pre></div></div></div></foreignObject><text x="68" y="217" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">left</text></switch></g><rect x="133" y="198" width="95" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 93px; height: 1px; padding-top: 213px; margin-left: 134px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><pre><font style="font-size: 23px">right</font></pre></div></div></div></foreignObject><text x="181" y="217" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">right</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -0,0 +1,10 @@
> ### E001 - Debugging
> Extra Notes - September 20, 2021
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- Largest number will always be the `main()` function.
- Look from bottom to top until we find the last call in code that is on out control
- We can use `list` to show nearby code.

View File

@ -0,0 +1,875 @@
> ### SG001 - Exam 1
> Study Guide
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Observing Pointer:**
- Can be used to <u>observe the contents of memory</u>, and potentially to modify the values under observation.
- **Owning Pointer:**
- Pointer that can be used to <u>maintain ownership</u> of dynamically-allocated memory resources.
- **`<cstring>` Library:** Contains functions useful for dealing with c-style strings.
- **`strlen(str)`:** Returns the length of `str`. <u>Does not count the terminating `\0`.</u>
```cpp
char my_string[] = "Hello";
// This would print '5'.
cout << strlen(my_string) << endl;
```
- **`strcmp(str1, str2)`:** Compares `str1` to `str2` *alphabetically*. Determines which one is *first* alphabetically.
- **`strcpy(dst, src)`:** Copies `src` into `dst`. <u>Does not perform bounds-checking</u>.
- **`strncpy(dst, src, count)`:** Copies a number of `count` characters from `src` into `dst`. <u>`\0` will not be automatically added</u>.
```cpp
char source_string[] = "Hello";
// Consider the length of the string plus one additional
// space for the null-terminator.
const int max_string_size = 6;
char dest_string[max_string_size];
strncpy(dest_string, source_string, max_string_size);
// The null-terminator must be manually added.
dest_string[max_string_size - 1] = '\0';
```
- **`strcat(dst, src)`:** Concatenates `src` to the end of `dst`. <u>Does not perform bounds-checking</u>.
- **`strncat(dst, src, count)`:** Concatenates a number of `count` characters from `src` to the end of `dst`. <u>`\0` is automatically added</u>.
- **Access Specifiers:** <u>The order of the specifiers does not matter</u>.
- **`public`:** Available both inside and outside the class definition. Can be accessed with the dot operator `.`.
- **`private`:** Only available inside the class definition. Useful to restrict access.
- **`protected`:** Similar to `private`, but deals with inheritance.
- <u>By default, `class` is `private`</u>. In comparison, a `struct` is `public` by default.
```cpp
class MyClass
{
public:
// The contents here are available outside the class.
// Constructors, Accessors, ... are placed here.
private:
// The contents here are not available outside the class.
// Useful for attributes that should have restricted access.
};    
```
- **Accessors and Mutators:**
- Provide a way to safely access data members.
- They follow the principle of least privilege.
- They are methods that allow us to obtain the values of attributes.
- They provide more control over what can and cannot happen.
```cpp
/****************
Rectangle.h
*****************/
class Rectangle
{
public:
// Accessors
double get_length();
double get_width();
double get_area();
// Mutators
void set_length(double l);
void set_width (double w);
private:
// These attributes are hidden from external methods.
// They cannot be accessed using the dot operator.
// They are protected but not usable.
// To use them, we need to create accessors and mutators.
double length;
double width;
};
/****************
Rectangle.cpp
*****************/
// Example implementation for accessor.
double Rectangle::get_length()
{
return length;
}
// Example implementation for mutator.
double Rectangle::set_length(double l)
{
// The programmer gains more control.
// We decide the value accepted.
if (l >= 0)
{
length = l;
}
}
```
- **Array:**
- Allows storage of multiple related values of the *same type*.
- **Array Memory Layout:**
- The values of an array are stored sequentially/contiguously in memory.
- The declaration `int tests[5];` would allocate the following memory:
![[A001 - Array Memory Layout.svg | 500]]
- Arrays are a <u>Reference Type</u>:
- The array variable does not refer to the entire array. <u>It only refers to the location of the first element</u>.
- The compiler just uses the array name to *remember* where in memory the array begins.
- The name refers to the starting point of the array.
- **Array Index:**
- Elements in an array are identified by an integer index, starting from $0$.
- <u>C++ provides no bounds-checking for indices</u>.
![[A002 - Array Index.svg | 500]]
- **Array Element:** Each individual data item in an array.
- **Array Size Declarator:**
- Specifies the <u>number of elements</u> the array will contain.
- <u>Must be a literal or a constant</u>. It cannot be a variable, because the size of the array has to be determined at compile time and not at runtime.
```cpp
// In this case, '5' is the size declarator.
int numbers[5];
// Using a constant as a size declarator is often done.
// Makes maintenance easier if the size needs to be changed.
const int SIZE = 5;
int numbers[SIZE];
```
- **Array Type Modifier `[]`:** The brackets `[]` are the Array Type Modifier.
- **Array Index Operator `[]`:** Allows accessing individual elements.
```cpp
// In this case, [] act as the Array Index Operator.
// This sets the first element of the array to 88.
tests[0] = 88;
```
- **Array Initialization:**
- <u>Array elements start out *uninitialized*</u>; at least for primitive types.
![[A003 - Uninitialized Array.svg | 500]]
- <u>Array are initialized according to the default constructor for the type they contain</u>.
- For example, an array of type `string` would be initialized to empty strings.
- **Array Initializer Syntax:**
- Only useful if all the values that will be stored are known at the time of writing the program.
- The size declarator is optional, as the compiler can count the values being added.
```cpp
// Both statements produce the same result.
int numbers[] = {1, 2, 3, 4, 5};
int numbers[5] = {1, 2, 3, 4, 5};
```
- **Uniform Initialization Syntax:**
- Similar to the Array Initializer Syntax, but without the `=` operator.
- The size declarator is also optional.
```cpp
// Both statements produce the same result.
int numbers[] {1, 2, 3, 4, 5};
int numbers[5] {1, 2, 3, 4, 5};
```
- **Arrays as Parameters:**
- Arrays can be passed to functions.
- <u>The size of the array must also be passed</u>, because arrays "do not know their own size".
- <u>Arrays are always passed-by-reference</u>.
```cpp
// To indicate an array in the formal parameters, [] must be added.
// To be able to use the loop, the size must also be passed as an argument.
void my_function(int numbers[], int size)
{
for (int i = 0; i < size; i++)
{
// Additional code...
}
}
```
- **Arrow Operator `->` :** Used for pointers to objects instead of the dot operator.
- **C-Style Strings:**
- Low-level representation of characters.
- <u>Null-terminated arrays of type `char`</u>.
- The declaration `char name [6] = "SMITH";`, would produce the following in memory:
![[A004 - C-String.svg | 400]]
- In this case, <u>the trailing `\0` is automatically added during initialization</u>.
- **C-String Input:**
- For input that does not contain whitespace, use the stream extraction operator `>>`. <u>No bounds-checking will be performed.</u>
- For input containing whitespace, use `cin.getline()`, which also <u>allows control of the amount of input</u>.
```cpp
const int SIZE = 64;
// This would create an array of 64 characters.
char name[SIZE];
cout << "Enter a name: ";
// getline will not store a number of characters larger than SIZE.
// Always consider that the array has to have enough space to include the null-terminator.
cin.getline(name, SIZE);
```
- <u>C-Strings cannot be directly assigned using `=`</u>.
- <u>C-Strings cannot be directly compared</u>.
- Many operations with c-strings can be done using the `<cstring>` library.
- **`try` and `catch`:**
- `try` introduces a block of code where execution may be interrupted by an exception.
- If an exception occurs, execution will stop, and the code in the `catch` block will be executed. It is used to detect and handle an exception if it actually occurred during the execution of a `try` block.
- <u>This will only happen if the parameter type in `catch` matches exactly the exception thrown by the function in `try`</u>.
- Consider that the parameter in `catch` should be *passed as a constant reference* in order to avoid creating a copy. If not, the compiler will warn about catching a polymorphic type by value.
- We cannot `catch` without `try`, nor `try` without `catch`.
- <u>We can have multiple `catch` blocks</u>. Each block will be scanned until one matches the type of the exception thrown.
- Any type may be thrown, even custom objects defined specifically for this purpose.
```cpp
try
{
my_function();
}
catch (const std::out_of_range&)
{
// Additional code to handle the exception...
}
```
- **Class:**
- Basically a structure.
- Used to encapsulate data and functions that are related.
- Can be thought of as a *blueprint* that describes a new type of thing.
```cpp
// This defines a class called "MyClass".
class MyClass
{
public:
// Additional code...
private:
// Additional code...
};
```
- **Object:**
- <u>An instance of a class</u>.
- Follows the *blueprint*, but has its own properties.
- Can be interacted with.
```cpp
// This creates an object of type "MyClass".
MyClass my_object;
```
- **`const` Type Modifier:**
- **`const` Methods**:
- Methods of classes or structures can be marked as `const` to <u>prevent them from modifying values of attributes</u>.
- <u>Often added to accessors, whenever possible</u>.
- Should not be added to constructors of mutators.
```cpp
/****************
Rectangle.h
*****************/
class Rectangle
{
public:
// Because it is marked as "const", this method cannot modify the attributes (length).
int get_length() const;
private:
int length;
};
/****************
Rectangle.cpp
*****************/
int get_length() const
{
// Implementation...
}
```
- **`const` Pointer:**
- <u>The value of the pointer, that is, the target memory address, cannot be changed</u>.
- Useful for starting or ending addresses of an array.
```cpp
int* const ptr;
```
- **Pointer to `const`:**
- <u>The pointer will not be able to modify the target data, that is, the data stored in the address the pointer contains.</u>
- Useful for read-only values.
```cpp
const int* ptr;
```
- **`const` Pointer to `const`:**
- <u>Neither the pointer nor its target can be changed</u>.
```cpp
const int* const ptr;
```
- **Constructors:**
- Allow an object to be created (*instantiated*) in an already-working state.
- <u>They do not have a return type</u>.
- <u>They have the same name as the class</u>.
- A constructor can take arguments.
- **Default Constructor:** A constructor that takes no parameters. Used to create a *default* object.
- **Implicit Default Constructor:** If there are no user-declared constructors, the compiler will always declare a default constructor for the class.
- **Explicitly Default Constructor:** If there are additional constructors, the user can define a default constructor by using the keyword `default`.
```cpp
/****************
Rectangle.h
*****************/
class Rectangle
{
public:
// Default Constructor.
Rectangle() = default;
// Additional constructors can also be created.
Rectangle(int length, int width);
private:
int length = 1;
int width = 1;
};
/****************
Rectangle.cpp
*****************/
// Implementation of the constructor.
Rectangle::Rectangle(int length, int width)
{
// Because the parameters are named the same as the attributes, we need to disambiguate.
// "this" refers to the actual attributes.
this -> length = length;
this -> width = width;
}
```
- **Initialization during Instantiation:** Done when a constructor takes parameters to initialize a new object.
- **Constructor Initialization Lists:**
- Compact syntax for placing values into attributes in a constructor implementation.
- <u>The initialization list is guaranteed to take effect before the body of the constructor executes</u>.
- <u>There is no need for name disambiguation</u>, because the lists can only contain initializations for attributes.
```cpp
class Rectangle
{
public:
// {} is the body of the constructor, empty in this case.
// It cannot be omitted.
Rectangle(int l, int w) : length{l}, width {w} {}
// Because there is no disambiguate, this also works.
Rectangle(int length, int width) : length{length}, width{width} {}
private:
int length = 0;
int width = 0;
};
```
- **Copy Constructor:**
- It is a constructor takes a `const` reference to an object of the same class as a parameter.
- Called in cases where a copy of an object is being created; in the following cases:
- An object is assigned to another.
- An object is returned by value.
- An object is passed-by-value as an argument.
- An object is thrown and caught by value (like an exception).
- An object is placed in a brace-enclosed initialized list.
- <u>The parameter has to be passed-by-reference, otherwise we would be creating a copy, which would call the copy constructor and end in infinite recursion</u>.
- If we have a working overloaded assignment operator, the copy constructor can be a one-liner.
```cpp
/****************
MyClass.h
*****************/
class MyClass
{
public:
// Copy Constructor
MyClass(const MyClass& const);
};
/****************
MyClass.cpp
*****************/
// Implementation of Copy Constructor
MyClass::MyClass(const MyClass& const)
{
// If we have a working overloaded assignment operator, we can simply
// assign the parameter to the current object.
*this = MyClass;
}
```
- **Destructors:**
- They are executed at the end of an object's lifetime; <u>either when an object is deleted or going out of scope</u>.
- They are named the same as the class, but beginning with `~`.
- <u>They take no parameters, have no return type, and cannot be overloaded</u>.
- <u>They cannot be directly called</u>, this is done automatically by the compiler.
- They are responsible for clearing any dynamically allocated memory. <u>Objects that perform `new` operations need a destructor</u>.
- We can guarantee that destructors will end at the end of an object's lifetime.
```cpp
/****************
MyClass.h
*****************/
class MyClass
{
public:
// Destructor for MyClass
~MyClass();
};
/****************
MyClass.cpp
*****************/
// Implementation of the Destructor
MyClass::~MyClass()
{
// An example use would be deallocating memory used by an array.
delete [] my_array;
}
```
- **Encapsulation:**
- Bundling of information and functionality that are related to the same thing.
- <u>Refers to variables and functions bundled together into one entity (`class`)</u>.
- Allows *hiding* the data and/or the implementation.
- Everything in a `class` will be self-contained.
- **Principle of Least Privilege:** <u>For any object, the user should not be able to access what is inside of it</u>.
- **Indirection Operator / Dereference Operator (`*`):**
- <u>Allows *following* a pointer to the address it is storing so we can operate on the data stored at that location</u>.
- Pointers *indirectly* refer to an object. We can <u>dereference the pointer</u> and access/modify the object.
```cpp
// Dereferencing a Pointer
// Here, '*' is the Indirection Operator.
// We are changing the value in the address that my_ptr points to.
*my_ptr = 100;
```
- **Inline Methods:**
- They are implemented directly in the class specification.
- They are <u>substituted during compilation</u>.
- The compiler will *copy and paste* the function instead of actually calling it.
- <u>They speed the program, but increase the size of the executable file</u>.
- Specified using the `inline` keyword.
- Useful for *one-liners*.
```cpp
class MyClass
{
public:
inline void my_function()
{
// Additional code here...
}
};
```
- **Members:**
- **Attribute:** A class's member data. A variable of a class.
```cpp
class Rectangle
{
public:
// In here, length and width are the class attributes.
double length;
double width;
};
```
- **Methods:** A class's member functions. A function of a class.
```cpp
class Rectangle
{
public:
// In here, area() is a method.
double area();
};
```
- **Member Access Operator / Dot Operator (`.`):**
- Allows access to an object's <u>public</u> members.
- Has to be used on an object, so it cannot be substituted for the `this` keyword.
```cpp
class Rectangle
{
public:
double length;
};
Rectangle r1;
// We can use the dot operator to access the attribute.
r1.length = 5;
```
- **Memory Leak:**
- <u>Occurs when memory that was previously allocated by a program is not properly deallocated</u>.
- Memory is no longer in use by the program, but the physical memory is still allocated to it for no reason.
- **Common Causes:**
- Allocating memory using `new` and not deallocating it later using `delete`.
- Using the wrong `delete` operator, for example. not using `delete []` for deallocating an array.
- **How to Avoid Memory Leaks:**
- Any object that uses dynamic memory allocation should be designed as a RAII object, meaning it should be responsible for its own initialization, memory allocation, and memory deallocation.
- Write all code between the `new` and `delete` statements.
- **`new` and `delete`:**
- **`new`:** Operator that allocates new memory.
- <u>It returns a pointer to the allocated space</u>.
- **`delete`:** Operator that deallocates memory previously allocated using `new`.
- <u>Failure to use `delete` results in memory leaks</u>.
- <u>`delete` does not change the value of the pointer, nor the value stored in the address of the pointer. It simply *marks the memory as free*</u>.
- After using `delete`, we should *null the pointer* using `nullptr`.
```cpp
// Allocate new memory.
// "new" always returns a pointer.
int* xPtr = new int;
// To access or modify the value inside xPtr, we need to dereference it.
*xPtr = 10;
// Deallocate the memory.
delete xPtr;
// Null the pointer.
xPtr = nullptr;
```
- <u>`new` will allocate memory from a pool of memory known as the *heap*</u>.
- This is a limited resource.
- If the pointer is lost or the address is changed before deallocating the memory, we will have a memory leak.
- Ownership can be transferred by passing the pointer to another part of the program.
- **`new` and `delete` for Arrays (Dynamic Arrays):**
- Normal arrays cannot be sized at runtime.
- Dynamic memory allocation allow variables to be used to determine the size of an array at runtime.
```cpp
// We can use a variable for the size declarator.
int array_size = 10;
// Allocate new memory for dynamic array.
int* dyn_array = new int[array_size];
// Additional code...
// Deallocate the memory of the dynamic array.
delete [] dyn_array;
// Null the pointer.
dyn_array = nullptr;
```
- If we ask for memory the OS cannot give, the program will crash. We can avoid this by checking if the memory was allocated:
```cpp
// This will not throw an exception.
// If allocation fails, it will return a null pointer.
int *xPtr = new(std::nothrow) int;
// We can now check if allocation worked.
if (xPtr == nullptr)
{
// Additional code...
}
```
- **Null Character `\0`:**
- C++ natively represents string the same way C does; as a null-terminated (`\0`) array of type `char`.
- The null character does not serve any other purpose than to show the termination of a string. It can be used in a sentinel-controlled loop.
- For an array to be a c-string, we need to consider the space for the `\0`.
- **Null Pointer:** <u>A pointer that does not point to any legal memory address</u>.
- Uninitialized pointers can point to any address, because they are a primitive type, and this causes *undefined behavior*.
- To avoid this, we can use a null pointer. They can be *treated* as zero for comparisons, but they are not an integer.
- *They point to null / nowhere*.
- **`nullptr:`** Keyword that represents the literal value of a null pointer.
- <u>Setting a pointer to `nullptr` guarantees it cannot be dereferenced</u>.
- Cannot be really used for anything.
```cpp
// Initialize xPtr to nullptr.
int* xPtr = nullptr;
```
- **Out-Of-Bounds Error:**
- <u>C++ does not perform bounds-checking for array indices</u>.
- **Undefined Behavior:** Anything that can be done that the language standard does not specify how to handle.
- The compiler is able to do anything, sometimes making sense and sometimes not.
- This does not guarantee to cause a compiler error.
- We can end up accessing garbage values in a different memory address, or cause the program to crash.
- **Overloaded Assignment Operator:**
- Pointers pointing outside the *physical object* can be problematic.
- Most operators in C++ can be overloaded to work with custom object types.
- C++ operators are actually special functions (or methods).
```cpp
// The assignment...
z = y;
// Is actually...
x.operator=(y);
```
- We can create a method in a class that matches the prototype for the assignment operator. This allows us to control how the assignment happens for our object.
- <u>This is necessary whenever the physical and logical objects differ</u>.
- The implementation needs to make sure a complete, independent copy of the source object is made.
```cpp
/****************
MyClass.h
*****************/
class MyClass
{
public:
// Both input and output objects need to be passed-by-reference.
// Both are "const" to prevent changes during the assignment.
const MyClass& operator=(const MyClass& source);
};
/****************
MyClass.cpp
*****************/
const MyClass& MyClass::operator=(const MyClass& source)
{
// The following example assumes an array stored in MyClass.
// Consider that "this" is the object to which we are assigning the source.
// First, deallocate the memory of the destination array.
delete [] dest_array;
// Then, allocate new memory for the destination array.
dest_array = new int[MyClass.size];
// Assign the size of the source array to the destination array.
size = MyClass.size;
// Assign each element to the new array.
for (int i = 0; i < size; i++)
{
dest_array[i] = MyClass.array[i];
}
// Return the destination array.
// "this" pointer needs to be dereferenced to return object and not address.
return *this;
}
```
- **Pointer:** <u>Variable that stores a memory address</u>.
- **Pointer Type Modifier (`*`):** Allows declaration of a pointer by being added to a variable declaration.
- **Address Operator (`&`):** Retrieves the memory address of its operand.
- Any type can have a corresponding pointer.
- <u>Pointers are a primitive type, and when uninitialized, they point to an undefined location</u>.
```cpp
// Declare an integer value.
int x = 75;
// Declare a pointer.
// The pointer will have the address of a value of type "int".
int* xPtr;
// Store the memory address of 'x' in "xPtr".
xPtr = &x;
```
- **Physical View:**
![[A005 - Physical View of a Pointer.svg | 500]]
- **Logical View:**
![[A006 - Logical View of a Pointer.svg | 500]]
- **Pointer Math:**
```text
xPtr + 2 [Adding an Offset]
xPtr - 2 [Subtracting an Offset]
++xPtr [Pre-Increment]
--xPtr [Pre-Decrement]
xPtr++ [Post-Increment]
xPtr-- [Post-Decrement]
xPtr - yPtr [Offset Distance between 2 Pointers]
```
- Pointers can be used as iterators.
- <u>Pointers are passed-by-value by default</u>.
- <u>Pointers can be used to access elements of an array</u>.
- Pointer and array notation is interchangeable.
```cpp
int a[5] {1, 2, 3, 4, 5};
// There is no need for '&'.
int * aPtr = a;
// Both statements produce the same result.
*(aPtr + 2);
aPtr[2];
```
- **RAII:**
- Acronym for *Resource Allocation Is Initialization*.
- <u>When we create objects that represent dynamic data structures, the object itself is responsible for its own initialization, memory allocation, and eventually, memory deallocation</u>.
- The lifetime of dynamic resources is bound to the object itself.
- <u>Refers to managing dynamic resources in a class such that external users are not aware of it</u>.
- **Pass-by-Reference:**
- Actually <u>passes a memory address</u>, not a data value.
- This is implicit, the programmer does not need to take any action.
- **Scope Resolution Operator (`::`):**
- <u>Used to establish ownership of an identifier</u>.
- <u>Must be used when we split the method definitions from the class declaration (having separate header and implementation files)</u>.
- **Inconsistent State:**
- Anything that changes the state of an object in a way that makes it inconsistent.
- An example is setting values of attributes that do not make sense (negative lengths, areas...).
- <u>Can be fixed by using accessors and mutators, that allow safe access to data members and disallow incorrect states</u>.
- **`<stdexcept>`:**
- Header that defined *ready-to-use* exception classes, divided in two sets:
- **Logic Errors:**
```cpp
logic_error
domain_error
invalid_argument
length_error
out_of_range
```
- **Runtime Errors:**
```cpp
runtime_error
range_error
overflow_error
underflow_error
```
- **`static` Members:**
- Normal members of a class are called **instance members**, meaning each object has its own copy.
- Members may also be declared `static`:
- **Static Attributes:** Only one memory location is allocated for the class, so all objects created from the class share the storage (data).
- **Static Methods:** Can be called without first creating an object of the class. Can access static attributes.
- **`this`:**
- <u>Keyword representing an pointer to the current object</u>.
- Used to disambiguate naming in method bodies.
```cpp
class Rectangle
{
public:
void set_length (int length)
{
// "this" refers to the length attribute of the class.
// The other length is the parameter in the function.
this -> length = length;
}
private:
int length;
};
```
- **The C++ "Rule of 3":**
- If you need any one of:
- Destructor
- Copy Constructor
- Overloaded Assignment Operator
- Then you probably need all three.
- **`throw`:**
- Used to generate an exception and *throw* it up the call stack.
- Exceptions do not add complexity and are impossible to ignore.
```cpp
if (condition)
{
throw std::out_of_range {"Details of the exception..."};
}
```
- **Uniform Initialization Syntax:**
- Allows initialization of all types of variables with the same syntax.
```cpp
// Examples of "Old" Syntax:
// Primitives
int x = 2;
// Object Types
Rectangle r1(2,4);
// Array Initialization Syntax
int x[] = {1, 2, 3, 4, 5};
// Examples of Uniform Initialization Syntax:
// Primitives
int x {2};
// Objects
Rectangle r{2, 4};
// Arrays
int x[] {1, 2, 3, 4, 5};
```
- **Differences between a Physical and a Logical Object:**
- **Physical Object:** Defined at compile-time.
![[A007 - Physical Object.png | 500]]
- **Logical Object:** Runtime representation of an object and everything "owned" by it.
![[A008 - Logical Object.png | 500]]
- **Deep Copy:** When both physical and logical objects are copied.
- **When to do Deep Copy:**
- We have a pointer pointing outside of the physical object, like objects in the heap.
- If we have system resources that we are holding open and cannot be copied.
- **Different Meanings of `*` and `&`:**
```cpp
int* x; // Pointer Type Modifier
*x // Indirection / Dereference Operator
x * y // Multiplication Operator
int& r; // Reference Type Modifier (Pass-by-Reference)
&r // Address Operator
```

View File

@ -0,0 +1,838 @@
> ### SG002 - Exam 2
> Study Guide
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Aggregation / Composition of Objects:**
> Occurs when <u>an object of one class is used as an attribute in another class</u>.
- Allows building bigger classes without rewriting code (*code reuse*).
- Creates the <u>has a</u> relationship between the classes (Whole-Part Relationship).
- <u>Object aggregation is the simplest way of reusing code</u>. Another way would be inheritance.
```cpp
class Item
{
public:
// Additional code...
private:
// This demonstrates aggregation of objects.
// "string" is an object from another class.
// We can say that Item "has a" string.
string name;
};
class ItemList
{
public:
// Additional code...
private:
// This is also aggregation.
// ItemList is based on Item, which is from a different class.
// We can say ItemList "has an" Item.
Item item_list[3];
};
```
- **Backtrace [`bt`]:**
> In debugging, a *backtrace* is a listing of the program's call stack at the moment that execution was paused, or stopped by a crash.
- When observing a *backtrace*, the most recently-called function is listed first.
- <u>The `main()` function will be listed last</u>.
- **Stack Frames:** Function calls and data of their arguments. The number (starting from $0$) assigned to each function.
- **Call Stack:**
```cpp
/* These first frames refer to code in an external library. */
/* Most of the time they will be error-free. */
#0 __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:157
#1 0x00007ffff7b78e87 in void std::__cxx11::basic_string<char, std::char_traits<char>,
std::allocator<char> >::_M_construct<char*>(char*, char*, std::forward_iterator_tag) ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#2 0x00007ffff7b78ecf in std::__cxx11::basic_string<char, std::char_traits<char>,
std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>,
std::allocator<char> > const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
/* These frames come from our code. */
/* There are of our interest for debugging. */
#3 0x0000000000403643 in Foo::tag (this=0x7fffffffd7a8) at Foo.cpp:49
#4 0x0000000000402c4e in sort (begin=0x7fffffffd550, end=0x7fffffffd7a8) at debug_inlab_1.cpp:129
#5 0x00000000004018ca in main () at debug_inlab_1.cpp:28
```
- **`try` and `catch`:**
> `try` introduces a block of code where an exception *might occur*.
> `catch` is used to detect and handle a specific type of exception, if it *actually occurs* during execution of a `try` block.
- If an exception occurs during a `try` block, execution will stop and the code in the `catch` block will be executed.
- <u>The `catch` block will only be executed if the exception thrown by the `try` block matches the parameter type in `catch`</u>.
- The parameter in `catch` must be *passed as constant reference* to avoid creating a copy. Otherwise, the compiler will warn about *catching a polymorphic type by value*.
- <u>We cannot `catch` without `try`, nor `try` without `catch`</u>.
- It is possible to have multiple `catch` blocks. Each block will be scanned until one matches the type of the exception thrown.
- Any type may be thrown as an exception, even custom objects defined specifically for this purpose.
```cpp
try
{
my_function();
}
catch (const std::out_of_range&)
{
// Additional code to handle the exception...
}
```
- **`const` Pointer:**
- <u>The value of the pointer, that is, the target memory address, cannot be changed</u>.
- Useful for starting or ending addresses of an array.
```
int* const xPtr;
```
- **Pointer to `const`:**
- <u>The pointer will not be able to modify the target data; that is, the data stored in the address the pointer contains</u>.
- Useful for read-only values.
```cpp
const int* xPtr;
```
- **`const` Pointer to `const`:**
- <u>Neither the pointer nor its target can be changed</u>.
```cpp
const int* const xPtr;
```
- **Copy Constructor:**
- Called in cases when a copy of an object is being created *on-the-fly*, like the following:
- An object is assigned to another object.
- An object is being *returned by value*.
- An object is *passed-by-value* as an argument.
- An object is thrown and caught by value, like in an exception.
- An object is placed in a brace-enclosed initialized list.
- It commonly takes a `const` reference to an object of the same class as a parameter.
- <u>The parameter has to be *passed-by-reference*, otherwise we would be creating a copy, which would call the copy constructor, resulting in infinite recursion</u>.
- If we have a working overloaded assignment operator, the copy constructor can be a *one-liner*.
```cpp
/**
* @file MyClass.h
* @brief Definition of MyClass
*/
class MyClass
{
public:
// Copy Constructor
MyClass (const MyClass& original_obj);
};
/**
* @file MyClass.cpp
* @brief Implementation of MyClass
*/
MyClass::MyClass (const MyClass& original_obj)
{
// By having a working overloaded assignment operator
// we can simply assign the original_obj to the
// current object.
*this = original_obj;
}
```
- **Destructor:**
> A destructor is executed at the end of an object's lifetime; <u>either when the object is deleted or going out of scope</u>. It must perform shutdown actions.
- <u>Destructors take no parameters and have no return type</u>. For this reason, <u>they cannot be overloaded</u>.
- They are named the same as the class, but beginning with `~`.
- <u>They cannot be directly called</u>, this is done automatically by the compiler.
- We can guarantee that a destructor will be executed at the end of an object's lifetime.
- <u>Destructors are not always required to be explicitly defined in a class</u>.
- <u>They should be explicitly defined when the logical object does not match the physical object</u>; that is, when dynamic memory was allocated and needs to be de-allocated.
```cpp
/**
* @file MyClass.h
* @brief Definition of MyClass
*/
class MyClass
{
public:
// Destructor
~MyClass ();
};
/**
* @file MyClass.cpp
* @brief Implementation of MyClass
*/
MyClass::~MyClass ()
{
// Implementation for the Destructor.
// An example would be deallocating dynamic memory:
delete [] my_array;
}
```
- **Dynamic Memory Allocation:**
- New memory can be allocated for individual data values or arrays at runtime.
- **`new`:**
> Operator that allocates new memory.
- It <u>returns a pointer to the allocated space</u>.
- **`delete`:**
> Operator that de-allocates memory previously allocated using `new`.
- <u>Failure to use `delete` results in memory leaks</u>.
- <u>Using `delete` will not change the value of the pointer, nor the value stored in the pointer's address. It will simply *mark the memory as free*</u>.
- After using `delete`, we should always *null the pointer* using `nullptr`. <u>Attempting to dereference a pointer containing `nullptr` will cause an exception</u>.
- <u>Objects that allocate memory using `new` will need an explicit destructor</u>.
- **`new` and `delete` for Single Items:**
```cpp
// Allocate new memory.
// Consider that "new" returns a pointer.
int* xPtr = new int;
// To access or modify the value inside xPtr, we
// need to dereference the pointer.
*xPtr = 10;
// De-allocate the memory.
delete xPtr;
// Null the pointer.
xPtr = nullptr;
```
- **`new` and `delete` for Dynamic Arrays:**
- Normal arrays cannot be sized at runtime.
- <u>Dynamic memory allocation allows using a variable to determine the size of an array at runtime</u>.
```cpp
// Allocate new memory for a dynamic array.
// We can now use a variable for the size declarator.
int array_size = 10;
int* dyn_array = new int[array_size];
// Additional code here...
// De-allocate memory used by the dynamic array.
delete [] dyn_array;
// Null the pointer.
dyn_array = nullptr;
```
- If we ask the operating system for memory that it cannot give, the program will crash. To avoid this, we can check if memory was allocated:
```cpp
// In case allocation fails, the program will not crash.
// This does not throw an exception.
// If allocation fails, this returns nullptr.
int *xPtr = new(std::nothrow) int;
// We can now check if allocation worked.
if (xPtr == nullptr)
{
// Additional code here...
}
```
- **Heap:**
- <u>`new` will allocate memory from a *pool of free memory* known as the **heap**</u>.
- The heap is a limited resource.
- If the pointer to a dynamically-allocated object is lost or its address is changed before de-allocating the memory, we will have causes a memory leak.
- Ownership of a dynamically-allocated object can be transferred by passing the pointer to another part of the program.
- **Exceptions:**
> An exception is used to communicate that something unexpected has occurred. They allow the programmer to deal with it in a controlled manner.
- Exceptions are *out-of-band*, meaning do not add complexity to functions or objects.
- <u>Exceptions are impossible to ignore</u>.
- Failure to catch an exception means it will continue *flying up* the call stack.
- If it gets all the way out of `main()` without being caught, the program will be terminated by the operating system.
- **Indirection Operator / Dereference Operator [`*`]:**
> Allows *following* a pointer to the address it is storing, so that we can operate on the data stored at that location.
- A pointer *indirectly* refers to an object. We can <u>dereference the pointer</u> and access or modify the object.
```cpp
// Dereferencing a pointer using '*'.
// We are changing the value in the address that the pointer points to.
*xPtr = 100;
```
- **Operator Arity:**
- **Unary Operators:** Operators with *one* operand.
- Logical Not `!a`
- Negation `-a`
- Increment `++a, a++`
- Decrement `--a, a--`
- Address Operator `&a`
- Indirection / Dereference `*a`
- **Binary Operators:** Operators with *two* operands.
- Addition `a + b`
- Subtraction `a - b`
- Multiplication `a * b`
- Division `a / b`
- Modulus `a % b`
- Stream Insertion `strm << a`
- Stream Extraction `strm >> a`
- Assignment `a = b`
- Comparison `==, !=, <, >, <=, >=`
- Array Index Operator `a[b]`
- Dot `a.b` *(cannot be overloaded)*
- Arrow `a -> b`
- **Ternary Operators:** Operators with *three* operands. <u>Cannot be overloaded</u>.
- Conditional `a ? b : c`
- **Overloaded Operators:**
- Almost all operators in C++ can be overloaded.
- The name for the functions of each operator is the word `operator` followed by the symbol for the operator being overloaded.
- **Overloading Operators as Standalone Functions:**
- <u>Useful when we have a left-hand operand that is not a member of our class</u>.
- Examples include `<<` and `>>`.
- If the attributes we want to operate on are `private` we need to rely on accessors.
- Two possible solutions are `friend` functions or overloading as class methods.
- **Unary Operators:** The only operand involved needs to be passed as a parameter.
- **Binary Operators:** Both the left-hand and right-hand operands need to be passed as parameters.
- **Overloading Operators as Class Methods:**
- <u>Overloading as methods keeps class-related code encapsulated</u>.
- It also allows access to `private` attributes without the need for accessors.
- There are some operators, like `=` and `[]` that can only be overloaded as class methods.
- **Unary Operators:** The object itself is being operated on, and is represented by the `this` keyword. <u>There are no additional parameters</u>.
- **Binary Operators:** The left-hand operand will be the current object, and is represented by the `this` keyword. <u>The right operand needs to be passed as a parameter to the method</u>.
- <u>Most operators do not change the operands. We should use `const` methods and *pass by constant reference*</u>.
- Only exceptions are `<<, >>, ++, --, =`, which **do** need to change one of the operands.
- **Examples of Overloaded Operators:**
```cpp
/**
* @file Vector2D.h
* @brief Definition of Vector2D Class
*/
class Vector2D
{
public:
Vector2D() : x_operator{x}, y_operator{y} {}
Vector2D operator- () const;
Vector2D operator+ (const Vector2D& rhs) const;
Vector2D operator* (int rhs) const;
// Comparisons return a bool.
bool operator== (const Vector2D& rhs) const;
private:
int x_component = 1;
int y_component = 1;
};
Vector2D operator* (int lhs, const Vector2D& rhs);
std::ostream& operator<< (std::ostream& strm, const Vector2D& rhs);
std::istream& operator>> (std::istream& strm, Vector2D& rhs);
/**
* @file Vector2D.cpp
* @brief Implementation of Vector2D Class
*/
/**
* Unary Negation Operator
*
* - The negation operator only takes one operand; the current object.
* - No parameters are needed.
* - It does not actually change the object, it returns a negated version.
*/
Vector2D Vector2D::operator- () const
{
return Vector2D {-x_component, -y_component};
}
/**
* Addition Operator
*
* - Takes two parameters, Vector01 (left-hand side, lhs) + Vector02 (right-hand side, rhs).
* - The left-hand side is the object itself, and is represented by "this".
* - The right-hand side is the parameter given to the method.
* - Neither side should be modified.
* - rhs is "const" qualified so it is not modified.
* - The method is "const" qualified so it does not modify lhs (the current object).
* - The method should return the resulting addition.
*/
Vector2D Vector2D::operator+ (const Vector2D& rhs) const
{
int x_result = x_component + rhs.x_component;
int y_result = y_component + rhs.y_component;
return Vector2D {x_result, y_result};
}
/**
* Multiplication Operator [1]
*
* - Takes two parameters, Vector2D (left-hand side, lhs) * int (right-hand side, rhs).
* - The left-hand side is the object itself, and is represented by "this".
* - The right-hand side is the parameter given to the method.
* - The method is "const" qualified so it does not modify lhs (the current object).
* - The method should return the resulting multiplication.
* - Notice that to do int * Vector2D we need an additional overload.
*/
Vector2D Vector2D::operator* (int rhs) const
{
int x_result = x_component * i;
int y_result = y_component * i;
return Vector2D {x_result, y_result};
}
/**
* Multiplication Operator [2]
*
* - Takes two parameters, int (left-hand side, lhs) * Vector2D (right-hand side, rhs).
* - We cannot overload this as a class method, because lhs is an integer that is not an attribute of our Vector2D class.
* - Because in a class method, lhs is always represented by "this", we cannot simply use the integer in its place.
* - We need to overload this as a standalone function.
* - Both the Vector2D and the integer are given as parameters.
* - We can take advantage of multiplication being commutative and call our previous method.
*/
Vector2D operator* (int i, const vector2D& rhs)
{
return rhs * i;
}
```
- **Overloaded Assignment Operator:**
- Useful when we have pointers pointing outside of the *physical object*.
- <u>It is necessary whenever the physical and logical objects differ</u>.
- The implementation needs to make sure a complete, independent copy of the source object is made.
```cpp
/**
* @file MyClass.h
* @brief Definition of MyClass
*/
class MyClass
{
public:
// Both input and output objects are passed-by-reference.
// This method cannot be "const" because it is modifying the right-hand side.
// The return type needs to be by constant reference to allow chained use of the assignment operator (a = b = c = ...).
const MyClass& operator= (const MyClass& rhs);
};
/**
* @file MyClass.cpp
* @brief Implementation of MyClass
*/
const MyClass& operator= (const MyClass& rhs)
{
// First, check for self-assignment.
if (this = &rhs)
{
// De-allocate memory used by destination array.
delete [] array;
// Allocate memory for a new array with the size of the given parameter.
array = new int[rhs.size];
size = rhs.size;
// Copy each element to the destination array.
for (int i = 0' i < size; i++)
{
array[i] = rhs.array[i];
}
}
// Return the destination array.
// We need to dereference "this" so we return the object and not its address.
// The function will return it by constant reference, so it will not be modified if used in chained assignment.
return *this;
}
```
- **Stream Operators:**
- Both stream operators (`<<` and `>>`) always have a stream object as the left-hand side.
- <u>They will always return the stream</u>.
- This is necessary for chained use of the stream operator.
- Example:
```cpp
// First, strm << "Something..." is executed.
// strm would then be returned.
// Then strm << "Else.." is executed.
strm << "Something..." << "Else...";
```
- <u>Do not create a copy of an open stream, always pass-by-reference</u>.
- Because the left-hand side (the stream) is not an attribute of any class, overloading a stream operator needs to be done as a standalone function.
- `istream` and `ostream` can handle additional streams and file streams like `fstream`, which makes overloading easier.
```cpp
// Overloaded Stream Extraction Operator
std::ostream& operator<< (std::ostream& strm, const MyClass& rhs);
// Overloaded Stream Insertion Operator
// Notice that the right-hand side cannot be const, because we want to write the read value to it.
std::istream& operator>> (std::istream& strm, MyClass& rhs);
```
- **Overloading Increment and Decrement Operators:**
- These methods cannot be `const`, because they modify the object.
- There is both a prefix and a postfix version of the operators:
- **Prefix Version `++c` and `--c`**:
- The increment / decrement happens before the value is returned.
- **Example:**
```cpp
int c = 1;
// This statement would print 2.
// Value is returned after increment.
cout << ++c << endl;
```
- **Postfix Version `c++` and `c--`:**
- The value is returned before the increment / decrement.
- **Example:**
```cpp
int c = 1;
// This statement would print 1.
// Value is returned before increment.
cout << c++ << endl;
```
- Both versions are unary operators, meaning they only take one operand.
- If we were to overload them as a class method, they would take no parameters.
- If they take no parameters, we would not be able to overload them.
- <u>In order for the compiler to differentiate between the prefix and postfix versions, we need to add a *dummy parameter*</u>.
- The postfix method cannot be by reference, because it needs to return a copy of the object *before* incrementing / decrementing it.
```cpp
/**
* @file ComplexNumber.h
* @brief Definition of ComplexNumber Class
*/
class ComplexNumber
{
public:
ComplexNumber (double imaginary, double real);
// Prefix Version
ComplexNumber operator++ ();
ComplexNumber operator-- ();
// Postfix Version
// Notice the dummy "int" parameter.
// Because it is not actually used inside the method, we do not need to give it an identifier.
// It is simply there to signal the compiler that this is the postfix version.
ComplexNumber operator++ (int);
ComplexNumber operator-- (int);
private:
double imaginary = 0;
double real = 0;
};
// To overload them as standalone functions, we must give the object as a parameter.
// Prefix Version
ComplexNumber operator++ (ComplexNumber& operand);
ComplexNumber operator-- (ComplexNumber& operand);
// Postfix Version
ComplexNumber operator++ (ComplexNumber& operand, int);
ComplexNumber operator-- (ComplexNumber& operand, int);
```
- **Overloading the Array Operator `a[b]`:**
- The array operator is a binary operator.
- The left-hand side is the array object.
- The right-hand side is the index.
- <u>The array operator can only be overloaded as a method</u>.
- <u>We need to *return-by-reference*</u>, which allows to also modify the value in the target index (`x[3] = 2`), as well as retrieve it.
```cpp
int& MyClass::operator[] (int index)
{
// If we have a working at() method, this mehod is a one-liner...
}
```
- **Linked Lists:**
> They are a set of data structures, called **nodes**, that contain references to to other data structures.
![[A009 - Linked List.svg | 400]]
- <u>They are an *abstract data type* (ADT)</u>.
- **Linear Relationship:** Each node will reference the next node.
- In doubly-linked lists, also the previous node.
- <u>The references may be either memory addresses (pointers) or array indices</u>.
- **Advantages:**
- Data can be added or removed during execution, unlike arrays.
- We can always add or remove new values, and <u>the list will grow or shrink accordingly</u>.
- We can insert new data between nodes easily:
![[A010 - Adding a Node to a Linked List.svg | 400]]
- We no longer require data to be in a contiguous order in memory, unlike arrays.
- This also means we need to know where the first element (node) is.
- <u>Every node will have the responsibility of locating the next element</u>.
- In linked lists, we cannot calculate an offset like in arrays. <u>We have to go *node-by-node* to locate or remove an element</u>.
- Linked lists are very directional. We cannot *travel* back through them unless we have pointers going backwards (doubly-linked lists).
- A linked list involves two separate data structures:
- **Node:** Needs *at least* the following two attributes:
- **Payload:** An object or value.
- **Next:** A pointer, or array index, to the next node in the list.
![[A011 - Node.svg | 150]]
- **List:**
- <u>Contains the algorithms that put the nodes together</u>.
- Lists are made of $0$ or more nodes.
- **`head`:** Pointer to the *first node* in the list.
- **`tail`:** Pointer to the *last node* in the list.
- **Empty List:** A list containing $0$ nodes. Both the `head` and `tail` pointers will point to `nullptr`.
![[A012 - Head Pointer.svg | 150]]
- **Basic Operations of a List:**
- Addition of nodes at the front (`head`) or at the end (`tail`).
- Add a node *in order* in a sorted list.
- Traverse the list, meaning going through all the nodes from beginning to end.
- Searching the list.
- Deleting a node anywhere in the list.
- Empty the list.
- **Debugging Session:**
- In order to produce useful debugger output, we need to use the following command flags:
```bash
g++ -Wall -Wextra -pedantic -std=c++17 -g -fno-inline *.cpp
```
- To start a debugging session using `gdb`, use the following command:
```bash
gdb a.out
```
- To run the program in the debugging session, use:
```bash
run
```
- Identify a useful frame using `bt` (backtrace).
- To select a frame of interest, use:
```bash
frame 4
```
- To see the values of the **arguments** of the function, use:
```bash
info args
```
- To see the values of the **local variables**, use:
```bash
info locals
```
- To see the 10 lines around the line we are observing, use:
```bash
list
```
- To print the value of any argument or variable:
```bash
print my_variable
```
- **Valgrind:**
- Tool that can be used to detect memory leaks.
- To use it:
```bash
valgrind --tool=memcheck ./a.out
```
- **Reading or Writing after Memory was Freed:**
```cpp
==2849== Invalid read of size 1
==2849== at 0x400603: main (valgrind.c:30)
==2849== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
```
- **Memory Leaks:**
```cpp
==2888== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2888== at 0x4C274A8: malloc (vg_replace_malloc.c:236)
==2888== LEAK SUMMARY:
==2888== definitely lost: 1 bytes in 1 blocks
==2888== indirectly lost: 0 bytes in 0 blocks
==2888== possibly lost: 0 bytes in 0 blocks
==2888== still reachable: 0 bytes in 0 blocks
==2888== suppressed: 0 bytes in 0 blocks
```
- **Mismatched use of `delete` and `delete []`:**
```cpp
==2972== Mismatched free() / delete / delete []
==2972== at 0x4C26DCF: operator delete(void*) (vg_replace_malloc.c:387)
```
- **Doubly-Freed Memory:**
```cpp
==3167== Invalid free() / delete / delete[]
==3167== at 0x4C270BD: free (vg_replace_malloc.c:366)
==3167== by 0x40060A: main (valgrind.c:12)
```
- **The C++ "Rule of 3":**
- If you need any one of:
- Destructor
- Copy Constructor
- Overloaded Assignment Operator
- Then you probably need all three.
- **`throw`:**
- Used to generate an exception and *throw* it up the call stack.
- Exceptions do not add complexity and are impossible to ignore.
```cpp
if (condition)
{
throw std::out_of_range {"Details of the exception..."};
}
```
- **Uniform Initialization Syntax:**
- Allows initialization of all types of variables with the same syntax.
```cpp
// Examples of "Old" Syntax:
// Primitives
int x = 2;
// Object Types
Rectangle r1(2,4);
// Array Initialization Syntax
int x[] = {1, 2, 3, 4, 5};
// Examples of Uniform Initialization Syntax:
// Primitives
int x {2};
// Objects
Rectangle r{2, 4};
// Arrays
int x[] {1, 2, 3, 4, 5};
```
- **Differences between a Physical and a Logical Object:**
- **Physical Object:** Defined at compile-time.
![[A007 - Physical Object.png | 400]]
- **Logical Object:** Runtime representation of an object and everything "owned" by it.
![[A008 - Logical Object.png | 400]]
- **RAII:**
- Acronym for *Resource Allocation Is Initialization*.
- <u>When we create objects that represent dynamic data structures, the object itself is responsible for its own initialization, memory allocation, and eventually, memory deallocation</u>.
- The lifetime of dynamic resources is bound to the object itself.
- <u>Refers to managing dynamic resources in a class such that external users are not aware of it</u>.
- **`<stdexcept>`:**
- Header that defined *ready-to-use* exception classes, divided in two sets:
- **Logic Errors:**
```cpp
logic_error
domain_error
invalid_argument
length_error
out_of_range
```
- **Runtime Errors:**
```cpp
runtime_error
range_error
overflow_error
underflow_error
```
- **Levels of Indirection:**
![[A013 - Levels of Indirection.png | 400]]
- **Undefined Behavior:** <u>Anything that can be done that the language standard does not specify how to handle</u>.
- The compiler is able to do anything, sometimes making sense and sometimes not.
- This *does not guarantee* to cause a compiler error.
- We can end up accessing garbage values in a different memory address, or cause the program to crash.
- **DRY Principle:**
- Stands for *"Don't Repeat Yourself"*.
- **`friend` Functions:**
- `private` attributes in a class can only be accessed by other members in the class.
- Adding the `friend` type modifier to a function or class gives it access to the `private` and `protected` data in the class being defined.
- The `friend` type modifier is added to the front of a function declaration or its prototype.
- <u>Use this sparingly</u>.
```cpp
class MyClass
{
public:
// Code here...
private:
// Code here...
friend MyFunction ();
};
```
- **Return by Reference:**
- When a function returns a reference, it returns an implicit pointer to its return value.
- Instead of returning the value of a variable, a function would return the variable itself.

View File

@ -0,0 +1,892 @@
> ### SG003 - Exam 3
> Study Guide
> Emilio Soriano Chávez
> ***
> <span style="color:#00BFFF">OOP and Data Structures</span>
> Fall 2021
- **Stack:**
> Data structure optimized for addition and removal only at one end. Focuses on the <u>last-in, first-out</u> access pattern.
- In a stack, we are always operating on the same *side*, which is the *top* of the stack.
- **Stack Operations:**
- **`push()`:** Add a value at the *top* of the stack.
- **`pop()`:** Remove the value from the *top* of the stack.
- **`top()`:** Access the value at the *top* of the stack.
- **Implementation with a Singly-Linked List:**
- <u>Access is restricted to the *front* of the list</u>.
- The `head` pointer would represent the *top* of the stack.
- `add_front()` becomes `push()`.
- `remove_front()` becomes `pop()`.
```cpp
/****************************************
* @file IntList.h
****************************************/
#ifndef INTLIST_H
#define INTLIST_H
#include <iostream>
using std::ostream;
class IntList
{
public:
IntList ();
~IntList ();
bool is_empty () const;
void add_front (int new_value);
void add_back (int new_value);
int get_head () const;
int remove_front ();
void write (ostream& strm) const;
private:
IntNode* head = nullptr;
IntNode* tail = nullptr;
};
ostream& operator<< (ostream& strm, const IntList& list);
#endif
/****************************************
* @file IntStack.h
****************************************/
#ifndef INTSTACK_H
#define INTSTACK_H
#include <iostream>
using std::ostream;
#include "IntList.h"
class IntStack
{
public:
bool is_empty () const;
void push (int new_value);
int pop ();
int top () const;
void write (ostream& strm) const;
private:
IntList list;
};
ostream& operator<< (ostream& strm, const IntStack& stack);
#endif
/****************************************
* @file IntStack.cpp
****************************************/
#include "IntStack.h"
bool IntStack::is_empty () const
{
return list.is_emtpy();
}
void IntStack::push (int new_value) const
{
list.add_front(new_value);
}
int IntStack::pop ()
{
return list.remove_front();
}
int IntStack::top ()
{
return list.get_head();
}
void IntStack::write () const
{
list.write(strm);
}
ostream& operator<< (ostream& strm, const IntStack& stack)
{
stack.write(strm);
return strm;
}
```
- **Queues:**
> Data structure optimized for addition at one end and removal at the other end. Focuses on the <u>first-in, first-out</u> access pattern.
- The first item we add is the first item we want to remove. This is similar to *queuing in a line*.
- Addition always occurs at the *end* of the queue.
- Removal always occurs at the *beginning* of the queue.
- **Queue Operations:**
- **`enqueue()`:** Add a value at the *end* of the queue.
- **`dequeue()`:** Remove a value from the *front* of the queue.
- **`front()`:** Access the value at the *front* of the queue.
- **Implementation with a Singly-Linked List:**
- <u>Addition occurs at the *tail* and removal occurs at the *head*</u>.
- `add_back()` becomes `enqueue()`.
- `remove_front()` becomes `dequeue()`.
- A `tail` pointer is needed.
```cpp
/****************************************
* @file IntList.h
****************************************/
#ifndef INTLIST_H
#define INTLIST_H
#include <iostream>
using std::ostream;
class IntList
{
public:
IntList ();
~IntList ();
bool is_empty () const;
void add_front (int new_value);
void add_back (int new_value);
int get_head () const;
int remove_front ();
void write (ostream& strm) const;
private:
IntNode* head = nullptr;
IntNode* tail = nullptr;
};
ostream& operator<< (ostream& strm, const IntList& list);
#endif
/****************************************
* @file IntQueue.h
****************************************/
#ifndef INTSTACK_H
#define INTSTACK_H
#include <iostream>
using std::ostream;
#include "IntList.h"
class IntQueue
{
public:
bool is_empty () const;
void enqueue (int new_value);
int dequeue ();
int front () const;
void write (ostream& strm) const;
private:
IntList list;
};
ostream& operator<< (ostream& strm, const IntList& queue);
#endif
/****************************************
* @file IntQueue.cpp
****************************************/
#include "IntQueue.h"
bool IntQueue::is_empty () const
{
return list.is_empty();
}
void IntQueue::enqueue (int new_value)
{
list.add_back(new_value);
}
int IntQueue::dequeue ()
{
return list.remove_front();
}
int IntQueue::front () const
{
return list.get_head();
}
void IntQueue::write (ostream& strm) const
{
list.write(strm);
}
ostream& operator<< (ostream& strm, const IntQueue& queue)
{
queue.write(strm);
return strm;
}
```
- **Templates:**
- **Function Templates:**
> A *generic* function that can work with any data type.
- Normally, to declare a function we also have to declare the data types for the return value and the parameters.
- This means we would have to write a *different* version of a function for every different type we would want to use (overloading).
- Function templates allow only writing a single function definition that will work with different data types.
- <u>Function templates are not actual functions</u>. They are just a pattern for the compiler to generate different functions.
- The template will not do anything itself, *until the function gets called*.
- **Template Function:** When the compiler finds a call to the function, it will examine the data types and generate a function with data types that match those provided in the function call.
- <u>When we call the function, the compiler will *re-write* the template using the type from the call, and compile the new function</u>.
- **Parts of a Function Template:**
- **Template Prefix:**
- The beginning of a function template.
- Needs to begin with the `template` keyword.
- **Generic Data Types / Type Parameters:**
- Placed between angled brackets `<>`.
- Each type parameter needs to begin with the `typename` keyword.
- Before C++11, the keyword `class` was used instead of `typename`.
- Following `typename`, a parameter name is used in place for the data type.
- This name can be any valid identifier.
- Usually capitalized by convention.
- Additional type parameters are separated by commas.
- **Example:**
```cpp
template <typename MyType>
MyType my_function (MyType my_parameter)
```
- <u>Multiple type parameters can be given</u>, as long as they are all used in the definition.
```cpp
template <typename TypeA, typename TypeB>
TypeA my_function (TypeB my_parameter)
{
// Additional code here...
}
```
- Function templates can be overloaded, as long as each overload has a unique signature.
- <u>When an object is passed to a function template, the object must be able to behave in the way expected by the function</u>.
- The type of the object we are passing must be able to work in the context of the function.
- **Class Templates:**
> Used to create generic classes and abstract data types.
- They are commonly used for container types, like vectors.
- Allows for containers that work with any type.
- They are declared similarly to template functions. A *template header* is placed before the class declaration.
- <u>All references to the class as a type must include the type argument list</u>.
- When declaring objects.
- When scope resolution is required.
- **Example:**
```cpp
/****************************************
* @file MyClass.h
****************************************/
template <typename MyType>
class MyClass
{
public:
MyClass ();
MyType my_method () const;
private:
MyType my_attribute;
};
// We are outside of the class definition.
// Every method also needs to be a template.
// We need to add the template header again.
template <typename MyType>
MyType MyClass<MyType>::my_method () const
{
// Additional code here...
}
/****************************************
* @file main.cpp
****************************************/
#include "MyClass.h"
int main()
{
// Type argument list must be included.
MyClass<int> my_object;
return 0;
}
```
- **"Normal" Libraries:**
- Class definition is placed in the `.h` file.
- Class implementation is placed in the `.cpp` file.
- This is possible because the `.cpp` file can be compiled into an object file (binary representation).
- **"Template" Libraries:**
- Both class definition and implementation must be placed in the `.h` file.
- This is because <u>templates cannot be directly compiled</u>.
- All code must be placed inside the *include guards*.
- Using Doxygen, template types are documented as `@tparam`.
- Additionally, the requirements for the expected data type must be added.
- **Advantages of Templates:**
- **Generic Programming:**
- Implementation of algorithms should not depend much on the type they are operating on.
- <u>Templates allow separating an algorithm from the underlying type</u>.
- Allow making code *more reusable*.
- They do not make the code more complicated.
- <u>Templates do not take any memory</u>.
- **Disadvantages of Templates:**
- Compilation time will be longer.
- <u>Harder to debug</u>.
- The compiler will not fully *syntax check*.
- Semantic errors will not be found, so the compiler is less helpful.
- Larger header files.
- **Trees:**
> Non-linear linked structures where each node may link to two or more nodes. <u>They are hierarchical</u>.
- Trees are commonly drawn *upside down*, and null pointers are not shown.
- <u>Trees cannot be traversed from beginning to end</u>.
- **Parts of a Tree:**
- **Root:** Entry point into a tree, drawn at the *top*.
- **Interior Nodes:** Any node that has at least one child.
- **Leaf Nodes:** Node that has no child nodes.
- **Edge:** Non-null, outgoing link from a node to its child node.
- Edges always have a *downward* direction.
- They represent pointers.
- **Height:** Longest path along edges, from root to any leaf, plus one.
- Can be thought of as the *number of nodes that are visited*.
- <u>Height of an empty tree is $0$</u>.
- <u>Height of a single node is $1$</u>.
- **Subtree:** Any node in the tree and all of its descendants.
- **Binary Tree:**
> Tree in which each node has <u>up to two children</u>.
- The binary tree is the simplest hierarchical data structure, and the most common type of tree.
- Useful for modeling real world scenarios.
- Depending on the case, the order of the values *may or may not* matter.
- Each node can have either $0$, $1$, or $2$ children.
- **Binary Tree Node:**
- Must store a payload value.
- Maintains pointers to the left and right subtree.
- <u>Pointers point to the next node, not the payload type</u>.
- ***Visiting* a Node:** Doing something with a node, like printing it, modifying it, or deleting it.
- **Representation of a Binary Tree Node:**
![[A014 - Binary Tree Node.svg | 150]]
- **Example Implementation:**
```cpp
/****************************************
* @file IntNode.h
****************************************/
#ifndef INTNODE_H
#define INTNODE_H
class IntNode
{
public:
IntNode (int value) : payload {value} {}
int get_value () const;
IntNode* get_right () const;
IntNode* get_left () const;
void set_right (IntNode* new_right);
void set_left (IntNode* new_left);
private:
int payload;
IntNode* right = nullptr;
IntNode* left = nullptr;
};
#endif
/****************************************
* @file IntNode.cpp
****************************************/
#include "IntNode.h"
int get_value () const
{
return payload;
}
IntNode* get_right () const
{
return right;
}
IntNode* get_left () const
{
return left;
}
void set_right (IntNode* new_right)
{
right = new_right;
}
void set_left (IntNode* new_left)
{
left = new_left;
}
```
- **Binary Tree Class:**
- Links the nodes together.
- <u>Has ownership responsibility of all the nodes in the tree</u>.
- The *tree class* allocates new nodes.
- <u>Must have a `root` pointer to the entry point of the tree</u>.
- <u>`root` is not a node</u>. It is a pointer.
- *`root` works similar to a `head` pointer in a linked list*.
- **Representation of a Binary Tree:**
![[A015 - Binary Tree.svg | 400]]
- **Binary Search Tree:**
> For any non-empty binary tree, it is a Binary Search tree if and only if:
> - For any node, the value of the left child, if not null, is lower [`<`] than the current value.
> - For any node, the value of the right child, if not null, is larger [`>`] than the current value.
> - The left subtree, if not null, is a binary search tree.
> - The right subtree, if not null, is a binary search tree.
- <u>The empty tree is *trivially* a Binary Search Tree, as it does not break any rules</u>.
- <u>New values are always added at the leaf nodes</u>.
- Binary Search Trees are optimized for searching.
- **Complete Tree:** A tree in which every non-leaf node has all possible children.
- **Balanced Tree:** A tree where, at any node, the height of the left and right subtrees does not differ by more than $1$.
- A complete tree is balanced by default.
- **Balanced vs Unbalanced Binary Tree:**
![[A016 - Balanced vs Unbalanced Binary Tree.png | 400]]
- The relationship in Binary Search Trees is *in order*, where everything to the left is *less than* the current value; and everything to the right is *more than* the current value.
- The shape of binary trees is determined by the order data is added.
- <u>Order of data addition affects efficiency</u>:
- **Best Case:**
- **Assuming the tree is balanced, access in a Binary Search Tree is $O(lg(N))$.**
- This is equivalent to a binary search, where we *eliminate* half of the remaining values every time.
- **Worst Case:**
- **If the tree is unbalanced, in the worst case, access is $O(n)$.**
- This is equivalent to having a linked list, where we have to visit every node.
- For a Binary Search Tree, we do not want data to *already come with an order*.
- <u>Truly random addition to a Binary Search tree eventually results in a balanced tree</u>.
- **Traversing a Binary Search Tree:**
- **In-Order Traversal [`LVR`]:**
- Left $\rightarrow$ Visit $\rightarrow$ Right
- Useful for printing all values in sorted order.
- **Pre-Order Traversal [`VLR`]:**
- Visit $\rightarrow$ Left $\rightarrow$ Right
- **Post-Order Traversal [`LRV`]:**
- Left $\rightarrow$ Right $\rightarrow$ Visit
- Useful for *destructing* the tree.
- **Example Implementation:**
```cpp
/****************************************
* @file BinarySearchTree.h
****************************************/
#ifndef INTNODE_H
#define INTNODE_H
#include <iostream>
using std::ostream;
#include "IntNode.h"
class BinarySearchTree
{
public:
BinarySearchTree () = default;
~BinarySearchTree ();
void add_recursively (int new_value);
void add_iteratively (int new_value);
void write_recursively (ostream& strm) const;
void write_iteratively (ostream& strm) const;
void erase_recursively ();
private:
void add_recursively (IntNode* current_root, IntNode* new_node);
void write_recursively (IntNode* current_root, ostream& strm) const;
void erase_recursively (IntNode* current_root);
IntNode* root = nullptr;
/* Disallow Copy-Constructor */
BinarySearchTree (const BinarySearchTree&) = delete;
/* Disallow Assignment Operator */
BinarySearchTree& operator= (const BinarySearchTree& rhs) = delete;
};
#endif
/****************************************
* @file BinarySearchTree.cpp
****************************************/
#include <stack>
using std::stack;
#include "BinarySearchTree.h"
BinarySearchTree::~BinarySearchTree ()
{
erase_recursively();
}
void BinarySearchTree::add_recursively (int new_value)
{
IntNode* new_node = new IntNode {new_value};
if (root)
{
add_recursively(root, new_node);
}
else
{
root = new_node;
}
}
void BinarySearchTree::add_iteratively (int new_value)
{
IntNode* new_node = new IntNode {new_value};
IntNode* current = root;
IntNode* previous = nullptr;
while (current)
{
previous = current;
if ((new_node -> get_value()) < (current -> get_value()))
{
current = current -> get_left();
}
else
{
current = current -> get_right();
}
}
if (previous)
{
if ((new_node -> get_value()) < (previous -> get_value()))
{
previous -> set_left(new_node);
}
else
{
previous -> set_right(new_node);
}
}
else
{
root = new_node;
}
}
void BinarySearchTree::write_recursively (ostream& strm) const
{
if (root)
{
write_recursively (root, strm);
}
}
void BinarySearchTree::write_iteratively (ostream& strm) const
{
stack<IntNode*> ptr_stack;
IntNode* current = root;
while (!ptr_stack.empty() || current)
{
while (current)
{
ptr_stack.push(current);
current = current -> get_left();
}
current = ptr_stack.top();
ptr_stack.pop();
strm << (current -> get_value()) << '\n';
current = current -> get_right();
}
}
void BinarySearchTree::erase_recursively ()
{
if (root)
{
erase_recursively(root);
root = nullptr;
}
}
void BinarySearchTree::add_recursively (IntNode* current_root, IntNode* new_node)
{
if ((new_node -> get_value()) < (current_root -> get_value()))
{
if (current_root -> get_left())
{
add_recursively(current_root -> get_left(), new_node);
}
else
{
current_root -> set_left(new_node);
}
}
else
{
if (current_root -> get_right())
{
add_recursively(current_root -> get_right(), new_node);
}
else
{
current_root -> set_right(new_node);
}
}
}
void BinarySearchTree::write_recursively (ostream& strm, IntNode* current_root) const
{
if (current_root)
{
write_recursively(strm, current_root -> get_left());
strm << (current_root -> get_value()) << '\n';
write_recursively(strm, current_root -> get_right());
}
}
void BinarySearchTree::erase_recursively (IntNode* current_root)
{
if (current_root)
{
erase_recursively(current_root -> get_left());
erase_recursively(current_root -> get_right());
delete current_root;
}
}
```
- **Inheritance:**
> Allows a new class to be based on an existing class. The new class will automatically inherit all members of the class it is based on.
- <u>Establishes the **is a** relationship</u>:
- This means going from <u>most general to most specific</u>.
- A real-world example of inheritance is:
- *A mammal **is an** animal...*
- *A dog **is a** mammal...*
- *A poodle **is a** dog...*
- Inheritance *classifies* things into hierarchies.
- Allows re-using code and encapsulation.
- **Base (Parent) Class:**
- More general class.
- *Starting point*.
- **Example:** A `Student` base class:
```cpp
class Student
{
// Additional code here...
};
```
- **Derived (Child) Class:**
- More specialized class.
- Derived from the base class.
- **Example:** An `Undergrad` derived class:
- An `Undergrad` **is a** `Student`.
- The `Student` class needs to exist to be able to inherit from it.
```cpp
class Undergrad : public Student
{
// Additional code here...
};
```
- **Characteristics of Inheritance:**
- <u>A derived class object **is a(n)** object of the base class</u>.
- The derived class has all the characteristics of the base class:
- This means it instantly has all members defined in the base class. [*Code Reuse*]
- Additionally, it will have all members defined in the derived class. [*Extra Code*]
- Derived class objects can access:
- All `public` and `protected` members of the base class.
- All members of the derived class.
- **`protected`:**
- Access specifier that works similar to `private`, except for derived classes.
- <u>`protected` members can be accessed by derived classes, but not by external methods</u>.
- Convenient for writing a base class, although derived classes can always use accessors and mutators.
- <u>`private` members from the base class are part of the derived class, but cannot be accessed</u>.
- **Inheritance Access Levels:**
- **`public`:**
- The true **is a** relationship.
- **`protected`:**
- <u>`public` items from the base class become `protected` in the derived class</u>.
- In this case, it is worth considering aggregation, which will be a closer mapping.
- **`private`:**
- <u>`public` and `protected` items from the base class become `private` in the derived class</u>.
- Does not give the **is a** relationship.
- The derived class object cannot be treated as an object of the base class.
![[A017 - Access Levels.svg | 400]]
- **Order of Constructor and Destructor Calls:**
- <u>The base class needs to be constructed before the derived class</u>
- The only way to do this is using the constructor's member initialization list.
- This guarantees that the base class gets constructed before the derived class.
- A derived class can pass arguments to the base class constructor.
- **Example Implementation:**
```cpp
// We are passing arguments to the Rectangle base class constructor.
// This guarantees it gets constructed first.
Square::Square (int s) : Rectangle {s, s} {}
```
- <u>The derived class must be destructed before the base class</u>.
- **Redefining a Method:**
> A method is said to be redefined in a derived class when it has the same name as a method in the base class.
- Useful for *specializing* the behavior of the derived class.
- Allows adding new functionality to a method.
- This is different than overloading:
- **Overloading:**
- Same function names.
- *Same scope*.
- *Parameter list is different*.
- <u>The compiler will choose depending on the arguments used</u>.
- **Redefining:**
- Same function names.
- *Not in the same scope*.
- *Parameter list can be the same or be different*.
- <u>The compiler will choose based on the type of the object reference (pointer type)</u> and not the actual object type.
- The local (derived) identifier will *shadow* the base identifier.
- We get the closest method that is defined within the scope.
- <u>Base class objects would use the base class method</u>.
- <u>Derived class objects would use the derived class method</u>.
- If we wanted a derived class to use the base class method, we can use the scope resolution `::` operator.
- *Redefining causes problems when using pointers*:
- **Static Binding:**
- <u>Function (or method) calls are bound to their implementation at compile time by default, not at runtime</u>.
- The compiler will use the implementation whose identifier is the closest available by scoping rules.
- **Example:**
```cpp
/****************************************
* @file BaseClass.h
****************************************/
#ifndef BASECLASS_H
#define BASECLASS_H
#include <iostream>
using std::cout;
using std::endl;
class BaseClass
{
public:
void x()
{
cout << "BaseClass::x()" << endl;
}
void y()
{
cout << "BaseClass::y()" << endl;
}
};
#endif
/****************************************
* @file DerivedClass.h
****************************************/
#ifndef DERIVEDCLASS_H
#define DERIVEDCLASS_H
#include <iostream>
using std::cout;
using std::endl;
#include "BaseClass.h"
class DerivedClass : public BaseClass
{
public:
void y()
{
cout << "DerivedClass::y()" << endl;
}
};
/****************************************
* @file main.cpp
****************************************/
#include <iostream>
using std::cout;
using std::endl;
#include "DerivedClass.h"
int main()
{
DerivedClass d1;
d1.x();
return 0;
}
```
```text
Output:
BaseClass::x()
BaseClass::y()
```
- In this case, the `BaseClass::y()` method was called.
- This is not what we wanted, but it is what is expected.
- At compile time, the `x()` method from the base class can only *see* the `y()` method in the base class. It cannot see methods from derived classes.
- As a result of static binding, the compiler will bind the `x()` method of the base class to the `y()` method of the base class, <u>at compile time</u>.
- **Class Hierarchies:**
- Derived classes can also be used as base classes.
- This complicates issues like method redefining.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,82 @@
> ### 001 - Review
> Class Notes - January 13, 2022
> Emilio Soriano Chávez
> ***
> <span style="color:#F4A460">Algorithms and Advanced Data Structures</span>
> Spring 2022
- **Inheritance:**
- *Is-a* relationship.
- Compare with composition. (*has-a* relationship)
- How to do it in C++.
- Access specifiers for inheritance.
- **Pointers:**
- Physical view of the pointer.
- The pointer itself is a location in memory that is a reference to a physical value.
- Indirection operator.
- Pointer type modifier.
- **Random Access Files:**
- Multiple levels of indirection.
- Pointers pointing to pointers.
- **Polymorphism:**
- Virtual functions can be redefined.
- Derived classes can be treated as a variable of their base class.
- Allowing derived classes to act as their base class.
- Virtual allows a base class pointer to point to a derived class.
- How STL containers work.
- They use iterators.
- Vectors.
***
**Linked Lists**
1. Singly Linked
2. Ordered Linked
3. Doubly Linked
4. Circular
- Linked lists are typically one-way lists.
- Doubly-linked lists half this time, only once.
- **Skip Lists:**
- For every element of a list we have a skip.
- *Add binary search to a linked list*.
- **Algorithm Complexity:**
- Big-O - *Upperly* bounds a function. Time will not ever get bigger than that number.
- $\Omega$-Notation: A function that can floor a function. A lower bound can be used for comparison.
- **Sparse Matrices / Sparse Tables:**
- **Stack and Queue:**
- Priority queues.
- Being able to process things in an order beyond the *first-in first-out*.
- Recursion
- **Trees:**
- Binary Search Trees
- AVL Tree - Balancing binary tree. Balances itself after every add.
- Multi-Way Trees
- Nodes can have more than 2 connections.
- **Sorting:**
- Insertion
- Selection
- Bubble
- Heap
- Radix
- Quick
- Merge
- **Hashing and Hash Tables:**
- Hash function.
- The output of the hash function should match something in the hash table.
- **Graphs:**
- Nodes and edges that connect nodes.
- Shortest paths.
- Minimum Spanning Tree (MST)
- **Data Compression:**
- Huffman Code Encoding
- **Memory Management:**
- Activation records.
- Call stack.
- 51 to 57 CH2.

View File

@ -0,0 +1,43 @@
> ### 002 - Complexity Analysis
> Class Notes - January 18, 2022
> Emilio Soriano Chávez
> ***
> <span style="color:#F4A460">Algorithms and Advanced Data Structures</span>
> Spring 2022
- **Complexity Analysis:**
- Big-O
- **Data Structures:**
- Particular way of organizing data in a computer.
- Specific operations.
- Can be a built-in part of the programming language, for example, arrays.
- They can also be defined by a programmer, like a linked list.
- **Algorithms:**
- <u>Clearly specified set of instructions to be followed to solve a problem</u>.
- Factors that affect the speed of a program:
- Type of inputs to the program.
- Type of code. Scripted vs compiled languages. High vs low level languages.
- Speed of processor. How quickly instructions are performed. Also bandwidth (how much we can move in/out).
- <u>Complexity of the algorithms</u>. Typically, more complex algorithms are faster, but they are tradeoffs.
- **Algorithm Complexity:**
- To evaluate an algorithm's efficiency, real-time units such as microseconds should not be used.
- We want to be able to evaluate its performance wherever we run it.
- Logical units that express a relationship between the size of the data to process ($n$) and the amount of time required to process it ($t$).
- $t = cn$
- We want to look at $n$ as a large number so that it matters.
- Any terms that do not substantially increase $t$ should be eliminated.
- The cost of a computational algorithm is typically measured in terms of computational time, $t$, without units.
- Nested loops are typically $n²$.
- Algorithm analysis focuses on large datasets.
- Asymptotic efficiency of algorithms.
- How the running time of an algorithm scales with the size of its input.
- Worst Case
- Average Case
- The slowest (or largest) part of a function will be used to determine the speed of that function.
- **O-Notation:** Upper Bound
- $f(n)$ is $O(g(n))$ if there exist positive numbers $c$ and $N$ such that ...
- Describes a function $g(n)$ that acts as the upper bound for the algorithm that we are trying to analyze.
- **$\Omega$-Notation:** Lower Bound
- Establishes a lower bound for the function.
- $f(n)$ will not grow slower than this bound.
- **$\Theta$-Notation:** Tight Bound (What it is closest to...)

View File

@ -0,0 +1,74 @@
> ### 003 - Lists
> Class Notes - January 18, 2022
> Emilio Soriano Chávez
> ***
> <span style="color:#F4A460">Algorithms and Advanced Data Structures</span>
> Spring 2022
- **List:** Way of ordering data.
- **Common Operations:**
- Adding new elements.
- Searching for elements.
- Removing elements.
- **List Implementation:**
- **Array:**
- Simple to use.
- If index is known, it is random access $O(1)$.
- **Disadvantages:**
- Adding or removing is awkward.
- Adding elements in the middle is hard because all elements have to be shifted.
- **Linked List:**
- **Singly Linked (Unordered or Ordered):**
- Each node stores a payload.
- Next pointer.
- Head and sometimes tail pointers.
- Last element points to `nullptr`.
- The longer the list, the longer the chain of `next` pointers that need to be followed.
- Deletion should return the value and release the memory occupied by the node.
- **Searching:**
- Scan a list to find a data member.
- No modifications are made.
- **Insertion at an Ordered Linked List:**
- Create the new node.
- Loop until current's next is larger than the new node.
- Set new node's next to the current's next.
- Set current's next to new node.
- Doubly Linked
- Circular
- **Searching an Array:**
- Sequential Linear Search: $O(n)$ because we have to evaluate all values.
- Binary Search (if ordered):
- **Insertion:**
- **Array:**
- No Shifting is $O(1)$
- Shifting is $O(n)$
- **Linked List:** $O(n)$
- **Deletion:**
- **Array:**
- No shifting $O(1)$
- Shifting $O(n)$
- **Singly Linked List:**
- $O(n)$
- **Ordered Linked List:**
- Adding values in a sorted order.
- `addAscending`:
- Check if the list is empty.
- Set `head = tail`.
```cpp
void addAscending (int el)
{
if (head == nullptr)
{
head = new IntListNode(el);
tail = head;
}
else
{
IntListNode* previous;
// Additional Methods...
}
}
```

View File

@ -0,0 +1,84 @@
> ### 004 - Skip Lists
> Class Notes - January 25, 2022
> Emilio Soriano Chávez
> ***
> <span style="color:#F4A460">Algorithms and Advanced Data Structures</span>
> Spring 2022
- **Sorted Linked Lists:**
- $O(n)$ to `find()`.
- $O(n)$ to `insert()`.
- **Sorted Array:**
- $O(lg(n))$ to `find()` [Binary Search]
- $O(n)$ to `insert()`.
- **Adding Binary Search to a Linked List:**
- *Skip every other node.*
- Every other node has a pointer to the next and the one after that.
- *Base-level pointer* which is the typical `next`.
- Takes additional storage, because each node will contain an additional pointer.
- Performance of find will now be $n/2$.
- Examine $n/2 + 1$
- **Better Performance:**
- Skipping every 2nd and 4th node.
- Now, we have $n/4 + 3$.
- **True $lg$ Performance:**
- Skip every $2^i$ node.
- 16 nodes gives 4 layers (because $lg(16) = 4$).
- 100 nodes is roughly 7 layers.
- `head` is a stack of pointers (`0 1 2 3 4`).
- 5 different levels for a 16-node list.
- We are trying to find 13.
- Start with the top-most layer that is not `nullptr`.
- `head[4]` is what we look at first, which takes us to `16`.
- `13` is led than `16`, so we go down the level.
- Now, use `head[3]`, which leads us to `8`.
- `8` is less than `13`, so we go forward.
- `8[3]` is `16`, which is more than `13`, which means we go down a level.
```cpp
int* head[100];
class Node
{
int payload;
/* Dynamically allocate an array of next pointers.
* Based on each node.
*/
int** nexts;
}
```
- Find `7`:
```text
head[4] is 16, which is larger than 7, go back
head[3] is 8, which is larger than 7, go back
head[2] is 4, which is less than 7, go forward
4[2] is 8, which is larger than 7, go back
4[1] is 6, which is less than 7, go forward
6[1] is 8, go back
6[0] is 7
```
- At any point, if our target is **less than** the next node, stay and go down.
- If the target is **more than** the next node, go to the next node.
- The number of coin flips is equal to the ceiling function of $lg(nodes in list + node we are adding)$.
- **Skip List:**
> Variant of the **ordered** linked list. Makes <u>nonsequential search</u> possible.
- **Drawbacks of Linked Lists:**
- <u>Sequential scanning is needed to find an element</u>.
- Searching stops until the target element is found or the end of the list is reached.
- **Characteristics of Skip Lists:**
- Has $n$ nodes.
- <u>The number of pointers indicates the **level** of each node</u>.
- **Maximum Number of Levels:** $\text{max\_levels} = \lfloor lg(n) \rfloor + 1$

View File

@ -0,0 +1,136 @@
> ### E001 - Study Guide for Exam 2
> Extra Notes - April 09, 2022
> Emilio Soriano Chávez
> ***
> <span style="color:#F4A460">Algorithms and Advanced Data Structures</span>
> Spring 2022
***
#### Heaps
- What a Heap is
- How a Heap works
- MaxHeap & MinHeap
***
#### Hashing 1
- Hashing
- Why we use Hashing?
- What is a Hash Function?
- Function that we has a value into.
- Maps keys into positions in the Hash Table.
- *Basic Idea*
- Every time we hash a key, we should get the same output every time.
- Dealing with collisions.
***
#### Hashing 2
- Collision Methods
- *Formula* Representation
- Be able to tell what type of method given the formula.
- $h(k,c) = (h(k) + c) \: \% \: \text{size}$ - Linear Probing
- Uneven distribution.
- We tend to cluster entries int he table.
- *Areas with gaps in-between*.
- *Everything is hashing to the same area*.
- $h(k,c) = (h(k) + c^2) \: \% \: \text{size}$ - Quadratic Probing
- Can cause secondary clustering.
- $h(k,c) = (h_1 (k) + c \times h_2(k)) \: \% \: \text{size}$ - Double Hashing
- Two different hash functions.
- Open Hashing (Chaining)
- Add to a link list in a particular Hash Index.
***
#### Graphs
- Definitions
- Nodes, Vertex, Edges, Arcs
- How a graph is defined?
- Set of vertices and edges.
- Vertices are simply labels.
- Edges are pairs of vertex labels.
- Directed v.s Indirected Graphs
- Edges are ordered in directed graphs.
- Indirected graphs have no orientation.
- Cycles
- We move through the same nodes more than once.
- If $v_1 = v_n$.
- Path
- Sequence of the edges.
- Adjacency (~)
- Two nodes are adjacent if they are connected.
- If we have (u -> v), then `u ~ v` is true but `v ~ u` is false.
- Acyclic Graph
- Has no cycles.
- Sub-Graphs
- Parts of a graph.
- Connected vs. Disconnected Graphs
- A sub-graph can be disconnected even if the whole graph is connected.
- Complete Graphs (Fully Connected Graphs)
- Every node is connected to every other node.
- Weighted Graphs
- Weights can represent distance, cost, etc...
- Numbers assigned to edges.
- Directed Acyclic Graph (DAG)
- A tree is a specific form of a graph.
- Representation of a Graph
- Adjacency Matrix
- *Represented like a table*.
- Undirected graphs have a symmetric matrix.
- Directed may be symmetric.
- Weighted graphs use weights.
- No connection is represented with $\infty$.
- Adjacency List
- *Array of linked lists*.
- List of all of the nodes that we are adjacent to.
- Graph Traversal Algorithms
- Breadth First Search
- Uses a queue.
- After visiting a vertex, visit all vertex adjacent to it before moving on.
- Depth First Search
- Uses a stack.
- Move as deep as possible and the go back up.
- Implement in code if given a queue/stack and a list.
- In weighted graphs, the sum of the weights is the cost/distance of the path.
- Dijkstra's Algorithm
- Single short shortest path.
- How to do it?
- MST
- Prim's
- Start at a given node.
- Select other nodes.
- Give the order in which they are selected.
- <u>Never connect multiple nodes back up</u>.
- Cresicle's
- Select the lowest (we do not have a starting point).
***
- 2-3 and 2-3-4 Trees
- How to add
- How to use them
- Doing a lookup.
- Be able to insert numbers and do the splits.
- Show the final tree.
- NO DELETION
- B-Trees
- n-way tree
- Know how to insert.
- NO DELETION
***
- Huffman Encoding
- How to encode and decode.
- How to build the coding tree.
- Benefits
- It is lossless.
- Know how to figure the compression size.
- Give compression ratio.
- Greedy Algorithm (What is this?)
- Examples
- Encode or decode a line given the tree.

View File

@ -0,0 +1,217 @@
> ### 001 - Chapter 01
> Class Notes - January 14, 2022
> Emilio Soriano Chávez
> ***
> <span style="color:#4169E1">Operating Systems</span>
> Spring 2022
- **Operating System:**
- Program that acts as intermediary between a user of a computer and the computer hardware.
- **Computer System Structure:**
- CPU, Memory, I/O Devices (Anything not within the CPU)
- Application Programs
- **4 Components of a Computer System:**
- Application Programs (OS acts as intermediate between the hardware and these).
- **What an OS does:**
- Manages dynamic resources.
- Scheduling processes.
- Process: Instance of a running program.
- **Kernel:**
- Running at all times on the computer.
- First thing that gets loaded.
- Nearly unrestricted access to the computer.
- Bootstrap Program
- Anything that is an I/O device does not have direct access to memory. It has to use the CPU as an intermediate.
- I/O devices and the CPU can execute concurrently.
- **Buffer:** A segment of memory.
- Each device controller has a local buffer.
- An operating system is interrupt driven.
- Interrupt architecture must save the address of the interrupted instruction.
- **Interrupt Handling:**
- OS preserves the state of the CPU by storing registers and the program counter.
- Anything that interfaces with the OS can trigger interrupts.
- **Interrupt Timeline:**
- I/O device transferring data in inst local buffer.
- It sends an interrupt to the CPU.
- The CPU stop sand handles the interrupt.
- Control returns to user program only upon I/O completion.
- The CPU can only process one interrupt at a time.
- **System Calls:** Request to the OS to allow user to wait for I/O completion.
- A word is the native unit of data.
- 64-bit words.
- **Storage Structure:**
- **Main Memory:** Large storage media that the CPU can access directly.
- Random access.
- Volatile.
- **Secondary Storage:** Large nonvolatile storage capacity.
- Hard Disks
- Solid-State Disks
- **Caching:** Copying information into faster memory.
- **Storage-Device Hierarchy:**
1. **Registers:** Located in the CPU.
2. Cache: L1 (smaller and closer to CPU), L2 (larger and further away), L3
3. **Main Memory:** Large and volatile.
4. Solid-State Disk
5. Hard Disk
6. Optical Disk
7. Magnetic Tapes
- Device Drivers
- **Caching:**
- Performed at many levels in a computer.
- Store data so it can be used later.
- Cache Invalidation: Source of the data changes. Re-load data so we are not working on old data.
- Faster but smaller.
- **Direct Memory Access (DMA):**
- All information goes through the CPU.
- When moving data between devices, the request goes through the CPU.
- With DMA, a device can directly access memory without going through the CPU.
- Useful for high speed devices.
- **Computer-System Architecture:**
- Single processor computers.
- **Multiprocessors:** Also applies to multiple-core processors.
- Also known as parallel systems, or tightly-coupled systems.
- **Types:**
- **Asymmetric Multiprocessing:** Each processor is assigned a specific task.
- **Symmetric Multiprocessing:** Each processor performs all tasks. Each CPU has its own registers and cache.
- **Clustered Systems:** Multiple systems working together.
- High-speed throughput and low latency.
- High-performance computing. (HPC).
- Asymmetric Clustering: One machine in hot-standby node.
- Symmetric Clustering: Multiple nodes running applications, monitoring each other.
- Storage area network.
- **Operating System Structure:**
- **Multiprogramming:** Needed for efficiency.
- A single user cannot keep CPU and I/O devices busy at all times.
- Multiprogramming organizes jobs so CPU always has one to execute.
- One job is selected and run via job scheduling.
- When it has to wait, the OS switches to another job.
- **Memory Layout:**
- OS always has the lowest region of memory.
- **Timesharing (Multitasking):**
- Logical extension in which CPU switches jobs so frequently that users can interact with each job while it is running, creating interactive computing.
- **Operating System Operations:**
- Hardware interrupt from one of the devices.
- **Interrupt Driven:**
- Exception or Trap:
- Software error.
- Request for OS service.
- **Two Modes of Operation:**
- Allows OS to protect itself and other system components.
- **User Mode**
- **Kernel Mode**
- Mode bit is provided by hardware.
- Some instructions are privileged and can only run in kernel mode.
- **Transition from User to Kernel Mode:**
- We do not want user applications to modify some data.
- System calls.
- OS sets a timer to switch to kernel mode.
- Sends a system call to issue an interrupt.
- After certain time, it will issue an interrupt to switch to kernel mode.
- *Returning after an infinite loop*.
- **Process Management:**
- A process is a program in execution.
- A process needs resources to accomplish its task.
- Process termination requires reclaim of any reusable resources.
- Single-Threaded Process
- Multi-Threaded Process:
- One program counter per thread.
- A system has many processes running concurrently on one or more CPUs.
- **Process Management Activities:**
- OS is responsible of:
- Creating and deleting user and system processes.
- Suspending and resuming processes.
- Providing mechanisms for process synchronization, communication.
- Providing mechanisms for deadlock handling.
- **Memory Management:**
- To execute a program, all instructions must be in memory.
- All, or part of, the data needed by the program must be in memory.
- Memory management determines what is in memory and when.
- **Storage Management:**
- OS provides a uniform, logical view of information storage.
- **File:** Abstracts physical properties to logical storage unit.
- Each medium is controlled by a device (disk drive, tape drive, etc.).
- **File System Management:**
- Files are usually organized into directories.
- Access control determines who can access what.
- Creating, deleting, and manipulating files and directories.
- Mapping files onto secondary storage.
- Backup files onto stable storage.
- **I/O Subsystem:**
- System call for the OS to deal with devices.
- Done through device drivers.
- **Protection and Security:**
- **Protection:** Mechanism for controlling access of processes or users to resources defined by the OS.
- **Security:** Defense of the system against internal and external attacks.
- **Kernel Data Structures:**
- Similar to standard programming data structures.
- Each node in the list is a task for the CPU.
- Singly Linked List
- Doubly Linked List
- Circular Linked List
- Binary Search Tree
- RBTree (Red-Black Tree) is a self-balancing binary tree.
- Balanced Binary Search Tree is $O(lg(n))$.
- **Hash Map:**
- Implemented as an array.
- We give a key to a hash function, which returns the index to find data.
- Advantages: Every input produces the same output.
- Disadvantages Hash collision, different keys can produce the same hashes.
- To fix this we can use a linked list.
- If the location already has something, just add it to the list.
- *A form of compression...*
- Useful for string keys.
- **Bitmaps:**
- String of $n$ binary digits representing the status of $n$ items.
- **Computing Environments:**
- Network computers.
- *Thin Clients*: The machine that does the processing is the one in the servers.
- Extra Mobile Features: Camera, GPS, Gyroscope (Mediated by the OS)
- **Distributed Computing:**
- Separate, heterogeneous systems networked together.
- Networks
- **Client-Server Computing:**
- Client connecting to server.
- A single device can be both the server or the client, depending on the context.
- **Peer-to-Peer:**
- Examples include Voice over IP (VoIP)
- Direct connection instead of going to a central server.
- **Virtualization:**
- **Emulation:** Use software to simulate hardware.
- Generally slowest method.
- Used when source CPU type is different from target type.
- **Virtualization:**
- Software manages hardware resources that already exist.
- Virtual Machines
- We can specify resources that the virtual machine uses.
- Each OS runs its own kernel.
- **Cloud Computing**
- **Real-Time Embedded Systems**

View File

@ -0,0 +1,149 @@
> ### 002 - Chapter 02
> Class Notes - January 21, 2022
> Emilio Soriano Chávez
> ***
> <span style="color:#4169E1">Operating Systems</span>
> Spring 2022
- **Operating System Services:**
- **Interface:**
- Command-Line Interface (CLI)
- Graphical User Interface (GUI)
- Batch [Non-Interactive]
- **Program Execution**
- **File-System Manipulation**
- **Communications:**
- May be via shared memory.
- **Error Detection:**
- Exceptions
- **Resource Allocation**
- **Accounting:**
- Keeping track of users and the resources they have.
- **Protection and Security**
- **Command-Line Interface:**
- CLI or command interpreter.
- Some commands are built-in to the shell.
- Others are programs added to the path.
- Multiple flavors implemented -> Shells
- Bourne Shell Command Interface
- To check if a program is built-in or not use `type`.
- The shell provides an interface to the kernel.
- It *encapsulates* the shell.
- The terminal is the GUI for the shell.
- Often, these terms are used interchangeably.
- **Graphical User Interface (GUI)**
- **System Calls:**
- How we write code that interfaces with the kernel / OS.
- Typically written in a high-level language (the system's language).
- **Application Programming Interface (API):**
- Any interface for two functions to interact.
- Function prototypes.
- Win32 API
- `CreateThread` (Linux equivalent is `clone`).
- POSIX (Portable Operating System Interface) API
- Works across operating systems.
- Different versions for the code depending on the OS.
- With POSIX, just use `pthread_create`.
- This would call the appropriate function when compiled.
- **Example of System Calls:**
- System call to get filename (read data from standard input).
- Open system call.
- Create output file (create syscall).
```c
#include <unistd.h>
// void * means we are working with binary data.
ssize_t read(int fd, void *buf, size_t count);
```
- Whenever using a syscall, we have to switch from user to kernel mode.
- **System Call Parameter Passing:**
- For Linux, if there are fewer than 5 parameters, it uses registers.
- If not, it uses a block.
- All data will be copied to memory.
- The address will be copied to a register.
- The CPU can then access the data.
- Parameters can also be placed, or pushed, to the stack by the program.
- **Types of System Calls:**
- Creating process.
- Terminate process.
- End, abort.
- Load, execute.
- Get / Set Process Attributes.
- Location memory can be locked.
- **File Management**
- Access control.
- Open / Close Files
- **Device Management**
- **Information Maintenance**
- **Communications**
- Networks
- In C, `printf()` will call the `print` system call.
```c
/** Example using sycalls **/
#include <unistd.h>
int main()
{
// STDOUT_FILENO is standard output [POSIX].
write(STDOUT_FILENO, "Hello\n", 6);
const* name[6] = {0, 0, 0, 0, 0, 0};
read(STDIN_FILENO, name, 6);
write(STDOUT_FILENO, name, 5);
return 0;
}
```
- **System Programs:**
- Not necessarily running in the kernel.
- Services provided by the OS.
- **Registry:** Used to store and retrieve configuration information.
- Used in Windows.
- Linux uses config files.
- **Background Services:**
- Daemons:
- Something running in the background.
- Launched at boot time.
- **OS System Structure:**
- Simple Structure - MS-DOS:
- More Complex - UNIX (Monolithic Kernel)
- Limited by hardware functionality.
- Handles *everything*.
- Layered
- Microkernel - Mach
- Only has the essentials for an OS.
- File system access might not be implemented.
- Kernel is really simple.
- Adding features does not require rebuilding everything.
- Easy to extend.
- More reliable because less code is running in kernel mode.
- More secure.
- MacOS is a hybrid between a monolithic kernel and a microkernel.
- **Layered Approach:**
- Layer 0 - Hardware
- The highest layer is the user interface.
- Microkernels execute file systems in user space.
- Interprocess communication.
- **Modules:**
- Core kernel that implements base necessities of the OS.
- All systems can talk to one another, they run in kernel space, and do not need to go through the kernel form communication.
- Can be loaded either at runtime or at boor time.
- **Hybrid Systems**
- `printk()` allows specifying log levels.
- We can filter according to the string output.
```c
// Implicit string concatenation.
printk(KERN_INFO "Message: %s\n", arg);
```
- How many `jiffies` / `quanta` have occurred.

Some files were not shown because too many files have changed in this diff Show More