 Okay, today we will start with separate compilation and writing make files and clean up to the extent we need for most of this course. After that, we will do a small module on iterators. So how do you create a configuration of variables and step through a space of their values systematically while maintaining state inside an iterator? Okay, we'll see three applications of that. But first, we'll talk about separate compilation. Suppose you are creating a library called mylib and you are going to provide three functions as part of that library. Perhaps you decide to separate out the three functions in three different source files. So fun1 is the source code for it appears in fun1.cpp and so on. Now mylib.hpp is what you declare as your contribution to the outside world. So mylib.hpp only has the function signatures but not the implementation or the body of the function. Mylib.hpp is used by your functions as well as by people who are going to use it later on. Each of your fun1.cpp, fun2.cpp and fun3.cpp hash includes mylib.hpp. And then it provides an implementation of fun1. This provides an implementation of fun2 and so on. When you compile these with the command g++ minus c whatever dot cpp, that turns this file into an object file. This object file is binary code but they don't have a main method defined and therefore they cannot be run by the system directly. They are just compiled binary code for each of the provided functions. And those are the .o files. .o extension is standard. You cannot name it to something else generally unless you take great pains. So there is no point doing it. Similarly the input is either cpp or cc. That's standardized. Now if your library has only one .o file then you may not bother to do anything more with it. You just give users the .o file and the .hpp file. But most often what happens is for large non-trivial libraries you end up with a lot of .o files. Then you use a command called the archiver or ar. C is create this file if it doesn't exist and r is replace, meaning if the file, if the archive file already existed but you give more arguments .o files, the archive will replace the internal file with the given file, sort of like the zip option. And then you give all these .o files and those will be packed into the archive. So effectively mylib.a is a file which packs all the .o files one after the other and it also adds a table of contents. So the table of contents is a small table somewhere in the file mylib.a which specifies what functions are provided with their binary signatures. And those binary signatures have to be compatible with whatever HPP you are also giving to end users of your library. If they get out of sync then you have problem in linking later on. So that's as far as you go in creating the library. So eventually the product of your work is this file mylib.a and mylib.hpp. When a customer comes and uses your library they write something like a main.cpp which will also hash include mylib.hpp. And once that is compiled to main.o, main.o and mylib.a have to be linked together. This is the linking step that can also be done by g++ with the suitable flags. So the two g++ or the three g++ flags which are important are minus i, minus l and minus l. So in this particular case, suppose main.cpp is in some directory and mylib.hpp has been unpacked after buying the software in some other directory, then g++ has to be told where to look for mylib.hpp. You could do it in two ways. Either the hash include statement in main.cpp has to specify the complete path. That is not a very portable option because if you take your code to a different machine the path might change. So instead what is more common to do is that the hash include statement only has mylib.hpp but the path where mylib.hpp should be found is specified here in the dash i directive. And in particular we shall see how this path is actually injected by the make file. So it appears only once in a make file or maybe even in what's called an environment variable in your shell which we will get to a little later. Now the second set of options is this. Now depending on how your library is installed you may or may not need to use the dash l flags. In case your mylib.a lives in some directory that you own and you can easily get into the library. The easiest thing to do is to say this linking step is just g++ main.o and mylib.a. That will work. g++ will understand that this is one object file but this is an archive of multiple object files. It will try to compile, try to look into main.o. Main.o will generally have what are called unresolved references. Main.o will use functions from mylib which it doesn't define. And then g++ will look at every such function which is required by main.o but not defined there and it will look up that name with its function signature in mylib.a. That's why the table of contents is provided. If it finds a matching function provided by mylib.a it will link between those two codes. What's linking? Sometime back we saw how function calls are made. The code for main may start at address thousand. The code for abs may start at 2,000. Mylib.a records the fact that the function for abs start at 2,000. Linking involves fixing those addresses so that when a call to abs is made from main.o to a function in mylib.a the addresses are correctly patched up in the resulting executable. That is main.exe. So if you don't give any argument to g++, a.out will be saved. There's nothing special in the name a.out. You can name it anything. In particular in the Windows world executables finish with .exe extension and you might want to do the same. So that's the whole story of separate compilation. Now how about makefiles? So the problem with this scheme is yes. Yes? Yes? Yes. Then the namespace are from this account. Yeah. So in this we have to be a signal name space or something. Yes, absolutely. So your .hpp file and your source cpp files will generally use namespaces and that namespace information will be packaged in the .a file as well automatically. The namespace information will be retained so that when you make outcalls from main.o into the library it will go by the whole namespace, the whole path. So another point is that this .a file and the final link step is efficient in the sense that if mylib.a provided thousands of functions but your main.o only use three, only those will be copied from mylib.a into main.exe. You will not copy everything that is provided by mylib.a, you will only copy things that main.o requires for its running. So the problem with this setup, it's efficient because you compile each source only once, you package it up with a table of contents which can be efficiently looked up, et cetera, et cetera. But the problem is in maintaining all this. So if there are hundreds of files in the library and I have here shown a very flat organization, it may well happen that Fun2 actually uses Fun1. There could be a complicated dependency structure between the files themselves. We write something like how to find an LU factorization that is used by Gaussian elimination code which is used by an inverting code as we have seen earlier. If you have that sort of dependencies within the library itself and there are hundreds of files, if one file changes, who is going to keep track of how to issue the right bash commands so that mylib.a is updated to the correct most fresh library file. So that is where make comes in. In make, you declare these dependencies as you start writing the source code. It goes lockstep with writing the source code itself. You understand which function depends on which function, and therefore which file depends on which file. You enter that into a make file. So just like in case of a C++ program, main is a standard defined name. It's the entry point of control when you start your program. Similarly if you run make from the command line, it looks for a file called either make file with a capital M or a small m. Inside make file, there is a sequence of rules. The order in which the rules are written doesn't matter. We will see what actually matters. But every rule has this form of a target colon dependency list. So if you figure out that fun2.o depends on fun2.cpp and mylib.hpp, you will say fun2.o colon fun2.cpp and mylib.hpp. The left-hand side depends on each item on the right-hand side. The right-hand side is a space-separated list of targets. Now after a rule, you write down an action which is one or more bash commands which will refresh the left-hand side based on whatever changed on the right-hand side. So here is an example. I have written gaussian.cpp to print matrices either in the intermediate stages or at the end, it uses a matrix printer library which exports a matrix printer.hpp and a matrix printer.o which in turn is compiled from matrix printer.cpp. So this half of the picture belongs to the library writer. The left half belongs to the client or the customer of the library. So the makefile takes a formal specification of dependencies. And in case anything changes, suppose originally everything was compiled to be fresh and then I went and changed matrix printer.hpp. Maybe I forgot to declare a const for the input matrix and I decided that I should add a const. Then that affects both my implementation matrix printer.o as well as the customer's code. So both of them have to be compiled and then finally linked into the executable file. Whereas if the interface remained the same and I only changed matrix printer.cpp to say do column alignment or some other better implementation of printing, then the only thing I need to recompile is matrix printer.cpp into matrix printer.o, but once matrix printer.o changes you have to trigger a recompilation and relinking of the final exe file. So roughly speaking, no matter how you specify the rules and actions in your makefile, make will go and compose a graph like this. Then from the bottom upward it will check every edge to see if the lower node has a timestamp which is newer than the upper node. If that is the case then make will redone the rule to construct the upper node. This in turn may make other edges invalid. So it will move upward in a wave front and fix every edge that seems to be out of date until the root node is fixed and generated fresh. So this is what makefile looks like. So in this directory, just like main is the standard input control point of your program, the standard final target of a makefile is called all. When you invoke make from bash without an argument, you implicitly mean make all. Make the target all fresh. So make reads this and say okay, to make all fresh, what do I have to ensure? I have to ensure that vectoruser.exe is fresh. What does vectoruser.exe depend on? It depends on vectoruser.o and libvp. This is the library of vector printers.a. Now this cryptic line basically says, run g++ in a linker mode with a dash o flag, $at corresponds to the left hand side. So I don't have to type it again. If you want, you can always type vectoruser.exe, it doesn't make a difference. $at is equivalent synonymous to the left hand side. It's just a shorthand, you need not use it. And $at is the list on the right hand side. Again, it's a shorthand, it's convenience for typing and avoiding mistakes. So instead you could just say compile and link into vectoruser.exe, the files vectoruser.o and libvp.a. Now how is libvp.a created? Well, in my case, I don't have fun1, fun2, and fun3. I only have one object file which prints a vector, vector printer.o. And I do this archive creation. If the archive already exists, then I replace the file vector printer.o in it by the given file. So this line is just a standard command as you would print on bash. And that could be more than one line. Vectoruser.o in turn is created from cpp and vectorprinter.hpp. Any one of them changes, you have to redo vectoruser.o. How do you do that? You do g++-c, vectoruser.cpp. Similarly, vectorprinter depends on the cpp and hpp files and is recompiled. What is this? You can define any arbitrary variable, like include directory, like I was saying there, dash i, okay, as a string. And then you can reuse it with a dollar curly in dirt any place you need it. This makes it convenient to specify include directories for downloaded packages, define it only once at the top, and then reuse it in any compiling or linking command throughout the file. In this case, home foo has nothing, it doesn't matter. An include directory is not checked for existence. If it doesn't exist, it's silently ignored. So, and finally, there's this other rule called clean. So the default rule is all. But when you invoke make, you can really put any target on the make line. You can say make, you can say make all, which is the same thing. Or you can say make vectoruser.exe. You can give the target explicitly also. If you don't give a target, then the default target is assumed to be all. Now, I can always define a target called clean, which depends on nothing. So if the right hand side is empty, it says clean doesn't depend on anything. What does it do? So it always, you can say that it has to always done, provided those things are available. So it removes vectoruser.exe, vp.a, vectoruser.o, and vectorprinter.o. So all the files that were created by the compiler and the linker, you're cleaning up, so that you can start from scratch. Now, observe the minus here. The leading minus tells RM that if the file does not exist, or there is some other error in removing that file, ignore the error and continue on to the next line. So because clean is supposed to be a robust, you know, carpetbombing operation, you want to remove everything that exists. Something that doesn't exist shouldn't disturb you, you should just go on and remove everything that is created as temporary files. So of course, be very careful in writing clean rules, because if you accidentally put in your .cpp files in the RM list, it will just go. So be careful about writing these clean rules. All right, so now, if we go in here, currently it has all the temporary files, lib, vp, etc. So I'll do a make clean. So it removes vectoruser.n all. There was no error because all the files existed. Now, suppose I say make, make will first pass that argument, but it doesn't matter. Vectoruser.cpp, then it compiles vectorprinter.cpp, then it creates the archive libvp.a out of vectorprinter.o only, but it could be fun1, fun2, fun3.o. And finally, it links into vectoruser.exe, vectoruser.o with libvp.a. Now, what does libvp.a look like? See that libvp.a has 2.8 kilobytes, approximately. Vectorprinter.o has about 2.5 kilobytes. So the only contents of libvp.a is vectorprinter.o and the table of contents. The table of contents has taken about 200 some bytes. Now, how does libvp.a look like? Of course, it will complain that it's a binary file. If you still insist on seeing it, that's what a .a file looks like. It's all kinds of binary machine instruction garbage, but you see some strings inlined in there, right? See, I declared a function called print, and so that somehow enabled in embedded in the table of contents, okay? And inside there is also the function signature of print. So it knows that print will take a vector as an input. If you start calling it from vectoruser with a different signature, this is the place where the signatures are compared. If the function input and output types don't match, you cannot pluck it out of libvp.a and use it. So all the input and output types are embedded in this binary file, and it knows what to do there, okay? All right, and it also knows that this file came from vectorprinter.o, okay? So the object file vectorprinter.o contributed this function called print, which takes a vector as an input. So if you want to find out the other gibberish meaning, then you have to consult the manuals for how .a files are organized, okay? But if you want a slightly more printable version of this, you can try to say strings libvp.a and see what you get. You get a lot of things. It's probably a better way to read it, okay? So it says it defines a function called print, which takes a vector, okay? It's provided by that. It was compiled by GCC, versions and so on and so on in the system. It has a symbol table, it has a string table, it has a data segment, it has a stack segment. So those are all the comments made. How much data segment does it need? How much stack segment does it need? So this is all the bookkeeping for passing parameters between the caller and the callee. All that is embedded here, okay? So how things are initialized, how things are destroyed, how the IO system is initialized. So all this is part of the binary file. So all this is fascinating in compiler design and runtime system design. Okay, so that's how you write a make file, okay? And as you've already seen, here we declared vector printer as print of that, okay? And you have this norm two, okay? Let me remove the norm two. No one is implementing it yet, okay? Now, one thing you observe is I've added this funny lines at the top and bottom. Suppose I take them away. So the header file only defines that. There's a function called print defined in the std namespace, okay? Now suppose I also define some kind of a void foo, just a function. Now, no one tells you that you cannot actually define the body of your function here either. Suppose I do that. So you're free to implement the function entirely in the header file. It's just a little inefficient to do that because your compiler will compile it every time you scan the header file. Now if I do that, and then I have vector user dot cpp, okay? Now let's do one small experiment. I included vector. Let's say by mistake I include vector the second time. I'm only including a definition. So including it the second time shouldn't make a difference. So I do that and sure enough, nothing changed. Everything, the build goes to fine. Now, suppose instead of that, I make the mistake with the header file I wrote. I include vector printer dot hpp to ice. So hash includes are not handled directly by the c++ compiler. Any line starting with the hash actually goes to what's called a cpreprocessor. The preprocessor says that, well, at this line you wanted to include this other file. I'm going to remove this line and in its place, I'm going to include the contents of that file. And that goes to the compiler. So it's one stage after another. The preprocessor only looks for hash lines and deals with that. It's a small language. And once the contents of the right hand side file is included in that position, then it's passed on to the actual compiler. So in other words, if you include vector printer dot hpp to ice, that whole file is literally included at that position twice over. And then that goes to the c++ compiler. So now let's try to make it. This time it says redefinition of void std foo. And that's because it looked at this file once. It found a definition of the function foo. It looked at it again. It found another definition of function foo. Just like here, if you define main again, the c++ compiler will be unhappy. You are not supposed to define main twice or anything, any function twice. So now what to do about it? The reason why this is not an artificial situation is that you could well have multiple inclusions in the following form. So you may easily have a file called h1.hpp, which includes h2.hpp. And then you might have u.cpp accidentally include both of them. So to avoid problems like these, that's why we generate that initial directive. So what the first line says, so you have to find some magic string which you're fairly sure doesn't appear anywhere else in your system. So I'll say if the following variable is not defined, typically people use the uppercase version of the file name, but not necessarily. You can put anything there. Only if that is not defined, should you process the rest of the file. And inside, you immediately define that same variable. It doesn't matter what that variable is defined to. All you're saying is that that variable is now known to the preprocessor. So this ensures that the first time around, vector printer is found undefined, it is defined and this content is inline into the code. The second time that I make a mistake and include it again, vector printer is found to be defined and the rest of this file is ignored. So writing header file, this is very standard in C and C++. So this time if I make it, it'll go through fine because the second inclusion is ignored because of that flag. So what did you see new today? We saw how, yeah. Because vector already has something like that inside. If you look at how vector is implemented, the vector can be found in user include C++ vector. There's a lot of boilerplate, there you go. You can define it to one if you want, it doesn't matter. So any standard header file will immediately define some weird string like this. Now this is not foolproof because that string may accidentally appear in some of the header file and thought interference between the two. But it's one device to avoid multiple inclusions. Yes. Why does it have to be dot A? So if there was a table or something. Yes. What does it have to be dot A? So I don't know the exact binary format, but as we saw, some things are already clear. It tells you which dot O file contributed to it and what, okay? And this is a complicated binary format which records the input arguments, output arguments, how to pass the parameters on the stack, all kinds of internal details. In fact, it even has data about the CPP file which created the dot O file, okay? This is to do debugging. So the information after this, this contains correspondence information between source C++ lines and dot O machine instruction so that when you are stepping through the code in a debugger as you'll soon do, it can build a correspondence between the object file and the source. So they can show you with a cursor where you are running in the source. That is, all this is automatically created. You don't have to think about it. Okay. So the new things we saw today are protection against multiple inclusion using hash defines, okay? Some details of make files, including defining string variables for reuse anywhere, how to define targets, left-hand side, right-hand side actions, how to declare additional targets for cleaning up the space, and how to ignore errors. So you would like to ignore errors in a cleanup operation. But of course, if you had multiple commands in any of the other rules, if there's an error, you would like to stop early so that you find the problem at the first step. So generally speaking, in G++ or AR, you don't want to put a leading minus. You want to fail quickly and fix the problem, okay? All right. So in this week's lab, we are taking it a little easy. You'll just practice writing make files, building a small library of your own with four .o files, and then using it from a simple client code, main.cpp. Okay. The next thing we will look at is this business of iterators. So what are iterators? So far, we have already seen one important class of iterators, and that is an index into an array auto vector. We have seen sorting, searching, merging, all kinds of operations where we were iterating over an array. So what is an iterator? In this case, the iterator is the integer ix. ix in this particular simple example goes from 0 to the size of the array, increasing by 1. And now inside the loop, you can access array ix as either left-hand side or right-hand side. You can write into it, read from it, and so on. But in general, there may not be such a simple notion of a single index. The space you are iterating over did not be a linear sequence of integers. For example, in later lectures, once we start looking at sparse mappings, like hash tables, we will see that you may want to iterate over the elements of a hash map, a mapping between, say, people's names and their age. Or already in the context of sparse arrays that we have seen, you may want to get an iterator which gives you pairs of dimensions and values. Or maybe you are designing something like a chess puzzle or something, and you want to iterate through all board positions of n non-attacking queens on an n-by-n chess board. Which is, of course, closely related to iterating through all permutations of n items or all permutation matrices of size n-by-n. So suppose I want to iterate through all permutation matrices of size n-by-n, which means that every row and every column is allowed to have exactly one one on it. That's like the queen. How do you iterate through all this systematically? And very often, these classes of iteration problems which walk through a configuration space systematically, you could write them very easily if only you could write a variable number of nested for loops. So here's an example. And we already saw code for that last time, somewhat hastily. So I want to count through all values of a number. Now, very early in the course, we started looking at binary counting. So suppose I have a four-bit binary number, starting at 0, 0, 0, 0. Now if I have to increment it, if I have to iterate to the next value, what do I do? I start looking from the least significant bit leftward, okay? I look for the rightmost zero, something that can be incremented, which in this case happens to be this one. And I increment it to 0, 0, 0, 1. It just turns out that the machine hardware knows how to do incrementing on integers. But inside, this is how it works. You look for the rightmost bit which can be incremented and then you increment it. Now if I say increment this one, then the rightmost bit that can be incremented is no longer this one, it's this one. Once I increment something, I have to reset everything that's to the right two zeros. So the algorithm is very simple. Scan from the right to the left, find the first incrementable bit, increment it and zero out everything to the right of it. That is the basic iteration step. So if my CPU had only bit manipulations in it and it didn't have an integer processing unit, then this is how I would need to implement increments. Now to generalize this, suppose I tell you that I want to look at all, say, two-digit, Redix three numbers. You want to iterate through that space. What is two-digit, Redix three numbers? So every digit can now be zero, one, or two. Just like in Redix 10, a digit can be zero through nine. So the first number will be zero, zero. Second number will be zero, one. Third number will be zero, two. At this point, I cannot increment this any further. So I need to roll over, carry over to the next, and reset that. And then we will have two-zero, two-one, two-two. So how many numbers will you have? We have Redix to the power num digits. So many configurations. Now the way I want to write the code is don't assume you're starting at this value. The way I want to write the code is you're given an arbitrary state and you want to walk to the next state. So once we know how to do that for complicated configuration spaces, we can write all kinds of non-trivial code. So to give you the sort of abstract view of an iterator, yes. So what's an iterator in an abstract term? You can initialize it. In particular, for the number case, we might initialize it to all zeros, but not necessarily. Given any state of the iterator, we can ask if there is a next state, or we are the last possible state and we have run out of states. If you're iterating through an array, then that test for next is whether you have gone past n or not, the size. If you're incrementing numbers stored in arbitrary Redix format, there is a next state if any of the digits is less than the maximum possible. That's the test for whether there is a next. If there is a next state, there has to be a method or a function which will move the iterator from the current to the next state. And finally, in some cases, you need to fetch and modify data which is associated with the current state of the iterator. For example, if you have an iterator over a vector, and you are pointing at a certain place in the vector, you may want to read or write or even delete that element, or insert an element at that position and so on. So those are operations you can do on the current state of the iterator. So any iterator in general provides these four facilities. So now, let's go back to our example of basically writing a Redix 3 odometer, if you will, mile meter on a card, where the least significant bit has to clock up quickly and then more slowly for the other guys. First of all, I have a print routine which was written slightly badly. So I want to print from the most significant digit down to the least significant digit. So that's a print routine which, okay? And so I start off with say number of digits equal to five and Redix 3. So I should get three to the power five or 243 different numbers. I stored those digits in a array called digits, which is initialized to N digits, all being zero, the first value. Now forever, what do I do? I say print digits, and then I have this code which calculates what to do next to go from one to the next. So I'll rewrite this in the following set, okay? If not, next number digits break. That's how your iteration code should look like. Initialize the iterator somewhere, then forever, print or process the current state of the iterator and then as long as there is a next state, go over there. So next number goes to the next state and if there is no next state, it returns false. If there was a next state that it successfully transitioned to, you return true. So now let's define next number. So next number has to return a Boolean. It modifies digits. So its input is vector int reference digits. Inside, this is the code. Did I successfully change the state or didn't I? I'll return that at the end. But inside, what am I doing here? So I initialize a did change Boolean variable to false. Now, I look for a change position from the least significant position of zero up to n digits minus one. If the digit at that position change was less than radix minus one, then I can bump it up, okay? Then I bump up that digit. I set did change to true because I've succeeded in iterating to the next state. And then for all load order positions, I set the digit to zero again. In case change is zero, then that inner for loop will not even execute. So this is perfectly general code. And if I manage to change something, I break immediately because I am only supposed to take one step, okay? So as a code, this is extremely simple. But this is a paradigm. This is not a particular algorithmic skill I'm trying to teach. This is a paradigm of programming where you understand the space of the state you are maintaining and you write genetic code so that given any configuration, you can update it to the next configuration or return false if you could. That's a pattern. You should recognize that pattern whenever your application demands it and then code it that way. So observe that the code is now become very simple. Okay, the code was, the problem was very simple to start with. So this doesn't make the point very strongly. Our next two examples will make the point much more strongly, okay? But the summary is that the main routine now looks very simple. Print the current configuration, move to the next configuration if possible, otherwise break, okay? So now if I run that, so N digits wasn't declared and radix was not declared, right? So I have to pass those things also. N digits is already known, radix is not known. That should work. So remember this is five digits on radix three. So start with zero, one, two, one, zero, one, one, one, two, two, zero, two, one, two, two, and then one, zero, zero, and so on. So it's a very simple piece of code. Hopefully not much debugging is required there. If you want to quickly check if the code looks right, you would count it, the output. You'll find that there are 243 lines as you would expect. So this is the number of lines, number of words, number of characters. So word count is a very simple utility. If instead I want, say, the number of digits is, say, 10, but the radix is two, I should get two to the power 10 values, or 1024 values. I'll just compile that again. And if I word count this time, I'll get 1024. So that's an example of a very simple iterator, which instead of iterating on one integer, iterates to a couple of integers, incrementing them in lexicographic or odometer order, in any radix. The next example we'll see is more non-trivial, which is, given an input n, print all the n factorials of n items. And we'll assume n is less than 27. We'll keep busy for a long time printing all permutations of 27 things. So we'll name the items to be permitted as the letters A, B, C, et cetera, to wherever we need to go. Now, one way to think about permutation is placing things on a line in an order. Suppose I have to place three things on a line. The first thing I place is A, and there is no sense of placing A in multiple ways on a line, if the only relative positions matter. So A can only be placed in one way on the line, and that way is numbered zero. And because A is the zero-eth element, the way in which it is placed is called way zero. Way zero can only take value zero. Now, once I've placed A, I can place B on the left of it, or I can place B on the right of it. So B is the one-eth element, and the way in which it is placed is called way one, which can be either zero or it can be one. So I represent that by a branching out, a decision branch, saying if B goes to the left of eight, we come down to the left subtree. Otherwise, you take the right branch, and B goes after A. That corresponds to zero for B and one for B. Okay. The third item I'm placing is C, which is the twelfth item, and its index is called way two, which can now take values zero, one, and two. Because two items have already been placed, three gaps have been created, and this is called gap zero, that's called gap one or gap two. And so way, basically way two takes one of those three values and decides where C goes. If way two is zero, then C goes to the very left. If way two is one, then C goes in the middle. If way two is two, it goes to the right. So now you see that the number of possibilities is exactly n factorial, because way zero can take one value, way one can take two values, et cetera, three values up to n. So the product of those is the number of leaves in the tree. So there's a one to one correspondence between n indexing variables, such that the ith indexing variable takes values between zero and i, up to n minus one, and the actual permutation. So if I gave you a path, any path, zero, one, one, there is a canonical representation where you can say, okay, for the sequence zero, one, one, I'll give you permutation ACB. It's not difficult to compute that permutation because, so the easiest way to do that is to walk from the bottom upward. What do I mean by that? Suppose I give you the sequence zero, one, one. So you say, well, I know there are three items. So let me make up three empty slots. And someone told me that after two items have been placed, C could have gone in any of those three positions and C chooses to go to number one. So let me place C there. Then I walk up to its parent and someone tells me that item B has been placed in position one again. But now, of course, you can't put it there. What it means is the one-th, non-empty position. So B has to go here. And naturally, I will come there. So it's not too difficult to write a piece of code which, given this sequence, walks backward on it and fills up the arrangement and prints it. So all I need now is to fake a bunch of nested loops which will vary way zero from zero to zero. Keep it constant. Will vary way one, zero, one, and so on. So if n is an input, then that's a problem because it's variable and I don't know how to write a variable number of formulas. Same story as before. So what I'd like to write is for way zero equal to zero up to zero. For way one equal to zero up to one and so on, convert the sequence way zero through way n minus one into a permutation, an arrangement of A2, whatever it is, and print it. So observe that way n minus one is the innermost loop and that changes fastest. Like the rightmost digit of an odometer. And then outer loops change more and more slowly. And that's exactly what's happening here. C is quickly flipping through the possible placement positions and then v changes one. So you're tracing through the leaves from left to right. So you're going through different paths in the tree. So it's clear how this works. So again, the problem is that we don't know how to do variable number of for loops, nested for loops depending on an input parameter. But by now it's clear how to handle this. We already saw that in case of the odometer we are storing this nested for loops in a vector of indexes. We'll do the same thing here. The only change is the rule of the game. Earlier you could increase a digit as long as it was less than radix minus one. This time you can only increase it up to the upper limit for every corresponding for loop. That's all, that's all that changes. So now, this is how I write the permutation code. Now that I wrote that silly iterating through numbers code, the pattern is exactly the same. You don't need to look at it right now but print perm is nothing but taking the way vector, the vector of the way zero to way n minus one and turning it into an arrangement of characters and printing them, that's all. It just takes the sequence and makes it out into a character sequence and prints the permutation. Now the important thing is what you do with next perm. So if you look at the sequence, if I am here, then I'm done. There is no way to increase it further because all indices are at their upper limits. If I'm anywhere else, then there must be at least one level where I can move right. Just like I looked at the units digit toward the left and tried to find the first digit I can increase, I'll again start from the bottom, I'll work upward, trying to find an index which I can push to the right. Now look at what happened in this transition. Suppose I was initially here at zero, zero, two. I can't increase two anymore but the zero can be increased. So when I make a transition from zero to one, I have to reset all lower indices to the zero value. So it's very, very similar. There's no difference from counting up except the upper limits are changing depending on the nesting level of the loop. So this is what we do. So next form is written as follows. So way has a size, so let n be that size. Again, I have a did change which is whether you successfully walked to the next path in the tree or you are already at the right most part. So did change is initialized to false. Find the largest index counter we can increment. Largest index is at the bottom. So for change equal to n minus one, change greater than equal to zero minus minus change. If way change is less than change, that's the important thing, right? At level two, you can go to two. At level one, you can only go up to one. So change is the level number. So if I can walk to a leaf to the right or some node to the right, then I increment way change just like I incremented zero to one, that horizontal arrow, that's what's happening. And I said did change to true, just like before. And then zero out everything with larger index. So zx equal to change plus one. So remember this two becomes zero because this zero become one. So that's what happens here. So from change plus one to n minus one, you set way zx equal to zero. Extremely similar to previous code. The only difference is this. That the only line which is different. It's not radix minus one. A fixed radix for all positions, but it's changed the level number in the tree. And that's it. I break as before, I return did change as before. And main doesn't change at all. So forever, I print the permutation. For clarity, I can also print the wave vector itself. I print the permutation. And if there is no next permutation, I break. Otherwise, I repeat them. And there are two arguments, there's one argument which is the number of items to permute. So let's say I give just two items. So what happens? Zero zero and zero one. The first guy can only be zero, remember? The second guy, we can go to the left of way or we can go to the right of it. The thing on the screen can be obtained by calling it on three. So zero, zero, zero corresponds to CBA. And then C is moving to the right. Then zero, one, zero. Again, C is moving to the right, nested in. So earlier it used to be BA, then it's AB. The final one is ABC. So once again, you can quickly, at least no, circumstantially verify if you wrote correct code by giving an input and doing a word count. So you say six permutations of three things. So you see the number of permutations very quickly. And chances are your code is okay if you are getting the right count here. So what have we learned so far? That the configuration space here is a little more complicated. It's not a Cartesian product of different counters. The right-hand boundaries are different based on the index of the counter. But you can handle that. You can turn that into a permutation. You get n factorial configurations. The third example we will look at is more irregular. In the sense that there is no clean structure like one times, two times, three times, up to n factorial and so on. And this problem has to do with that semi-magic square. So what is a semi-magic square? It contains non-negative integers. Each row and column adds up to the same positive integer C. So in particular, a permutation matrix is a semi-magic square having C equal to one. So if every row and every column adds up to exactly one, that's a permutation matrix. Now it's possible to prove, although the proof is not entirely trivial, that you can always subtract a permutation matrix from a semi-magic square. If you do that, then the total of C will uniformly decrease to a total of C minus one. And by induction, unless C minus one has become zero, we can keep doing this. So given any semi-magic square, you can keep on subtracting a permutation matrix from it until you zero out the whole matrix. The only problem is that you cannot do this greedily in the sense that, given the original semi-magic square, you cannot say that for every row, I will greedily choose the column where I place the one for the permutation matrix. What do I mean by that? So I have a small example here. So I'll just write this down on a piece of paper that will be easier for us. So here is an example of a semi-magic square. So that's a semi-magic square. Every row and column adds up to five. And suppose you say that, well I'm going to hunt for a permutation to subtract out of it, so that all rows and columns are up to four now. So a tempting algorithm would be that I run down the rows. In every row, I look for the first non-zero. So here I find the first non-zero in the very first column. So I choose the first column to be one. And then I say that, and I freeze the first row. I say I'm decided on row one. In the second row, I find the first one in the second column. So I pick that, I freeze that decision. Similarly, in the third row, I find the first non-zero here, so I freeze that. Now, because this guy has a first non-zero in the second column, which is already covered, I can't do that. So I have to pick the first uncovered non-zero column. So I pick one there. In this particular matrix, this might succeed in extracting a permutation out of the original matrix, but it's not difficult to create an example. I leave that as an exercise to you. Such that if you follow this greedy policy, you will actually paint yourself into a corner. You will actually find that everything that can be picked in the last row is already covered. And you should have taken a non-greedy approach. So in this, you have a choice. You could either have taken this or you have taken that. And by greedily taking the left one, you have somehow cut off a solution in the last stage. So you could easily create examples like this. So some of these, you cannot pick the column greedily. Now, I can always be inefficient and walk through the possibilities like this. The matrix can be somewhat largest. There may be a lot of zeros between two elements. So I want to come up with a sparse representation of the matrix where I have squeezed out all the zeros. So I'll create a new data structure called non-zero columns out of the original matrix called magic where NZ calls only records the rows of, sorry, the columns where non-zeros appear and what their values were. There are a bunch of possible representations, but here's one. So I'll say that row zero has a non-zero only in column zero. Row one has non-zero in columns one and two and so on. This is zero, one, two, three. So suppose I start with this. So I record where the non-zero columns are, fine. And now I have this another vector called column taken. Column taken corresponds to our nested for loop indices. So initially column taken will be all zeros. We'll be a little silly about this. So it turns out there are far better algorithms to find combination matrices out of magic squares, but we'll do it along the lines of our iteration scheme. And then, so it's like this variable says where I am in this array. This variable tells me where I am in that array. So column taken at this position can go between zero and zero. Column taken in that position can go between zero and one, zero to zero, zero to two, zero to two. So this is the configuration space. Column taken is a vector of indices where each index can go between that lower and upper value. And now it shouldn't be any problem to rewrite my iterator so that column taken passes through all legal values in this NZ calls situation, fine. So think of writing nested loops involving these five variables and clocking each of them up according to their individual lower and upper limits, fine. Now if I give you NZ calls and column taken together, it's also very easy to figure out if this is actually a solution. Why? Because I'm going to run through the rows. I'm going to detect which column you took. I'm going to mark that column with a bit or a count. What should happen is you should hit every column exactly once. If not, it's not a solution. If yes, that's the solution. We look for the first solution. We subtract out that permutation matrix. Once you get a permutation matrix solution, you can always go greedy. You can always subtract that out. You'll still end up with a semi-magic square with total being one less. And by definition, you can keep continuing that. So let's look at how that code looks like. So I'll start off main and we'll do a top-down design so you don't need to worry too much about what various functions are doing. So main starts with reading in the matrix called magic. Now magic has to be squared. Check for that. So NZ calls. Let's go back to this data structure. So now we have to fill in this data structure called NZ calls. So what is NZ calls? It maps from a row to a vector of variable size. So this is really a vector of vectors where the inside vector can be of arbitrary sizes. It cannot be empty because to be a semi-magic square to start with, every row has to have some non-zero element. So that is how you declare the NZ calls variable because both boost and C++ standard template library defined vector, you have to disambiguate and say which one you want. So because I've included both those namespaces and they both define vector. So when you declare things, you have to now tell the compiler which vector you want. So we want STD vector set. So an STD vector of, STD vector of ints. That is NZ calls. So C++'s scanner is in dire straits. So you have to separate this by a blank. Otherwise C++ will confuse it with the input output operator on C in and C out. Okay, we'll get all confused. So it's a duct tape solution, what cell type solution, what can you do? Fine. So you declare NZ calls and you record the non-zero columns from magic into NZ calls. We'll see how to do that. This is top-down design, let's not worry about it. It's easy to do. And then there's this state which is the columns taken. That's just a vector of ints. Now I resize it to the proper size, all zeros to start with. And now the basic loop remains absolutely similar to what we have been doing. Forever, if NZ calls and columns taken is a solution, namely defines a permutation matrix, then print that permutation. Otherwise, if there is an X configuration, then just repeat the loop. If there isn't an X configuration on NZ calls and calls taken, then break. So in this case, I'm just cycling through and printing all possible permutation matrices that can be subtracted out of magic, but I'm not actually doing the subtraction. So feel free to copy the code and then add code here to actually subtract out the permutation matrix and re-compute NZ calls to continue with the process. But it's very close now. So now let's look at the implementation of... So record non-zero columns is very easy. You just run row-wise and find where the non-zeros are and then stash it into NZ calls. So is solution, print permutation, and next configuration. So we'll look at is solution and next configuration. So what is is solution? Is solution declares a vector of integers which is the number of hits on a column. It resizes it to whatever the side of the matrix is. And then for each row, I look for the calls taken. Which column am I referring to? And then in NZ calls, I look at RXCX and I bump up the column, hit for that column. Because it's going a little fast, but it's not too difficult to see why. Going back to the picture will help. So suppose call taken at index one is at one. Then I... Because it's one, I go here, so that's column two. So column two is now hit. So you do call hit two plus plus. That's what's going on in this code. It's not too fundamental to the current discussion, so I'm not going to spend too much time on it. So find out the number of times each column has been hit and if any column has not been hit exactly one time, then it's false. If all columns have been hit exactly once, then this is the solution. That's all. The important thing is how to go to the next configuration. And it's absolutely similar to whatever was happening before, except that there is no simple formula for the upper limit. The upper limit for each index comes out of the NZ calls data structure. Fine? So I look for a row starting from the lowest, size minus one down to zero. If calls taken is less than NZ calls dot size, rx dot size. So I do have to flip back and forth between two screens, but I didn't have time to work out on the board. So this basically says NZ calls one dot size is equal to two. NZ calls three dot size is equal to three. So my call taken rx should be between zero and less than NZ calls rx dot size. So I check for that condition. If calls taken rx can be increased, then I actually increase that call taken and I return true. And if I have increased that call taken, all lower rows are reset to zero, exactly in pattern with the two earlier examples we have done. And the rest is absolutely identical. I go over to the next configuration if I succeeded. If no call taken can be increased any further, I return false. So if I run this and on that specific matrix, so it prints four ways in which you can subtract a permutation matrix from the original matrix. So remember, I'm printing all possible permutation matrix configurations, which can be subtracted. If it's solution, print the permutation. So let me translate this into a more readable format. So that was the original matrix. In some sense, the values don't matter anymore. All you need to know is that it's at least one. So it's just the non-preso positions which matter. The first permutation matrix that's being printed is zero, zero. Let me make up a grid. The first solution printed is zero, zero. One, two, three, three, two, four, four. So clearly that is a solution. Any dot here has to correspond to a non-zero here. And there has to be exactly one dot in every row and column. The second solution my program is outputting is zero, zero. Zero, zero is a constant in all of them, has to be. One, one, two, three, like before. But now I have three, four, and I have four, two. And you can verify that dots are only where non-zeros are. And again, I have exactly one dot in every column and row. And so on. There are four solutions. Four solutions will come out of the iterator. So that's an example. So some of the problems which appear to be somewhat difficult in earlier labs will, of course, adjust for the hardness. That's a different story. But the important thing is that they are hard because of lack of structure of thinking about the problem. So once you have this part of managing configurations and walking between them systematically, using whatever state you want, then things become easier to code, in many cases. Now C++ itself defines a whole family of iterators. This is not readable from a distance, but you can go look at the slide offline. There are forward iterators. There are bidirectional iterators. There are random access iterators. On various iterations, you can do various operations like plus, plus, minus, minus, comparison with things, accessing them. So we'll just look at one small example, which is how to iterate over vectors. So there's still time left. Give me another five minutes. So suppose we want to print all elements of a vector using an iterator. How do you do that? There's a vector of ints called vec, suitably filled. Now the for loop, instead of using ints, uses a different type of object. What is it? On vector of int, you have something defined called an iterator. And that's called vx. vx is an iterator over vec. You initialize it to vec.begin. So vec.begin is a special function or method defined on any vector, which takes you to the first position. First position actually exists in a non-empty vector. You can also compare vx against vec.end. vec.end is a special, meaningless iterator value on the vector. Now you shouldn't ask what vx exactly is. So it's almost like there's this artist who used to call himself Prince, but at one point he decided that he didn't want a name anymore. So if you asked him his name, he just gave a strange symbol on a piece of paper. That symbol wasn't reproducible by newspaper and journalists, so they started calling him the artist formerly known as Prince. So similarly, you shouldn't ask what exactly is an iterator. You can't print it. It's like the Brumman. You can't describe. You can only talk about the Brumman negatively. But you can do various things with vx. You can compare it against the end to detect if you're finished. You can increment it. So plus plus has been overloaded to means advance the iterator. Just like in our three examples, we're advancing the iterator to the next step. That's packaged into a function called plus plus. Yes? In this case, it's like a cursor. But in case it's not a vector or something else, then it doesn't need to be a cursor. We'll see examples of that when we discuss collection objects in C++. So this is very simple. And you access the contents of vx using star vx. The only interesting point is that you can manipulate the contents at vx and maybe delete the element entirely. So let me just quickly show you the last example. So I create a vector. I push back a bunch of values, 0 to 4, to be precise. And then I create an iterator, vec.begin, et cetera. I can try to print vx. And that results in g++ being very unhappy. You're not supposed to print an iterator, just like you shouldn't take pictures of certain people. So you don't have operator less than, less than defined for an iterator. So let's delete this statement. But you can print the contents of the vector. Also, if the contents of the vector is 2, say, you can choose to erase it from the vector. Now, so one thing you'll observe is that I need to say vec.erase vx. Why do I need to do that? This is aesthetically displeasing because I've constructed vx out of vec. vec should know that it's tied to vec. Why do I need to pass vec again? Ideally, I should say vx.erase. And Java does that. Java has the correct view on life. But c++ being an old thing, it has to support some legacy reasons why you have to call it on vec. Now, after that, we'll say erase start vx, but we'll observe something funny. So let's now compile this code and run it. If I do that, it says 0, 1, 2. It prints the element. Then the element is 2. It erases it. But once you erase it, the iterator is immediately positioned on the next one. So you print a wrong message. It actually erases 2. It didn't erase 3. In the resulting array, if I print that out again, so from scratch in the next loop. And I only print it out like that. You see that the 2 is gone, not the 3. So what happened is, as soon as you call erase, vx is repositioned to the next element. So this is pretty handy in coding things like that needle and hashtag examples. So needle and hashtag can be decoded to now use iterators instead of indices. So I'll leave you to code that up yourself. We can go over it in the next lecture. But in the next lecture, what we'll do is we'll start with recursion and recursive function.