diff --git a/Academy DSA Typed Notes/Python Refresher/Refresher Functions b/Academy DSA Typed Notes/Python Refresher/Refresher Functions
new file mode 100644
index 0000000..71e57fb
--- /dev/null
+++ b/Academy DSA Typed Notes/Python Refresher/Refresher Functions
@@ -0,0 +1,601 @@
+# Refresher: Functions
+
+### Recap
+
+* DRY RUN - Running code line by line, we run the code just like the compiler does without using human intelligence.
+* Loops - instead of writing the code multiple times, we use loops to perform the same task
+* Nested Loops - These are loops inside loops.
+* We usually use `i` for rows and `j` for columns.
+---
+
+### QUIZ 1
+
+What will the sequence be generated by the following?
+
+```c
+range(-5,0,1)
+```
+
+
+### Choices
+- [ ] -5,-4,-3,-2,-1,0
+- [ ] Nothing
+- [ ] 0,-1,-2,-3,-4,-5
+- [x] -5,-4,-3,-2,-1
+---
+
+
+### QUIZ 2
+
+What will the sequence be generated by the following?
+
+```c
+range(-10,-5,-1)
+```
+
+### Choices
+- [ ] -10,-9,-8,-7,-6,-5
+- [ ] -10,-9,-8,-7,-6
+- [x] Nothing
+- [ ] -6,-7,-8,-9,-10
+---
+
+### QUIZ 3
+
+What will be the output of the following?
+
+```cpp
+for i in range(1,10):
+ if(i % 3 == 0):
+ break
+ print(i, end = ' ')
+```
+
+### Choices
+
+- [ ] 1 2 3 4 5 6 7 8 9
+- [x] 1 2
+- [ ] 1 2 4 5 7 8 9
+- [ ] 1 2 4 5 6 7 8
+---
+
+
+### QUIZ 4
+
+What will be the output of the following?
+
+```cpp
+for i in range(0,4):
+ for j in range(0,i):
+ print('a', end = '')
+ print()
+```
+
+### Choices
+- [ ] a
+aa
+aaa
+
+- [x] aaaa
+aaaa
+aaaa
+aaaa
+
+- [ ] aaa
+aa
+a
+---
+
+## Need for functions
+
+
+* We are working at a banking facility. We are to code the receipt that is printed after the ATM transaction. It is something like:
+
+```cpp
+******************
+Transaction Complete
+Thank you for visiting
+******************
+```
+so we will write the code for this:
+
+```python
+print('*' * 5)
+print("Transaction complete")
+print("Thank you for visiting")
+print('*' * 5)
+```
+* Now for every type of transaction same message will be printed. And for every transaction contained in the big file of 10000 lines of code, we have the same 4 lines for different transactions.
+* This is not following the DRY(Do not repeat yourself) principle. Which is a bad practice.
+* Let us say the back says they don't want to print `thank you` they wish to print `thanks` instead. So we will have to update it at all the places separately.
+* Functions are like alias - I can write a line of code and I can name it `abc`. Everywhere I can call abc the same piece of code will be executed.
+* Functions make code modular.
+* Now if there are any changes, we change it in one place and it will reflect in all the other places.
+
+## Syntax of Defining a Function
+**Defining a function**
+```python
+def function_name():
+ # actions
+```
+
+**To Call the function**
+```python
+function_name()
+```
+
+
+
+
+* The naming convention for a function is the same as variables.
+ * It should be composed of alphabet, number, and underscore
+ * should start with an alphabet or underscore
+ * It should not be a keyword
+* In Python we use snake case. (there are two cases camel case and snake case).
+
+## Return Statement
+* Functions not only put some code in a block they return something.
+* Example function
+```python
+def area_circle_rad_5(){
+ print(3.14 * 5 * 5)
+}
+area_circle_rad_5()
+```
+**Output**
+```plaintext
+78.5
+```
+* This is how we define a function and call it. Now we want to add `1` to it or `2` to it. Can't we take it to a variable?
+* This is what return statements are for. It is like a black box where you want some task to be done. But after the task is done you want it to return some value as well.
+* So the syntax is something like:`
+```python
+def function_name():
+ return 5;
+
+abc()
+a = abc()
+print(a)
+```
+**Output**
+```plaintext
+5
+```
+* We are storing the return value of the function in a variable `a`. Now we can print this variable as usual.
+* Unlike other programming languages in Python we are not required to define the return type with the function name.
+
+
+**Note:** Functions execution ends at return. Once a return statement is found rest of the lines of the code will not be executed. It is just like a break in loops.
+If you do return without any value, it will return `None`.
+```python
+def abc():
+ return
+abc()
+```
+
+## Parameter and Argument in Function
+* Let us say we want to calculate the area of a circle whose radius `10`. We will again write the code as
+```python
+def area_circle_rad_10():
+ print(3.14 * 10 * 10)
+```
+* So for every different radius we will write a different code? This is not a good practice.
+* In Python we can take as many parameters as required. So we can simply take radius as a parameter from the use.
+```python
+def area_circle(radius):
+ return (3.14 * (radius ** 2))
+
+area = area_circle(5)
+```
+* If you want multiple parameters you can simply separate them by comma, their types are not required.
+* If the function is not returning anything by default it will return `None`.
+* In Python, a parameter is a variable used in a function definition, while an argument is an actual value passed to the function during a call.
+* About the above function `radius` used in the `area_circle()` function is a parameter, the `5` passed to it while calling is an argument.
+* But people use it interchangeably.
+
+## Execution of A Function in Python
+* Python compiler executes line-by-line
+* Everything in Python is an object of a class.
+* Function is also a class.
+* Function is just like a variable, a variable gets declared in a single line, and functions get declared in multiple lines.
+* In Python if you call a function without defining it, it will show an error.
+* We can call a function inside a function.
+* Let us say we have a bank function to do transactions for saving accounts. We have multiple functions to check the balance, to execute the transaction, to print the thank you message, etc.
+
+## Scope of a Variable
+```python
+a = 1
+def arae_circle(radius):
+ area = 3.14 * (radius ** 2)
+ return area
+b = area_circle(10)
+print(a)
+print(area)
+print(b)
+```
+* Whatever we have done so far, we have done it in global scope.
+* Here `a` and `b` are global variables. But `area` is a local variable to the function `area_circle`. It can only be accessed in the function. As the execution of the function is over the Python compiler frees up the memory. Thus `area` is no longer a valid memory location.
+* Though we can do this:
+```python
+a = 1
+def arae_circle(radius):
+ area = 3.14 * (radius ** 2)
+ print(a)
+ return area
+b = area_circle(10)
+print(a)
+print(area) # this will give error
+print(b)
+```
+* This is because `a` is a global variable and can be accessed everywhere.
+
+
+* In Python variables defined in `if` or `for` loops can be used outside their block as well but it is not true for the functions.
+
+```python
+a = 1
+def abd():
+ a = 2
+ print(a)
+abc()
+```
+
+**Output**
+```plaintext
+2
+1
+```
+* So, this will have two variables called `a`. One is global and another is local. When we say `print(a)` it will refer to the local variable and print its value.
+* To update or use global we use the following syntax:
+
+```python
+a = 1
+def abc():
+ global a
+ a = 2
+ print(a)
+abc()
+print(a)
+```
+**Output**
+```plaintext
+2
+2
+```
+Here global variable `a` is reused and its value is changed inside a function.
+
+---
+
+### QUIZ 5
+
+Is this a valid function?
+
+```python
+def print_hello:
+ print('Hello')
+```
+### Choices
+- [ ] Yes
+- [x] No
+
+There are no paranthesis in the function and they are must in a function.
+
+```python
+def print_hello():
+ print('Hello')
+```
+---
+
+### QUIZ 6
+
+Is this a valid function?
+
+```python
+def print_name():
+ print('Hello', name)
+```
+
+
+# Choices
+- [ ] Yes
+- [x] No
+
+The variable name must be taken as an argument.
+
+---
+
+### QUIZ 7
+
+How many times the function foo is called?
+
+```python
+
+foo()
+foo()
+for i in range(1,5):
+ foo()
+```
+
+### Choices
+- [ ] 5
+- [x] 6
+- [ ] 7
+- [ ] 8
+---
+
+### QUIZ 8
+
+What will be the output of the following?
+
+```python
+def area_rectangle(l, b):
+ area = l * b
+area_circle(3, 4)
+print(area)
+```
+# Choices
+- [ ] 12
+- [ ] 7
+- [x] Error
+---
+
+There is no function named `area_cirle()`.
+
+### QUIZ 9
+
+What will be the output of the following?
+
+```python
+def foo(a):
+ if(a):
+ return 100
+ a += 20
+ return a
+print(foo(0))
+```
+### Choices
+- [ ] 100
+- [ ] 120
+- [x] 20
+- [ ] Nothing
+
+A function's execution ends once a `return` is encountered.
+
+If you do return without any value, it will return None. Though this is not helpful it is still valid.
+```python
+def abc():
+ return
+abc()
+```
+
+### QUIZ 10
+
+What will be the output of the following?
+
+```python
+def solve(a, b):
+ print('Function Started')
+ return 0
+ c = a + b
+ print('Function Ends')
+c = solve(2, 3)
+print(c)
+```
+### Choices
+- [ ] Function Started
+Function Ends
+5
+- [ ] Function Started
+Function Ends
+0
+
+- [ ] Function Started
+0
+Function Ends
+
+- [x] Function Started
+0
+
+
+We are doing early exit via return `0` and therefore the result is `0`.
+
+
+## Positional and Keyword Arguments
+Let us consider an example:
+```python
+def bio_data(name, age, gender):
+ print("Name: ", name)
+ print("Age: ", age)
+ print("Gender: ", gender)
+
+bio_data(23, "Male", "Aakar")
+```
+
+#### **`What is the problem with the above code?`**
+
+* **The order of the parameter matters**, age will printed with the name, and so on
+* Python has two types of arguments **Positonal** and **Keyword**.
+* The above function call is referred to as positional arguments. Where order or position of arguments matters.
+* The other one is keyword argument. Here we have to give the name of the argument but the order doesn't matter.
+* The name must be the same as given in the function definition.
+```python
+bio_data(name = "Aakar", gender = "Male", age = 23)
+```
+**Note:** A single call can have both together, but the Keyword argument must always be after positional arguments. Python will show an error if not done this way.
+**Cannot Do**
+```python
+bio_data("Aakar", age = 23, "Male")
+```
+**Must Do**
+```python
+bio_data("Aakar", age = 23,gender = "Male")
+```
+
+## Default and Non-default Arguments
+* Let us say we are writing the above function. For example, people don't like having to declare their gender. So, we can give the default argument gender with the value `Unspecified` in Python.
+```python
+def bio_data(name, age, gender = "Unspecified"):
+ print("Name: ", name)
+ print("Age: ", age)
+ print("Gender: ", gender)
+
+bio_data(23, "Aakar")
+```
+Now, it is both ways okay even if we define or do not define the argument.
+
+**Note:** A single call can have both together, but the Non-default argument must always be after default arguments. Python will show an error if not done this way.
+**Cannot Do**
+```python
+bio_data(name, age = 23, gender)
+```
+**Must Do**
+```python
+def bio_data(name, age, gender = "Unspecified"):
+```
+
+### QUIZ 11
+
+What is the correct way of calling this function?
+
+```python
+def print_biodata(name, age, gender):
+ print('Name', name)
+ print('Age', age)
+ print('Gender', gender)
+```
+
+### Choices
+- [ ] print_biodata('Hari', 'Male', 23)
+
+- [ ] print_biodata('Male', 'Hari', 23)
+
+- [x] print_biodata('Hari', 23, 'Male')
+
+- [ ] print_biodata(23, 'Hari', 'Male')
+---
+
+
+### QUIZ 12
+
+What will be the output of the following?
+
+```python
+def print_biodata(name, age = 23, gender = 'Female'):
+ print('Name', name)
+ print('Age', age)
+ print('Gender', gender)
+```
+### Choices
+- [x] Yes
+- [ ] No
+
+First there should be non-default arguments and then the default arguments.
+
+### QUIZ 13
+
+Which of the function call is correct?
+
+```python
+def print_biodata(name, age = 23, gender = 'Female'):
+ print('Name', name)
+ print('Age', age)
+ print('Gender', gender)
+```
+
+### Choices
+- [ ] `print_biodata(name='Hari', 23)`
+- [ ] `print_biodata('Hari', 23, age=23)`
+- [x] `print_biodata('Hari')`
+- [ ] `print_biodata()`
+
+* 1st option - `name` is a keyword argument and the age is a positional argument. Positional argument cannot come after keyword argument.
+* 2nd option - In the above quiz we are defining age twice which is not valid.
+* 3rd argument - Rest two arguments are default
+* 4th option - We still need name argument as name is not a default argument.
+
+* `*args` - opens a list/tuple (arguments)
+* **`kwags` - opens a dictionary (keyword arguments)
+* `*` is used for opening a single-dimension iterator.
+* Let us say there exists a function that takes an unspecified number of parameters.
+* This can be achieved with the help of `*args` and `**kwargs`. This lets us pass as many arguments as we want.
+
+```python
+def abc(a, b, *args, **kwargs):
+ print(a, b)
+ print(args)
+ print(kwargs)
+
+abc(2,3,4,5, name = "Hari",age = 23)
+```
+**Output**
+```plaintext
+2 3
+[4, 5]
+{"name" : "Hari"
+"age" : 23}
+```
+* You cannot have more than one `*args` or `**kwargs` as their behavior of it is unspecified.
+
+
+## Docstrings
+* Python provides a special functionality **doc strings** just like comments but slightly different than multiline comments.
+* If someone wants to know how the function works they have to come to the code and see. So many Python code readers and default Python provides a documentation of functions and classes and these are called **Doc String**
+* You have to write a multiline comment right after the function definition.
+```python
+def bio_data(name, age, gender = "Unspecified"):
+ """
+ This function prints the bio-data using the information provided
+
+
+ Args:
+ name
+ age
+ gender
+
+ Returns:
+ None
+ """
+ print("Name: ", name)
+ print("Age: ", age)
+ print("Gender: ", gender)
+```
+To read the doc string, just add a `?` after it.
+```python
+bio_data?
+```
+
+## Question - Simple Interest
+* Take two parameters, how much they want to invest, and how long they want to invest it for.
+* Simple interest - 7%.
+* Write a function `print_interest`
+
+
+```python
+si = 7
+principal = int(input("How much you want to invest>"))
+time = float(input("How long you want to invest it for (in years)?"))
+
+def print_interest(principle, time):
+ result = principle * time * si / 100
+ return result
+
+result = print_interest(principle, time)
+print(result)
+```
+
+### Question - Simple Interest
+* **Round function** in Python rounds the number. It takes the number and the precision we want. It rounds off this number and returns it.
+```python
+round(3.1415,2) -> 3.14
+```
+* Given a radius `R`, print the area of a circle but up to two decimal places only.
+
+
+```python
+PI = 3.14159
+def print_area(radius):
+ area = PI * (radius ** 2)
+
+result = print_area(5)
+print(result)
+```