[Review: Quiz 1] [Ali Nahm, Oreoluwa Barbarinsa, Lucas Freitas, Rob Bowden] [Harvard University] [This is CS50.] [CS50.TV] [Lucas Freitas] Welcome everyone. This is review for quiz 1. Just as a disclaimer, this is--I mean, we're going to try to cover as much material as possible, but that doesn't mean that we're going to cover all of the things that can be in quiz 1. So be sure you also take a look at lecture, sections, everything that you can. Quiz 1 is going to be on Wednesday, next Wednesday. So be sure to study. It's going to be, pretty much, like the first quiz regarding its format, but it's probably going to be much harder. At least, last year when I took 50, I thought it was much harder. So study a lot. I'm going to cover data structures and Huffman coding. This is something that a lot of people think is complex, but I'm going to try to make it as easy as possible. First of all, what we want you guys to know for quiz 1 is to understand the conceptual descriptions of each of the data structures that I'm going to present. That means that you don't have to actually implement a hash table in your quiz 1. We don't want you to implement a whole hash table; maybe we'll try to make you implement some functions, the most common operations, but we're not going to make you implement everything. So it's important that you understand the concept behind each data structure and also that you are able to code in C, just the most common operations they have for each data structure. And also be able to review pointers and structs, because they appear a lot in these data structures. First, linked lists. Linked lists are actually very similar to arrays, but the difference between a linked list and an array, first of all, is that a linked list has a very flexible size, while in arrays you have to either choose a very large size for the array, so you know that you're going to be able to store all your data in that array, or you have to use malloc to have a flexible length of array. In linked lists it's very easy to just get more elements, put more elements in the linked list or remove elements. And actually, if you don't want the linked list to be sorted, you can search and remove elements in constant time, so O (1) time, so it's very convenient. You just have to be careful to always remember to malloc and free the nodes, just because if you don't, you'll have memory leaks. So linked lists--the definition of a node is just like what we have right there. I put int n, but you can store any data you want. So if you want to store a string, it's fine. If you want to store a struct, it's fine, a double, whatever you want. I just put int n for the examples here. And you have a pointer to the next node. So, basically, a linked list has some data, and then it points to the next node. If it's the last element in the linked list, it's going to point to NULL. So this is an example of a linked list. Okay, so now let's see what we should do if I want to insert an element in a linked list. First, a function insert will be of type void because I don't want to return anything. And I'm going to take an int as an argument, because I want to know what I want to insert. So what's the first thing I should do? Well, I should malloc on newnode, so that is the first line. I'm just creating a new node to put in a linked list. So what can I do? Well, we know that in our implementations of linked lists in class, we always put the head as a global variable. So what we can do is change the head. I can make this new node be the new head, and it's going to point to the previous head. How can we do that? The first thing I have to do is change the 'n' in the new node to value, which was passed to the function. Then newnode's next is going to be the head. The head is going to be newnode. So it's pretty simple. For deleting a node, we can do it like-- One way we could do that is to say, okay, if I wanted to delete, for example, 3, what I could do is just point the previous node to the next node of 3. So I would just do something like that. But what is the problem with doing that? I have a memory leak, so I don't have access to the number 3 anymore. The problem with that is that I'm not going to be able to free that node. I'm going to have memory leak and (unintelligible) is going to hate me. So instead of doing that, I should probably have a temporary pointer. So I put temp. It is going to point to the node that I want to delete. And then I can move the previous nodes to point to the next node of the node that I want to delete. And finally, I can free the pointer. Do I have to free the pointer that I created right there? I don't have to, just because-- the difference is that this node was created using malloc, so it's in the heap, while this one was just declared as a NULL switch in the stack. So I don't have to free it. Okay. So now let's talk about stacks. Stacks are pretty straightforward. We did stacks and queues in class just using arrays, but you should be familiar--just be aware that you can also do stacks in queues using linked lists as well. So if you have an array, what would be a stack? A stack, first, will have to have a size. You have to store what is the size of the stack that you have right now. And also you would have an array, in this case of numbers, but if you want, it can be an array of strings, an array of struct, anything that you want to store. About the stack: The difference between a stack and a linked list is that in the stack you only have access to the last element that was put in the stack. It's called last in, first out. Just like you have a stack of trays, if you put a tray on the top of the stack, you have to remove that tray first to have access to the other trays. It's the same thing with stacks. So if I want to, for example, add an element to a stack, what should I do? It's called push, and it's pretty straightforward. The first thing you have to do is check if the size of the stack is not greater or equal to the capacity of the stack. Because if you already are on full capacity, you cannot add anything else. And then if not, you just have to add the element to the stack. And finally, increment the size. So it's pretty straightforward. So I just add the number 2. And if I want to pop, which means that I want to remove the last element that was added and return the value of the element, the first thing I have to check is that the stack is not empty. Because if it's empty, I cannot return anything. In that case, I'm returning -1. Otherwise, I'm going to decrement the size of the spec, and return numbers (s.size). Why did I decrement the size and then return s.size? It's because, in this case, the spec has size 4, and I want to return the fourth element, right? But what is the index of the fourth element? Three. Since I do size - - is going to be 3, I can just return s.numbers(s.size) because it's 3. So it's just the index. Now queues. Queues are pretty much the same thing. The only difference is that instead of having last in, first out, you have first in, first out. Probably if you're waiting to go to a concert, you wouldn't be happy if you had a stack instead of a queue. Being the last person to come would be the first person to enter the concert. You probably wouldn't be happy. In the queue, the first person to get in is also the first person to get out. So in the definition of a queue, besides having the size in the array, you also have to have the head, which is the index to the head of the stack. So the first element right now. Enqueue is the same thing as push for stacks. If you were very naive, you would just say, well, I can just do exactly the same thing as I did for push. I can just check if it's not beyond the capacity. If it is, I return false, otherwise I can just export the new value and then increment the size. But why is this wrong? Let's see this example. I'm trying to enqueue a bunch of stuff, and then I'm going to dequeue and enqueue. There's a lot of commands, but it's very simple. I'm going to enqueue 5, so add 5, and then 7, 1, 4, 6, and then I want to dequeue something, which means that I'm going to remove the first element. So I'm going to remove the number 3, right? The first element. Okay. Now if I try to enqueue something else, what is going to happen? According to my implementation, I was going to put the next number in the index q.size. In this case, the size is 8, so the index 8 will be right here in the last position. If I try to enqueue 1 right here, I would be overwriting the last position to the number 1, which is completely wrong. What I want to do is wrap around and go to the first position. Maybe you would just say, well, I just have to check if I can actually put something there. If not, I just say, oh, the new full capacity is actually capacity - 1, and you cannot put an element there. But what is the problem? The problem is that if I just dequeue everything right here and then I try to add something else, it would just say, well, you were at full capacity, which is 0. So your queue is gone. You have to wrap around, and a way of wrapping around that you guys learned in visionary and other psets was using mod. You can try it at home to understand why you would do q.size + q.head mod capacity, but if you check right here, we can see that it works. So in the last example, q.size was 8 and the head was 1, because it was this position here of the array. So it will be 8 + 1, 9. Mod capacity 9 would be 0. It would go to the index 0. We'll be in the right position. And then try the queue at home. Some important things: try to understand the difference between a stack and a queue. At home, try to get very familiar with implementing enqueue, dequeue, push and pop. And also understand when you would use each of them. So let's relax for 10 seconds with a bunch of Pokemons. And now let's go back to data structures. Hash tables. A lot of people were scared of hash tables. in problem set 6, Spell Checker. Hash tables and tries, a lot of people get scared of them. They think they're so hard to understand. Yeah? [Rob Bowden] Problem set 5. >>Problem set 5, yeah. Thanks Rob. Yeah. Six was Huff n' Puff, yeah. Problem set 5 was Spell Checker, and you had to use either a hash table or a try. A lot of people thought that they were super hard to understand, but they're actually pretty simple. What is a hash table, basically? A hash table is an array of linked lists. The only difference between an array and a hash table is that in the hash table you have something called a hash function. What is a hash function? I don't know if you guys can read here. This is an example of a hash table. So you can see that you have an array with 31 elements. And what we do in a hash table is have a hash function that is going to translate a key, each int to an index. If, for example, if I want to choose for B. Harrison, I would put B. Harrison in my hash functions, and the hash function would return 24. So I know that I want to store B. Harrison in 24. So that's the difference between just having an array and having a hash table. In the hash table you'll have a function that is going to tell you where to store the data that you want to store. For the hash function, you want to look for a hash function that is deterministic and well-distributed. As you can see here, you see that a lot of the data that I wanted to store was actually 19 instead of using 31 and 30 and 29, which were all free. So the hash function that I used was not very well-distributed. When we say well-distributed, it means that we want to have, roughly, at least 1 or 2 for each of the-- like, a difference of 1 or 2 for each of the indices in the arrays. You want to have, roughly, the same number of elements in each linked list in the array. And it's easy to check if it's valid in the hash table, view as hash tables. Then trees. This is a tree. Trees in computer science are upside down for some reason. So right here you have the root of the tree and then the leaves. You should just know the nomenclature for parents and child. Each node has its children, which are the nodes that are below the parent. So, for example, 2 is going to be the parent for 3 and for the other child right there, while 3 is going to be the parent for 1 and the other children that are there. And 1 is going to be 3's child, and so on. We have something much more interesting, called a binary search tree, in which all the values on the right of a node are going to be on the right, right here--on the right, are going to be greater than the element in the root. So if I have the number 5 right here, all the elements on the right are going to be greater than 5, and on the left all the elements are going to be less than 5. Why is this useful? Well, if I want to check if the number 7 is here, for example, I just go to 5 first and I'm going to see, is 7 greater or less than 5? It's greater, so I know it's going to have to be on the right of the tree. So I have much less stuff to look at. In implementation of a binary search tree, the node, I'm just going to have to have data, so int n; you could also have a string or anything you wanted. You just have to be careful on defining what is greater, what is less. So if you had strings, for example, you could define that all those things on the right are going to have larger length, the left are going to have lower lengths, so it's really up to you. How can I implement find for BST? The first thing we'll have to do is check if the root is NULL. If it's NULL, it means that the thing is not there because you don't even have a tree, right? So I return false. Otherwise, I'm going to check if the number is greater than the value in the root. I'm going to try to find the element on the right of the tree. You see that I'm using recursion here. And then if it's less, I'm going to look at the left. And finally, otherwise, if it's not less or not greater, it means that it's the value itself. So I just return true. You can see here that I used if, if, if. And remember, in quiz 0, we had a problem that had if, if, if, and you were supposed to find the inefficiency, and the inefficiency was that you used if. You should have used if, else if, else if, and else. So, should I use else if and else if and else here? Does anyone--yeah? [Student speaking, inaudible] That's perfect. So she's saying that it doesn't matter, just because the inefficiency that we had before was that because, maybe if some condition was satisfied, so you have performed an action, but then you were going to check all of the other conditions. But in this case, it returned right away, so it doesn't matter. So you don't have to use else if. And finally, let's talk about tries, which is everyone's favorite. A try is a tree of arrays. It's very fast to look up values, but it uses a lot of memory. And it's usually to filter words, so when you want to implement, for example, I don't know, like a phone book in your phone and you want to be able to type B and just have names of people who have B. It's very easy to implement that using a try, for example. How do you define a node in a try? You just have to have a bool that is going to be is_word. That represents that using all the characters before that node, you were able to form a word, and then you'll have an array of pointers to nodes. Can you see that we have an array of parent nodes, so node* array? Yeah? So let's see how that will work. For the spell check, we have an array of 27 elements, because we have all the letters plus the apostrophe. Before here I'm just going to use 2 because I want to be able to write on the board. Okay. So this is an example of a try. If I just define the first node, I'll have an array of 2 elements that are 2 pointers to NULL, so I just put 'a' and 'b'. And I'm going to have a bool that says is_word. It's going to be false for the first one, just because, before that you don't have any characters. So an empty word is not a word. So it's false. If I want to add 'a' to this dictionary, what would I have to do? I would just have to malloc a new node for 'a', and then add its word to true. So it just represents that having 'a' is going to be true. Make sense? Then if I want to add 'ba', I'll have to malloc 1 for 'b', and then I'm going to set up the boolean to false, because 'b' by itself is not a word. Then I'm going to malloc another one for 'a', so 'ba', and then I'm going to set up it's a word to true. Because 'ba' is a word. And then if I want to see if 'b' is in this dictionary, I can just go to the first one, 'b'. I go down, and I look at is word, and it says false. So it's not a word. If I want to check 'ba', I go to the first one, 'b', and then go to 'a', and I see true, so it is a word. Make sense? A lot of people get confused by tries. No? Finally, Huffman coding. Huffman coding is very useful to save memory and compress text files, just because a lot of times you use 'a' and 'e', for example, in your documents, but I don't know if you guys use 'q' or 'z' as much. Having just 1 byte for every single character, every single--the 256 characters that we have in the ASCII table is not very optimal, just because there are some characters that you use much more, so you should probably use less memory for those. How do I use Huffman coding? We have to do a Huffman tree. A Huffman tree has nodes that have a symbol that is going to be like, 'a', 'b', 'c', the letter, whatever letter you have, a frequency that is the frequency that the word appears in the text, that you were creating the Huffman tree for, and then a node that is going to point to the left of the Huffman tree and another node that is going to point to the right. So just like a tree. How do you build a Huffman tree? You're going to pick the 2 nodes that have the lowest frequencies. If you have a tie you're going to pick the 2 nodes that have the lowest ASCII values as well. Then you're going to create a new tree out of those 2 nodes that is going to have the combined frequency in the parent node. And then you're going to remove the 2 children from the forest and replace them with the parent. And you're going to repeat that until you only have 1 tree in the forest. So let's see how you would do a Huffman tree for ZAMYLA. You can see here that all the letters have frequency 1 except for 'A'; that has frequency 2. So I created nodes for all the letters I put in order of ASCII value and frequency. So if I want to create the first tree, it will be with 'L' and 'M'. So it's here. The frequency of the pair will be 2 because it's 1 + 1, then the next 2 with the lowest frequencies are 'Y' and 'Z'. And then I have all of them being--have a frequency of 2. So which ones are the ones that have the lowest ASCII value for the next one? 'A' and 'L'. So I create the new node, and finally, it's 4 and 2, so 2 is going to be on the left. And this is the Huffman tree. Then if I want to write some text, like in binary to convert to text, using the Huffman tree is very easy. For example, if I say that moving to the left is a 0 and moving to the right is a 1, What is that going to represent? So like 1, 1, so right, right, and then 0, so left would be L, and then 1, 0, 0. So 1, 0, so just 1, 0, 'A'. And then 0, 1, so 'Z'. And then 1, 0, 0--no. 0, 0 will be 'Y', so Lazy. So that's all for me, Rob's going to take over. [Rob Bowden] So, week 7 stuff. We've got a lot to go over really fast. Bitwise operators, buffer overflow, CS50 library, then HTML, HTTP, CSS. All in like 15 to 20 minutes. Bitwise operators. There are 6 of them that you need to know. Bitwise and, bitwise or, XOR, left shift, right shift, and not. Right shift and not you barely saw in lecture at all. We'll go over it quickly here, but it's good to know that these are the 6 that exist. Remember that bitwise operators are like when you do 3 + 4. You aren't dealing with the binary of 3 and 4. With bitwise operators you are actually dealing with the individual bits of the numbers 3 and 4. So the first one that we'll say is bitwise not, and all it does is flip all the bits. So here, if you're writing this in C, you wouldn't write it as ~11011 or whatever, you would write it like ~4, and then it would flip the binary representation of 4. So here, ~ of some binary number 1101101 is going to exactly flip all 1's to 0's and all 0's to 1's. As I say there, the frequent use of this, and we'll see it in a bit, is like we want to come up with some number where all of the bits are 1, except for one of them. So it's usually easier to express the number where just that single bit is set, and then take the ~ of it, so every other bit is set except for that one. So that's what we're going to use more in a bit. Bitwise or. Here are 2 binary numbers, and these 2 numbers are pretty representative, since they represent every possible combination of bits you could need to operate on. Here, when I or'd each bit, we're just going to compare straight down. So on the left side we have a 1 and a 1. When I bitwise | those, what am I going to get? One. Then bitwise | 0 and 1 is going to give me? One. Bitwise 1 and 0 is going to be the same thing, one. Bitwise 0 | 0 is going to give me 0. So the only case where I get 0 is in the 0 | 0 case. And you can think of that just like your logical ors. So if you think of 1 as true and 0 as false, the same thing applies here. So true or true is true; true or false is true. False or true is true; false or false is the only thing that's actually false. Here's the example that you should know as a pretty good example of when bitwise operators are used. Here if we or capital 'A' with Ox20, and we'll look at these in a second, we get something. And if we or lowercase 'a' with Ox20, we get something. So let's pull up ASCII table. Okay. Here we see that 'A' is-- here we have 'A' is decimal 65. But I'll go with hexadecimal, which is Ox41. Pretty sure we saw it in class. I think we saw it in class that it's pretty easy to convert from hexadecimal to binary. So here, if I want to put 4 into binary, that's just going to be 0100. This is 1's place, 2's place, 4's place, so this is 4. Then I can split 1 into binary, which is going to be 0001. And so this is going to be the representation of 'A' in binary. Taking lowercase 'a', it's now going to be Ox61, where, splitting these up into its binary, so a 6-- Let's actually do it--is there no eraser? Eraser. Ox61. So splitting 6 into binary is going to be 0 + 4 + 2 + 0. And splitting 1 is going to be 0001. Looking at the difference between these 2, we see that the only difference between a lowercase and a capital 'A' is this single bit. So coming back to here--okay. Coming back to here, if we look at what the bit Ox20 is, so splitting Ox20 into its binary, is 0010, 0000. Ox20, the only bit that is set is this bit that we are concerned with, with switching between capital and lowercase 'a'. If I or 'A', which is this one, 'A', if I or 'A' with Ox20, what am I going to get? [Student, inaudible] >>Lowercase 'a', because it's going to flip this bit to a 1. And if I or 'a' with Ox20, what am I going to get? Lowercase a, because just oring 'a' with Ox20, I'm just going to be oring this single bit to a 1; it's already a 1, so it doesn't matter. So we get 'a' and 'a'. Bitwise and. Again, we can think of this as our logical and counterpart. On the left side we have true & true. It's going to be true, and for all of the cases, false & true or true & false, or false & false, none of those things are true. So what we end up getting is 1000. So now, here, here's where I've used the trusty bitwise not, where we had Ox20. So this is Ox20. Now what I want to do, bitwise ~ of Ox20. That is going to flip all the bits. So I have 1101, 1111. And so 'A' anded with ~Ox20 is going to give me what? The only bit we really need to think about is this one, since, if all of these bits are set to 1, then we're going to get exactly what 'A' was, except for, possibly, what this bit is. Because if it was a 1, now it's going to be set to a 0, because whatever this is, anded with this is going to be 0. So what is 'A'&~Ox20 going to give me? [Students answer, inaudible] >>And what is 'a' and--it's 'A'. And what is 'a'&~Ox20 going to give me? 'A.' Because this is currently a 1. Anding with this 0 is going to make it a 0, and now we're going to get a 'A'. Both are 'A,' and last but not least of this type, we have XOR. It's very much like or, except it means exclusively or. This is like what you usually think of as or in the real world. So you do either 'x' or 'y', but not both. Here 1^1 is going to be 0. Because true, this is--it doesn't work as well with the logical true and false as bitwise & and or do, but true ^ true is false. Because we only want to return true if only one of them is true. So 1^1 is 0. What about 0^1? Is 1. 1^0 is 1, 0^0 is 0. So under all circumstances, 0 bitwise something 0 is going to be 0. 1 bitwise something 0 or 0 bitwise 1, if it's | or ^, it'll be a 1, and if it's & it'll be 0. And the only case where 1 bitwise 1 is not 1 is with exclusive or. That's 0110. So here now, using XOR--so we're back at 20. 'A'^Ox20 is these 2 bits we're comparing. So a 1 ^ 0 is going to give me a what? A one. 'A'^Ox20 is going to give me? Lowercase a. 'a'^Ox20 is going to give me? Capital A. Because whatever this is doing, this XORing with Ox20 is effectively flipping whatever this bit is. If this is a 0, it's now going to become a 1. Since this is a 1, 1 ^ 1 is 0. So our 'a' has become 'A', and our 'A' has become 'a'. So XOR is a really convenient way of just flipping the case. You just want to iterate over a string of letters and alternate the case of every single character, you just XOR everything with Ox20. Now we have left shift. Left shift is just going to, basically, push all of the numbers into, or to the left, and insert 0's behind them. So here we have 00001101. We're going to push 3 0's in from the right, and we get 01101000. In nonbinary terms, we see that that's really dealing 13 left-shifted with 3, which gives us 104. So left shifting, we see here, x<>3, which is, effectively, x / 2^y. So now, here, it's a similar idea. Why is it just roughly x / 2^y, and not actually x / 2^y? Because if I had shifted by 4, I would have lost a 1. Basically, what you think of, just think of integer division in general. So, like 5 / 2 is 2. It's not 2.5. It's the same idea here. When we divide by 2, we can lose odd bits along the way. So now--that's it for bitwise. That's all you need to know. Remember the use cases we saw in class, like a bit mask is useful for bitwise operators, or you use them for bit masks. Capital letters and lowercase letters, conversions is a pretty prototypical example. Okay, so buffer overflow attacks. Anyone remember what was wrong with this function? Notice we declared an array of 12 bytes, 12 chars, and then we copy into our buffer of 12 chars the entire string bar. So what's the problem here? The magic number 12 should pretty much immediately pop out as--why 12? What if bar happens to be more than 12 characters? What if bar is millions of characters? Here the issue is memcpy. If bar is long enough, it will just completely--'c', 'c' doesn't care that it was only 12 characters; 'c' doesn't care that it can't fit that many bytes. It will just completely overwrite char, the 12 bytes we've allocated for it, and everything past it in memory that doesn't actually belong to that buffer with whatever the string bar is. So this was the picture we saw in class where we have our stack growing up. You should be used to these pictures or get familiar with them again. We have our stack growing up, memory addresses start at 0 at the top and grow down to like 4 billion at the bottom. We have our array 'c' somewhere in memory, then we have our pointer to bar right underneath it, and then we have this saved frame pointer in our return address and our parent routine's stack. Remember what the return address is? It's when main calls a function foo, calls a function bar, inevitably, bar returns. So when bar returns, they need to know that it's going back to foo that called it. So the return address is the address of the function that it has to return to when the function returns. The reason that's important for buffer overflow attacks is because, conveniently, hackers like to change that return address. Instead of going back to foo, I'm going to go back to wherever the hacker wants me to go back to. And, conveniently, where the hacker frequently wants to go back to is the start of the buffer that we originally had. So notice, again, Little Indian. The appliance is an example of a Little Indian system, so an integer or a pointer is stored with the bytes reversed. So here we see--is this? Yeah. We see Ox80, OxC0, Ox35, OxO8. Remember the hexadecimal digits? We don't reverse the hexadecimal digits in Little Indian, because 2 hexadecimal digits make up a single byte, and we reverse the bytes. That's why we don't store, like, 80530CO8. We store, instead, each pair of 2 digits, starting from the right. That address refers to the address of the start of our buffer that we actually wanted to copy into in the first place. The reason that's useful is because, what if the attacker happened to, instead of having a string that was just a harmless string of like, their name or something, what if, instead, that string were just some arbitrary code that did whatever they wanted it to do? So they could--I can't think of any cool code. It could be anything, though. Any disastrous code. If they wanted, they could just do something at seg faults, but that would be pointless. They usually do it to hack your system. Okay. CS50 library. This is, basically, getInt, getString, all those functions we provided for you. So we have char* string, and that's the abstraction that we blew away at some point during the semester. Remember that a string is just an array of characters. So here we see an abridged version of getString. You should look back at it to remember how it's actually implemented. Key details are, notice we get in a single character at a time from standard in, which is just like us typing at the keyboard. So a single character at a time, and if we get too many characters, so if n + 1 is greater than capacity, then we need to increase the capacity of our buffer. So here we're doubling the size of our buffer. And that keeps going; we insert the character into our buffer until we receive a new line or end of file or whatever, in which case, we're done with the string and then the real getString shrinks the memory, like if we allocated too much memory it'll go back and shrink a bit. So we don't show that, but the main idea is it has to read in a single character at a time. It can't just read in a whole thing at once, because their buffer is only of a certain size. So if the string that it tries to insert into buffer is too big, then it would overflow. So here we prevent that by only reading in a single character at a time and growing whenever we need to. So getInt and the other CS50 library functions tend to use getString in their implementations. So I highlighted the important things here. It calls getString to get a string. If getString failed to return memory, remember that getString mallocs something, so whenever you call getString you shouldn't (unintelligible) free that string that you got. So here, if it failed to malloc something, we return INT_MAX as just a flag that, hey, we weren't actually able to get an integer. You should ignore whatever I return to you, or you should not treat this as a valid input. Finally, assuming that did succeed, we use sscanf with that special flag, which means, first match an integer, then match any characters after that integer. So notice we want it to equal 1. So sscanf returns how many matches if successfully made? It will return 1 if it successfully matched an integer, it will return 0 if it did not match an integer, and it will return 2 if it matched an integer followed by some character. So notice we retry if we match anything but 1. So if we entered 1, 2, 3, C, or 1, 2, 3, X, then 1, 2, 3 would get stored in the integer, X would get stored at the character, sscanf would return 2, and we would retry, because we only want an integer. Quickly blowing through HTML, HTTP, CSS. HyperText Markup Language is the structure and semantics of the web. Here is the example from lecture where we have HTML tags. We have head tags, body tags, we have examples of empty tags where we actually don't have a start and close tag, we just have link and image. There is no closing image tag; there's just a single tag that accomplishes everything the tag needs to do. The link is an example; we'll see how you link to CSS, the script is an example of how you link to an external JavaScript. It's pretty straightforward, and remember, HTML is not a programming language. Here, remember how you would define a form or at least what this would do? Such a form has an action and a method. The methods you will only ever see are GET and POST. So GET is the version where the thing gets put in the URL. POST is where it is not put in the URL. Instead, any data from the form is inserted more hidden in the HTTP request. So here, action defines where the HTTP request goes. Where it's going is google.com/search. Method. Remember the differences between GET and POST, and, just say as an example, if you want to bookmark something. You will never be able to bookmark a POST URL because the data is not included in the URL. HTTP, now, is HyperText Transfer Protocol. The HyperText Transfer Protocol, you would expect it to transfer HyperText Markup Language, and it does. But it also transfers any images you find on the Web, any downloads you make start as an HTTP request. So HTTP is just the language of the World Wide Web. And here you need to recognize this kind of an HTTP request. Here HTTP/1.1 on the side just says that's the version of the protocol I'm using. It's pretty much always going to be HTTP/1.1, as you'll see it. Then we see that this was GET, the alternative being POST, that you might see. And the URL that I was trying to visit was www.google.com/search?q= blah, blah, blah. So remember that this, the question mark q= blah blah blah, is the sort of stuff that is submitted by a form. The response it might return to me would look something like this. Again, starting with the protocol, which is going to be that, followed by the status code. Here it's 200 OK. And finally, the web page that I actually asked for will be followed. The possible status code you might see, and you should know several of them. 200 OK you have probably seen before. 403 Forbidden, 404 Not Found, 500 Internal Server Error is usually if you go to a website and something's broken or their PHP code crashes, whereas in the appliance we have that big orange box that comes up and says, like, something is wrong, this code doesn't work or this function's bad. Usually websites don't want you knowing what functions are actually bad, so instead they'll just give you 500 Internal Server Errors. TCP/IP is 1 layer under HTTP. Remember that there is Internet outside of the World Wide Web. Like if you play an online game that doesn't go through HTTP, it's going through a different--it's still using the Internet, but it doesn't use HTTP. HTTP is just one example of protocol built on TCP/IP. IP literally means Internet Protocol. Every computer has an IP address; they are those 4-digit things like 192.168.2.1, or whatever; that tends to be a local one. But that is the pattern of an IP address. So the DNS, Domain Name Service, that's what translates things like google.com to an actual IP address. So if you type that IP address into a URL, that would bring you to Google, but you tend not to remember those things. You tend to remember google.com instead. The last thing we have is ports, where this is the TCP part of IP. TCP does more. Think about, like, you have your web browser running. Maybe you have some email application running; maybe you have some other program that uses the Internet running. They all need access to the Internet, but your computer only has 1 WiFi card or whatever. So ports are the way that we're able to split up how these applications are able to use the Internet. Each application gets 1 specific port that it can listen on, and by default, HTTP uses port 80. Some email services use 25. The low-numbered ones tend to be reserved. You are usually able to get higher-numbered ones for yourself. CSS, Cascading Style Sheets. We style web pages with CSS, not with HTML. There are 3 places you can put your CSS. It can be inline, between style tags, or in a completely separate file and then linked in. And here is just an example of CSS. You should recognize this pattern, where the first example is we're matching the body tag, and here we're centering the body tag. The second example, we are matching the thing with ID footer, and we're applying some styles to that. Notice that ID footer text-aligns to the left, whereas body text-aligns center. Footer is inside the body. It will, instead, text-align left, even though body says text-align center. This is the whole cascading part of it. You can have--you can specify styles for the body, and then things in the body you can specify more specific styles, and things work as you expect. More specific CSS specifiers take precedence. I think that's it. [Ali Nahm] Hi everyone. If I could just get your attention. I'm Ali and I'm going to go through PHP and SQL really fast. So we can begin. PHP is short for PHP: Hypertext Preprocessor. And as you all should know, it's a server-side scripting language, and we use it for the back end of websites, and how it does a lot of the computations, the behind-scenes part. Syntax. It's not like C, surprise, surprise. It always has to start with the, if you can see, the--I can't move ahead. You can see you need the new kinds of braces and then you also need the ?php. That's always how you have to frame your PHP text, your PHP code. So it can't just be like C, where you kind of put it on first. You need to always surround it. And now, the major syntax is that all variables need to start with the $ character. You need to do it when you're defining them; you need to do it when you're referring to to them later on. You always need that $. It's your new best friend, pretty much. You do not--unlike C, you do not need to put what kind of variable type it is. So while you do need the $, you do not need to put, like, int x or string y, etcetera, etcetera. So a slight difference. As a result of this, it means that PHP is a weakly type. PHP is a weakly type language, and it has weakly typed variables. In other words, that means that you can switch between different kinds of variable types. You can store your number 1 as an int, you can store it as a string, and you can store it as a float, and it will all be that number 1. Even though you're storing it in different forms, it's still--the variable types are still holding in the end. So if you look here, if you remember from pset 7, many of you probably had issues with this. Two equal signs, 3 equal signs, 4 equal signs. Okay, there are no 4 equal signs, but there are 2 and 3. You use 2 equal signs to check the values. It can check across types. So if you can see at the first example, I have num_int == num_string. So your int and your string are both, technically, 1, but they're different types. But for the double equals, it'll still pass. However, for the triple equals, it checks value as well as the different types. That means that it's not going to pass in that second case here, where you're using 3 equal signs instead. So that's a major difference that you should all have shown now. String concatenation is another powerful thing you can use in PHP. It's basically just this handy dot notation, and that's how you can bind strings together. So if you have Cat and you have Dog, and you want to put the 2 strings together, you can use the period, and that's kind of how it works. You can also just place them next to each other, as you can see here in the bottom example, where I have echo string 1, space string 2. PHP will know to replace them as such. Arrays. Now, in PHP, there are 2 different kinds of arrays. You can have regular arrays, and you can also have associative arrays, and we're going to go through them right now. Regular arrays are just this in C, and so you have indices that are numbered. Right now we're just going to create one and put-- so this is how we create an empty array, then we're going to put into the index number 0. We're going to put the number 6, the value 6. You can see it at the bottom here. Where's--at index number 1 we're going to put value number 4, and so you can see there's a 6, there's a 4, and then as we're printing things, when we try and print the value stored at index number 0, then we'll see the value 6 being printed out. Cool? So that's regular arrays for you. Another way you can also add things to regular arrays now is you can just append them at the end. That means that you don't have to specify the specific index. You can see number, and then in the square brackets there's no index specified. And it will know--PHP will know to just add it to the end of the list, the next free spot. So you can see the 1 right there at that 0 spot, the 2 went right there at the first spot. The 3 goes--is added there as well. So that kind of makes sense. You're just constantly adding it, and then when we're echoing the index of number 1, it will print out the value 2. Then we have arrays that are associative arrays. Associative arrays, instead of having numerical indices, what they do is, they have indices that are by string. You can see, instead of--I got rid of all those number indices, and now it's key1, key2, key3, and they're in double quotes to signify that they're all strings. So we can have an example of this. The example of this is that we have the tf, and that's the index name. We're going to put "Ali" as the name, at the index, calories eaten, we can put an int this time instead of a string, and then at the index likes, we can put an entire array inside of it. So this is kind of--it's a similar concept to how we had indices with numbers, but now we can change the indices around to have them as strings instead. You can also do this, besides just doing it individually, you can do it all in one chunk. So you can see that tf of that array, and then we set them all in one giant square bracket set. So that can speed things up. It's more of a stylistic choice than not. We also have loops. In C we have loops that work like this. We had our array, and we went from index 0 to the end of the list, and we print it all, right? Except the problem is, for associative arrays, we don't necessarily know those numerical indices because now we have the string indices. Now we use foreach loops, which, again, you hopefully used in pset 7. Foreach loops will just know every single part of the list. And it doesn't have to know exactly the numerical index that you have. So you have the foreach syntax, so it's foreach, you put the array. So my array is called pset, and then as, the word as, and then you put this local temporary variable that you're going to use just for the specific thing that's going to hold the specific-- one instance or one section of the array. Pset num will hold 1, and then maybe it will hold the number 6, and then it will hold the number 2. But it's guaranteed to go through every single value that's in the array. Useful functions that you should know in PHP are the require, so that makes sure that you're including certain files, echo, exit, empty. I highly recommend you look at pset 7 and look at those functions. You might have to know those, so I would definitely know what, exactly, those are all doing. And now we're going to go through scope really quickly. In scope, PHP is kind of a funky thing, unlike C, and so we're just going to go through it quickly. So let's say we start at that arrow that we have there. And we're going to start with $i. So the variable 'i' is going to be 0, and we're just going to keep printing it in that big white box over there. We're going to start with i0, and then we're going to echo it. So there's the 0. And then we're going to increment it by the for loop, and then it's going to be value of 1. One is less than 3, so it's going to pass through that for loop, and then we're going to see it printed again. We're going to increment it again to 2, and 2 is less than 3, so it'll pass the for loop, and it'll print the 2. Then you'll note that 3 is not less than 3, so we'll break out of the for loop. So now we've exited, and then we're going to go into aFunction. Okay. So you have to note that this variable that we've created, the 'i' variable, is not locally scoped. That means that it's not local to the loop, and that variable we can still access and change afterwards, and it will still be effective. So if you go into the function now, you'll see that we also use the 'i' variable, and we're going to increment 'i' ++. You would think, at first, based on C, that that's a copy of the 'i' variable. It's a totally different thing, which is correct. So when we print it, we're going to print 'i' ++, which is going to print out that 4, and then we're going to--sorry. Then we're going to end out of that function, and we're going to be where that arrow is right now. That means that then, however, even though the function changed the value of 'i', it didn't change outside of the function, because the function has a separate scope. That means that when we echo 'i', it hasn't changed in the scope of the function, and so then we're going to print 3 again. Different things about scope in PHP than in C. Now in PHP and HTML. PHP is used to make web pages dynamic. It kind of makes things different. We have it different from HTML. With HTML, we always just have the same static thing, like how Rob showed, whereas PHP, you can change things based on who the user is. So if I have this, I have, "You are logged in as--" and then the name, and I can change the name. So right now the name is Joseph, and it has the "about me," but then I can also change the name to have Tommy. And that would be a different thing. So then we can also change different things about him, and it will show different content based on the name. So PHP can kind of change what's going on in your website. Same here. Still, note that they have different content, even though you are technically still accessing that same web page on the surface. Generating HTML. There are 2 different ways that you can do this. So we'll go through that right now. The first way is, you have--yeah, sorry. So you just have your regular for loop in PHP, and then you echo in PHP and you echo out HTML. Using what Rob showed you of HTML script and then using the PHP print to just print it out to the web page. The alternative way is to do it as if you separate out the PHP and the HTML. So you can have a line of PHP that starts the for loop, then you can have the line of the HTML in a separate thing, and then you end the loop, again, with a PHP. So it's kind of separating it out. On the left side, you can that you have all the-- it's just 1 chunk of PHP. On the right you can see that you have a line of PHP, you have a line of HTML, and you have a line of PHP again. So separating it out into what they're doing. And you'll note that either way, for either of them, they still print out the image, the image, the image, so that HTML still is printed the same way. And then you'll still see the 3 images show up on your website. So it's 2 different ways of doing the same thing. Now we have forms and requests. As Rob showed you, there are forms of HTML, and we will just breeze through this. You have an action and you have a method, and your action kind of shows you where you're going to send it, and the method is whether it's going to be a GET or a POST. And a GET request, as Rob said, means that you're going to put it in a form and you'll see it as a URL, whereas a POST request you will not see in a URL. So a slight difference. However, one thing that's a similar thing is that POST and GET are equally insecure. So you may think that just because you don't see it in the URL, that means the POST is more secure, but you can still see it in your cookies in the information that you're sending. So don't think that about one or the other. Another thing to note is that you also have section variables. You guys used this in pset 7 to get your user ID information. What happened was that you can use this associative array, the $_SESSION, and then you're able to access different things and store different things across the pages. Last thing is that we have SQL, Structured Query Language, and this is a programming language to manage databases. What, exactly, are databases? They're collections of tables, and each table can have similar kinds of objects. So we had a table of users in your finance pset. And why are they useful? Because it's a way of permanently storing information. It's a way of tracking things and managing things and actually seeing it on different pages and keeping track. Whereas if you just store it at that one immediate moment and then use it later, you won't be able to access anything that you've saved. We have 4 major things that we use for SQL commands. We have select, insert, delete, and update. Those are really important for you guys to know for your quiz. We'll quickly go over select right now. Basically, you're selecting rows from a database. So if you have, right here-- we have these 2 different things, and we want to select from the classes table where awesome--where in the awesome column the value is 1. So you can see here, we have these 2 things of class name, CS50 and Stat110, and we have the class IDs and the slogan. So we want to select all of that information. Then you can see right here that it's kind of picking out of that awesome column, where all the things are 1, and then it has the class ID, class name and slogan that it can pick out. How exactly do you do this in code? You have to use PHP. So that's kind of how PHP and SQL are related to each other. Now we have our code, and we're going to use our query function as we did in pset 7, and we're going to run the SQL query. Then we're going to have-- we always have to check if row's triple equal if false. So again, you want to check the type and the value, and then if it doesn't work, then you want to apologize, as usual, as we did in pset 7. Otherwise, you want to loop through everything with those handy foreach loops that we just went over. Now that we're looping through and we've made it past, let's assume that our query passed, now we have our foreach loop. And the first row it has, so here's the row, right here; it's boxed. It's going to print out all the information that it's gotten. So it's going to print out at the bottom "Wanna Learn HTML?" Then it's going to go to the next row, because it's completed the first for loop, and so then it's going to print out the second line of it, which is going to be STAT110, Find all the Moments. One last thing is on SQL Vulnerabilities. I know David touched on this a little bit in lecture. You can read this later. It's really funny. SQL Injection is a kind of tricky thing. Let's say that you just stick those variables right into your query, as you can see in that first line. So it seems fine, right? You're just putting in the user name and password to your SQL query, and you want to ship it off and get whatever is in your data table. That seems pretty simple. So lets say someone puts in, for the password, this OR text right here-- should actually be in the red box. So let's say that they put that password into--that's what they enter. So they're putting OR "1" = 1. Kind of a silly password to have. Now let's just replace it in, and you'll note that in that SQL query now, it evaluates to always true, because you'll note that you can SQL query select all of this information or you can just have 1 = 1. So that's always going to evaluate to true. That's not going to really work, because that means that the hacker can break into your system. The solution to this is that you have to use the PDO system, which means that you have to use question marks, which is what you guys used in pset 7, where you're going to use a question mark in place of where you want to put something, and then you're going to have a comma, and then you'll have afterwards, after your string, the different variables that you want to replace into your question mark. So you'll note here that now I have these red question marks. Then I put the variables after my strings so I know to replace them in that order afterwards. That will make sure that if someone does it like this, and they have the or 1 = 1 situation, that will make sure, in the back end, make sure that it won't actually break the SQL query. Okay, so that's pretty much it, a whirlwind of PHP and SQL. Best of luck to all of you, and now to Ore. [Oreoluwatomiwa Babarinsa] Okay everyone. Time to go over some JavaScript and some other things very quickly so we don't hold you up tonight. JavaScript. Yes. JavaScript is kind of a cool thing, purportedly. The things you really need to know about JavaScript, it's sort of like the client-side end of what your web app is going to be doing. There's some things you just don't want to take care of all the time on the server side. All the little interactions, highlighting one thing, making something disappear. You really don't want to have to talk to your server all the time for that. And some of that is not even possible to do on the server side. This is why we need something like JavaScript. Cool things about JavaScript: It is dynamically typed. What this means is that your program doesn't need to know what, exactly, the variables are when you write it out. It'll just sort of figure it out as it's running. Other things that are cool about it: It's a curly brace language, which means the syntax is similar to C and PHP. You don't have to do much rework when you're learning JavaScript. Here we have a little bit of JavaScript. Interesting thing right here is that, if you look at it, we have a bit of JavaScript right there in the head tag. What is does is basically just include a JavaScript file. This is one way you can include JavaScript into your program. Then the second little bit is actually some inline JavaScript, very similar to an inline style with CSS, and you're just writing some code very quickly there. JavaScript has arrays. Just another way to keep data around, very useful. Very nice and easy syntax. You use square brackets to access everything and keep everything together. Nothing too complex. The cool thing about JavaScript and scripting languages in general is that you don't have to worry about array sizes. You can just use array.length and keep track of it, and also the array can grow or shrink as you need it to. So you don't even need to worry about any sort of, oh no, I need to allocate more things, or anything like that. The cool thing here is that JavaScript has something called objects. It's an object-oriented language, so what it has is, essentially, a way for you to group data together, somewhat similar to a struct, but you can access it like a struct or in an associative array syntax. It's pretty simple and what you can do with this is group data together if you have a bunch of data that's related. Because it's all the things you need to describe a car, you don't need to have it in a bunch of different places. You can just stick it into 1 object in JavaScript. As you probably know, iterating is one of those tedious tasks. You just do it over an over again. You need to talk to every object in the car, or you need to go through every item in a list or something like that. So JavaScript has, similar to PHP, a foreach syntax. In this case, it's a for in loop. You want to use this only on objects. There are some problems that occur if you use this on arrays. It generally is one of those things, though, that is very useful, because you eliminate a lot of overhead because you don't have to pull up everything in your object by yourself. You don't have to remember all the key names. You just sort of get them back in this syntax. In this, with for, you just want to remember that you're getting back all the keys, in a very similar way to hash table. If you remember from that, when you would put in a string you could get something out that would have an associated value with it. What you can do with this is you can say, all right, I put in a car, and I called it a Ferrari. So you can put in the string Ferrari again later, and you can get that out. And you can do that in a loop, with the for in loop. So just more about objects. The key thing from this you need to remember is that you can use the object struct like syntax whenever you want with these, except if what your going to use as a string isn't a valid variable name. So if you look at that there, we have key with spaces. Well, if you were to put object.key, space, with, space, spaces, that just wouldn't make sense syntactically. So you only can do that with this sort of bracket syntax. Also, JavaScript is very scope-wise to PHP. You have 2 ways of addressing scope. You can not have the var in front of a variable, and that just means this is global. You can see it from anywhere. Even if you were to put this in an if statement, anywhere else in your code after that point you could see that variable. Another thing, though, is with the var, it's limited to whatever function you're in. If you're not in a function, well, it's global. But if you are in a function it's only visible within that function. I don't have an example, but, yeah. It's one of those things where you can manage what variables you want to be global, what variables you want to be local, but you do need to be careful about this, because you don't have the type of fine grain control you do in C, where if something is declared in a for loop, it's going to stay in that for loop. The thing we actually care about using JavaScript for is manipulating web pages, right? I mean, that's why we're doing this. To do that, we use something called the DOM. The Document Object Model. Basically, what it does is it takes all your HTML and models it out into a bunch of objects that are nested within each other. You start out with something like this. You have, on the right for me, a bunch of code out there that's sort of-- You would think that'd be very hard to manipulate, because you'd be parsing through a bunch of text and having to piece apart things. And what if it wasn't correctly formatted? Bad things would happen. So JavaScript takes care of this for you, and you get a nice data structure, like the one on my left, where you just have a document, and inside that you have something called HTML, and inside that you have a head and a body, and inside that head you have a title, etcetera, etcetera, etcetera. This simplifies manipulating a web page so that it's just, oh, I just want to talk to this object. Sort of a very similar way you would talk to another object you made yourself. Like I said, all the DOM is in the document object. Either it's just one place and then you can go within it to find things, and you can do it--this is the old style of doing it, up there, where you do document.getElementById, and then the name, and as you can probably tell, this gets very unwieldy after a while. So you probably don't want to do that. That's why we have the next thing we're going to talk about after this. The key thing here is that, all right, you have all these elements, right? So maybe I can change the color of something when the page loads. So what? What if my user clicks something? I want it to do something interesting when they click something. That's why we have events. You can, basically, find any element in your DOM, and then say, hey. When this loads or someone clicks it, or when they mouse over it, do something with it. And what you have is, you have functions that handle this for you. These functions are event handlers. What they're--it's just a fancy way of saying, this function is only executed when this event happens. So it handles the event that occurs. This is how you would lay out an event handler. I have some button, and when you click it, it explodes. So don't click the button. This is one way of approaching it, right? You have a button tag, and on click you have a string that says, oh, by the way, I do this exploding thing for me. Otherwise, it's just like a regular button you just made. You can also do this another way, by grabbing the DOM element, but we'll save that after we talk about jQuery. jQuery: It is a library that is cross-browser. You can use it in pretty much anything. And it just gives you a lot of tools to work with. Because JavaScript, while powerful, doesn't have all the tools you need out of the box to really tackle a web app you might want to do. So it simplifies a lot of things, gives you a lot of functions out of the box that you would normally have to write yourself, over and over and over again. And just makes things very simple. You also have selectors, which let you take out all those elements from your DOM much more simply, instead of having to use these very long function calls. More on these selectors. You have, up there you have, let's say I want to get an element with the ID "rock." Well, in jQuery, it's just $ and then a string that has a pound, and then "rock." It's very simple and a lot faster than the traditional JavaScript way of tackling this problem. And you have similar things for classes and element types. jQuery is--one of the cool features is you can sort of compress down your queries on your DOM very, very fast. Now we're back to event handling, and this is how you would handle one event in jQuery. So what we're going here is we're saying, all right. I have a script tag, right? So I have this inline JavaScript. What we're going to do is we're going to say, all right. When the document is ready, which means the document's been loaded, we are going to go in to that function, and we're going to say, all right, this function's actually doing something else. It's basically saying, all right, get me the element with the ID "myid." And then give this a function handler that executes when you click it. Basically what this does is, it says, all right. The page is loaded, so I'm going to in, find this element, give it this event handler, and it basically sets up your page for you. And this is how you want to think about event handling. You just want to think about, all right, when something occurs, what do I want to happen? You don't want to think about, okay, I need to make sure this thing talks to this thing, this thing blah blah blah, because you just want to talk thing in terms of events. When this happens, this happens. When this happens, that happens. And if things trigger other things, that's great. But you don't want to try and do complicated code where you're triggering multiple things at the same time, because you're just going to give yourself a headache. All right. Now we can get our page to handle events, but let's say my user clicks a button. What if I want to send that request back to the server, but I don't want to reload the page, because having to reload a new page every single time gets kind of tedious, and why do I need to pull down the header again, and the footer again, and all the elements of the page again just to refresh the greeting or the time? So that's why we have something like Ajax. What we can do here with Ajax is we can say, all right, I want to send some data to the server, and I want to get a response back so I can update my page, or maybe just do some algorithmic calculation that doesn't necessarily show anything to the user. What do you need to do this? Well, you need a URL you need to talk to. Your server can't just magically listen in from nowhere. You need to have a specific place you're sending this data to. And you also need some data to send, or maybe it's a dataless query. You just want to ping back to the server and say, hey, I'm alive, or something like that. And then you want a function that basically handles with success. Let's say you get back some information from your server, and you want to change the user's title on their page. So you would get the information back, and you would push that to the screen. What happens is, when the page is ready, you create an on click function for this button called greeter. What this then does is, when that button is pushed, you talk to greetings.php, you make a POST request, and you say, hey, get me something from your page. We don't really need to describe that, but greetings.php, let's just say, gives back "hello world." So we get back this "hello world," and on success of this, assuming nothing goes wrong, then we just go to this target place that we specified and we just stick the response in there. And this is a very simple way of setting up an Ajax query. Very quickly, Rob sort of mentioned this already, things can go wrong, bad things can happen, so you want to familiarize yourself with these HTTP response codes. What these are are just, like, 200, everything went okay. Something else, bad things happened. It's generally the thing you want to remember. But it's nice to know all of these. And finally, once we've gone through all of that, we need to talk very quickly about design, and then we can let you all leave. Design. Things you want to remember. Ask yourself these questions: Who'll be using this? What will they be using it for? What do my users care about? What don't they care about? You just don't want to make an app and let it just grow and become this giant, all-consuming thing that you can't even finish. You want to have discrete goals and plans and things you want to address. Make it effortless. All of this says, basically, make it easy for the user to use it; don't make it a giant blob of text like this slide is, actually. You just want it to be something where it's very easy for someone to go in and do what they want to do. You don't want them to have to navigate 5 pages to get to your prime function of your site. If Google had 5 pages before you could even search something, no one would use it. And lastly, paper prototype, focus group. Have good design and testing practices. Just because you think it works for you, doesn't mean anyone else thinks it works. But yeah, that's it. [CS50.TV]