Now, we've finish with an example that uses a number of different modules to illustrate the power of modular programming. So, the fundamental ideas for modular programming first to summarize, we said we've got a client, we've an API and an implementation. The API says what functions are available, implementation implements those and the client uses them. So, a client is a module that calls for library's methods. So, that's like our GaussianPlot the last one that we did. It calls the method Gaussian.pdf. The API, that's called the applications programming interface or API that tells the client what the signatures are and describes the methods. So, we usually write APIs like this, you've seen a bunch of them for our standard input and output methods. So, we specify the name of the library and we specify the functions that are in it. Most importantly, we give the signatures of the functions and then we give a brief description of what they do. Then the implementation has the actual code that implements the functions and so we did examples for Gaussian and there's plenty of others. The idea is that the API is a contract that between really the client and the implementation that gives just enough information, but not too much allowing the implementation to be independently developed and allowing clients of all sorts to use it. If we want to later improve the implementation or change it in some way, we can do that without requiring that we recode the clients as long as we stick to the API. So, for example, we've built some other libraries for this course and encourage and all programmers build libraries in order to help them better organize their programs. So, we're always generating random things. So, we took all our methods for generating random numbers and put them in one place and again, they came with the software that you downloaded at the beginning of the course. So, if you want a uniform random number between zero and N minus one, well, that's a cartoon we try to do better than that, but at least we're expressing the abstraction that what we're computing with is supposed to be. I'm equally likely to have any of the values between zero and N minus one. People are still working on have efficient methods that can guarantee this in some way or maybe we want a real this between two given double values or maybe we just want to flip a coin as a true with probability p or maybe we want it distributed according to the Gaussian distribution either with mean zero and standard deviation one or a given mean and standard deviation. Or, maybe we want to give a discrete set of probabilities in a given array and then return an index I with probability A of I. Or, also one thing we do sometimes is randomly shuffle arrays. So, all of these things that we want to do, we put in one library. So, really the first step in developing a library is articulate the API, what is it that you think you're going to want to do? We went through many passes of the material in this course before really settling on, okay, these are really going to be useful and even without necessarily having clients for all of them, it seems as though there's going to be future clients that are going to want to use these and so we're going to articulate this API and then implement it. Some of these are really easy to implement, some of them are built-in to the Java system one liners. That's not really relevant, what's relevant is that this is the way we want to think of these numbers and we're expressing that with the API and therefore, our programs are more compact layer, more uniformly expressed. So, this is the implementation of standard random. Again, a lot of them are one-liners and we're not giving all of them in a typical client. So, if I say, well, I want to generate random points where the X, Y coordinates are Gaussians centred at a half with a certain standard deviation. I'll take N from the command line and I'll draw a lot of them. So, this is 10,000 points drawn in that way. Immediately we can work with randomness of maybe the kind that we're interested in studying. That implementation is done once and we can work at a higher level of abstraction. So, a lot of these methods we could implement over and over again in our programs are one-liners, but you don't have to, you can work at a higher level of abstraction to try to achieve that with all the libraries that we build and provide. So, the best practices are to try to make your modules relatively small. If you have a big long program, you want to break it into smaller pieces and separate and classify the tasks and then think about how they're going to work together, implement a layer of abstraction like with random. You want to develop them independently, you don't want to be developing client and implementation together. The fact one good technique is to code the client before you code the implementation, so that you know that your goal after all is to make your client code better and more compact and easier and more self descriptive and you want to do that first and then decide if you can implement it. But, you also want to anticipate the needs of future clients when possible. The other thing is, you should always have in every module a main test client that at least runs all the code once. So, for a standard random, we have a client that generates, takes N from the command line and just generates random numbers of the right type calling all the routines, all the functions in the module. You can get a quick look at whether it's doing the right thing. You want to do more extensive testing as module comes into heavy use in some separate module. Is it really producing random values or not? We'll give an example of such a module in a minute. So, we'll talk more about this kind of design activity as the course moves on, as we build bigger and bigger programs. Here's another example statistics on arrays of real numbers. We're doing data analysis all the time, and again you want to find the max, the minimum, average, the sample standard deviation, the median, all of the statistics of any given array of doubles, and maybe you want to plot them too. So, you can plot points, you can plot lines, plot bars. Now, these are easy routines to write and there's a lot of different ways to do them, but by condensing these things in one place, even though they're easy to implement, they are even easier to use, and in fact our purpose in creating the library was to have easy to use things. If you don't like our implementations, you can develop your own implementations, you don't have to change the clients. The whole idea is to clarify client code and give yourself the flexibility to improve implementations in a controlled way. So, let's look at a example client that has a program that uses several of the libraries that we've talked about. So, you can run an experiment where you flip in coins and ask how many heads did you get. It's called the binomial distribution. So, we could put that in random maybe, but what we do is take N as an argument and call StdRandom.bernoulli, and just count the number of times that it comes up heads. You expect to have N over two heads. So, if StdRandom.bernoulli is working we should get about N over two heads. Actually there's much more that we should expect if StdRandom.bernoulli is actually working. So, let's test it. So, we're going to run, we've done this many times before, we'll take a parameter trials and we'll run that experiment flipping coins trials times and ask how many heads. You should get a Gaussian distribution for that. So, what we want to do is write a program that will do this test, that's going to be our example. Now, that program without the libraries that we've talked about, it'd be fairly long program by the standards of the ones we've looked, 50, 60, 70 lines maybe, but with the libraries, it's very naturally expressed computation. So, there's the binomial that we did on the previous slide, so then this code breaks into naturally a few parts. We're going to get the command line arguments that control the experiment. So, one is N, how many flips in trials is, how many times we're going to perform the experiment of doing N flips. Then the next part is to just run the experiments. So, the for loop just goes trials times, and then each time we iterate the for loop, we're going to run that binomial experiment, flipping the coin N times. It's going to return a value which is how many times it came up heads. We'll just use that value to index into an array, it's got to be between zero and N, and just count for every experiment, it's the frequency of occurrence. We'll increment the frequency occurrence of it's value, and that way we keep track of for all values of frequency of occurrence, and you expect N over two to occur more often than others, and you expect small numbers and very large numbers to be less frequent. Then we'll normalize it, that's divide it out by the number of trials so it's between zero and one, and then call our plot parse function. So, the results of the experiments will be a bar graph, and we want to test. The math says that that should be a normal distribution. So, we can use our Gaussian PDF function to plot that theoretical curve, we use plot lines for that, in this mass a lot of calculation, and we can immediately test that Bernoulli our random thing at least satisfies the theory that if it's used as the basis for our coin flipping experiment, you get a bell curve with N over two occurring most frequently. That's an example of the power of modular programming. When you look at it it's a pretty complicated program but each module is pretty simple and pretty independent, and these modules are useful for this application but they're useful for lots of other applications. Like when we talked about standard draw, we looked at an IFS application, that use Standard Random and Standard Draw. So, you can get an interacting interlocking modules, but there's a lot of code here, but it's by putting it into small coherent modules, it's much easier to understand it, debug it, maintain it, improve it, and reuse it, all of these things we can use later. That's really the power of modular programming. So, why do we do it? It allows independent development of small programs. It allows every programmer to develop and share layers of abstraction, and it provides self-documenting code. So, our programs are written at a higher level. We develop the API so that the client programs and what they want to do is easy to understand, it's specified in the name of the function. The fundamental idea is that separating the client from implementation benefits everyone including clients that don't even exist yet, but the contract between implementation and clients that's the API, that benefits all past clients because the implementation has to stick to the API and that's what the past clients are depending on. Now again, it's for a given general task, it's not always an easy thing to understand how to break it into independent modules. That's part of the art of success being successful in applications programming, and specifying the API is a deep question. It's one thing to specify the signature of the function, it's another thing to say really what it does in some kind of meaningful way that you can stick to for both client and implementation. To say that our Gaussian library includes a function that implements the Gaussian cumulative distribution function is shorthand for quite a bit of knowledge, and that's going to happen with any different kind of function of really what do you mean by that. But that's something that we're going to come back to again and again as we develop more complicated and interesting programs. Without modular programming, we wouldn't have much of a chance.