 So the first thing we'll look at, and we've already seen part of it, is say enum. In the beginning, we had ints. But if you wanted to call some ints by a special name called action, you can do that. That gives you a new type, basically. Now, there's another shorthand in C++ and C called type def. Type def is define a type. So this is a comfort feature. Strictly speaking, you may not need it. But it's a shorthand. It helps you create shorthands for types that are too long to type, again and again. For example, we already saw that in a map from words to counts, while inserting it, we had to say something like pair string int. If you're tired of typing it all the time, you can define it away and call it a word count. So word count has a word and a count. So pair of string and int for your program, you define it to have the name word count. Now, you can use word count as a type, just like int and double and float. That's become a type in your program. This is an alias type. You can call it that I have alias the type pair string int to the string word count. Now, this makes types more readable. Even if you're not actually abbreviating anything, even if you're actually making the string longer, just like in our ticket and time and action, it's useful to say type def int ticket. So the ticket that a customer gets is actually an integer. But for the purpose of my code, I'll call it a ticket, perhaps to distinguish it from the money amount of the customer deposits. That will make the code more readable. So you can use int, security, and well. You can use int separately as well. And you can assign between them. They're type compatible. Similarly, time is defined as a double. So it's in the opposite order. So when you say type def, you give a known type and a new name for it. Yeah? So you can use two types as ints. And then type def int ticket and type def int. You can do that? Sure. They're both int. But they have different names for the purpose of your code. Just like I said, you could have int ticket, you can have int amount. On the other side, you can have int amount. You can do that also. So the convention in current C++ coding or Java coding is to define our type names and class names starting with uppercase. And variables start with lowercase. This is not followed by the standard C++ library or even boost. But that's because they're considered to be almost built into the language, almost. They're not. But your application, it's better to have types with uppercase and variables lowercase, just because other people do that. Now, so remember the customer in event simulation. So this is how you define a customer. So you would say type def struct. And so this struct tells you that it's not one atomic value, but it's a bunch of values stuck into one envelope. That struct is like an envelope in which there are different cards. On each card is written a value for a field. So what's a field? So the ticket number of a customer is a field. The name of the field is this thing, the second thing starting with lowercase. The type is the first thing. Now, of course, a type could be int, in which case you'd say int ticket. You're free to do that, instead of saying ticket ticket. So once you do this, what happens? Logically, the compiler now understands a new type called a customer. And the customer has four members or fields. Members or fields. And they are named. So the first field is called ticket. It has type capital ticket, which is an int in this case. The second field is arrival time, et cetera, to the end. The ordering is not important because they have all different names anyway. There's no need to index them. The ordering will be important when you initialize it. We'll see how. Now, you can declare a new variable called cast to have type customer. And it's not clear what it initializes the field to. The fields are junk when you declare a customer cast. So it's not initialized in any known way. So there could be garbage in the fields at this point. So one advantage of declaring a struct is that you can assign one customer variable to another customer variable, and all the members would be copied in one shot. So when you say equal to, you're actually copying all the members to the other variable. And you're free to use one user-defined type in another, just like here. Ticket and time are defined by us, not by the system. But you're also free to use the standard predefined types, like in double vector. You could also be in stock customer. There are two alternative syntaxes. So it depends on which version of the company. I don't remember if you need a semicolon, don't you? You probably do. So there may be some arcane internal differences between these two. But for our purposes, they're equivalent. In the first case, you don't need a time limit. Yeah, in the first case, you don't need a time limit. And we can write as a cost before the selling question. Here, yes. You can also declare a variable. Like a customer, cost? Yeah. But generally speaking, it's not good to declare a variable in the same breadth as a type. So why would you do that? Nothing of no that, cost. Well, if it's outside any method, yes. Yeah. So we can write in ticket. Yeah, of course, I didn't take it. Any type is legal. Vector, anything you want. So that we're finding a type, like you can see global or local? No. I don't know if syntactically it's allowed, but generally speaking, in your code, all the definitions of types and struct should go in header files, which are outside the scope of all functions. If they write up the hash, it could all that. Unless you have a single file project, in case that depends on which CPP file they're using or sharing which structs. So generally speaking, if there is a struct definition which is used by two or more CPP files, then it should make a header file out of it and include it in the two CPP files. They're a sharing. Also, the definition of the structure will be included in the non-CPP file in the header file. The definition of the structure is in the header file. There's not a CPP of the header file. There is a header file which is included by the CPP file. Currently, we are only talking about source code. You're not producing an object yet, right? Yeah. There is a database to define the customer inside a function. They're not outside. No. I'm not even sure all compilers will allow you to define types inside function bodies. That's what I've seen. I mean, I have to check. I don't know it myself. If you declare a new type inside a function body, is it even allowed by the compiler? I don't know. But if it is allowed, certainly you won't see that definition outside the function body. So this is how structs work. Of course, I haven't told you one important thing, which is how do you access members of the struct? So if you have a variable called cast, which has been defined to be of type customer, then you're allowed to access the members by name. So you say cast.ticket, cast.arrival time. So the dot notation gives you access to the member variable. And you could use them both on the left-hand side and right-hand side. So you're selecting pairs, like that. Dot first and dot second. That's here and dot. Yeah. So we can also do the pair, dot, struct, and pair. Yeah. It's the same thing. Pair is implemented as struct with these members. So here also, plus, narrow, okay? There's no add-on. I can't do that. No. Add-on is a totally different. So y in case of, okay. So if you declare something to be a variable to be of type pair, so don't get confused between iterators and pairs. Slightly different things. So if you say pair in P35, that's a declaration. You access the first thing in P by saying P dot first, not P add-on first. P add-on first will not compile. I didn't say, no. I didn't. I know what I did. I said something like map kv colon colon iterator ix. If that's a declaration, then you say ix add-on first. ix is not a pair. It's an iterator, or second. So a pair has a dot first, which is exactly a member. No. No, no, no. I didn't say that. That's what I'm saying. They're totally different. So the pair is similar to step-check, and the k-test has a little pointer. Yes. We haven't done pointers yet, so I was not taking that even word. All right. So you access on left-hand side and right-hand side using the dot notation. How do you pass parameters to functions? How do you pass structs as parameters to functions? Exactly the same as ints and doubles and everything. By default, you pass by value, which means the entire struct will be copied over to what the function sees locally. And if you change things locally in the function, it will not be seen also. Just like passing ints by value. No difference. But you can also pass it by reference. We want to get it changed. So we'll see a couple of simple code examples to demo all this. The last thing is how do you initialize? You can, of course, initialize by first declaring a customer cast, and then saying cast dot ticket equal to this, cast dot arrival time is that, and so on. That's a little tedious. Fill in all the fields by name one by one. Or you can write an expression. So suppose you want to look at the third one first. So you declare a customer cast. And you want to assign them a ticket number and an arrival time when they haven't started service or finished service yet. So you would say customer cast and curly bracket. And now these initializing values have to go in the same order as the declaration of the struct. That's the only place where ordering is important. Otherwise, it's not. So the ticket clock, clocks up, gives you a ticket number that gets assigned into ticket. And now, the time now, double, gets assigned to arrival time. And service begin time and service end time are currently names. The last here, the second bullet, is where you need a customer as an expression without a name. So the last one actually named the customer as cast. Sometimes I don't bother to name something because I'm going to immediately insert that thing into a vector. Or say that queue itself. So I might want to write something like customer cast and then the initializers. And then right next thing I'll do is queue.pushback cast. So generally speaking, export programmers try to avoid things like this. Because it's tempting to call a customer cast and someone else might do it later on. That might collide. So generally speaking, dangling names are not a good idea in your code. You should destroy a name as soon as you're done with it. See at this point, cast is no longer required. So you can either scope it so that cast is destroyed right there and cast is not available beyond. So don't leave around dangling variables for people to misuse. That's defensive coding strategy. So the other possibility is that instead of that code, you don't even give cast a name. You say queue.pushback. And that's second expression, customer with those things. So there's no name customer. I'm sure there are other weird ways to declare things, but these are enough for most purposes. Yeah? Sir, this is a structure which has been renamed customer. This is a structure which has been named customer. Not renamed. That's its name. Sir, if you don't specifically, just try to add some names. If you just try. That's kind of exactly what you want to do. I think there are ways to do that. But why would you do it if you are going to reuse that type over and over again? Give it a name and make it easier to read. So that's how you access and initialize a member. And here are some more simple examples. So what's the point? So here I have used the other syntax without a type there. The struct point has double x and y in 2D. What's the circle? Circle has a center, which is a point. And it has a radius, which is a double. How do you initialize those? You can say point Pt, point 5, point 9. And you can say circle, circ, point 6, point 8, and 2.3. Any nested structure has to be given nested curly brackets. Now this naturally raises the question, what would happen if I tried to pass Pt as the first initializer? So let's try it out and see. Luckily, nothing to print. Just see if it compiles. We're not bothered about running anything. Expect an initializer before Pt, I see. We need the semicolon. Java doesn't. Only available with. So here's the asking for ISO standard flags. So that compiles. I get this struct.ofile. So it doesn't compile it again. So now, instead of this initializer, suppose I say try it again. Can I do that? I don't know. I've never tried that. You can do all these things. So what's the benefit? Things that individual variables that logically belong together in one record, like a portions, ticket number, and arrival time. If you split that up over different collective or collection data structures, that would be ugly and difficult to maintain and hard to understand. So instead of that, we package them into one place and keep it in one place. So any questions about the basic syntax of structs? Yeah? What are the syntax? So I told you that there's this kind of a standard setting body war between ISO and C and what have you GNU foundation. So they differ on what syntax should be legal in your code. And to allow the compiler to accept certain constructs in your code, you have to set some flags. So here we are saying use the new ISO C++ syntax. If you didn't do that, that would be the ANSI C++ syntax, or GNU, which is close to ANSI. Yeah. We know it's better to run ordered and unordered maps. Don't even ask here. Now, I remember struggling with unordered ordered maps between Windows and Linux in 1998 and still not fixed. So yeah. So GNU C++ is further ahead in supporting interesting types and templates. Whereas, of course, Microsoft C++ is much better at debugging and profiling. So that's the usual state of the word. If you did what? Sorry. It was protesting. It wouldn't allow you. So if you didn't do that, there's an extended initializer list only available with one of these two flags. So G++ tries to be very close to the ANSI standard. But internally, it can handle other syntaxes. For example, if you flip it to the GNU's internal standard, it can support it. Or if you switch to the ISO standard, it can support it. So currently, G++ is patterned after one of the three standards called the ANSI standard, which does not allow initializer lists. So this thing is called an initializer list. So these things won't be allowed in the old framework. But let's look at the earlier event simulation code to see how all this can actually get in action. So action is an enum. Just like type def, if you declare an enum, you have made a new type called action, which just happens to be integers but with a different alias name called action. And the second line is just to print the action string. Otherwise, I'll print it and I'll get confused. Then I define tickets as integers. I define time as double. I have a peculiar incantation to define what NAN is. Doesn't matter. You can set it to minus infinity, whatever you want. Something that's a illegal time. What's a quiet NAN? I don't even want to start about it. As against a noisy NAN. So here's the customer type. As I mentioned, you can always say a struct customer. It's functionally identical. Doesn't matter. As a ticket and this three times for collecting statistics. Then in main, I declare first some random number generators, exponential distribution. Remember we talked about? So we have two exponential distribution generators. Interlevel time is 100. Service time is a little more than 100. So the queue actually does eventually pile up. And here is the event multi-map from times to action. And in it, I first insert the first action at time 0, which is the arrival of a customer. And then I have this list of customers which I call a queue. And then I start the ticket clock at 0. While events is not empty, I get the time now, which is events.beginfirst. So remember the event is a sorted order of pending events in the future. So I get to the next event in the future, which is begin. That gives the minimum time event to come. And I shift my time to that point. Now is the time, which is begin arrow first. And what I have to do is begin arrow second. And I take it out of the event pool. I print out what I have to do. Then I switch on the act. If it's an arrival or itself, it's leave and so on. But as I said, I won't go into the details of the logic. There's some subtlety there. So I don't want to spend time there. But that's how the structs will be used. For example, here, as you can see, queue.pushbackCust. In this case, I wanted to print some properties of the customer, maybe. But otherwise, you could also, let's see, is Cust used any point after that? So I don't really need Cust there. So what I could do is I could just say I could equally well write that. So it pushes back a nameless customer with those fields. I'm reading back the customer from the back of the queue. So I don't need the name Cust. Anyway, then I insert events about the next arrival. And then if someone leaves, I do something. But as you can see, just like in queue, I could do .popfront. Similarly here for all kinds of customers, when the person is about to leave, I can do things like, let's see. To collect statistics, when you're about to pop front, see I'm saying queue.front.ticket. So this is a little bit of a stretch, perhaps, the first time around. See what's happening here is, if the queue is a queue of customers, then queue.front is one customer. And that customer has a service end time. They just finished. So that's equal to now. So first time around, it might be a little stretch to read. Instead, you can say something like customer finished equals queue.front. And now you can say finished.service time end time is now. And you could say customer who just finished. Collect statistics on that customer, which is not your treat. You could say queue of 0, maybe. I don't think queue allows indexing. Queue is a list. It's not a vector. So you're only allowed access through iterators. Pushback is supported. And front and back are supported, but not indexed access. Yeah. I spent two hours thinking about the best way to write it. This is the most compact way. Drive is at the back of the queue to the system. So you can do that. That's a little easier to read. The customer who just finished is at the front of the queue. The service end time is now. This is a leave event and so on. Customer finished.ticket left at time now. And then I pop the front. So it gives you some idea about how to declare structs, customer in this case, and how to declare variables of that. You now have a list of customers. You can make up customers and push them into the list. And you can talk about queue.back.ticket, or you can actually pull it out into a variable and so on. So I think this is best driven by questions, because there would be issues about structs and their operations, which won't even cross my mind because I'm so familiar with it. But you should speak out to understand very clearly everything that's going on. So somebody is a struct is like an envelope in which you have multiple cards. Each card has a name and a value. Each card also has a type. And the cards are addressed by name, usually. Customer.ticket, customer.arrival time. The envelope itself is declared as a type, but then you have to have instances of those envelopes. So can we just say a struct can be like compared to a data type? Yes. We have an int, and we are naming it to a lot of... Ticket, say, right? Type the... The name to customer and it can have different... Just a moment. You're asking, a struct says I'm going to define an envelope, a small package of variables. What is a struct? So a customer is... So what do you think IIT has on you? IIT has your first name, it has your last name, it has your date of birth. It probably has both your parents' names, what state you come from, et cetera. So all those are your attributes. So rather than keep them in different vector of dates, vector of string, what I'm doing is I'm packaging it up into one package. So this type name is customer. And a specific customer is called cast. So just like int. So think of it this way. If I really wanted to do ugly coding, I could take a 32-bit int and store two shorts in it, right? Short is 16-bit. The first 16-bit would be my first value and so on. So similar to that, but I want to make it much cleaner by giving the different fields different names. I've not touched class yet. I'll be getting into that next week. So so far, is this okay? So a struct is a small bundle of variables which you eventually access through one variable like cast.ticket, cast.arrival time and so on. Cast itself is a variable name that leads you on to fields. So cast is not an integer. Cast is not a double. Cast is a package which holds various things. And coming back to this kind of code, so you can declare point as a package of two doubles. You can declare a circle as a package of a point under radius, center under radius. Now if you're writing a function, say double area, what should I write? Double area. Return. Pi from somewhere into... So that's how you write area. Now of course, see that area doesn't change the circle in any way. But in this case, even though you're not changing it in any way, all the fields will be copied. So three doubles will be copied whenever you pass circle into area like this. If you don't want that, you'll have to say that. But if you do that, then you are now free to change the center of the radius inside the function. No one will control you. So if you don't want that, then you'll have to say const. Just like other types. No difference. So let's try this. Let's say circ dot center dot x equals 5. So by the way, just like package namespaces, dots can nest. So circ is a variable of type circle. We know that a circle has a center. We know that center has a field called x. So you should be able to write circ dot center dot x. Now this should not compile. Assignment of data member point x in read only structure. A point is a point in 2D. It has an x coordinate and a y coordinate. And a circle has a center which is a point. Is that clear? Oh, because I declared const. So I take that away, it should compile. So what else do we need to know about all this? Yeah, so that's the example. Now, consider the following situation. So now we get to this business of what's called a constructor. So suppose we need the idea of the circle very often in whatever program we're writing. So we wouldn't like to calculate it every time. Because that involves the squaring and pi and stuff like that. So we would like to pre-compute the idea of a circle and store it. Now how do you do that? We don't want every declaration of a circle variable to compute the idea there. So you don't want to say something like, you know, now we want to now say a struct circle, point center, double radius and area. At the same time, I don't want an outside ANC to assign the area because then the outside ANC will have to know how to calculate the idea of a circle. Aesthetically, the best thing is that a circle knows what its area is. Why should I have someone else calculate my area? So this is not the primary reason why you write constructors, but this is one use of a constructor that I can pass it just the center and the radius and it will automatically compute its area and store it. So let's see how to let a circle compute its own area when it's initialized. So now what I'll do is I'll say, so the constructor is defined as follows. It is basically a function. It looks like a function with the same name as the type itself. This, just like a function, is the number of the arguments you want to pass to help in the construction of the object or the struct. For example, suppose I don't want to declare any fixed values and I want to just assign, so let's say by default, I want my circle to be the unit circle centered at the origin. If I don't tell you any circle, that's the circle you should assume. That's called a default initializer of the default construct, which is not given any argument, which will give some default values to all its members. So now let's try out a main method with all this. I'll call it about the ticket. So I'm just declaring a sort, and then I'm printing out its x and y. So 0, 0. Suppose I did not have that constructor, garbage. So that's another reason why having constructors is a good idea because you can always start off a declared variable with some values that are predictable and you know what the value is. But sometimes maybe you don't want that. Maybe if you don't initialize it, or maybe you want to fit in NAMS. You probably don't want an arbitrary value. If you want initialized circles to be detected, you probably want to store NAM in there, or infinity, or something like that. But you can't do that automatically. To do that, you have to write this constructor. So that's called a default constructor without any arguments. But if you wanted to, you can have as many constructors as you want, but only with distinct signatures. You can't have two constructors with the same signature, otherwise the compiler gets confused. So you can say, I'll give you a x, y. Let's say, I'll name them differently. I'll say x, x, y, y, and r, r for the radius. So it's not confused with its own stuff. Yeah. Double x is double x, double y is double y. Right, double z. Yeah, that's right. It's just like a function. And inside I can say center dot x equals xx. So this is now no longer a default constructor. It actually takes the arguments you want to stick into your struct. Now if you still declare circle as circ without any arguments, then the first one will take effect. If you declare with suitable arguments like 1, 2, 3, then the second one will take effect. It's pretty obvious stuff where it's just good to know. So does this compile? So 1, 2. Now, so this is where the constructor is handy. Suppose you have area here. So now in this you can say area equals 3.14 because the radius is 1. Here you can say area equals 3.14 into rr into rr, radius into radius. So this will now, without your requiring to specify the area here, it will calculate the proper area. Now one thing that might have crept into your mind is, what if I calculate the area during construction, but then later on I go and change the radius. Then the area will be invalid. Depending on your code, you may or may not need to change the radius at all, depending on your application. If you know that the radius of a circle will never change, your best bet is to label this as constant. So that once a circle object is created, point and center and all these things become constant. So circle is immutable. Yeah. I'm not even sure this will compile. So why is this happening? I'll leave you to find out of time here. Sir, can you write, instead of writing two circles, can we write double x is equal to 0 to 1 by 5? Yeah. So I believe there is a construct, but I've forgotten what that is. Do you know? If I say circle without any argument is equivalent to circle with all 0, 0, 1. What's the syntax for it? I don't know. Can you write constant value while there are already default values in there? Yeah. It has some sort of default value. Yeah. So will that give an error? Because it already has some default value in there. So I need to check the manuals. I don't want to teach things out of order. So I want to check the specification of constant inside structs and teach it to you in the best order. So to 12.26, I want to stop now so that we can deal with constant in the next lecture. But the important things we have covered so far in this lecture are that you want to package things into one place. So I'll want to spend the remaining few minutes in actually doing something useful with structs. For that, let's... So point and circle is fine, but let's look at one important example long back. So sparse arrays. Remember, we kept on having these two variables, vector in dims and vector float valves. And in the specific case that we started writing about, like, sums, there's a dims, a valves, b dims, b valves. And only these names kept the two vectors together in our minds. So this is an ideal place where you should be using structs. So a sparse vector is actually a struct which has two dense vectors inside it, the vector of dims and the vector of valves. So just to type things to familiarize, suppose we say sparse vector, sparse map. It's a way of mapping from dimensions to values. So I'll write something like struct sparse map. Inside I'd have a vector in dims and I'd have a vector float valves. So what happens if you do a default initialization of sparse map is that the runtime system will also call default initializer on dims and valves, which will therefore end up empty. And that's what you want. That's the behavior you want. Now the next thing we'd like to do is if you saw this, we had all these routines, right? We wanted to find the norm of a sparse map or vector. We wanted to find sums of two things. We wanted to merge things, right? So what we'll look at next time is apart from just the initialization, how can we add other methods to a struct? And that will basically graduate structs to full-fledged classes for us.