JASON HIRSCHHORN: Welcome, everyone, to Week 6. I'm happy to see you all alive and well after Quiz 0, because I know that was a bit rough. But thankfully, you all did incredibly well. And so that is wonderful. If you're in my section, I've given most of you back your quizzes already. A couple of you, I'm meeting after class. And if you're an extension student and you haven't received your quiz back yet, your TF is probably working on it and grading it, and will get it back to you shortly. So my extension students who are watching right now-- hopefully live-- I will get your quizzes shortly as well. Our agenda for today is as follows. First, we're going to go over some resources that CS50 provides to you. We're going to go over Quiz 0 next, and I'll answer any questions anybody has about particular problems. And then, we will be going over file I/O and problem set 5. Those last two topics will take up the bulk of section today. I put this list up every week as a reminder to you all, but of core section, we only have 90 minutes-- we aren't able to cover everything that I would love to cover for you guys. But we do have a ton of resources for you to draw upon as you get to know the material and work through your problem sets. A reminder that I have online a text box, set up for you to fill out if you have any feedback for me, both positive and constructive, about section. That URL is located right down here. So please, take a moment if you have any feedback, whether during section, or after, or after you watch the video online, to give me your feedback. I really appreciate any and all of it. So I've been having small conversations with a lot of my students throughout the week-- as I hand back quizzes, talking about the course, seeing how you're doing. And one theme has come up over and over in talking about-- in particular-- problem sets. And I have encapsulated that theme on the board right now. Essentially, there's a difference between turning in something that is done correctly and something that is done well. Most people have been doing fantastic in terms of correctness-- 5's or 4's on all psets. Most people are getting those all of the time. However, just because you've done something right does not mean you've done something as elegantly, or efficiently, or as cleanly as you could have done it. And that's what the design-- and to a lesser degree, style-- axes are for. So I am pushing you all, and other TFs are pushing you guys, to not only turn in things that are correct, but turn in the things that are coded well. Not doing unnecessary FOR loops, not recalculating variables if you don't have to. For example, looking back to problem set 4, when placing the bricks on the screen, every row-- every brick in a given row has the same y-coordinate-- the same height coordinate. So that y-coordinate did not need to be calculated inside the interior nested FOR loop that you likely used to put those bricks on the screen. It only needs to be calculated every time you switched a row, or moved down a row. So say if there are 10 bricks in a row, each brick can have the same y-coordinate, and that y-coordinate can just be calculated once for all of those. It does not need to be calculated 10 times, nor does that calculation need to happen in the actual function call-- the new gracked function call. So if that was a little confusing for you, more generically, things that don't need to happen every single time you go through a FOR loop shouldn't be put inside the FOR loop, and shouldn't happen every time you go through the FOR loop. Another good design example we saw in Week 3 for 15, you could keep track of the zero. So when you initialize the board, you save-- in a global variable, perhaps-- the x and y-coordinate of the zero. And then whenever you-- in your move function, whenever you make a successful move, you update the location of the zero. That would save you from having to do nested FOR loops to look through the board every time in your move function and find the zero, or find the tile, and then check what's next to it. Instead, you have the location of the zero, you can just look above, below, and to the left and right of it, to find the tile you were looking for. So in terms of the programs we're writing, they're never large enough that some of these design decisions are really going to hamper your program, or make it run more slowly, or perhaps even run out of memory. But we're still pushing you guys to write as elegant and efficient code as possible. So if you do end up writing things that have a significantly larger scope, they will be written with good design in addition to being correct. So a number of you have brought that out. That's something we're looking for-- something we're going to continue to push you guys on. If you ever have any questions about the design of your program, feel free to reach out to me, and I'm happy to walk through your program with you, and point out some of the design decisions you made, and give you some suggestions on how to make even better design decisions. So we're going to move on to talking about Quiz 0. Before we do that, does anybody have any questions about what I've covered so far? [RUSTLING NOISE] JASON HIRSCHHORN: Seven seconds. OK. Let's talk about Quiz 0 for a bit. Most of you have your Quiz 0's back. If you don't, hopefully you remember it a bit. But if you've taken Quiz 0, then you also have access to the PDF online in the sample solutions. Does anybody have any questions before we jump into the week's material about a particular problem on Quiz 0-- why the answer is what it is? Is anybody confused about anything? Even if you got the problem right, but just would like me to explain it a bit more, I'm happy to do so now. So I have asked you guys to come prepared with some thoughts about Quiz 0. So who would like to get us started with a question or comment about Quiz 0? [PAPER RUSTLING] JASON HIRSCHHORN: Not everybody did perfectly. So I know [LAUGHS] there have to be some questions about Quiz 0. OK. Yes. Ompica. OMPICA: Number 10. JASON HIRSCHHORN: Number 10. Which one was number 10? OMPICA: The-- JASON HIRSCHHORN: I haven't-- OMPICA: The include-- JASON HIRSCHHORN: Number 10 was eight to i-- writing eight to i? OMPICA: Yeah. JASON HIRSCHHORN: OK. So another question you could have asked was am I prescient? The answer is yes. In section before the quiz, I asked you guys to code both Sterling and eight to i. Both of them happened to appear on the quiz. So hopefully, you paid attention to that. And if you had, then you would have probably done well on those two. But eight to i, we didn't actually code it in class, but it was, again, asked on the quiz. So a couple of things to take note when coding eight to i. The first thing, per the question, was that you needed to check if the string was equal to null. A couple people tried to check later on in the program if s bracket i was-- so a specific character in that string-- was equal to null. But remember, that null is essentially-- it's good to think of null as a zero pointer-- a pointer to zero-- someplace in memory where you can never access. So if something is equal to null, you know that it hasn't been initialized, or there's nothing there. So s is a char star, s bracket i is a char. So it makes sense to compare s to null, but not s bracket i to null. But again-- so that was the first thing that you were supposed to do-- check to make sure that you actually got a real string. Next, you wanted to go through each character in the string. And so that would be like an s bracket i, for example, if i is your iterator. And take that character, and get its actual value. You have it stored as a char, but the ASCII value for zero-- zero as a character-- is not actually the integer zero. It's some other number that you can look up in the ASCII table. So one way to correct for that-- probably the best way to correct for that-- is subtract from it the character value-- zero as a character. So minus single quote, zero, another single quote. That will take whatever number you have as a char, and get it equal to the number as an actual integer. And that is very similar to the approach a lot of people took in the problem set 2, with Caesar and Viginere-- those ciphers, when you were rotating them. So after you have it as a number from zero to nine, then-- depending on where it goes in the ultimate number-- you need to multiply it by a power of 10. Some people moved from the back to the front, and multiplied the individual number by a power of 10. Some people moved from the front to back-- and so took the highest order numbers first-- and would save those in a global counter variable. And then each time through the FOR loop, multiply that giant global counter variable by 10, to make space for the next char. So that was a little confusing without me writing it on the board. But the sample solution is available to you. But those were the big things we were looking for. Also a check to make sure that each individual character was indeed a character between zero and nine, and not some other character, like an A, for example. Those were the things we were looking for in that question. Does that answer your question? OMPICA: Yeah. JASON HIRSCHHORN: OK. Are there any other questions about Quiz 0? What about compiling? Everybody compiling right? No. There were a-- [LAUGHS] Any questions about the compilation process? Wow. [PAPER RUSTLING] JASON HIRSCHHORN: Yes. Michael. MICHAEL: Is number 7-- random? JASON HIRSCHHORN: Number 7. Number 7 was get a random integer. Excellent. So you're given an integer a and an integer b, and you want a random integer between a and b. We can actually write this one on the board, because this one was one line of code-- one way to do it. So we're given drand as a function we could use. And what does drand-- assuming it's been seeded-- what does drand return? MICHAEL: A float between 0.0 and 1.0. JASON HIRSCHHORN: A number-- yeah. A number between 0 and 1. And so we have b and a. And then we have our random number between 0 and 1 given to us by drand. Some people tried to put b, or b minus a, or something inside those parentheses. That would mean that they're arguments to this function. drand does not take any arguments-- like getString does not take any arguments. So it's just open paren, close paren-- and that, itself, is the function call. And that gives you a number between 0 and 1. Of course, we have a whole range that numbers can be in. Say, if b is 10 and a is 5, we really want a number with a range of 5. So the next thing we need to do is multiply this by the range b minus a. So assuming that's multiplied. And that'll give us a number within a given range. And that specific range being the difference between b minus a. And finally, that'll only give it from-- say the range between b minus a is 5, that'll give us a number from 0 to 5. But if a is in fact 5, we need to boost this range up to where it's actually supposed to be, by adding a. So that gets the logic right. And then, would you have another question? MICHAEL: No. I just feel really dumb right now. [LAUGHS] JASON HIRSCHHORN: No. Don't feel really dumb. A number of people struggled with this question. And then, the other question is, drand, you said, gives you a float-- returns a float. But this function actually asked for an integer to be returned. You don't need to cast this explicitly to an integer, because these operations will treat it as all a float-- as a floating point number. Like this will-- even if this is an integer, this will be multiplied correctly. All the multiplication will work. You don't need to cast it here. In fact, you shouldn't cast it. That would-- if you would cast a number that's between 0 and 1-- a random number, a floating point-- then it will either be only 0 or 1, so you'll lose all of that precision. But at the end, when you return, it automatically gets sent back as an integer. So you don't need to do that casting yourself. So this was the answer to that question, number 7. Any other questions on Quiz 0? Yeah, Annie. ANNIE: When do we use recursive-- when do we use iterative loops? JASON HIRSCHHORN: When do you use recursive-- so more generally, the pros and cons of recursion versus an iterative approach. Can anybody offer a pro or a con? Please? Not can anybody. Who can offer a pro or a con? [PAPER RUSTLING] STUDENT 1: Recursive is less coding-- less typing? JASON HIRSCHHORN: So generally, recursion especially, a function-- or an algorithm like merge sort-- which lends itself to a recursive approach-- might be more straightforward to code recursively. And just make more sense to do it recursively. So that would be a pro to recursion. Others? Yeah? STUDENT 2: Con to recursion-- It uses more memory. JASON HIRSCHHORN: So exactly right. A recursive function will keep adding stack frames to the stack. So if you're operating on a lot of numbers, and have to call this function a lot, then you will certainly take up more memory, while an iterative approach will only put one stack frame on the stack, because it all happens within one function. Any other pros and cons? Yeah. STUDENT 3: Pros for recursion. You don't have to determine in advance how many times the code had to be repeated. You can have a predetermined number of times that you have to iterate, then recursion is better, because it takes that result. JASON HIRSCHHORN: I think that's true. But I think in both cases you would never-- you would probably get some input from the user. Or this function would have some input that would determine how many times it should run. So generally, you wouldn't hard code-- even in an iterative approach-- how many times that loop should be run. Did you have another you were thinking about, Annie? OK. So those are probably the two-- the biggest pro and the biggest con to a recursive versus an iterative approach. OK. Anything else on Quiz 0? Let's move on. File I/O. There is a wonderful short this week on file I/O that hopefully you have watched multiple times, and admired. A lot of work went into that, and I've heard it is insanely helpful. I also included the link on this slide, in case you have not had a chance to watch it 10 times. So, we are going to briefly go over the major steps to opening and working with files, and then we are going to dive into a coding problem before examining the problem set. So again, I'm going to put this up on the screen, but I'm going to talk for just a minute about what we're doing here with file I/O-- what does that mean? That means that we can create our programs, and then have our programs exit, and not have made any impact on the world outside of our program. But when we start working with files-- both reading them in and creating them-- we can have some effect on the world outside of our program. Just like if Microsoft Word wasn't able to make any Word documents, then once Microsoft Word quit, all of your work would be gone, and it would really be useless. We do ultimately want to be able to write programs that can affect the world around them, both by taking in complex inputs-- in terms of files and via files, and also creating interesting and compelling outputs-- in terms of different types of files. So that is why we are starting to learn how to work with files. More specifically, what we do is as follows. It's very simple. There are only a couple of steps, and they are listed here on this code. So we're going to go through this code line by line. First, you see highlighted-- when you're working with a file, regardless of the type of file it is, you need to open it. And that is with a call to fopen-- right here. You include the name of the file. If the file is not in your directory, or the folder where this program lives, then you also need to include a path to where that file is. We're going to assume that this file called "text.txt"-- a simple text document-- is in the same folder as this program is. So that's another thing to keep in mind-- that if you want to open a file somewhere else, you actually need to include its location. Second, you can pass an argument to fopen, and that's what you want to do with the file. There are three main arguments that you're going to pass to fopen. Who can give me those three? Who can give me one of them? Yes. STUDENT 4: The file name? JASON HIRSCHHORN: Sorry. Three main arguments you can pass as the second argument to fopen. You're right-- the file name is the first argument. But the second argument to fopen are generally three strings, and-- yes. Aleja. ALEJA: A for append. JASON HIRSCHHORN: A, if you want to append to a file that already exists. STUDENT 5: R for read. JASON HIRSCHHORN: R, if you want to read from a file. STUDENT 6: W for write. JASON HIRSCHHORN: And w, if you want to write to a file. So in this case, we're writing to the file, so we have w. You open it, you also have to save the file somewhere, and that's with the code to the left hand side of the assignment operator-- I'm creating a pointer to a file called, in this case, file. We are not going to worry what this all caps FILE thing is. Suffice it to say, it is a long stream of zeros and ones. And that's how we are going to operate it and understand it. The next thing we need to do-- and this is incredibly important-- whenever you open a file-- in fact, whenever you call malloc, for example, and get some memory and try and save it in a pointer, you always want to check to make sure that that function did not return null. So in this case, we are checking to make sure that we actually opened the file correctly, and there was no error in our program. Next, once we've checked to make sure that we have a working file, we can write to, or read from, or append to the file. In this case, I am simply printing one line to this file. How do I know that? Well, I'm using this function called fprintf. All of the functions you will be using when writing to, or reading from, or manipulating files will be similar to functions you've seen before, but start with the letter F, standing for file. And fprintf, unlike our normal print app, takes one additional argument, and that is the file where you want to print this line to. I don't have anything to the right of ohai. I don't have the third argument to printf-- or the second argument to printf, the third argument to fprintf, because I don't have any placeholders here. I'm not including any variables. But again, fprintf and all of these file functions that operate with files are generally going to need the file on which they're operating. Finally, the last important thing to do is to close the file, just like with-- whenever we malloc something, we want to free something, lest we have a memory leak-- we want to close our file. If this program exited without closing the file, odds are nothing would go wrong, especially if it was a small file. But it is certainly good coding style and practice to always close your file when you're finished using it. So that is the basics of file I/O. You've probably seen that before, or watched it in that fantastic short. Does anybody have any questions, before we go into some practice coding problems, about file I/O or the steps I just went over? [TYPING SOUNDS] JASON HIRSCHHORN: Do you have a question, Avi? AVI: No. JASON HIRSCHHORN: OK. I'm going to wait another seven seconds. [LAUGHS] That's a really good tip. You guys just don't like asking questions. That's fine. OK. So our first practice problem is, we are going to duplicate the function of a command line tool that you probably used before-- copy-- the copy tool. If you type cp and then pass it two arguments into your terminal, you can copy a file. And that is what we are going to write right now. So again, reading off of this slide, I'd you to write a program that takes two and only two command-line arguments-- a source file and a destination file-- and copies the contents of the source file to the destination file one byte at a time. So that's a lot to ask for. Again, a good approach to this is to not go straight to the C code, but break it down into a couple of steps. First, think about the logic-- exactly what I'm asking you to do-- and understand all of the steps to this problem. Not in C, just in some pseudocode, or even a mental model of what's going on. Next, once you have the pseudocode down, figure out how the pseudocode maps onto tools and things we've learned to use in C. And finally, once you have all that together, you can code the problem. Take 5 to 10 minutes to work on this problem. I'll put the instructions back up in a second. And then we're going to go over the pseudocode, and code it live as a group. If you have any questions while you're working on this, feel free to raise your hand, and I will come around and answer them. STUDENT 7: Can I swipe a piece of paper? JASON HIRSCHHORN: What's up? [TYPING SOUNDS] JASON HIRSCHHORN: OK. Let's go over the pseudocode first, and then I'll give you a couple more minutes to finish coding. Who would like to start me off with the first line of pseudocode for this function? STUDENT 8: Check to make sure that you were given two files. JASON HIRSCHHORN: OK. And if we're not? STUDENT 8: I would return 0. JASON HIRSCHHORN: Should we return 0? STUDENT 8: Return a-- blanking. Sorry. JASON HIRSCHHORN: Yeah. Probably not 0. Because 0 means everything was good. OK. So that's the first line of pseudocode. Who has the second line of pseudocode? STUDENT 9: Open both the files? JASON HIRSCHHORN: Open both files. OK? STUDENT 10: Check to see if the file is NULL? JASON HIRSCHHORN: Check to make sure neither are NULL. As an aside-- slash 0-- is that NULL? STUDENT 11: No. JASON HIRSCHHORN: That's not NULL. That is called the NULL terminator. It's actually spelled with only one l. So checking something against that-- that's actually a character-- so checking something against that is not the same as checking to see if it equals NULL. And some people-- on their quizzes and their problem sets-- have got the two of those confused. But the two of those are in fact different. One ends a string-- one is a pointer to 0. STUDENT 12: Why wouldn't you check to make sure that the files are not NULL before you open them? JASON HIRSCHHORN: So open saves something in that file. And if you go back here-- so this line-- fopen-- will give you an address and store that address in file if it works. If it doesn't work, it will store NULL-- STUDENT 12: Oh. OK. Got you. JASON HIRSCHHORN: In file. So you can't check for NULL before you've opened them. NULL means something didn't work correctly. OK. So check to make sure neither is? Or are? What do we think? We'll go with that. STUDENT 13: Is. JASON HIRSCHHORN: Is? Neither is? STUDENT 13: Is. JASON HIRSCHHORN: OK. We seem to have some consensus on that. Neither is NULL. OK, next line of pseudocode. Who hasn't given me a line yet? We will wait for you. Yeah. STUDENT 14: You have to read from the first file? JASON HIRSCHHORN: OK. STUDENT 14: Or we use fscanf or something like that the first file? JASON HIRSCHHORN: So we want to read from the first file and-- let's put that right here. Read from the source file. And then, what do we do after we read from the source file? Somebody else? STUDENT 15: Write into the destination file? JASON HIRSCHHORN: We write to the destination file, and-- OK. What else are we missing? Somebody else who has not given me a line of code yet-- of pseudocode. Yeah. STUDENT 16: Maybe you can always check whether there's something to read for, like the next line? That are like the next line, see if it exists. [ELECTRONIC BEEP] JASON HIRSCHHORN: Oops. That's my journaling software. Yeah? STUDENT 16: Yeah. JASON HIRSCHHORN: So give it to me one more time. STUDENT 16: Check whether there's still a next line from the source file to read. JASON HIRSCHHORN: OK. So we're not reading lines-- were reading bytes here-- but you're correct. We want to read and write until there are no more bytes. OK. And so these should really be indented a bit, because they're under there. Right? Until we're out of bytes, we're going to read from the source file and write to the destination file. And then, what is the last line of pseudocode? Someone who's not given me something yet. STUDENT 17: Close the files? JASON HIRSCHHORN: Exactly. Close the files. So there's our pseudocode. I'm going to put the pseudocode into gedit, and in a couple of minutes we will code this together. OK. Let us get started as a group. Nishant, I have my new file. I've just opened this up. Untitled document 1. What's the first thing I should do? NISHANT: Include libraries? JASON HIRSCHHORN: OK. What libraries? NISHANT: Stdio.h, stdlib.h, I believe? JASON HIRSCHHORN: OK. What is stdlib for? NISHANT: I forgot. JASON HIRSCHHORN: OK. So include stdio. What should I do even before I start coding? NISHANT: Write a header? JASON HIRSCHHORN: How do I get it colored? [INTERPOSING VOICES] NISHANT: How do you get it colored? JASON HIRSCHHORN: How do I color coding? NISHANT: I don't know. Oh. Save. JASON HIRSCHHORN: Save. Yes. I should save it as a .c. So save it on the desktop as cp.c. Sweet. And if I want to get full style points, what should I include at the top? NISHANT: You could write your name, name of the program, and the purpose of the program as well? JASON HIRSCHHORN: Looks good. Excellent. So you've started us off perfectly. #include-- we'll also write-- OK. So I think I'm all set to go. Who has the first line of code for me-- or the first lines of code that it will take to satisfy our first comment in pseudocode? You. STUDENT 18: Shouldn't it be int argc, and then char* argv? JASON HIRSCHHORN: I think you're right. Let's change it to int main, open paren, int argc, comma, char* argv? Like that? STUDENT 18: Brackets. JASON HIRSCHHORN: Brackets. Open bracket, close bracket, close parent. Perfect. Now I can take command-line arguments. OK. Ensure we're given two files. You can give me that as well. STUDENT 18: If argc-- this one does not equal 3. JASON HIRSCHHORN: If open paren argc does not equal 3? STUDENT 18: Yeah, you return 1 or anything. JASON HIRSCHHORN: Sorry. STUDENT 18: Return 1 or anything. JASON HIRSCHHORN: Return 1. OK? Great. Open both files. Who can help me open both files? Who hasn't given me code yet? Kurt? KURT: So all caps F-I-L-E star source. JASON HIRSCHHORN: I'm going to take out the vowels. Those are cool. It's like Tumblr. STUDENT 18: Equals fopen-- JASON HIRSCHHORN: Equals fopen? STUDENT 18: Open paren, argv, open bracket. JASON HIRSCHHORN: Wait. Sorry. Open paren. OK. STUDENT 18: Yeah. Argv sub 1. JASON HIRSCHHORN: Sub 1? STUDENT 18: Yeah. Argv open bracket 1-- yes. And then comma, and then open double quote, r, double quote, close paren, semicolon. JASON HIRSCHHORN: Sweet. And what about the other one? STUDENT 18: Very similar, but instead of S-R-C, you'd call it D-S-T. JASON HIRSCHHORN: Oo! I like that. STUDENT 18: Just D-S-T. Yeah. And then argv, open bracket, 2. Yeah. And then w instead of r. Yeah. JASON HIRSCHHORN: Great. Next couple of lines. Also, if anybody has things to add to lines that we've done, feel free to add those as well. Check to make sure neither is NULL. Who can give me the code I need to satisfy that line of pseudocode? Archer. ARCHER: If src equals equals NULL or dst equals equals NULL, then you return-- JASON HIRSCHHORN: What? ARCHER: Return 2? JASON HIRSCHHORN: Return 2. So if open paren src equals equals NULL, or-- whatever that thing's-- pipe? Pipe? We'll call it pipe. Pipe, pipe, dst equals equals NULL, return 2. OK? Until we're out of bytes-- we sort of skipped over this step from the pseudocode part to going to here. But until we're out of bytes-- what does that sound like? What type of C structure-- but I don't use the word structure, because we're going to start using that in other cases-- but C tool does that sound like? STUDENT 19 : A loop. JASON HIRSCHHORN: A loop. Sounds like a loop. So who can give me the first line of the loop code right here? You can also pick what kind of loop you want, if you give me this line of code. There are three kinds. You get to pick. I would suggest one of those. Avi. Which one do you want? AVI: FOR. JASON HIRSCHHORN: FOR. AVI: Int i equals zero. JASON HIRSCHHORN: OK. AVI: This part I'm not sure about. But i is less than size of star source? I'm not sure of that. JASON HIRSCHHORN: OK. AVI: Because you want the size of a file, right? JASON HIRSCHHORN: So this probably won't give us the size of the actual file in bytes. So what else could we do? What is another type of loop? Or should we stick with the FOR loop? STUDENT 20: Could you do a WHILE loop? And then, what you'd do is you'd-- because we have a char* for the file. So if we just keep incrementing that until we'd find the NULL character at the end of it? Or no, is that not how files work? JASON HIRSCHHORN: So we can keep incrementing the char* until we find the NULL-- STUDENT 20: Essentially keep going character by character until we hit the end of the file. JASON HIRSCHHORN: Yes. So that's what we want to do. We want to keep reading, character by character, until we get to the end of the file. STUDENT 20: Yeah. Find-- what's the end or stop sign at the end of a text file. JASON HIRSCHHORN: OK. So when we get to the end of the file-- how do we know we've reached the end of a file? If I'm calling-- so let's step back. What is a function? Let's go to this line right here. Read from the source file. Who can give me that line of code? STUDENT 21: Fscanf? JASON HIRSCHHORN: Fscanf. OK. What if I want to read, very specifically, one byte? STUDENT 21: I don't know. JASON HIRSCHHORN: OK. Even simpler than fscanf-- what is a-- I want to read from a source file? Read from a source file. What is a function-- yeah. STUDENT 22: It's fread? JASON HIRSCHHORN: Fread. I think let's stick with that one for now. What kind of arguments does fread take? STUDENT 22: Probably the file type, and then location in the file? JASON HIRSCHHORN: What can I type here to figure out what type of arguments fread takes? MULTIPLE STUDENTS: Man fread. JASON HIRSCHHORN: Man fread and fwrite. Looks like they hang out together. So fread takes how many arguments? STUDENT 23: Four. JASON HIRSCHHORN: It takes four arguments. It takes a pointer, a size, and that thing, which is weird, and some file. OK? Let's read about it right here. "The function fread reads n memb elements of data, each size bytes long, from the stream pointed to by stream, storing them at the location given by pointer." So four arguments. Why don't I just copy this, and paste it right here. OK. So who can start filling out these arguments for me? Avi. AVI: Take out the void. Put just src. Take out pointer and the star. Put src. Then-- JASON HIRSCHHORN: So I'm going to stop you there, because that's incorrect. You're right with src, but where should src go? [INTERPOSING VOICES] JASON HIRSCHHORN: It should go over here. That's the src-- our src is a type. Let's look here. This is asking for a type FILE*, we actually usually see them like that. So this is asking for an argument of type FILE* called stream that is src. OK? What size of things do we want to read? I gave you this in the problem description. STUDENT 24: One byte at a time. JASON HIRSCHHORN: One byte. How big is a byte? Its size is in bytes, so what can I put right there? STUDENT 25: One. JASON HIRSCHHORN: One. Right. Its size is in unit byte, so 1 is 1 byte. How many do I want to read at a time. STUDENT 26: One? JASON HIRSCHHORN: One thing. I want to read one thing of size 1, one bite at a time. And where do I put it, once I read it? STUDENT 27: Destination? JASON HIRSCHHORN: So I can't put it straight into destination. STUDENT 28: You're gonna put it into a third pointer? STUDENT 27: To the destination. JASON HIRSCHHORN: OK. Yeah. STUDENT 29: You can declare something to act as a temporary storage earlier. JASON HIRSCHHORN: OK. Give me that. STUDENT 29: Another file pointer, maybe? JASON HIRSCHHORN: OK. So this is void star-- it's a type void star, so it doesn't have to be a file pointer. And if I'm reading one byte, where would be a good place to store one byte? STUDENT 29: An array? JASON HIRSCHHORN: An array. OK. And what else is something that's just size one byte? STUDENT 30: A char*? STUDENT 29: Yeah. JASON HIRSCHHORN: A char* is not one byte. STUDENT 29: A char. JASON HIRSCHHORN: A char is one byte. Right? So let's call this buffer is a generic name used for these things to store something temporarily. So I create a buffer. Right? But it does take a void*. So maybe you are right, that it should be a buffer of size 0. So it stores one-- right. Because this right here-- char buffer is a character, but this takes a void*-- a pointer. So I could do this and now buffer is a pointer. What else could I do? STUDENT 31: Put a star next to char. JASON HIRSCHHORN: I could have created it char*. OK. What's another thing I could do? Or let's go with this one. Char* buffer, so what do I put in here? STUDENT 31: Buffer. JASON HIRSCHHORN: Buffer. Buffer is a pointer to a char. And in that location, we're putting one byte of something we've read. Yeah. Avi. AVI: Just a quick question. Do you want to malloc buffer? JASON HIRSCHHORN: Who can answer that question? STUDENT 32: Well, It doesn't really point to anything right now, so-- JASON HIRSCHHORN: But do we want to malloc it? STUDENT 32: If you were to do it that way, I guess, yeah, because you'd need some place for it to point to. JASON HIRSCHHORN: Do we have to malloc it? STUDENT 33: If you're going to use it outside of the loop. JASON HIRSCHHORN: Are we going to use it outside of the loop? STUDENT 34: Yes. STUDENT 35: Wait. Do we want to declare it in the loop to beyond? JASON HIRSCHHORN: So I guess we have some pseudo WHILE loop here that we're trying to figure out, that we haven't gotten to yet. We don't need to malloc it. We're operating in main, it's only going to be used inside this loop. It does not need to exist outside this. So it can be a local variable. You have a pointer to a local variable. STUDENT 36: But it's not pointing to anything. JASON HIRSCHHORN: No, it's not initialized to anything. But we're not going to use it also. We're going to put something in it the first time we use it. So that seems OK. So we don't need malloc here. And I think it's OK as is. OK. We have the fread line. Let's do the next line. If we want to write to a file, what is a good function to use to do that? STUDENT 37: Fwrite? STUDENT 38: Fprintf? JASON HIRSCHHORN: Fprintf is one. What's another one? STUDENT 39: Fwrite. JASON HIRSCHHORN: Fwrite. And for our purposes, fwrite, which we saw here, is probably the better choice. It takes four arguments as well. Nishant, can you give me the arguments? NISHANT: The first one's going to be just buffer. JASON HIRSCHHORN: OK. NISHANT: The second one's just going to be 1. Third one's going to be 1. And the fourth one is going to be dst. JASON HIRSCHHORN: Does anybody have any questions about that line? That looks good. OK. So now it looks like the one thing we're missing-- actually, let's write this last line. Close the files. Who can finish us up writing these last two lines? Yes. Sorry, what's your name? LUCY: Lucy. JASON HIRSCHHORN: Lucy. LUCY: Fclose src and then fclose destination. JASON HIRSCHHORN: Fclose, open paren, src, close paren, semicolon. And fclose-- yeah? LUCY: Open parentheses, dst and then semicolon. JASON HIRSCHHORN: Great. And what should I include at the end? LUCY: Return 0. JASON HIRSCHHORN: Return 0. Do I have to? Just a question. Do we have to include return 0? MULTIPLE STUDENTS: No. JASON HIRSCHHORN: No. Main does it automatically if you get to the end. But I think it's nice to include it explicitly. Especially when we're returning other things throughout the program. OK. This is what we're missing-- WHILE what? Who can think of some-- has some sense of what things could go in there? Even if it's just in some pseudocode like language? What are we really-- what do we want to go until? Yeah, Lucy. LUCY: The end of file. JASON HIRSCHHORN: The end of file. So what do you mean by end of file? LUCY: Once you reach the end of the file, stop. JASON HIRSCHHORN: OK. So once we reach the end of the file. How do we know when we've reached the end of the file? STUDENT 40: I think buffer will be set to NULL. STUDENT 41: Buffer is declared inside the loop. JASON HIRSCHHORN: So you think buffer will be set to NULL. Why would buffer be set to NULL? STUDENT 40: Because when you fread, you're trying to put nothing into buffer. JASON HIRSCHHORN: OK. So you're thinking fread-- when we've reached the end of the file, what is fread going to do? I think that's the question we have to figure out. What does fread do? Does it put NULL in buffer, or does it do something else? How can we figure out what it does? STUDENT 42: Man. JASON HIRSCHHORN: Man. So let's look over here. Return value. On success, fread and fwrite return the number of items read or written. This number equals the number of bytes transferred only when size is 1. If an error occurs, or the end of the file is reached, the return value is a short item count or 0. So for our purposes, if fread reaches the end of the file, and reads from the end of file, there's nothing left to read, what's it going to return? STUDENT 43: Zero? JASON HIRSCHHORN: What? STUDENT 43: Zero? JASON HIRSCHHORN: Zero. It's going to return zero. So we know that fread, when we've reached the end of the file, is going to return zero. How can we use that to our advantage? AVI: You can declare a variable outside of the loop called check. If check equals-- for now-- one. JASON HIRSCHHORN: OK. AVI: And then you can put an IF statement right after fread saying if fread equals zero-- no. JASON HIRSCHHORN: Who can help Avi out? AVI: What's the value returned by fread? JASON HIRSCHHORN: We just went over that. AVI: How do you represent it? JASON HIRSCHHORN: So it returns-- let's look up here-- it returns a size_t, which is essentially an integer. So it returns an integer. And in our case, it will return 1 or 0-- 1 if it read one thing-- one byte, and 0 if we've reached the end. So if fread-- yeah? STUDENT 45: Can't you just put the full fread(buffer, 1, 1, src) into the while loop? JASON HIRSCHHORN: So you propose doing this into there? [INTERPOSING VOICES] JASON HIRSCHHORN: Hold on. So we're ridding of that. So you're proposing putting fread into there? What should we also move if you want to do that? STUDENT 45: The buffer outside. JASON HIRSCHHORN: We should also move this out here. STUDENT 45: But does that constantly move it forward? [INTERPOSING VOICES] JASON HIRSCHHORN: OK. So this is what Okshar proposed. We create our buffer. We WHILE fread, then we fwrite. Thoughts on this? STUDENT 46: My only question is, would it actually execute the command fread? JASON HIRSCHHORN: Great question. When you're putting a function call inside of a condition, does that function call execute? We've seen examples of this before. Right? STUDENT 46: OK. Yeah. So it does execute. JASON HIRSCHHORN: We've seen things like that before, where we have a function call inside of a condition. Does that function call execute? Yes. So the answer is yes. This function call will execute. But again, is it what we want? What is one way we could figure out if it's what we want? MULTIPLE STUDENTS: Run it? JASON HIRSCHHORN: We could run it. But before we do that, we could also reason through this. If-- say we have one byte in our file, we'll get to here, we'll get to this code. This will run. fread will return one byte and store it in the buffer. And this will evaluate to 1, right, after he returns 1. So WHILE 1. Does that mean the code inside the WHILE loop will execute? STUDENT 47: Yeah. It's true. JASON HIRSCHHORN: Yes. 1 is true. It's not 0. So the code inside here will execute. So we'll write that. We'll move back to this line once again. Now we have-- we're at the end of our file. We read from the end of our file, because we only had one byte in it. Fread returns 0, stores something in buffer. I honestly don't know what it stores in buffer. We could probably look up to see what it does. That I honestly don't know. We don't know, who cares what it stores in buffer? But it does return 0. And will WHILE 0 execute? WHILE 0 won't execute. So then we'll move down here. So let's get a show of hands if this is the code we should run, or if we should do changes first. So if you think-- you have to vote. If you think we should run this code as is, please raise your hand. OK. There's one-- do you have a question, concern? Yeah. STUDENT 48: After we move buffer outside of the loop, do we have to malloc it? JASON HIRSCHHORN: Great question. After we move buffer outside of the loop, do we have to malloc it? This is a scope question. If we initialize buffer outside of this loop, will it exist inside of the loop? MULTIPLE STUDENTS: Yes. JASON HIRSCHHORN: Yes. Its scope covers inside of the loop, and, really, anything below it inside of this code, including the things inside here. So we don't need to malloc it. It's a local variable, and its scope still includes the loop. STUDENT 49: Do we need to free it? JASON HIRSCHHORN: Do we need to free buffer? STUDENT 49: Yeah, if we don't malloc. JASON HIRSCHHORN: Do we need to free buffer? We don't. Again, it is a local variable, so we don't need to free it. OK. Let's see what happens. So it is uninitialized. That was what something that Marcus proposed earlier. So we have that error, variable buffer is uninitialized when used here. How can we fix this? STUDENT 50: Malloc it? STUDENT 51: Equals NULL? STUDENT 52: Say buffer equals NULL. JASON HIRSCHHORN: OK. Looks good. We have it now. Let's create something to try copying. So we have our text file. How can we run this program? Yeah. STUDENT 53: You can do dot slash cp, test.txt. And then you can name another file which it will store into. JASON HIRSCHHORN: OK. We'll call it out.txt. Cool? Seg fault. Thoughts on the seg fault? This is great. How can we find out where the seg fault is? What? STUDENT 54: Gdb. JASON HIRSCHHORN: Gdb. We run gdb by writing gdb dot slash, the name of our program. No command line arguments there. We're going to set a breakpoint at main. If I want to start gdb, what do I do? STUDENT 55: R. JASON HIRSCHHORN: R. And then what? STUDENT 55: The arguments? JASON HIRSCHHORN: Then the command-line arguments. Let's walk through. N is just taking me line by line. I'm going to go until I get my seg fault. There's my seg fault. It looks like fread caused my seg fault. I know fread caused my seg fault, because that was the line we just executed. And the only thing that was happening in that line-- two things were happening. Fread was going, and then we were doing some WHILE checking. I'm willing to bet that the WHILE checking was not causing my seg fault. Most likely, fread was causing my seg fault. I also see something here, memcopy. Memory copy. Sounds like moving memory from one location to the other. Sounds like something that would happen in fread, maybe some memory moving from here to here. Let's go through this again. How do I start it over and run it again? Yeah. STUDENT 56: Do you need to put an ampersand before buffer? JASON HIRSCHHORN: So ampersand before buffer would give me the address of buffer, which is a char*. Let's run through this one more time. How do I run through it one more time? STUDENT 57: Can you just type run again? JASON HIRSCHHORN: Just type run again. So we're not going to execute this line. So buffer is a NULL pointer. Correct? It is pointing to-- let's see. If we have our-- draw a quick picture of this. Can everybody see if I write over here? So in the stack, we have a local variable and it's called buffer, and it's a pointer to a char. What address is this char at? STUDENT 58: 0x0. JASON HIRSCHHORN: Right. That's what this is. In here, inside buffer, is stored 0x0. That's what we have-- the setup we have right now. So this line, fread, puts something from source where? Into this box or this box? Which box? Left box or right box? This right box. It follows the pointer, and puts it in here. When we try and touch memory at location 0, what do we get? A segmentation fault. That's the error we have right now. Yeah. STUDENT 59: Don't you have to put star buffer? Or no? For fread? JASON HIRSCHHORN: So fread takes a pointer. So it passes in buffer. And then it'll de-reference it somewhere inside fread. But again, we saw, it takes a pointer. We don't need to pass it star buffer. That would be passing it whatever's here. And that would probably give us an error because we're de-referencing it. Right? When we de-reference this pointer, when we try to access this location, we're getting an error-- our segmentation fault. So-- oops. We're going to quit out of gdb. Our line-- our problem-- is right here on this line. And it's a problem because of this line. How can we create a box that is accessible in fread. Right? We need to create a box that's one byte large, the size of a char. But we need that box to be accessible when this function executes. So where-- yeah. Any ideas? STUDENT 60: Just set it as any random character. Just do char buffer equals the character. And then, when you have buffer there-- JASON HIRSCHHORN: Wait. Char buffer? So no star? STUDENT 60: Yeah. Take out the star. Equals a random character. JASON HIRSCHHORN: OK. So give me one. STUDENT 60: Like a or something. And then when you have buffer there, you use a-- STUDENT 61: Star? Oh no, the ampersand. STUDENT 60: Use the ampersand. JASON HIRSCHHORN: OK. And what about in fwrite? STUDENT 60: Use the ampersand again. JASON HIRSCHHORN: All right. So your idea is, we create a char and put something in it, and then write to that char. STUDENT 60: Yeah. JASON HIRSCHHORN: What do people think? STUDENT 62: It's convoluted. JASON HIRSCHHORN: OK. Let's draw it out. So this time, I'm going to draw this in red on the stack here, and then we will have-- ooh! Sorry. So this time we have something called buffer, and it's on the stack. Correct? And we're saving in it a, initially. Then we have our call to fread. What fread does is it takes a byte from our file and puts it somewhere. It puts it in whatever the thing's pointing to. Well, before we had this address-- 0x0. Now what address do we have? STUDENT 63: Whatever address buffer is. JASON HIRSCHHORN: Whatever address buffer is. It's probably going to be something like that. Probably going to start with a b and an f, and then have six other hexadecimal digits. Doesn't matter. Some address. And we're passing that address in. And we're going to put our one byte thing at that address. So we're going to put our one byte thing inside here. And then we're going to write from what's ever inside here. Does anybody have any questions about that? Who thinks this code will work? Raise your hand if you think this code will work. You have to take a stance. And who thinks this code won't work? Raise your hand. Everybody else should be raising their hand. OK. Michael, where are you standing? MICHAEL: I can't decide. Kind of in the middle. JASON HIRSCHHORN: You're in the middle. Pick one. MICHAEL: I'll have faith and say it will work. JASON HIRSCHHORN: OK. You'll have faith and say it works? What happened? [INTERPOSING VOICES] JASON HIRSCHHORN: No seg fault. How can we check to see if two things are equal? Two files are equal. STUDENT 64: Diff. JASON HIRSCHHORN: Diff. Diff checks for the differences between two files, and if it returns nothing, they're identical. And if we open up, we get our file. So that was the correct solution. Let's look back at it one more time. We actually didn't even need to initialize it. It would probably look a bit cleaner if you didn't put something random in there. The point being, you needed to create some space to store something from fread and take something out of fwrite. And that thing had to be either a local variable on the stack-- you could've malloc'd some space. So we actually could have written malloc here, and that would have worked. And then we would have been storing our things somewhere on the heap. But this is actually, probably, the most elegant solution. Just create some space on the stack for these things to go. I would have two other comments. If you were to take turn in this, and then get scored on this, my comments would be as follows. These 1's here, to me, look like magic numbers. This 1, in terms of fread, makes sense. That's the number of things to read or write. But this one right here should probably be something else. So what is one solution? STUDENT 65: Size of byte. JASON HIRSCHHORN: Like that? STUDENT 65: Size of char. JASON HIRSCHHORN: Size of char. Yeah, byte is not a type. So size of char works. We could have, at the top of our code, #defined that. Called something BYTE and it's really a char. Actually, an even better approach might have been this-- uint. Anybody know what that is? Sorry. I have it backwards. Wait, no. Which way does it go? Anybody know what that is? Yeah. STUDENT 67: Supposed to help standardize across systems things that have-- like unsigned integers that have 8 bytes? JASON HIRSCHHORN: That's exactly right. On different machines, the size of a char-- not usually a char. Chars are usually one byte. But the size of other data types are different sizes on a 32-bit machine versus a 64-bit machine. A uint8_t is always 8 bits-- always one byte. And I need to include that standard int header file. So now, this would have probably been the best way to write this code. So I get rid of the magic numbers. And I also have a more logical type for buffer. It is not simply a char, it is a byte, which is what we expect it to be. And up here, we've actually been a bit more robust. We're not calling it a char, which-- maybe, who knows-- could be a different size on different machines. We're actually saying this is exactly one byte, always, no matter what. And if we look here, we make cp. Uh-oh. What happened? STUDENT 68: It might be switched. JASON HIRSCHHORN: What? STUDENT 69: Is it? STUDENT 70: You didn't define it as a type. STUDENT 71: But it should be defined in standard. STUDENT 72: What's going on? STUDENT 73: Should define be all caps? JASON HIRSCHHORN: So it's not #define. Actually, in this case, I'm going to use typedef. Because we're using it as a type in one location. So in this case, we actually want to typedef like we're printing a new type byte, and it is, essentially, this. It's a bit different than #define. And now, our code works perfectly. So, again, #define takes something, replaces it everywhere with the other thing. It's just a macro-- shorthand to get rid of magic numbers. But in this case, because we're using it as a type-- right here-- in order for that to work, we need to typedef whatever byte is. And we're defining it right here. It's not a struct, it's actually just an unsigned integer. It's one byte long. This code will be available online, and you all should have it right now. So we have-- perfect-- 13 minutes left to go over problem set 5. I want to walk through copy.c together, and then we'll talk briefly about the other parts of the problem set. So let me pull up copy.c. And the cool thing is, we've actually already written a lot of this code. The code we wrote literally just came out of here when I was writing this on my own. But this is copy.c, forms the foundation for the first two parts of the problem set for whodunit.c, which you need to write, and resize.c. Recover.c, which is the third and final part of the problem set, is not based off of this file. You're going to need to write that file, we give you a template for that file, but it has nothing to do with copy.c. But because copy.c is the foundation for the first two parts, we're going to walk through it now, so you have a good sense of what it does. And the comments give some of it away. We've already written some of this. First, we're making sure we get three arguments. Next, we're remembering the file name. So we skipped this step when we coded our thing-- when our cp. But here, they're making it a bit cleaner. They're checking to make sure both files are good, in addition to opening them. We wrote all this code just now, so I'm not going to dwell on this code. Next is some stuff that's specific to the types of files we're using, which are bitmap files. Bitmap files have some metadata associated with them. So the first couple of bytes tell you about the file. They aren't the colors of the pixel in that image. They tell you about the file. And if you read through the problem set, you'll have much more information on what types of metadata structures are included with bitmaps. But that's why we have this first set of-- this code right here. We are reading the metadata-- two pieces of metadata-- the file header and the info header. And we are checking some parts of it to make sure it is a true bitmap file before continuing. And again, these are details we don't need to go into now. If you read through the problem set, you will understand these. Long story short, these are just saying, this is a bitmap file, and confirming that. Next, we're writing those to the out file. We see that here. We're writing to the out pointer. Next, we're determining padding. So again, as is particularity with a bitmap file, some lines include padding at the end. And if you read through the problem set, you'll learn more about padding. This is the formula to find padding. Important to remember-- when you change the size of a bitmap file, the padding changes. When you change the size of a file, the padding changes. It's never going to be greater than 3-- it'll be 0 through 3, inclusive. But when you change the size of something, the padding changes. If I only have one pixel in that row, I need three bytes of padding, because each row has to be multiples of four bytes long in a bitmap file. But if I double it, to go from one pixel to two pixel, each of which, let's say, is a byte, then I need two bytes of padding to make that equal to four. So when I change the size of something, I need to change the amount of padding I have. Does that make sense to everyone? Next, we iterate over each row, or through all the rows. And then we iterate through each column in each row. We're treating this bitmap like a grid, like we've treated the board in 15. Like we treated the bricks when we printed them on the screen. A grid of rows and columns. Then-- we saw this. We actually just coded this. We created some temporary storage. We read in there, and then we write it out. This is exactly what we just did. Next, because I said each line ends in some padding, we skip over that padding-- the old padding. And then we add it back. In this case, we're creating the same exact file. We're just copying it. So this line is kind of silly. We could literally just put the padding in. But if you change the size of the file, do you still want this line? So if we change the size of a file, do we still want to skip over the old padding? STUDENT 74: Yes. JASON HIRSCHHORN: So we do. Because this, again, deals with the source file. We don't care about the padding from the source file. We want to go to the next line. But we don't simply put back the old amount of padding. We need to put back the new amount of padding. So when we're changing the size of a file, we still want to skip over the padding in the old file-- what we're reading in from. But what we're writing to, we're going to need to put back some different number of padding that we've determined. Yeah. STUDENT 75: The order of those two lines doesn't matter, right? Because you're handling different files. JASON HIRSCHHORN: Exactly. The order of these two lines does not matter. We write this line. This is here for the file we're writing to. That's important, so we get the right amount of padding. This has to deal with the in file. We want to skip right over the padding. We don't want to read-- if we're reading a byte at a time, we don't care about those padding bytes. We want to move to the next line. Finally just like Lucy gave for us, we close the files and return 0. So this is copy.c. And we actually wrote-- we spent most of section writing this, essentially. You made this. So hopefully you have a good sense of what's going on in here. The big difference, honestly, is just this first part that deals with peculiarities of bitmap files. So I have as my next slide, what do we need to do? Well, let's think about whodunit. And for somebody who read through the problem set, what do we need to do in whodunit? Simply. Aleja. ALEJA: Can you take out the part of each pixel that denotes red. And then-- kind of? JASON HIRSCHHORN: OK. So take out the part of each pixel that denotes red. That's close, but not all of it. STUDENT 76: Well, there's different ways to do it. JASON HIRSCHHORN: OK. Give me one way. STUDENT 76: Take out all the red, and then emphasize the blue and green. JASON HIRSCHHORN: OK. So given both these ways-- it sounds like we give it a pixel, it has a red, blue, and green level. We want to change the relative levels of the red, blue, and green, depending on that pixel. Where in this code should we change the relative red, blue, and green levels of a given pixel. After we've read it-- before we write it? Give me the line number. MULTIPLE STUDENTS: 83. JASON HIRSCHHORN: 83. So right here. For whodunit, the code you need to write should all go right there. And that's the only code you need to write. Because, like we heard, all you need to do is change these relative blue, red, and green levels from each pixel. You've read it in, and now you're going to write it out. How do I get-- if I have this thing called triple, right here, and it's of type RGBTRIPLE-- well, if we looked in bmp.h, what is RGBTRIPLE? STUDENT 77: It's a struct. JASON HIRSCHHORN: RGBTRIPLE is a struct. We see that right down here. And so if I wanted to access, say, the red level of the struct, how do I access the red level of this struct? [CLASS MURMURS] STUDENT 78: RGBTRIPLE.rgbtred? JASON HIRSCHHORN: Is that correct? STUDENT 79 : It should be triple dot, instead of RGBTRIPLE dot? JASON HIRSCHHORN: Triple. Triple is the local variable, so here, there's no pointers here. So we just use the dot notation. This will give me the level of red. If I want to change it, I just set it equal to something different. So again, this line of code accesses this variable inside this struct, and we can set it to something new. So for whodunit, again, this is, in essence, what we need to do. Very simple. Just change some relative levels, and this is where that code goes. Resize, on the other hand, is a bit trickier. In fact, resize is probably the trickiest part of this problem set. We have three minutes to go over it. But again, we've already written most of this code, so we should be pretty familiar. What are some things we want to do in resize, if you've read over the problem set? If you give them to me, we can talk about them. What are some things we want to do? STUDENT 80 : Vertically-- so you have to horizontally resize it, but vertically resize it as well? JASON HIRSCHHORN: So if we're given a pixel, and we want to resize it by a factor of two, it now needs to be resized horizontally and resized vertically. Does that make sense? Yeah. So that's probably the biggest challenge. And we'll talk about that in a sec. Yeah. STUDENT 81: The way I thought of it was you needed print it out-- JASON HIRSCHHORN: Wait. Don't tell us what you did. We're going to talk in logic. STUDENT 81: OK. What was the question? JASON HIRSCHHORN: You just raised your hand. There was no question. Let me present it. Let me just discuss this briefly. So we have one pixel, we want to replicate it, both horizontally and vertically. So ideally what we do here is, we read in our pixel, we write it however many of times. But then we have our trick here, because then we want to skip to the next line and write it at the beginning of the next line. So if we want to replicate both horizontally and vertically, what is one good way to do that-- one good though to do that? So we don't need to constantly seek around our file to place the things. That question might not have made sense, but I think an answer to it will help. STUDENT 82: Create an array? JASON HIRSCHHORN: So let's think of each file as a row. Let's think in terms of rows. If we have our first row from our small picture, we can make that row into a large row from a large picture, and then replicate that row however many times it needs to be replicated, rather than going pixel by pixel, which gets confusing when dealing with files. Because if we had-- I'm running out of space. If this is our file, and we have that one pixel there, and we want to put it right there, we still have some things that need to go over there when we're writing and creating our new file-- our file that's twice as big. But it's really hard with file functions to skip around to new lines like that, and then go back here and put things in there. It's almost impossible to do something like that, if that makes sense. So if we think in terms of rows, we can take our row, and then put it-- replicate rows vertically. And that's how we deal with resizing vertically rather than horizontally. That was kind of quick, and a little confusing. Unfortunately our time is up. I will stand outside for those of you here who have questions about the problem set, including recover. So let's adjourn for now. And again, if you have any questions, we can chat outside.