So guys I am back with another Diablo 4 video and today guys I bring you another extremely amazing XP and loot farm. This one guys you do not want to miss trust me. How's it going guys my name is DPJ and I am giving away an Ultimate Edition version of Diablo 4. To beat me with a chance of winning it simply drop a like on this video leave a comment down below and make sure you are sucked good luck. Okay so we've all saw the one farm where there's tons and tons of elites all grouped up that without a shadow for that will be nerfed sometime soon you just know how believe it works but guys I bring you another dungeon you can farm this one's amazing for solo as well where there are I wouldn't say an equal amount of elites grouped up but there are tons and tons of elites all grouped up together in one room I counted five or six in another area there's like six or seven in another instance there's like another five and there's many many more underway too and you'll see gameplay that in a quick second. Now the dungeon we are looking at guys is called the forgotten ruins and it's located right here on your map so you want to come down to there bottom left of your map in Khajitstan as I say Khajitstan that's where we want to come to people forgotten ruins so here's your closest waypoint the iron walls encampment so you want to come to this one and make your way over to here and it's actually quite simple quite a quite a nice little trip as well ain't got a stress to get here nothing can't you nothing gonna hurt you on your way well when you're here guys simply go in now the dungeon itself is quite straightforward the only annoying part about it is in this first area here you have to clear out the adds in every room to make your way you have to you can't can't get around that but once you do and they're actually all easy adds to take out guys you progress on now in this room right here I'm telling you now there's probably five or six elites which you can take down what I do wise you'll see this on the screen now I just group them all together bring more together and just slay them once that's done guys I just progress on through the dungeon and although you will come across quite a few elites on your way the main rooms are here now once you come to the bathroom you'll need two things to collect one comes from down here one comes from down there you have to slap me up by the way one at a time can't take them both you have to do one at a time but this room here and this room there there are tons and tons of elites all grouped up together there's also guys elites in this room around the edges this one here around the edges and just a load guys it really is absolutely low so it is a great great farm for sure for guys I'd say more so a solo farm this is easy to do solo the first one we saw yesterday with all those elites grouped up although you can do certain builds solo it's easier much easier in a team actually better in a team you get more xp this one the same but even still this is way easier to do as a solo player because it just isn't as many elites but there's still a great amount of elites here which will earn you a ton of xp a ton of loot dependent on your world tier level world tier two great xp legendaries world tier three plenty unique sacreds and legendaries you name it world tier four let's not even get started guys but yes this is a great great farm I'd suggest it's one you definitely should use because I will think please it may take a double look at this when they see it because I mean like I said the amount of elites in here are well there's a lot more than what you see in a normal dungeon and they seemingly when they find these dungeons when people post on them when they're getting used and abused please basically take out a lot of the enemies take out a lot of the loot chests take out a lot of the elites this one can probably see that happening too at the same time so use it while you can guys it's a great great farm remember if you don't know how to farm dungeons run the dungeon you don't have to do the bus if you don't want to it's just extra loot and extra xp but if you don't want to do the bus guys run the dungeon kill all the elites then what you want to do guys is if you're in a party so if you're playing with more than one person you simply just leave your party by pressing back go on to your social and leaving party will be here that will spawn you then outside of this dungeon door you'll re-invite your friends back into your party then you can just enter the dungeon again and repeat the farm if you're playing solo guys what you'll need to do is once you've done what you need to do in here press up on your d-pad and go to leave dungeon or come down to here on your map so bring up your map and come down to leave dungeon select that then you'll end up outside of this dungeon door once you're outside of that dungeon door guys you want to pull up your menu come down to leave game once you've left the game then want to load back into the game you'll reload into the game outside of the dungeon simply re-enter the dungeon guys and repeat the farm that's all you've got to do but yes guys like i said a great great farm i haven't seen anybody post on this yet it's gonna it's gonna pick up i'm telling you now do you mind if i release that ring here it's definitely gonna pick up so you're probably gonna see this this farm on a few of the channels where it's completely fine but yes guys take advantage of it while you can especially if you're trying to level up for those those are levels this is a great great farm for that and there we have it guys now what i will say is if you are looking for players people to play with you want to help people you want to help yourself if you want to share builds ideas you name it join my discord link down below i'm trying to build a small diablo for family so yes do check it out show you something i love my discord is linked down below but there we have it guys the end of the video has arrived if you enjoyed it leaving a like helps me out if you're new around here want to see more be sure to subscribe and hopefully guys i will see you on that next one


Okay, so let's put some pieces together and use our harmonic oscillator model, the potential energy for a harmonic oscillator, and insert that in the Schrodinger equation and begin to solve the quantum mechanical problem. So we know the Schrodinger equation in a couple different forms, a couple different ways of looking at the same equation. Hamiltonian acts on a wave function to give us back an energy times the same wave function that Hamiltonian always has the form minus h squared over 8 pi squared mass, mass for a diatomic molecule is the reduced mass, times a second derivative, that's our kinetic energy term, add to that the potential energy multiplying the wave function and that should equal energy times the wave function if that function in fact solves Schrodinger's equation. So for us in particular, talking about the harmonic oscillator, our potential energy has the form one-half kx squared, so I'll just rewrite that one more time, so our potential energy looks like one-half kx squared multiplying a wave function. So here's the version of Schrodinger's equation for the harmonic oscillator, I've used the harmonic oscillator potential energy one-half kx squared, notice that the function that we're solving Schrodinger's equation for is a function of x, it's a one-dimensional function, so each of these wave functions is just psi of x and Schrodinger's equation is telling us we're looking for a function that if we take these derivatives, these constants times the second derivative, add it to a quadratic function multiplying the wave function, I should get back the original function. So that's the differential equation that we need to solve, that one has a different character than the ones we solved before because of this x squared and the potential energy. So that's the next task we have to solve and we'll do that in a separate video lecture, but let me encourage you before you get to that next video lecture to ask yourself, what would that function look like, what types of functions are there that if we take their second derivatives, add it to x squared times the function, some things cancel when we get back the original function. For example, if you want some things to try, we've seen before that sine of x or sine of constants times x, that seemed to be good at solving the rigid rotor in the particle in a box function. So is that what our wave function should look like? Alternatively, maybe the wave function looks like some polynomial, so let's go ahead and say sine of a times x or a times, I don't know, x cubed or x to the fourth times a different constant. So maybe psi is a polynomial, it's just got x to the fourth, x cubed, x squared, some polynomial involving powers of x, that's another typical option for what a function might look like. You can consider exponential functions, e to the ax, so some function of this type is going to end up being useful to Solve Schrodinger's equation. And I encourage you, before you move on to the next video, go ahead and take some second derivatives, add them to x squared times these functions, or at least do as much of that math as you need to to decide whether this will or won't solve Schrodinger's equation. Whether this will or won't solve Schrodinger's equation, whether this one will or won't solve Schrodinger's equation with the harmonic oscillator potential energy. So after you've done that, I'll see you in the next video lecture where we'll explore what these solutions do in fact look like.


All right, apparently people like review less, but I guess this should be quicker At least give you a good idea what the midterm will look like speaking of the midterm. Hey, they didn't fulfill my request Joy, so the midterm is March 1st at the original time in that crappy crappy room, so yikes so Yep, sorry Original time was like 6 p.m. March 1st Yeah, so the midterm will actually start at 6 30 So to give you time to like because some of you might have a midterm that ends at 6 So give you half an hour to make it go the bathroom do whatever So be like 6 30 to 7 45 so that'll be fun Sorry about the room not my fault. I asked they ignored me, so I guess we have to live with it So speaking of the midterm. Let's essentially we'll just do a quick recap of everything we learned doing sample midterm and then Feel better about the midterm because it probably won't be too bad so again three major concepts in this course virtualization concurrency and persistence and so far we've only really gotten into virtualization, but we've learned a fair bit about it and we have next week we'll start virtual memory and That will also of course be on the midterm and that's generally where things start going even more downhill, but hopefully we have enough Processes typically catch people up. So hopefully we've got enough practice with that and especially in lab 2 You'll get lots more practice with processes and don't try and over complicate lab 2 So it's you don't have to create pipes or do anything like that That's all done for you if the test case uses pipes It creates its own pipes you just have to set things up nicely and kind of see the weird thing virtual studio or visual studio does So first concept we saw What the kernel was and it was essentially any code that ran in kernel mode because on our CPU There was there's different modes of operation that give you access to different instructions There's user mode which has been all your normal processes run in user mode It's everything we know and love and then our CPU also has a kernel mode That has special instructions that can interact directly with the hardware do Some fun memory stuff that we'll see next week and all that sorts of fun stuff And there was a nice interface between them There was that system call interface that transitioned you from user mode to kernel mode And we saw every program has to use this interface and the nice thing about that was well We have this nice little tool called s-trace which will tell us everything a process does and we can go ahead No matter what language it's written in doesn't matter. We can see what it's actually going to do on your machine For the midterm, we don't care about the file format of elf and any of the details That was just like so we understood every single byte so you don't have to worry about that but you should know the difference between API and ABI and how we broke an ABI and just the different kernel architectures, so a microkernel versus a monolithic kernel that just has all the responsibilities versus a microkernel that has like the minimum set of responsibilities, which the minimum set of responsibilities is like basic IPC Scheduling and managing virtual memory, which again, we'll see next week so we went through processes Unix systems just clone processes every process has a parent-child relationship you can only create a new process with fork and After the fork both processes are exactly the same the only way to tell them apart Is the return value of fork so in the child it will be zero in the parent it will be greater than zero and that will be the process ID of the child and then at that point you're up to the scheduler the scheduler decides when to run either process and In a lab 3 that will be coming much in the future You will also be doing a little bit of scheduling and it will be lots and lots of fun So on top of that creating processes while you have to properly manage processes, which is lab 2 You're basically just managing a bunch of processes So operating system has that strict parent-child relationship You should be able to identify and prevent zombie processes and orphan processes So your lab 2, you know the idea is you won't have any zombie processes because we're all Your the idea of that library is it's a very good and responsible parent So it will definitely wait on all the child children processes and for orphan processes That's the extra like 20% of the lab where you become The awful name sub reaper and then you can actually adopt a bunch of different processes too. So that's like 20% of lab 2 So then we saw basic IPC Read write system calls just read some bytes to somewhere file descriptors are just numbers If it's easier to think of a file descriptor from like the last lecture Think of a file descriptor as kind of a pointer. So it points to something So whenever you fork you have all the same file descriptors in either process And then after the fork they're all independent But they're all essentially pointing to the same things So that's why you can actually share some So they share the or the pointers are independent, but what they're pointing to is actually shared between the processes So we saw a redirecting file descriptors for communication through like opening files so we can replace File descriptor one with a file and suddenly our program just outputs to a file instead Which is kind of cool and you can do all sorts of shell tricks with that And then we saw in the other example where we messed with pipes to replace the file descriptors with pipes So And then we saw signals too. So they're like interrupts for user processes The kernel has to handle three different kinds of interrupts because they'll also come from hardware In user mode all you deal with is signals and They can represent they can come from hardware, but they don't have to So we saw that you get a signal if you get you know if one of your children Parish you get a signal if someone presses a keyboard you get a signal if Someone wants you to exit and you also get a sig kill if someone really doesn't like you and then you have no choice Your process is now done Then we saw scheduling scheduling at least the first lecture was pretty basic It just had some trade-offs. So we saw first come first serve most basic scheduling algorithm think of being at a line at any Store or anything like that when you go to cash out Then we saw shortest job first which prioritizes Processes that have the shortest job first in order to minimize waiting time So how long all the processes combined wait together? Then we that was for non preemptible processes. So we can't switch between them and then we saw well What about if we can preempt processes and actually switch between them? So that was when we tweaked it we did shortest remaining time first So whatever process has the least amount of time left to complete then we would go ahead and Switch that in and start executing that again Most of these are just done after the fact because in practice We don't know how long a process is going to live for and it may Request some resources that it needs to wait for and it's way more complex, you know in real life Then we saw round robin which optimizes fairness and response time But we saw there was some kind of engineering trade-offs with the quantum length or basically the length of the time slice So if it's short you get better response time Everything cycles through quicker, but you also context switch more. So that's the act of switching between processes Which is essentially just a waste of CPU time. So if that is Too high of an overhead. So In other words, you're spending too much time switching between processes and that's a bad thing So you don't want it too small And you don't want too large because if it's like infinitely large, it's essentially first come first serve Which is bad has all those problems starvation being like a major one Then we saw a more complex scheduling that just added more features. So we saw there's a priority feature We could screw ourselves over because if processes depend on each other Well, if a higher priority process depends on a lower priority process Then that's what we call priority inversion because we should at least temporarily treat that low priority process as a high priority process because of that dependency and that can go on forever There's also different trade-offs depending on the processes So if you're interacting with the processes, you want it to be interactive Be good get a quick response out of it While others you might just care about throughput like how many you can complete in a day or something like that um, so You may have different goals between processes and the operating system would know about them necessarily Then we saw well, that was Just all the complications for having a single cpu core to schedule things and then we introduced multiple cpu cores So we could just do the same thing, but that's bad Or we could do a scheduler per cpu core, which is a bit better But you might get some imbalances and then we saw a way to deal with that by just you know changing What processor or what process a What core a process is running on just depending on how busy things are Then we saw the completely fair scheduler which tries to model ideal fairness of just giving every process an equal amount of cpu time and that's like the actual scheduler used in linux and Most of the other systems are going to have something similar Then we saw libraries. So this is That was good because lab two. Hey, you're making a library So we learned what a dynamic library was how to compare them to static libraries Static libraries are basically just a collection of code And when you compile your program everything gets copied into your executable So you can't change anything from then going forward You'd have to recompile if there was any changes while a dynamic library. We'll just look it up at runtime And if you update the library and you run your program again You use the updated library, which is both good and bad depending on who wrote the library so we saw that if you break an abi in your library really bad things are going to happen and you can't expect anything To work anymore. So if You do have a library that you depend on and they break that you will have a very difficult time debugging so there's like kind of an ebb and flow with What programmers prefer if they prefer static libraries or dynamic libraries and it seems to just cycle every few Decades or something like that So some people like static because it will work until someone, you know bricks it Then they have to re or yeah static just works until everything gets like super super out of date And then people start complaining And then they all switch to dynamic where they get all these updates But then everyone starts doing that and they write really crappy code and then they break everything So everyone is like, oh, yeah, I want things to be the same again And that cycle just kind of kind of continues forever All right, so let's get into the midterm and something more fun So that's the link that's the latest midterm. I have written. So this is like the style of things I do uh, this was closed book so yours Is essentially open book where you get a piece of paper that you can do whatever you want The only reason it's not open book is because I don't want you to kill like 10 000 trees. So One piece of paper is essentially open book and you know, it actually gets you to study a bit too. So The only thing we can't answer already is the virtual memory question, which is last So we'll skip that and we should be able to answer it next week All right, so here it is So there's just a Starts with like a few like one or two sentence quick short answers like what's the concept of virtualization has applied to operating systems So that's just making something believe it has access to all the resources when it might not so we don't know about virtual memory, but We don't know how it's actually implemented, but you can actually still talk about virtual memory. So And with virtual memory all the processes think they can access the entire range of memory when they actually are sharing physical memory You could apply the same thing to a virtual cpu all this A process just thinks it's the only thing running on the cpu when realistically they're all sharing the same cpu cores Then the next one was just kind of an easy one So what service would you find a male monolithic kernel that's not in a microkernel? So microkernel the basics that a microkernel has to do is Scheduling which is like switching what processes to run because it needs to maintain control and being that more privileged mode Then virtual memory it has to do in basic ipc So if you take the opposite of that Well things that would be in a monolithic kernel, but not a microkernel or like file systems device drivers or advanced ipc Then us one What should you use to monitor all system calls the process makes hey s trace And then next one that we saw yesterday if you include a c struct in your library's header Why shouldn't you ever trade it or change it? And the answer to that is well, you're essentially changing the abi of your library because The way c lot lays out that structure and memory depends on the order of the field so if you change the order of the fields or anything about the struct it will change how memory is laid out and If you give that to a you know, if you put in a header file or something like that And someone uses that while they're going to compile that Essentially they're going to compile that version of the struct into their code And if it doesn't match what's in your library's code Then really bad things are going to happen. So you either have to hide your struct and just give it a pointer which Remember at the beginning the solution to everything in computer science Is just add another layer of indirection. So that's essentially making a pointer hiding things And adding another layer of indirection there through pointers And then five where the two responsibilities of init. So we kind of know this now So it's first job. We know that when you boot up your kernel All your kernel cares about is Launching a single process and then as far as the kernel is concerned its job is done So it creates process id one which is generally something called init And then init has to make all the rest of the processes or you know through grandchildren or something like that So and then its second job is it's kind of like the default orphanage or however you would want to say it or reaper So it has to acknowledge all the orphan processes that make beseed or that it may receive So it just kind of infant it creates some processes and then infinitely calls weight And then Oh, the next one was page replacement, which we haven't got into yet All right. So this one should be more fun. So Here's some code Can we still see that So here's some code because this was closed book I said what fork does in the problem But I guess since here's open book and you've been doing lab two and a bunch of stuff I don't have to tell you what fork does So Copy of the current running process. Everything's the same Greater than zero process id of the child If it's zero it's the child process And then I said what weight does but we kind of know what weight does So we don't check and in this I didn't check return values or do anything like that. We just kind of ignored it So what were our questions? so Our first question was How many processes get created? So anyone has their guess how many processes actually get created here not including the original one that starts executing Yep three Who anyone disagree with three Same guy So yeah, the answer is three. So why is it three? So Oh, that is not for you Okay, it closed it. I don't know it didn't Okay. So why is it three? So through the question I said One way to argue about it to make it easier to argument Argue about it is the first process that calls that starts executing main is process id 100 So we'll have process 100 here and it starts executing main And we don't count that towards the newly created processes So it comes along first thing it does is call fork which creates a new process So we get a new process probably called So in process 100 we create a new process called process 101 likely and then well The only difference is the return value of the fork their copies Doesn't matter at this point because they're both essentially Essentially, there's nothing that executed main just ran So in process id 100 The return value of fork would be 101 because that's the process id of the new child And so pit one sorry would be 101 and then in the child pit one Would be zero because that's the return value of fork so now We would essentially have two processes 100 and 101 After the first pit one value gets set And then at this point you don't actually know what's going to run first. So we can just Pick one at random. It doesn't matter So let's say process id 100 Whoops runs first So sorry that's 101. So if process id 100 runs first Well, it's going to call that fork create a new process. It's going to be process id 102 probably And then it would set its value of pit two equals to 102 And in process id 102 Well, it would be a copy of 100 at the time of the fork So it would have pit one equal to 101 Because it just copied it it existed before And pit two gets set after the fork. So it is its independent value and it's the return value of the fork Which in the child would be zero So then at that point pit Or process 100 and process 102 are both here and then process id 101 would probably execute Call fork create a new process called 103 And the only difference and it's a copy at the time of the fork. So pit one equals zero and then That's the only thing like it's copied and then after the fork Well, we set the value of pit two. So in process id 101 pit two would be equal to 103 And then in process id 103 pit two would be equal to zero So now We've processed 100 process 101 process 102 process 103 And they are all at this point And we've made it through all the fork. So that's all of our processes. So process id 100 was our original and it spawned Three other processes 101 102 and 103 So any questions about that? And if we'd like draw the parent child relationship well process id 100 was the first process and then first it created 101 which was its direct child And then afterwards it created 102 which was its direct child And then process id 101 is the one that created 103 so 103 is a direct child of 101. So that's kind of what our all our parent child relationships look like So any questions about that? Okay, then we have We essentially have you know We have our four processes in total and Let's see the other questions So here we go. So how many new processes get created three? Does process id 100 or any of its children create any orphan processes? And why or why not? So the answer to this is new because the first process creates two children and then We can see that well process id 100 would call wait One time because process id 101 here It would execute because We're looking at its value. It's pid one is 101. So it would wait once and then wait for 101 to die and then The next thing it would do is wait Go into here because pid two is greater than zero. So it would wait twice. So Two children two weights So that matches up. So it wouldn't create any orphans Then process id 102 Well, here it has one child. So it has that one 103 child so 101 would call So would process id 101 go into this if branch So process id 101 would not go into the if branch because pid one equals zero. So it wouldn't it skips that weight But pid two is 103. So it would go into that branch and call wait So one child one weight all good And that's actually all the weights we need but We can so It doesn't create any orphans But then there's the last part which says There's an issue with this pro program when you run it. It seems fine. And that's because we don't check for any errors What's the issue and how could we fix it and you can just describe what you need to do To fix it instead of writing the code so Can anyone guess what my issue is what error would I actually see if I like Exhaustedly check all the system calls and all the errors that would happen Give you a hint. It is related to to that orphan thing. So we check these two processes so The hint is what would this process do that is kind of weird Yeah, so it so process id 102 Well, it's value of pid one is 101. So it would go into this branch and call weight And does it have any children? No, so it probably shouldn't have called weight if you actually checked for errors Weight would have returned an error that said hey, you don't have any children So why the hell are you calling weight? like So that's essentially our error and to fix it. Well Well What we could do is just make sure we just call weight once so the easiest fix is if we detect we are the New child process we just reset pid one equal to zero because it just means we're the child process Or you can do any number of things just to make sure you don't call weight Don't call weight more times than you need to so Fairly easy fix so any questions about that Okay, cool. We'll look at more then so Here's some basic ipc code. So this is Oh, yep So process id 100 like if you just look at what process id 100 does It starts executing main calls fork which creates a new process so it would get and then fork would return the process id of the newly created child and we just Set pid one equal to that So if we just go about let's just assume process 101 So all it's going to do is call fork and then fork would return 101 so we set pid one equals to 101 and then it would call fork again creating another child And then this fork returns 102 so pid two equals 102 and then just keeps going Yeah Yep No because if you set your variable you're independent right because of the fork So you wouldn't affect the first original process Yeah, so anything after the fork remember you're independent. I'm only getting the value of pid one Because it already exists by the time I fork For every case except the first one where pid one doesn't exist yet But like we saw any changes you make after the fork independent In the processes because remember we had that example where we even printed the address of some variable And it was the same in both of them And we changed it in one and it didn't change it in the other. Yep No, it just gets an error from the weight system call that says you don't have any children So it just boots you out immediately which If you check for errors you might encounter that in lab two if you have screwed up your code So that's why it's good for to check to errors because if you have too many weight calls, that's not good and you Depending on how you do lab two you might actually expect to at some point Encounter that you have no children left And there's no other way to know other than reading the reading the error and knowing that you got the correct error so there's like Yeah, there's at least two major ways to do lab two So you get to pick your poison. So that should make lab two more fun All right, any other questions Yep So weight's just weight's just a system call So the kernel will go ahead and not return that for the usual weight until A child is dead So w status is just a way to get like all the information out of a dead process back to the parent So in this case, uh, it would write a value to it, but I never use it So in lab two one of the things you have to do when one of the child processes dies is tell me its exit status So you would actually need to go through and read all that like we showed in the examples Yeah, in fact, I think I could have called weight was just null So yeah But a good thing to know is that weight by itself will wait for any of the children to die not a specific one And if you want to wait for a specific child to terminate then you use weight pid and then give it a pid argument that you want to specifically wait for Or if you want to turn weight pid into weight you give that pid argument as negative one and it'll wait for any child Okay, any other questions about this? Okay, the next one So we got more code More code more forking more fun So In this At least reading it real quick well, it looks like we Thankfully it's written in such a way that it doesn't hurt your brain through fork fork fork fork. So The main process would go through essentially create a pipe and then fork and then the first child would go ahead Mess with some file descriptors and then exec so we don't have to argue about the child anymore Then the main process would continue along create a new Create a new process which eventually calls exec And if it's the parent it just closes and then waits for two processes which Makes sense because our original main process Creates two processes. It waits two times and we don't care In what dies in either order? so that's Assuming no functions fail So it says we compile the program on the previous page executed as a new process again Assume that the system calls don't fail for whatever reason and then One process becomes ls one becomes wc, which is just like a word count program But we don't even have to know about that So explain how they communicate how the two processes communicate together You should explain in terms of each process and read and write system calls So since they both operate on file descriptors, so how do these processes communicate? And what is the communication? Yeah, so the way this works is well we create a pipe Here I can draw a better pipe than that Wow look pipe so we create a pipe And then we have a right end of the pipe which would be fd One and then anything we write to it It pops out of fd zero So we have a pipe that's you know Managed by the kernel and then we create two processes and in the ls process. Well in ls We make its file descriptor one point to fd one And then the rest of them are all kind of default So yeah in the ls process the for the first child We would dupe to fd one to file descriptor one So we're essentially replacing whatever was file descriptor one to point to the right end of our pipe And then we close it so we don't have any extra file descriptors open.  Well what we do is It has three Of the standard file descriptors, which we so far haven't touched And then the dupe to call would replace file descriptor zero to essentially point to the same thing as fd zero, which would be the read end of the pipe Wow, that's a big eraser So we set it up so that it gets the Read end of the pipe before we exec so that anytime it tries to read from standard in It would do a read system calling that and it would Read from the read end of the pipe and get anything that the other process writes to the other end of the pipe And then in the parent process Well, it closes both ends of the pipe because it doesn't actually need the pipe It doesn't use it and then it calls wait twice so Okay, so that that's the first part of the question explaining that hey, we set up the pipe so that in both the processes if it writes to standard out It fills up the pipe if the other one reads from standard in it reads from the pipe and empties it So any questions about the first part? So we are good with pipes. Okay then the The worst part is when you run this program. It looks like it hangs. So I give you a hint. Oh, yep Oh, sorry. I'm bad. Well the second. Oh, there's a question. Will the second if statement be executed twice The answer to that is no. So the second if statement Or which one this one? So this one will not be executed twice So what's going to happen? We can give things numbers again So process ID 100 We'll start executing our program Normal c execution so it gets some space Wait, so Just curiosity. Where does it allocate space for that file those file descriptor ints that array? Yeah The stack okay, it's everyone good with the stack when I say it like that so they get allocated on the stack Okay So those get allocated on the stack That like them telling you in first year doesn't matter right now, but trust me it's going to matter because When we get into more advanced topics That distinction will definitely matter So these get allocated on the stack And then our main process creates a pipe it would fill in those values with whatever as part of the pipe system call And then process ID 100 calls fork So it would create It would create a process 101 probably that is not It'd create process 101 Where they were both exact copies of each other Including what all the file descriptors that had open everything it points to So this would have the pipe open it would also have you know the values for fd Zero fd1 And they're the same in both processes And they would probably be three and four and then process ID 100 So in process 100 PID would be equal to 101 and in process ID 101 PID would be equal To zero So only 101 would go into that if branch and when it goes into that if branch It's not coming out So It goes into that if branch messes with the file descriptor and then calls exec ve So remember what exec ve does is it transforms this process and starts executing another program so process ID 101 would hit exec lp and I said assume that you know Nothing fails So it would exec lp ls so process ID 101 would just become ls and start executing that program ls and then I Wouldn't ever return from exec lp because it's executing something different now. It's not executing your code at all So it would never return from exec lp So process ID 101 would not hit the second if branch because it doesn't exist anymore. It's executing something else And then when ls calls exit eventually which ends terminates the process that process is done So how could it ever get back? Um then other ones say we have a parent with two children If the child writes to the pipe and the other will the other child be able to read from it So in this case we set up our two children. So we do have two children. We have you know process 101 which would be ls and then process 102 Which would be uh wc And we set it up in such a way that when process 101 writes to the pipe process 102 will see Whatever it writes to from the read end of the pipe So we created two child processes Set and set them up so that they can communicate so Our communications between our two child processes and what we saw last lecture when we had a sub process is We just communicated to our child directly. So there was parent to child communication which was less steps And in this case, we are essentially making the children communicate together So Yeah, sorry. So hopefully that answers that any more questions about that. Yep Yeah, if exec LP succeeds this process is starts executing a different program And yeah, the process ID will stay the same But that process will end whenever that ends So it starts executing something completely different so it never returns Yep Yeah, so this That is the second part of the question. So, yeah So let's go to the second part of the question Which just says when you run this process, it looks like it hangs why What would you do to fix it? So we got the fix So the fix is that hey, I didn't properly close my file descriptors so Yeah, so here I didn't close FD zero And in here I didn't close FD one But the major problem there is well Which one of those missing calls caused my program to look like it hangs? Because one of these doesn't really matter. The other one's really important. Yeah Yeah, so the Answer is this one is the important one So The right end of the pipe is the important one because remember if You're consuming things and you're just reading from standard in over and over again The only way you know you're done is if you get a zero out of that out of that read call, which means Yeah, in the context of pipes means that hey There's no one available. No one's alive to write any data to the pipe. It's not possible to get more data So whatever you empty the pipe, nothing else is coming in So it's closed for business so if you don't close file descriptor fd one In that same process. Well, that's the right end of the pipe open by the same process for By the same process, which is really annoying And it's just not going to ever finish because someone has the right end of the pipe open So that read will just sit there and wait and be like, yeah You know just wait a bit. You can have more data coming in later so I need to close file descriptor one in the second child in order for It to know that nothing else in pot is possible from coming out of the right And it'd be good practice to also close this one because I don't need it anymore Yeah, yeah so Yeah, so the question is after I called dup and then I Close fd zero. Am I not affecting fd one or sorry fd zero Yeah, so it makes it a bit easier to visualize it. I guess if you Kind of consider the file descriptors as pointers so Right before I do any dupe two calls it would probably look something like this Where file descriptor zero points to the original Zero whenever the process got made. I don't know what it actually refers to One is original one Two is original two three is the read and of pipe and then four is the right And so what dupe two will do is essentially take this argument and make It points to the same thing as fd zero. So in this case fd zero Here I'll put the numbers here fd zero would be three So if you go through the value, it's like make zero points to the same thing as three so I would take zero and then I would point it to the same thing as three and then It would also close zero So before it did that so this would now be gone. There's no way to get it back So it makes Zero point to the read end of the pipe and also threes the read end of the pipe So I have two file descriptors actually point to the same thing So if I read from three or read from zero, I'd actually be reading from the same thing And then the close of three essentially would just remove that entry Oops, it would just move that. Sorry. It would just remove that pointer So I still have a way to access the read end of the pipe through file descriptor zero But now if I try to access file descriptor three It's now no longer valid because it's closed and you can't It's just gone now and then if I remove file descriptor four I would just be removing that and That was the only reference to the Right end of the pipe. So now Nothing references the right end of the pipe and you can't get it back anymore So any more questions about that? It's probably easier to think of file descriptors as kind of pointers and then When you fork You have the same set of file descriptors and they all point to the same thing But afterwards if you do dupe twos or clothes or whatever You do it on your set of file descriptors. Whatever process that is so Process id 102 here closing its file descriptors doesn't affect the main original process Its file descriptors would be untouched And You know it's in good form that you close any other open file descriptor So your process only has file descriptors zero one or two open And there's no other garbage open and you'll see that vs code does not follow that because one of the things you have to do in lab Two is closed any file descriptors that are already open and you'll quickly discover some garbage that's open Uh, and yeah, you can discuss it under this chord because it Spoiler alert you'll have I think file descriptor 19 and 20 open because vs code I don't know it's written by microsoft. So they're not very good at linux All right I guess that's it. We're out of time. Uh, so just remember pulling for you. We're all in this together


okay can everybody hear me okay just checking that everything is up and running properly okay I'm going to assume that people are seeing this okay and that people are hearing this okay if anybody's got any comments thoughts anything else you can put them into the YouTube chat or the WhatsApp chat I will be watching both of them if you guys are watching drop a comment in YouTube so that I know kind of what my stats are at the moment because I can't actually see how many people are watching etc and I'm just gonna be hoping that this is working okay okay so what I'm gonna be doing tonight is the start of writing something which is basically a BGP LS client now BGP link status of rooting demon it uses TCP and shouldn't be that difficult to get up and running and yes I work with JetBrains IDE because quite frankly when you're doing a large project like this along with its remote development capabilities it works pretty well now I've already created a project in this the first thing that I'm gonna need to do here is I need a CMake file the thing is when you're doing any large project and by the time this is done it's gonna be pretty large you really don't want to be just doing it all in a single file so you want a decent kind of CMake file that you can build on and quite frankly CMake is just cool so let's start there we're gonna be using 318 which is what I typically use we're gonna call this BGP LS we're gonna set C standard to 99 and I'll explain some of the flags that I typically use O3 get the compiler to optimize it G3 so that I've got my debugger and that no address of packed member is because I'm gonna be using packed structures at some point and I still need to be able to take pointers to members of the structures effectively when you're writing in C normally the compiler will pad your structures to memory alignment boundaries unless you explicitly tell it to pack a structure in which case there is no padding but that can create compiler warnings when you start trying to take point of references to things inside packed structures and cause you a little bit of headache so I'm just gonna tell the compiler to pretty much ignore that by the way if anybody's got any questions as I go please just put them in the YouTube comments or alternatively in what's that next thing I'm gonna add an executable now this I'm going to add an executable for main dot C and we'll start with TCP dot C main dot C I'm gonna create purely just as something to run what I'm doing TCP dot C is going to be the start of it because I'm going to need to create a effective server here I try and keep my main to basically a BAM minimum and put everything in separate files where I can easily find it it makes it a lot easier for large projects then I'm gonna go and create main dot C I don't need an associated header and I'm gonna go and create a new source TCP dot C this one I will need a header for and at that point you'll see that my project is saying that it's not configured yet and I've got an error with my C make minimum required because we should say version and if I reload this I've got a clean C make file at that point I've got my main dot C I've got my TCP dot C I've got my TCP dot H now when I'm building TCP typically speaking TCP has sockets it has a dress length it's got the socket address it's got the socket itself and because this is multiplexed okay so let me answer that quickly why the preference of C make over make C make is a thousand times more powerful it allows me to do stuff like doxy-gen it allows me to do a lot more conditional compilation a lot more easily C make is really really powerful and it's just easier and cleaner than make as well you can actually use C make to generate make files or you can use C make with something like ninja you can use C make with me son so see make just more flexible just better I hope that answers the question as I said with regards to TCP there are a bunch of things here so we'll start with a header because when you're creating stuff for TCP I've prefer where possible to define my header structures before I start actually writing code so we'll start and we're going to need a socket structure now socket structure the reason that I do this is because I'm going to be making this thing to work with both IPv4 and IPv6 for and before I can do that I need to include a whole bunch of stuff here with I'm gonna need for later I'm gonna need my timing I'm gonna need socket age I'm gonna need tights I'm gonna need dh I'm gonna need standard boolean I'm gonna need urns ID I'm probably gonna need generic typing this next one I'm gonna use the dpdk spin lock library because it's just well easier than trying to write my own spin lock right now other thing I'm gonna need listen port is 179 I'm trying to think there's anything else I need okay I don't think so that should do because a socket can't really be v4 and v6 at the same time what it technically can but best to just put them into a union that you don't use more memory than you need to you need to be able to store the length of the address the address of the socket now keep in mind that because a v6 address is much longer than a v4 address you can make the socket rest the v6 length and you'll have enough space for both and then you've got your socket itself which is just an integer so that'll be fine your actual kind of TCP socket structure then I'm gonna need session so a session is an actual TCP session that is associated with a socket so that will contain my socket structure we probably want to create a client as well that differentiates between server sockets and client sockets because this thing technically will be able to do both both a active and a passive session which is why I will add a Boolean there what I mean by an active session is I am connecting to the router on a passive session the router is connecting to me I need to be able to differentiate those because in BGP the way the protocol works is there is a different handshake sequence dependent on who establishes the session this is also BGP based so I need to put timers in here for next lead try I want to know when the session was established I want a receive buffer we'll call that red buff I'm going to find buffer size up here maximum NTU on a normal Ethernet connection is really unlikely to be over 9200 bytes so we're just gonna call that 9200 and be done with it we're gonna create a send buffer that will also be 9200 bytes in size now there are a couple of ways that I can build a TCP server in this particular case I'm probably gonna thread it so I will add this if I decide not to use the threads later on in the code I can always just delete this then I want to make this a double link list makes it easier to find delete remove sessions and I'm just looking to see if there are and I'll add a boolean that will allow me to signal if this thing is done or not let's see so I've got my structures defined it's worth actually splitting this just so that I can keep track of what I'm doing so anyone got any questions about what I've done so far pretty standard structure definitions etc. WhatsApp or YouTube otherwise I shall continue okay so the first thing I need to do if I'm creating a TCP server is to create a function that is effectively going to listen on the thread we'll listen for sockets now the first thing that I'm going to do here is because I'm probably going to be threading this my function definition has to be of a void type because pthreads require void type functions let me just double check that before I light you all yeah so now that handler is as you'll notice is a void now I probably need a handler a handler struct as well there so see so the thread handler structure is basically there so that I can effectively read information about all my threads outside of my listener etc. and first thing I do is put a the thread handler is also basically by the way going to be largely global will be passed from main I put the boolean shutdown in there because it allows me from outside of the thread to set that and effectively shut down the whole damn program without a heartbreak on the threat then I am going to put a struct and I'll explain what this neighbor is in a second it's a BGP kind of specific structure I'll create now and then I want a neighbor count to know how many neighbors a neighbor thread will neighbor structure would look something to this effect just double checking something quickly you would have a pure IP which we're gonna make as a character a child for now you'll have a local IP so these will be this will be the IP of the router that's allowed to connect to me or that I and yes it's being recorded for the guy who just asked me in what's out basically with BGP you have your pure IP that's the router you're connecting to or the router you're connecting from your local IP is so that if you have multiple IP addresses on the box that you're working on you know that you want to listen for from a specific IP BGP also requires that you declare what your router ID is this again is normally in the form of an IPv4 address so we'll just make this a 255 character string just a little bit of space you'll also have your local autonomous system number that again is BGP specific and I'll explain it when we get there you'll have your autonomous system number of your peer then things get a little bit more complicated I'm just trying to think of the best way to do this we're gonna make this a single address family and a single sub address family again these are BGP terms let me add a comment in here so all of the stuff is derived from the BGP standard which is RFC 4270 I think it's RFC 4271 let me just verify that yeah RFC 4271 which by the way I can show you what that looks like see if I can if the screen goes blank for a second it's just because I am about to see if I can add this okay not a problem but anyway you can go and look up RFC 4271 and it will it's available added to the comments actually that one over there so that's a basic neighbor we also want to allow somebody to shut down the neighbor we want a keep alive time because BGP requires that I send keep alive and these two I'm gonna comment out for now because I'm not gonna define them straight away but I know that I'm gonna need them kind of later on in the game basically with BGP because BGP when you're receiving the packet they don't always arrive aligned so you almost have to put this through another queue so that you get correctly aligned packets otherwise things are gonna break pretty badly then so we have our thread handler now keep in mind that because I'm passing this in as a voyage to be able to use it at all I'm gonna need to cost it so strut red handle handler equals handler that basically just make a pointer to the same void pointer so that I can easily access it effectively then I need a res that's basically a result structure then missing something here you'll see that I created my sessions here but I also need another structure called TCP listen which has my main listening socket on it my main server socket a list of connected sessions we'll track the last session in the list because it just makes it easier when I'm adding new things and put a spin lock on it because I don't really want to screw with that link list on the session list without having things locked because if I do that I'm gonna create problems then it just creates me a basic variable on so that I've got something to compare with etc then basically that'll just give me a half a second time for when I need to sleep and that's because some of these functions don't take times back we'll add a time valve TV so the difference between this this is in nanoseconds and this is in usax which is yeah I may not need one of those but I'll define but anyway for now just in case and I can always delete them later then I want my basic session and first thing that I want to do so listener if you look where I allocate that if not listener so if the cutoff fails then listeners automatically gonna be null so I can just say that what I tend to do when I'm printing error logs when I'm coding like this and I can always go back and change it is I tend to print the name of the functions called it so that I know where that debug log comes from if I make the the logging messages such that if I'm hitting error I know where the log came from and I can jump to the right point in the code just makes things easier when you start running etc now this here is gonna return a standard error so I'm gonna for now and then I'm going to return null now I could technically return a structure with more errors in it from the thread because the thread is a void pointer type so I can return anything but because this is just my listener I'm already printing an error I don't really need to return anything else I'm just gonna return a null on this one now because I want this thing to listen on both v4 and v6 if I listen on an IPv6 family I can actually listen for both of the same time so if I this now it's interesting the client doesn't pick this up to the IDE doesn't pick this up terribly well because of unions etc but it does work this is AF I need six server oh that's why sorry since six ports now in networking everything is working in big Indians so if you look back at where I defined this here I did a definition of port 179 keep in mind that that is in little Indian order because this is a UN 16 and so I have to swap this yes Charlie I do not use malloc because malloc does not zero allocate Charlie just commented in the WhatsApp that I prefer to use caloc instead of malloc anybody who uses malloc while caloc exists needs to be shot because caloc will zero initialize and allocating memory that isn't zero initialized can lead you to all sorts of problems so for the love of God please can people stick to caloc rather than malloc and sorry about the fan noise it should shut up in a second it's just my router decided to wake up and get crumpy about the heating yeah sorry sorry about the background noise not much can you guys still hear me okay am I still audible there we go shut up okay then so effectively I'm telling it now that in 680 are any will allow it to listen on any any port on any address bound to the box when I create the socket then I need to create a listener socket this is a streaming socket because I'm just gonna get streaming data in from the socket tested for an error TCP listener bail basically if I can't create the listening socket I'm done then typically when I'm working with sockets like this I will set certain socket options on this thing this is a socket basically the reason that you do this is because when a TCP socket when you close the application and have to restart it some of the time it takes Linux kernel of all while to release the socket this will basically allow the socket to be grabbed again so that you can restart the application without your socket being in a fin weight or a reset weight state which can end up with you not being able to restart your application for a couple minutes until the kernel decides to clean itself up that creates me a network socket up until this point now the next thing I have to do is tell it that I actually want to listen on the socket so listen also and I want to know how many sockets I'm prepared to kind of queue up before I accept them I'm just gonna say that I'm gonna allow for 10 in my queue and my typing is terrible and I will bail if I can't do that now I've created my socket I've listened on my socket and okay so I just got asked what I'm trying to do with the code Joe I'm effectively writing a BGP demon to get and receive routing information from routers using the BGP protocol this first part of the code is me doing the TCP work to effectively create a TCP server that is at least vaguely vaguely resilient to errors etc. I hope that answers the question now you guys all remember that when I created that handler structure I put a shut down Boolean in it keep in mind that this listener thing is running in a thread and I need to be able to actually tell this whole thing to shut down in the event of me hitting control C or whatever oh here goes my router again sorry for the noise but let me continue anyway so effectively this is saying while that shutdown is not true keep going and then I'll explain this in a second so effectively this polar here what am I missing here put an error over here so see what that is because remember I paused in-hand list I'm actually using hndl or over here hndl or and this should be a poll fd and the other thing that I forgot here is that I need to I'm just checking something also going to need to include into here there we go some of this polling stuff requires GNU source because I'm using certain GNU source specific functions so I have to define this the IDE tends to bitch and whine that it's never used but it is actually used because it actually tells the thing to compile the GNU source so just ignore that so basically I am creating a polling structure which I'm going to use to poll for new connections effectively then I'm going to find out a flycode not quite sure why this thing is whining but it's whining but well we'll ignore that for now now this is basically polling the socket that I'm listening on to effectively say is anybody trying to connect to me and it's got a timeout the reason that I put a timeout here and don't just let it hang around waiting for it to poll is because if I hit control C or something I wanted to test this while loop so I'm just going to tell this thing to keep timing out and then it'll just keep polling once it's finished timing out there is only one file descriptor in the polar pointer here that's why number of file descriptors is set as one you can actually create an entire array of pollers as a pointer array and then you can listen on multiple sockets at the same time and poll them at the same time I'm only using one at the moment test the return code the return code is what poll will put in this thing and I'm basically testing it to check whether or not I got a response from an inbound polar and I can continue if I didn't get that and I got some other event then I want to switch on the reply code and we can say this will be sockets basically if I get an error off that polar on this particular socket I've got a problem and I probably should just bail because something's gone really really wrong otherwise I will get a zero return code in which case the zero return code basically means nothing answered I got no input I got a timeout effectively and then I'm just going to repeat one loop otherwise I'm going to go into a default case and the default case I'm just trying to think here let's do this properly so we're going to call something here call call locked and I'll explain this in a minute okay now first thing is that you see I'm getting a bunch of errors here because I haven't defined call locked and I haven't defined TCP accept yet call locked is gonna look something like gonna go back to our header file here and you can generally put this at the bottom here or at the top it depends on your coding style I'm just gonna throw it in here for now missed out here because this is a macro I have to escape every line and effectively what this simply does is a fairly simple macro that basically will lock my spin lock call a function get a return value and when the function returns it will unlock the lock again and then the macro will return the result that the function gave me the reason that I do this as a macro is because if you forget to unlock or lock something at the long time you can create race conditions which can be a real pain in the arse to deal with and to fight just to everybody on the stream does everybody understand that macro or can I give some more explanation speak now or forever hold your peace what's up YouTube comment whatever I'll give it a couple of seconds so that I know that everybody is caught up because I don't know how lag the stream is and if you do get it say something so that I know the people have actually heard this okay so Franklin have you ever worked with C macros before basically you need to think of a macro as effectively what it is which is a code substitution so in C if you define something like I have there as a macro effectively what happens is it does a replace on the code so if I'm lucky here it's not actually showing me the replacement values but this cool locked will effectively get replaced by this piece of code so effectively what you will end up here is over here that funk is getting replaced by TCP accept as well as anything inside the brackets of TCP accept so the first parameter here is equivalent to this whole thing over here lock is the equivalent to my second parameter the next line this void return is because I need to effectively define a variable inside this replacement code block the macro will then call the lock against this here now parameters to function macros need to be bracketed like this which is why I've got extra brackets here and I'm making it a pointer because I need to actually lock a pointer here this will then effectively call this first parameter and store the return value in this variable it'll then unlock this lock over here and this final line here acts almost as a return for whatever I've got in this variable I'm hoping that makes sense to everybody any specific questions on that before I move on because you'll see that I use a fair number of macros like this as I go and it's kind of important that people understand so if you've got specific questions please speak now in the chat etc okay I'm gonna continue and I hope that people grasped that but the next thing here is that you'll actually see here that it's moaning that I have an undeclared function of RTE spin lock that's probably because I don't the reason that I'm trying to figure out why it's not allowing me to call that and I'll show you something else in a second so that error is now gone but I've still got an error now the funny thing is because of the macro substitution if I look at my errors here I start seeing errors all on my line 59 you don't actually see a lot of those errors on line 59 because it's going to treat this whole macro as line 59 in the code so it does get a little bit of pain in the ass to debug some of these macros sometimes to figure out where the error actually is in the macro but it is what it is you'll see there for example it's saying incompatible integer to pointer conversion type the reason it's saying that is because my TCP accept function doesn't exist yet ret is defined as a pointer the default return for a non-existent function would be an integer so therefore throw a warning that I'll ever will change the moment that I go and define this TCP accept function which I'm going to do right about now now the other thing about the TCP accept function I'm never going to be calling TCP accept from outside of TCP dot see and I don't really want anyone to do that so I'm going to declare this as a static it's expand this a bit because function definition and I'll just put a default return the reason that I've done that is just to show you that suddenly down over here the moment that that function is defined I no longer have an error on my TCP accept because it's accepting it and my macro itself is now functioning because it can see it but I've still got an error on session handler over here and that would be because I didn't actually define my session handler check something okay so this p3 create is going to call another function called session handler which will actually be my main handling session so what I'm going to first do is I'm going to accept it create a session and then pass it to my session handler so I can actually comment that out for now and you'll see that I've got no more errors in the code up over here but first I need to create the TCP accept function the TCP accept function so the first thing I need to do is create a socket structure helps if I don't make typos you need to tell it again telling it what function I'm calling this in socket so if I can't allocate for the TCP socket just bail because something broke but it doesn't like that so I have made a typo over here and that's okay I just want to make sure typo something oh here we go so that will allocate for the TCP now remember I said that I'm accepting this on both v4 and v6 so I effectively need to know now whether or not this is a v6 or a v4 and I'm gonna kind of cheat a little bit here so firstly I'm gonna go and get the peer name six you'll notice that I do a lot of error printing in my code I do that because believe me it's easier than not having an error at all the next thing that I want to do here and I'll explain this in a second so effectively the IPv6 address is normally treated as an array of bytes but I need to know things about this in its various components which are gonna allow me to tell whether or not this is effectively a v6 address or a v4 address and I can do that by effectively casting parts of the address into various bits and pieces which you will see in a second so I know that if this is a v6 address then the first part of my v6 address the first UN64 is probably not going to be all zeros neither is the second UN16 here and the third is going to be like that that will effectively tell me that if the first 10 bytes of the v6 address field of all zero and the next two bytes are set at 0xFFFF that is going to be a IPv4 address encapsulated in an IPv6 format I believe that that is defined in RFC 4291 if not that it will be RFC 8200 but effectively this will tell me if it's a v4 or a v6 address if this is a v4 address I need to change my family to AFINET from AFINET6 which is the default that I've got up here and my v4 address will then become 32 bit of so effectively what I've said here now is if this isn't a v6 address get the v4 address and then use it to populate my character string up here with whatever the hell that address actually is and then just for good measure I can put something to tell me that I've got it otherwise I'm going to tell this thing AFINET6 that will effectively now get the name that the socket is coming in it'll get the address of the socket that's coming in from now normally if I was writing this and I wasn't doing this I apologize if this is more than rocket science frankly is everybody else following along here or should I take a bit of a break and continue this a little bit later what would people prefer please let me know in the YouTube comments or on WhatsApp I've had a request to kind of take a break possibly until tomorrow so that people can digest all of this so far thoughts I will go with the popular opinion okay I'm going to take a break for today I shall come back I shall leave this exactly where we left off and people can check it out etc and I will do part 2 of this tomorrow evening so I hope that was useful and we shall continue tomorrow thanks very much