 Let's get started. Couple of announcements. First of all, some people have asked what the course home page is. That is the course home page that has a link to the Moodle site where we will be posting discussions, homework, slabs and such dynamic data. Static content like slides, other resources, notes will be all there. Second announcement is that on Moodle you will find a new posting giving you links to required reading. So, there are some pages in Wikipedia about binary numbers, how to manipulate them, as well as new pointers to Professor Ranade's upcoming textbook. To keep some sync with the lectures every week now, we will announce some chapters or sections to read from Professor Ranade's book and you should complete reading that before you go to the tutorial. If you have problems understanding some parts of the book or my previous lectures, hopefully one of them may help solve the other and if in spite of reading the chapters, you cannot resolve your problems then take those questions to the TAs in the tutorial section. There are also some feedback that not everyone is clear with how the system works and how your code is executed. So, I thought I will spend 5 to 10 minutes cleaning up that part and then we will proceed and continue with the loops. So, this was the first complete source code we saw. We do this thing called include IoStream and we will see what it does in a couple of slides in pictures. Then you access things inside the IoStream library by using a certain namespace that we have already covered. We basically said that things like see in and see out, you can compare them to people living in hostels. In particular, the hostel in this case with the namespace STD or the standard library and either you have to refer to them by the full name giving the hostel number or you have to say using hostel 5 and then whoever you refer to is by default in hostel 5 and then you write this main routine and now you are allowed to use see in and see out which are differences to facilities inside the IoStream library. Now, you can save this to a file which you can arbitrarily call cf.cc centigrade to fire and height whatever name you choose. Typically, the compiler will ask that you end the file name with either .cc or .cpp c++ by convention. This is a text file you submit that to the g++ compiler g++ cf.cc outputs a file called a.out. This is another file you cannot read that file, it is an executable file. So, cf.cc and a.outer files, a file is just a sequence of bytes until you try to use it it does not matter what that sequence of bytes is. This sequence of bytes is interpreted and used differently depending on who is reading or writing that file. cf.cc is a text file to be written by a programmer and then to be read by the c++ compiler. a.out is an executable file to be run from the shell command line and you can rename this file as you wish. This is the new picture let me explain this carefully part by part. The hardware assets of the computer are shown in the lower most layer the CPU, RAM, disk, keyboard and display may be a mouse as well. These hardware resources are managed by the operating system which may be Windows, Linux, Mac OS etc. On top of the operating system there will be two things running. One is the bash shell by which you interact with the hardware and make programs run and the other is the c or c++ or c and c++ execution environment which actually comes into being when bash invokes your main function in a.out. The main function you wrote in the source code is packaged up as a function and placed in a.out in suitable binary format and when you type .slash a.out in bash what it does is to transfer control to the main function inside a.out. Now this compiled code runs with the help of the c and c++ execution environment. The execution environment supports certain things in your code in what is called native mode. So it understands what a character is, it understands what a short is, it understands what an inflow double is and it can represent those in RAM and if you have written statements using if switch or while as we have seen those are executed directly using the c++ execution environment. But you may be using more complex things like finding the square root of a number or using the keyboard or display to read or write values to the screen or you may be doing string manipulations those are not directly supported inside the runtime environment. The runtime environment does low level activity controlling statement operations, transferring control in a while loop and so on. Whereas more complicated functions are implemented inside these libraries. The libraries come in two parts. One is pre-compiled code which is embedded in the c++ execution environment. Just like you compile your code into a executable, other people wrote these complicated libraries and they are compiled into library files. But to be able to use them the pre-compiled libraries also provide some header files. The header files basically just list what all facilities are offered in each of the libraries. When your code includes these header files it basically gets to know the names of all the variables and methods or functions that are defined in the respective libraries. That is the hash include statement. So if you did not do that hash include the g++ compiler would see seen or see out or square root or so on log and you could not figure out what you mean because you did not say which library you have to draw it from. Once you do the includes the g++ compiler will know. So it does what is called a linking process. So it links in whatever you need from those libraries into your code. And your code itself does not include that code as such. It just has a reference to that code. Those libraries are running are available inside the execution environment and they are executed from your code whenever needed. That is the entire model of execution. So the reason why the libraries are split up into two parts is because the header the list of facilities provided is usually much shorter than the whole implementation. The implementation is much longer. And the system is organized so that multiple people running c++ programs can share one copy of the code in those libraries kept in the system. That is the reason why these things are separated out. Any questions on this? So you should be clear on at least your view of how to compile a code, how is it running, why is main important and how upon invoking various things from inside main you are utilizing facilities provided by c++ libraries. After that we will resume with the matter of while loops. So the last example we saw was how to raise a floating point number say double base to a non-negative integer power. And the first solution we saw was quite simple. You initialize a variable called to do to the power you have today's. And then you keep on decrementing to do and you multiply answer by base each time. So this results in a number of multiplications which is exactly equal to the power. And then we saw that there is a more efficient version which uses the so-called squaring method. And the insight from of for this came from the binary representation of the power. So the power variable is can be expressed to the sum of powers of 2. And in the specific case where power is already a power of 2 like 32 we saw that there is no need to multiply base together 31 times spending 31 multiplications. Instead we can do repeated square. We can first take base and square it to get base squared. Then without using base at all we can get base to the power 4 and so on. And within 5 multiplications we can get to base to the power 32. So instead of approximately 32 or 32 minus 1 multiplications we can use just log 32 to the base 2 namely 5 multiplications. And so this is far more efficient. Now once we can deal with a power value which is a power of 2 we can also deal with power value which are not powers of 2. Suppose I get the value 29 I can always express it as a sum of powers of 2 that is its binary representation. And then I will compute the powers of base which are powers of 2 as I go through a loop and I will accumulate or multiply in suitable parts of those. So as I was saying let the bit position bx take values from 0 through 7 let us say power is a byte which is 29. 29 in binary looks like 1 plus 4 plus 8 plus 16. So what I will do is I will keep squaring base as before and as bx sweeps from right to left if the bxth bit of the power is 1 then I will include the power of base in the product otherwise I would not. So for example if power is equal to 29 it is clear that base to the power 29 is the same as base to the power 1 times base to the power 4 times base to the power 8 times base to the power 16. Now we already know how to get the particular bit in a number by using the logical end and we will basically be using that trick. So this is the faster code I initialize to do to power again this time I have an answer which is 1 just like before but I also have a term which will start with base to the power 1 but then it will be squared every step I will square term. So it will be base squared base to the power 4 base to the power 8 and so on. So while to do is greater than 1 if the least significant bit of to do is 1 then I will product term into answer otherwise I would not but in any case I will square term and I will shift to do by 1. So instead of saying I am looking at bits 1 by 1 I am scanning left and equivalent action is to shift this number right and look at the right most bit. So instead of having a bit pointer and then you know moving that left and reading what bit there is I can get the equivalent effect by taking this original number looking at the least significant bit and keep shifting it right until zeros are injected from the left and the number totally becomes 0 and that is why I declared it as unsigned so that the bits injected from the left will always be zeros otherwise you know we might get in some trouble. So this will also calculate the power of base and in this case the loop invariant may not be exactly the same but it may also be the same. So as a homework you can just check out what the loop invariant is going to be. Remember last time in the slow code the loop invariant was answer times base to the power to do was base to the power power after every loop iteration. So this time see what it is going to be. Now what if power is a real number instead of an integer how are you going to deal with that? So think about that offline. We can always take the integer part of it separately and do it this way and then you get a fraction and then you have to do something else with that part. So we ran this code and we saw that although we did not measure the time taken by it the faster code should be faster because it has far fewer multiplications but at the end of it we saw that the values were slightly different. So when printed the slow method and the fast method output what look like the exact same double number but if you compare them as saying if fast value is equal to slow value the answer was no they are not equal and we saw that the reason was limitations in machine accuracy because of the finite number of bits used to represent numbers raise them to powers and multiply them it turned out that the slow and the fast value differed in the last two bits of the mantissa. And I use some tricks to print it out that way you do not need to understand the tricks the point is a double precision number is 8 bytes long so that is you know however many bits I found a method to print out all the bits of the double precision number and then we found that the mantissa differed in very small significance bits but because the test for equality is an exact test for bitwise equality that did not work out so that is another lesson for if you are doing numerical algorithms and you expect equality between floating point numbers you may sometimes need to do the test with a epsilon tolerance threshold. So you have to say you know the absolute value of one thing minus the other should be less than some small amount instead of looking for exact bitwise equality. So the next example will look for is the factorial example. So suppose I want to calculate the factorial of number n I initialize a long long int fact to 1 and I read in n and then initially do not read the point 5 so say while n is greater than 0 and this is one of the slightly annoying but nifty was to code in C++ you say factorial is multiplied by n but after that I decrement it within the loop and then you test again whether n has become 0 or not. So until the time n goes down to 1 you keep multiplying by n. So this avoids the need to declare some other intermediate variable like i and clocking it up from i equal to 1 to n you can also do it that way there will be no difference. Now this can easily overflow even for moderate n even if you use double so I crossed out the long long int and put double in the hope that I can find factorials of largest numbers but let us see how far that will take us. So remember the largest double is approximately 10 to the power 308. So n factorial is roughly equal to 10 to the power 3.8 if n log n is roughly log of 10 to the power 308. So n factorial is log of n factorial is roughly n log n roughly speaking. So n log n is roughly 709 or n is roughly 709 divided by log of 709 which is just 108. So after calculating the factorial of 108 double cannot hold it anymore. So if you want to compute factorials of even larger numbers then neither long long int nor double will do the trick and you need to use some libraries for what are called infinite precision integers which actually use an arbitrary number of bytes in RAM to pack larger and larger integers. So another example let us try to approximate the logarithm of a number to the base 10. So one definition of the logarithm crudely is how many times must we divide a number x by 10 until the result goes below 1 that is the integer part of some logarithm is plus minus 1. Now the basic structure is the same you read in the floating point number x and then you count the number of divisions you have done. So you say int num divs equal to 0 and then you say while x stays above 1 I divide x by 10 and I increment num divs. You can also do that by saying plus plus num divs and then I print out the number of divisions I made. So that is a way to approximate the logarithm of a number. So sometimes the part that is shown in the red color at the top is called the prolog or the prelude of the loop then there is the loop body and then there is the epilogue of the loop whatever you do afterwards. In this case some of them are trivial we are just doing initialization and printing but sometimes they are more important than this. Any questions about the examples so far? So instead of writing x equal to x slash 10 as we saw we can write x slash equal to 10 or you could say num divs plus equal to 1 or plus plus num divs. So quick review what have we done so far we looked at a bunch of data types and we also looked fairly closely at how they are represented in RAM. Some part of that might have been unfamiliar you have not done binary arithmetic before so it might take a little effort to figure out a few of those tricks. Why do we care? We care because we want to understand the limits of what can be represented and what cannot be represented within the hardware limitations. We will see many more examples of that in the upcoming while loop examples in this lecture. Why do we want to understand the limits so that we can write more reliable and robust code. If you already know that you cannot represent the factorial of a number greater than about 108 reliably and if that number sticks in your mind then anytime you want to do say series summations. See if you try to calculate the factorial of a larger number then it will go to infinity. And so later on suppose you are calculating e to the power x as a series and there is this factorial somewhere. So you kind of understand that beyond that point the denominator becomes infinite and I should be careful about manipulating that expression. So having this few constants in your mind is good if you are writing programs which easily go near the limits or beyond the limits. And they also give us insights for better algorithms. For example we started with understanding the bit representation of integers and somehow it stuck that every bit is worth twice the bit before and in a way that led to the squaring trick. So instead of counting through the power one by one and multiplying base each time we figured out this trick that because every bit position is worth twice the previous one that is equivalent to squaring base as we shift. So sometimes representation can also give you ideas for better algorithms. So that is the reason we care. Next example. So this is the way Archimedes originally found the idea of the value of pi. So what he did was he took a circle of radius half. A circle has radius half then its circumference is 2 pi r or exactly pi. So then he started inscribing polygons with larger and larger number of sides inside the circle. So suppose we start with hexagon with 6 sides. Now every segment of that hexagon is a equilateral triangle. So if the radius is half then the side of the side is also of length half. So there are 6 sides of length half. So I start with a lower approximation to pi which is 3. Now we have first a polygon which says sides x in general. So this edge is x long and so if we double the number of sides by bisecting that wedge and making this the new wedge which is half as wide in degrees then this segment is x over 2 long. Now let us mark up this value. So this is half unit long that is x over 2 long then how long is A? By Pythagoras theorem A is square root of half squared minus x over 2 squared. What is B then? Remember the radius is half therefore B is half minus A. And then what is Y? Y is the hypotenuse of another right angle triangle. So Y is equal to x over 2 squared plus B squared. So now we started with the side length of one polygon. We have doubled that polygon. Now we can solve for the side length of the next polygon. Now originally we had 6 sides with length half each. Next what will happen is the length will go from half to something complicated given by Y but the number of sides will go from 6 to 12. Then 12 will become 24. And the new Y in terms of the previous x will have this relationship. So let us code this up. Can we use this to get the entire circumference? So we will get the total circumference of a polygon that is getting closer and closer to the circle. So eventually we expect the circumference to be pi. So here is the kind of code that we can write. So you start off with double sides equal to 6 and x equal to 0.5. Those are the initial conditions. And then suppose you go in infinite loop. We print out the perimeter which is sides times x. And then we calculate those 3 values. We calculate a, b and y. There are simple expressions. You can draw on the math dot h library and then you can write square root expression. So square root 1 minus x into x divided by 2 and so on. And then calculate a, b and y. And then we make a transition. We make y the new x and we double the number of sides. The last 2 lines in the loop they make a transition from one iteration to the next. They make y the new x and they multiply the number of sides by 2. Anyone not clear with this code? You need a couple of more moments to look at the code. What is happening? Clear? So let us code this up. So it is exactly that code except that instead of using c out I am using the printf library to do some pretty printing. You can see it better but there is nothing fancy. So ignore that line. I am just printing out sides and the perimeter. Sides and sides times x. That is all you need to see. And then I am doing exactly what I was saying in the slide. So let us compile this. Let us print this out. I will actually pipe it through what is called a less command where I can stop the output so it does not scroll past infinitely fast. So you can see what is happening slowly. So 6 sides, perimeter is 3, 12 sides, perimeter is 3.1, 3000 odd sides, 3.14159. So as far as I remember the first several digits it is what is the correct value? 3.141592 something. I have forgotten beyond that. About 300,000 sides. So seems getting closer and closer to pi you can look up and then the number of sides gets into scientific notation because it is too large to print as an integer. And seems like this things are getting closer and closer to pi. Keep going, keep going. Seems like it is not changing at all here. So seems like things have stabilized but still let us keep going. So it looks like mostly like pi. Number of sides has become astronomical now. 10 to the power 98 sided polygon because in doubling it is very easy to increase the number of sides drastically, very quickly. Oh what happened there? So at the time that the number of sides has reached around 10 to the power 160 funny things start happening. And then suddenly at 10 to the power 161 I am back to perimeter equal to 3. Why did that happen? And then the perimeter has become 0. And go long enough the grand finale has become a 9. So clearly the reason Archimedes succeeded is because Archimedes did not have a modern computer. He stopped much earlier than this. So this is again happening because of limited precision in representing floating power numbers. What is happening is that extremely large number of sides is being multiplied by an extremely small side length. And in the limit we are trying to getting towards infinite number times 0 length and the system precision cannot deal with it. So although if you are trying to do this analytically we will understand that there is a limit. The machine cannot deal with limits very well. Especially limits of the form 0 over 0 or 0 times infinity those do not go down very well. So if you are computing some kind of a value which is the limit of an expression you need to be very careful. And I do not even know of the whole science there about given a finite machine precision which cannot distinguish between values which are too close up to what point should we iterate this to get the best approximation to pi. If I if you had a table somewhere someone gave you the whole value of pi to a glorious 10,000 digits not whole value ever. You could compare with this and say look we reach the best accuracy at so many sides and then accuracy started suffering again as a post mortem analysis. But if no one gave you the true value of pi can you figure out where to stop so you get the best value of pi that is a very fascinating subject. We will not go into that in much depth but we will do some of it later on. So you can see that much caution is required it is not just using Archimedes theorem and just crunching it through. And it is not just that you lose accuracy eventually you will get man so it is pretty serious. So the reason is that is this effect sides times x, x goes to 0 the side of the wedge goes to 0 sides goes to infinity any questions on this. But that is not the only way of finding pi people have found pi in many many ways. So another way is to do what is called numerical integration. So we take a full circle we cut it in half let us say the radius of the circle is 1 so the area is pi r squared. So it is area is pi and therefore the area of the semicircle is pi over 2 it is half pi. Now what is the equation of this if we take only the positive part of the semicircle between x equal to minus 1 and x equal to plus 1 the y at any point is positive square root of 1 minus x square everyone happy with that. So because x square plus y square equal to 1 is the equation of the circle. So we will approximate the area under the function fx between x and x plus h we will have a interval of width h which will gradually shrink and then the area will be approximated that one bar will be of width h at the bottom and fx high. So the area of that one bar is h times fx and then we will sum up the areas under those bars to approximate the area. So the inaccuracy will come from the tiny notches of the upper boundary. So it is a smooth curve we are going to approximate it by some bunch of steps. So that is where the inaccuracy is going to be. But as h becomes smaller and smaller the estimate should become more accurate but by now we have wised up. So we know that if h decreases indefinitely we will again perhaps fall in trouble. So how do we implement this? This is again extremely simple. So the second version suppose you know before Archimedes I sort of flamed out it showed a level of stability. So there is some period of stability when the value of pi was sort of constant and then it started regenerating. Suppose you want to detect that and break away at that point. So the way to implement that is instead of saying while true I will have a Boolean variable called keep going. I will initialize that to true. So I will say while keep going so I will enter the loop. And then after I compute x and y so I will initially remember the perimeter as sides times x as before. When I compute y and I multiply sides by 2 after doing this assignment I will compute the new perimeter as sides times x again. Note that although these expressions look the same I have updated both x and sides in between. So this is the old perimeter of the previous polygon and this is the new perimeter of the new doubled polygon. Now I say something like if the new perimeter minus the old perimeter is less than say 10 to the power minus 10. Now see because I am starting with a lower estimate of pi and then I am strictly increasing it. The polygon is always inside the circle. The new perimeter is always larger than the old perimeter. So I basically say that the new perimeter does not increase beyond the old perimeter by much. In fact you could just say as soon as the perimeter starts to decrease there is something wrong. You could also do that. Either tests are possibly okay. I have put an arbitrary threshold here maybe that is not so good. What I do is I change keep going to false and then in the next iteration keep going is found to be false and so the loop exits. So this is one way to control the problem. So this time I will not put the less. I will just execute it sort of out of control and it finishes with a number of sides which is around 393,000. So it is kind of happy that beyond that nothing much is changing 2 within 10 to the power minus 10 and then you can quit at that stage. Let us change this to if new perimeter say is less than perimeter then we stop. That should never be. So that is the first sign of trouble. This time it ends with much more. So 10 to the power minus 10 was overly conservative. Double can deal with more sides. In particular it can deal with about 10 to the power 156 sides before y starts dropping a little bit. So here is the drop. So after this point it dropped I did not print the last value. It was increasing up to the last value. So these are some of the techniques by which you arrange numerical accuracy. So let us come back to the integration part. So I say start of h the base of the strips as 0.01 small enough value and then I keep going again. I want to just print successive approximations to pi. I start of area under the semicircle as 0 and I start at x equal to minus 1. And then while the strip has not gone beyond the right edge of the semicircle. So while x plus h is less than equal to plus 1. I say area is incremented by the area of that strip. The area of the strip is h times the y value. The y value is square root of 1 minus x square. And then I increment x by h. So the base of those rectangular strips is h wide. So I shift to the next rectangular and I keep going. I add up strip by strip. And then at the end of this area accumulation, this is the inner while loop which finishes the area accumulation. I print the h value and twice the area because I expect the area of the semicircle to be half pi. So I am printing twice the area to get pi. And then I half the interval width. I say h slash equal to 2. Let us just see what happens. So I compile this code. I can let it run free. You will see one of the effects of you know halving the interval width. First one is very fast and then as I keep decreasing the h, more and more sums have to be done. So it takes more and more time. But you can see that as h becomes thinner and thinner, the approximation again gets better and better. But it is spending a lot of time doing every successive halving because a lot of strips have to be added up and it takes time. So far mostly our programs have run instantaneously. This is perhaps the first example where the computer is not instantaneous and takes time to do things. So I want to let it go for a little while just to see when we start suffering again because of the accuracy effects. So let us just assure that at some other point, there will again come a point where h is very tiny. And so the idea of each strip goes to 0 but there is like an infinite number of strips. And once again I am having this 0 times infinity kind of evaluation. And again you know the program is going to suffer. It is going to give worse and worse values. And once again you can you know you can do something like this. So one trick that is used to deal with it is that here is your smooth function that you are trying to sum over. Instead of saying that what we are doing is this is one strip and that is the next strip. You can argue that this will be a strict lower bound to the area if the curve looks like. We are always losing this kind of area here. So you can again say that as long as my area increases by thinning down the strip width I will keep going. If the area starts decreasing there is something wrong with numerical precision and I will stop right there. So you can do it. So this is another example of you know you can compute the same number in many different algorithms. Some of them are remarkably faster than others. So clearly the Archimedes method is much faster than numerical integration in this case. This is taking a long time to reach the same level of accuracy. So in numerical analysis which some of you will take in future that is the study of you know how to most you know in the fastest way approximate these quantities to the most accuracy. Use as little computing power as possible. So for example nowadays computing has become a service. You do not need to buy your own computers. You can pay Amazon some money and say I will use your form of computers for two days to do some molecular simulation and I will pay so much per hour. Now if you want to optimize the amount of money you are paying to Amazon while getting your computation done then you need to find the best possible algorithm so that your value is computed with minimum CPU effort to the maximum accuracy. So that is why optimizing accuracy with finite precision is very important. Let this continue. We will see if at some point we hit NAN although this might make my machine a little slow. Any questions on this so far? So we took this problem of approximating pi. Now why do you want to approximate pi? Well it is not just that. We can use similar techniques to integrate any surface or integrate any function and so on. Yes? It could do that because of various effects but theoretically it should never decrease. So the first decrease shows you trouble. Yes after that you can do all kinds of crazy things. So this was this case where we had one loop nested in the other. The outer loop was in charge of having the base of the strips. The inner loop was in charge of adding up the area. So gradually we will write much more complicated code. In scientific code you can easily have 5 or 6 levels of nesting of loops. It is quite common. And you need to work out the variables carefully and understand what is going on. So what is the moral of the story? Well I do not know if you like morals. On an ideal computer increasing the number of polygon sides or decreasing interval with h without bound should get you better and better estimates. In practice the limitations of finite precision arithmetic or hardware limitations prevents this. And this is why we need to know our hardware representation. If I did not talk to you about how double and long and so on are represented you would be completely mystified by this and will not understand what to do. In general limit like expressions are troublesome to evaluate reliably because of this 0 by 0 or 0 times infinity effect. Luckily for many, many mathematical expressions and quantities there are far better ways to evaluate them. For example as convergent series. So you do not need to find pi using this trick. There are other expressions for pi which are series that keep on decreasing monotonically term to term. And those are much safer to evaluate. So the first example of a nice series which converges is e to the power x. So this is a well known series e to the power x equals 1 plus x plus x squared plus x over 2 factorial plus x cube over 3 factorial and so on. So in general the sum is, so everyone knows this. So we initialize an answer to 1. So in this case for simplicity I have already taken the first term into account because even e to the power 0 is at least 1. So the minimum value of e to the power x is 1 so we will start off with 1 for positive x. So we assume that x is positive here. So initialize answer to 1 and then we have base equal to x. Now remember that I have to compute this factorial and so the denominators look like 0 factorial, 1 factorial, 2 factorial etc. Now we have already seen how to compute factorial but surely I am not going to re-compute factorial from scratch for each of the expressions. What I should do is use the previous factorial to compute the new factorial. So each term is like e to the power i over i factorial. The next term is x to the power i plus 1 divided by i plus 1 factorial. So I should not really be using all those smart techniques earlier to do x to the power i every time. I should really go from this term to the next term by saying that x to the power i plus 1 is nothing but x to the power i multiplied by x and i plus 1 factorial is nothing but i plus 1 times i factorial. This will be the most efficient way to compute the parts of the expression. So fact holds this value and base will hold the whole new modulator. So I start off my answer with just the first term which is 1 assuming x is positive and then I have this keep going thing again. I do not want to keep going forever because we need to get e to the power x within finite time. So the first thing I do is answer plus equal to base over fact. For the first time what happens? For the first time base is equal to x and fact is equal to 1. So I started off with 1. I get the next term which is x divided by 1. And then what do I do? I multiply base by x. So what used to be x becomes now x squared. And what does fact become? So I also have this index called ix which corresponds to i over there. So I multiply fact or the factorial by ix and then I auto increment ix to the next iteration. And you can forget this for the moment. It is clear that if I executed the loop forever I would get a closer and closer approximation to e to the power x. So let me trace this pseudocode on paper with symbolic values to show what is happening. So the variables are answer, base, fact and ix. There are the four variables in the code. If you haven't, have you written down the pseudocode or should I write it on the board? How many have written down the pseudocode and can follow only this screen? Otherwise let me spend a couple of moments to write down the pseudocode. So I will not bother with the termination condition for now. So it is a while true. The basic action is answer plus equal to base by factorial and base is multiplied by x the input and factorial is multiplied by ix plus plus. And then I will drop the rest of the loop to simplify. So this is the code. I start off with answer equal to 1, base equal to x, fact equal to 1, ix equal to 1. And then right in the beginning of the loop I say answer plus equal to base over fact. So answer becomes 1 plus x over 1. Then base is multiplied by x. So it becomes x squared. Factorial is multiplied by ix. So is this correct? This may be wrong. I have taken the first term I am going over to the next one. So this should be plus plus ix. So let me change the code over here. But let me just change the code as little as possible. Suppose I do that. So then fact becomes 2 and ix becomes 2. Now in the next equation what happens is answer becomes x plus 1 plus x squared over 2. Base becomes x cubed, fact becomes 2 times 3. So that is 3 factorial. This is actually 2 factorial. And ix becomes 3. So then I get 1 plus x over 1 plus x squared over 2 plus x cubed over 3 factorial and so on. So of course it and then to see if I can quit I check if base over factorial goes below some epsilon. And in that case I said keep going to false and then I bail out of the loop. So I said again epsilon to some arbitrary 10 to the power minus 10 and I said double x equal to 1. So I am finding just e to the power 1. So that we can recognize it as e. Answer equal to 0 to start with base equal to 1 etc. So this time I can actually start from the very first term. And then if base etc keep going equal to false. And then in the next iteration because while we will find keep going false it will quit. And in this case I am printing out iteration by iteration. I am printing out iteration number and the answer to a lot of significant places. So let us compile this. This is still continuing by the way. Locally its value seems to be still increasing. So see the Archimedes method reached similar accuracy very very fast. So this is finding e to the power of 1. So this is really the number of terms that have been included in the sum. And within 13 iterations I decide that the base divided by factorial has become so small that I do not care less than 10 to the power minus 10. And so the answer found is very close to what you recognize as e. And why is the answer shrinking to 0 so fast? Because there is factorial in the denominator. Factorial as you know is reaching astronomical values very very quickly. And so 10 to the power minus 10 is nothing. In very few terms just 13 terms I am done. So that is how you compute e to the power x in general e to the power of 1 in particular. So the next observation is that quitting a loop every time by declaring a Boolean value like this and testing it and then setting it for a termination condition and then it is painful. I had to declare a new Boolean variable so Boolean is from Java this is wrong it should be as Boolean. So that is tedious to have to declare a variable, change it if a particular condition is met, test it and so on. So that is why the break statement was introduced in the language. So we start out saying while true with the intention of carrying through it for all eternity and then we do the same thing as before this time there is probably a bug plus plus. And if the termination condition is met if base over fact is less than epsilon then I say just break. And break has the effect of terminating the immediately enclosing while loop. So the arrow shows that that break statement effectively cancels all remaining infinite iterations of the while loop and it returns control to the first statement after this curly bracket. So for example you can print the final value there instead of printing all intermediate values. So let us change our code to do that. So I will not declare this Boolean variable I will start out saying while true and I will say break and I will finally just print out after the while loop I will print out this value. So again this printf is an equivalent for printing out but it prints to more precision that is all. So at iteration 14 it prints out the final value. So we started out reviewing the fancy raising something to a power using squaring trick then we reviewed that and then we looked at several more examples of while loops. You can the fun examples were computing the value of pi by summing up the perimeter of polygons of increasing number of sides inscribed in the circle and then we saw that the obvious limit is not what you get with finite precision arithmetic because 0 edge length is multiplied by infinite number of edges and the various ways of trying to solve it you could use domain knowledge from your program or application to note that decreasing perimeter is wrong and you can stop there or you can have some error threshold and whenever pi the value stabilizes to within that window you can quit. The next thing we saw was numerical integration by taking a function and then looking at the area under it as being composed of a bunch of tiny strips we can add up the area as an approximation to the true integration and the thinner we make the strips the closer you approach the area but once again there is this problem that as the area of each strip goes to 0 and the number of strips goes to infinity finite precision arithmetic can again not deal with it very well and then we said that well for certain functions you can avoid the limits and you can compute it to the power x as series. Now by the way many of you might know that e can also be expressed as various limits so limit x tends to infinity 1 plus 1 over x to the x that is also e but you would be advised to try to compute e this way for that same reason because it looks like something that is just slightly more than 1 raised to an infinite power this is real recipe for disaster okay it is even worse than what we have seen alright so you should avoid using that kind of technique so we also saw how one of the methods for computing pies was way faster than the other and gave you much more accurate values of pie far before the other one had you know even gotten underway okay so any further questions clarifications on this and now you see why break is used you can quit a loop early or whenever you feel like without declaring a variable and testing it in the next iteration in fact in many applications trying to repeat the beginning of the loop body would be wrong or to mess up your invariance and so on it's much better to quit the loop as soon as you can yeah so why are you developing algorithms to compute powers of numbers or pie or e to the power x if you already have this library functions someone already wrote the library functions for us why do you care because techniques like taking limits numerical integration cities summing the universal use it again and again in doing scientific programming or engineering programming and also it familiarize us with the loop and break constructs so far there are other ways to write loops using the for construct and the do while construct will see that but while is universal you can you implement anything using while it might just look a little messy code might become more compact if we could use a for loop and we will see for loop in the next lecture okay so so let's continue with some more small problems where while loops can be used gainfully questions doubts so here is a puzzle it's a counting problem so let's say the input to the program is an integer which is Len for length okay and the question is how many sequences of Len bits are there say 50 bits 60 bits which do not have two consecutive zeros because this could be like an interview question if you so let's give things names okay this is of course a function of length roughly intuitively speaking the number of sequences of length so first of all how many sequences of length bits are there without any restrictions to the power length right just like how many sequences of 8 bits are there that's 256 okay but now we have this restriction that it can't have two consecutive zeros intuitively you'd still feel that this number should increase with Len the more bits I give you the more configurations there are and in spite of this restriction you'd expect that the answer will increase with lens so let's let that number be something some function of length we will express that answer as a sum of two parts a of length is the number of sequences that end with zero without having any two consecutive zeros and b of length is the number of sequences that end with a one without having any consecutive two zeros inside it so the required answer is a of length plus b of length so if a sequence ends with a zero then by law the second last bit has to be a one otherwise you'd have two consecutive zeros therefore a of lengths must be b of length minus one is this clear so you know in longhand is the number of Len bit sequences ending in zero no two consecutive zeros okay be Len is same thing ending in one so suppose I tell you that a particular string ends with a zero then surely this bit cannot be a zero this has to be a one so how many Len bit strings end with zeros well the same number as ends with a one you know with one less right so a of Len is equal to b of Len minus one and meanwhile what is b of Len okay if the sequence ends with a one okay then the previous bits can be anything so therefore b of length is equal to a of length minus one plus b of length minus one if the bit sequence ends with a one then the previous bit could be either zero there's no restriction so now a of Len minus one is b of Len minus two right if a length is b length minus one then a of length minus one is b of length minus two so I can write b of length as b of length minus two plus b of length minus one so let's start with small cases suppose I tell you length equal to one just one bit sequences okay now of course the one bit sequence cannot have two consecutive zeros so how many such bit sequences are there okay just zero and one right so we can set up I think there's some small bug in the slide the base cases with a of zero bit so if you have only zero bits or let's say let's say with one bit so let's set the base cases properly so one bit sequences ending with zero which have no consecutive zeros there's only one right and one bit sequence is ending with one which don't have to again just one okay so the base cases are a one equal to b one equal to one fine now note that b length depends on only b length minus two and b length minus one so let's write out the first few values for for ease so a one is number of one bit sequences ending in zero that's just one such right so that's equal to one b one is the number of things ending with a one there's only one such just the bit one okay so that's also one so b one is equal to one okay how about b two b two is the number of two bit sequences without two consecutive zeros which end with a one so there is this bit there is one this could be anything zero or one therefore this is b two is equal to two okay there are two strings which end in one and which don't have two consecutive zeros zero one and one one fine so the base cases are b one equal to one b two equal to two what is b three by definition we saw that this is the sum of those two guys so this is equal to three okay what is b four that's the sum of the previous two values so two plus three five okay b five is three plus five eight okay anyone recognize what this series is right so another way to you know hide the name for one actually is to pose this as a puzzle now suppose you want to you know calculate these sequences one after the other because each b value depends on only the previous two values it's enough to keep around the last two values we don't need to keep around remember all the b's we have encountered right so how would I code that up I would declare a length which I need to report at the end I read that if length is zero or length is one then I'll report say zero or one depending on what the convention is it's a little different so let's say length equal to one or length equal to two I'll report a length and this equal to say the length itself so say base cases are a one equal to b one equal to one and b two is equal to two you already saw so if length is one or two then we just return length as the answer okay sorry for the bad formatting here otherwise if length is larger then we can clock up from two onwards or say three onwards okay so while lx is less lx is the index okay I'll I'll clock it up to reach length while that happens I'll say b argument for length so b length within brackets because you can't declare something with a bracket in it we'll just write b length is b length minus two plus b length minus one and then I'll print out the new value lx with b length and then I'll shift forward one step by incrementing lx and then shifting so by which I mean b length minus two will now become b length minus one and b length minus one will become b length okay so let me show that on the board for clarity hopefully without protest from the ps system so think I'm on this infinite slotted storage okay here is length therefore here is length minus one and this is length minus two okay the basic step of computation is very simple I take this I take that I add and I store here the next value is the sum of the previous two values but because I have a limited number of variables I'm sliding them around this array okay so what I do is this value is called b length this value is called b length minus one and this value is called b length minus two and let's say at some point we had these values three two three and five over here so this was two this was three after adding it this had value five right to be able to move on to the next position what I do is I wipe out three I copy so two I copy three into it okay I wipe out this three I copy five into it and then I compute eight here that's equivalent to saying that the upper series remain the same slid bilane to this one I slid bilane minus one to this cell and I slid bilane minus two one step so I have three variables which are trying to represent this sliding window on this sequence okay and after computing each value I logically slide all the variables forward by one step and that's how I manage within three variables okay so this is how we can compute the Fibonacci sequence one after the other number and a length and bilane are related in a very simple way so if you really wanted to report the number of such sequences it's fairly easy to compute alongside and report the number okay so now for the more interesting exercise which you can do as a homework which is okay I can always compute the number of such sequences can you print them out one after the other okay so suppose I said don't just give me the number of such sequences actually write out all the sequences so I'll give you an input number say 20 write out all the sequences which have 20 bits in them but do not have two consecutive zeros anywhere okay so that that's a much more fun exercise I'm not sure we can do it immediately but for those of you who are getting along fine you can think about it okay gradually we'll talk about how to anymore at those things now as you know these things can get out of hand very quickly just like factorial Fibonacci also grows fairly quickly okay every variable is the sum of its two previous values so there's roughly an exponential growth it's not as bad as factorial but it's sort of like exponential okay so the number of sequences grows very fast and so if you enter into your PC write out all these sequences up to length 50 you'll probably wait for you know several big bang cycles or something to print out not the CDL but the actual big bang in the universe for it to print out all the such sequences okay so I'm not too sure this belongs here but I will just go ahead and finish the last example for while that I have and then in the next lecture we will start with for loops for loops are nothing fundamentally too different from while loops but they give you some syntactic comforts you can do a bunch of things together okay so the next problem I'll discuss is how to reverse a string so I get a message hello world and I want the first letter to become the last letter and vice versa right so if your input is hello the output will become the opposite thing the output has to be o l l e capital H okay that should be the output so how to do that so here's a string message I read the message and then again I wanted to continue with this kind of a style of thinking of storage as you know hello as being stored in consecutive characters so think of memory as having h here e there l l and o okay what do I do in the program I read the thing using a gate line you can read read up that outside so remember this is position zero of the string where h is stored and it's one two three four so that at position four o is stored okay zero one two three four and I'm I set up left to be equal to zero and I set up right to be equal to the last one message dot size is five and right points to that okay and now I'm going to change exchange the left with the right and and make this travel so left will increase right will decrease so and while left is still to the left of right otherwise I'm done I will load into a temporary variable temp the character h so temp will be equal to h h in quotes actually and then I will overwrite because I've already saved h I'll override this with o and finally message right will become temp so this will become h so now I'm done with the two extreme positions and I'll increase left and I'll decrease right so it's a very obvious algorithm there are some details which look into it enough but you might not be familiar which is what does message box left mean so message is like an array of characters and message of left is the storage cell of message dot left okay but one interesting thing is that on the left hand side message left means here's a cell to write into whereas on the right hand side message left means read out of that cell and give me the value so when you access arrays arrays on the left hand side of an expression means something slightly different from the right hand side so we'll look at arrays two weeks from now and we'll see more details of that