I'm looking at a slide counter that says slide two of 177, so I'm just gonna get started. Hey everyone, hope your day has been going well so far. You've seen some cool talks and learned some new and exciting things. Thanks for coming by and I'll do my best not to keep you from the happy hour. I'm Justin Weiss and I'm on the engineering team at AHA, which is the world's number one product road map company. We're a distributed company and we're hiring remote devs all across North America. I also occasionally write from my site, JustinWeiss.com, and I wrote a book practicing Rails to help developers learn Rails without getting overwhelmed. Now, I've been using Rails for over a decade and one of my favorite things about Rails is just how great it is at solving the problems of like the 99% of websites. Take some information, do some stuff with it, show it to some other users. But eventually, hopefully, your site becomes super popular. You start taking lots of information from lots of people and sometimes you start taking lots of information from lots of people into the same place and at the same time. And this can be disastrous. I mean, have you ever written a beautiful multi-page bug description, thrown some photos in there, maybe a short screencast? And then you have this coworker who went out to lunch. They came back, they noticed a typo, and they helpfully destroyed your entire morning's work. Or had a bunch of smart people who were all trying to put flood information into the same table and are constantly overriding each other's changes. Now, wouldn't it be great if everybody could just work on a document at the same time? Like if your text editor could deal with all of those updates, instead of you having to coordinate on Slack like a bunch of stranded pre-teens from Lord of the Flies. As more and more companies and more and more people will put all of their documents online, you start to be able to expect to work on them together. Google Docs is probably the first example that would come to your mind. But there are new apps like Quip and Coda that are building entire businesses around this idea of collaborative editing. And other text editors are adding it, like Sublime, Atom, even Confluence. And if you don't wanna have to pass around the editing football just to get work done, you need it in your app too. What does two developers working on the same document look like? Since you're developers, you'll probably think of Git. You know, you make changes on your document, somebody else makes changes on their document, you merge in one of you fixes complex. Now, part of that's great. And you can just make changes. You don't have to check in and check out the file or make sure that the other person's done with it before you can start editing it. That's called making changes optimistically. And I can do stuff without having to tell you about it first. But part of that's not great. If you and I are editing the same document at the same time, we don't wanna be interrupted every few minutes to deal with conflicts. Like that would just be unusable. But conflicts don't constantly happen. They only happen when we make changes that interfere with each other, which isn't super common. When a conflict does happen though, what if we weren't bugged about it? Like what if the document could just make its best guess what to do and one of us could come and fix it if it guessed wrong? In theory, this is a terrible idea that would never work. But in practice, it mostly does. And that's because the system doesn't need to be right. It just needs to be consistent and it needs to try to keep your intent. Here's what that means. If I type the word hello into a document, the system should do its very best to make sure that hello ends up in the final state of that document. That's what I mean when I say intent. And if somebody else types buy into the same spot at the same time, the two documents should eventually end up exactly the same whether that's hello buy or buy hello. They need to be consistent at the end like you can see here. What about conflicts? Well people are kind of natural conflict resolving machines. Like if you're walking down the hallway and somebody's about to walk into you, you'll stop. And you'll probably both move over to the same side. And then if you're unlucky, you'll both move back over to the other side.  So you want your editor to quickly respond to the person using it. Like if I make a change, I don't wanna have to wait for a round trip to the server before I can see the result of my change. And you eventually want the other people to be able to see that change that you made, at least as best they can. And you wanna be able to do this as quickly as possible. So how can you make that work? The first thing you might think of is sending diffs. Like Jane changed line five from this to this. But it's kind of hard to see intent in a diff. All a diff really does is it tells you what changed, not why. A better way would be to think in terms of actions that a person could take. Like I inserted character A at position five, I deleted character B at position eight. You can make these actions or these operations pretty much whatever you want. Everything from insert some text to make this section of text bold. You can apply these operations to a document and when you do, the document changes. So applying this operation up here would change that document from hello to hello world. If we have operations and we can send operations around and we can change documents by applying operations, you almost already have collaboration. Like what if client A sent its insert world at five operation to client B? Client B could apply that operation and you'd end up at the same document. Bingo job's finished, it's perfect except it isn't. And that's because you can both be changing the document at the same time. So let's say you have two clients. They each have a document with that text at. Now the left client types the letter C at position zero making the word cat. At the same time, the other client types are at position one making the word art. Now the client on the right gets the insert C at zero from the other client and ends up with cart. Like so far so good. But then the client on the left gets the insert R at one operation from the other client and ends up with crat. And I have no idea what a crat is. But worse than that, we violated one of our most important rules already. Our two documents need to be consistent with each other. They need to end up the same. Because now if the client on the left deletes the character A, it's actually deleting the character R on the other document. Like that's wrong and it's broken and it's confusing in a way that a person can't fix anymore. So something else needs to happen. And that something is operational transformation. Now they call it that because you take operations and you transform them. So let's look at the problem again. We have two documents where operations happen at the same time. And when I say at the same time, I mean that they both happened on the same state of the document, that at one. Whenever we have two documents that happen on the exact same document that means we might need to change one of them. And that's because these operations can affect each other. Like the order that you run these operations in matters. On this client, that means that insert R has to change so that it can happen after the insert C happens. So what would that look like? Well after you insert C, the old position one up there, the one in yellow, shifts over by one spot. It becomes position two. Everything moves over by one. If the R should go in between the A and the T, just like it did over on the other side, it needs to go into position two now, right? So when you get that insert R at one, you have to change it to insert R at two before you can apply it. How about on the other side? Well, it gets the insert C at zero, but the position zero hasn't changed at all. So it doesn't need to do anything. It can just insert that right in there and it all works. So this is an example of why you might need to change an operation. But what are you actually trying to accomplish by transforming it? Or really what you're trying to do is say, if operation A and operation B happen at the same time, how could I change operation B so that it takes into account the effects of operation A before I run it so that they can run it one after another instead of at the same time? This can sometimes be abstract, especially as you end up with more complicated operations and it can be hard to think about. So I draw boxes instead.  But check this out. In the upper left corner, I would write a document state at. I draw an arrow going right and write one of the operations, insert C at zero on it and then write the result of that operation in the top right. And then I draw a line going down. Next, I draw an arrow going down on the other side and this one gets the other operation, insert R at one.  And then I draw an arrow going right. And now you have a decision to make because in that lower right hand corner, what should the document look like after both of these operations have happened? Sometimes this is obvious. Sometimes you have to think about what your user would expect having run both those operations and sometimes you just need to make a decision and go with it. In this case though, the answer isn't really ambiguous, it should be cart. And so we'll fill in those two blank arrows to get to that point. What would need to happen to turn art into cart? You need to insert C at zero. What would need to happen to turn cat into cart? Insert R at two. And those two arrows, your right arrow and your bottom arrow, those are your two transformed operations. Once you know those, it becomes really easy to test and TDD this stuff because you have your actual, which are the operations that you happen at the same time, and you have your expected, which are the ones on the right arrow and the bottom arrow. And now you know what to expect and you can write the code that will generate those for you. But before you could actually write a transformation function, there's one last thing that you need to answer. And that's how do you break ties? Like if both clients are trying to insert the same text into the same position, one of them needs to go before and one of them needs to go after and that needs to happen consistently. So pick a consistent tiebreaker. If you're using a client server communication, you can just decide that the server always wins. Or if you're doing client to client or you just want to do it this way, you can give every client a random client ID and just say that the biggest client ID always wins. So we have some operations that transform and some return values that we expect to get back. What would this transform function actually look like? We can start with this. This transforms the top side against the left side to get the bottom and then it transforms the left against the top to get the right and that completes our square. But that's also just kind of punting the question a little bit down the road and probably not what you're actually interested in seeing. A better question is, what would one of these transform operation functions look like? Well, let's focus on this one part. How do you take the left operation and have it take into account the effects of the top operation so that it becomes the right operation? How do you get that right arrow from that left and that top? If we're only thinking about inserting text for now, it's actually not too hard. We'll write it to you to handle it the rest later. Next, we'll return a new operation because we don't wanna mess anything up by changing the one that's passed to us. Like somebody could be storing that for something else later and we don't wanna mess with it just for the purposes of transforming it. Now we need to think, what might cause our position to change? Well, if somebody is inserting text before our position, that means that our position needs to change. Or if they're inserting it at the same position as us and we lose the tiebreaker, then we also need to change. And then how much do we need to change by? Well, the length of the text that was inserted. Like if we put one character before us, we need to move over by one. If we put two, we need to move over by two. And this is exactly what we saw before because somebody typed a C before our operation. We need to move our operation over by one position. And that's it. This is probably about as simple as transformation functions get, but most of them still follow the same kind of pattern of figure out if the other operation can affect ours somehow and if it can take into account the effect of that operation in the one that we return. These can get complicated, but transformations are really functional from the mathematical sense. Like you give them the same inputs, they'll give you back the same outputs. And this makes it really easy to unit test them. And there are some other properties that transformation functions have to fulfill. For example, there's a property that says if you have two documents at the same state and you apply the first operation and then the transformed second operation, you end up at the same state as if you apply the second operation and the transformed first operation, which is kind of a mouthful. But all it really says is with one of these squares, if you start in the upper left-hand corner and you take the top arrow and then the right arrow, you end up in the same spot as if you took the left arrow and the bottom arrow. No matter which way you'll go around that square, you end up in the same place. And that's exactly what we saw here. It's really nice having math because it makes it even easier to test your transformation functions. Like you can generate a whole bunch of random operations. You can transform them in all kinds of different ways as long as they fulfill that property and then you can apply them into documents. And as long as the documents are equal when you compare them at the end, that means that those transformation functions worked properly. So even though these transformation functions can get complicated and when you look at them, it can be sometimes difficult to understand that they're doing the right thing, it's really easy to verify that they're doing the right thing. Now these squared diagrams, they really only work if there are two clients sending operations at the same time. Like there's only really two paths from that top left to the bottom right. If you have three clients, you end up with three dimensional diagrams. If you have four, they have become four dimensional diagrams and so on. And every path through one of these diagrams has to end up at the same place.  One for each client server connection. The clients don't talk to each other directly, they talk through the server and the server broker's order between the cluster of clients. In Rails, we're fairly used to dealing with backend services like we're probably not writing a Rails app that doesn't depend on a database but supports collaborative editing. And so from now on, I'm going to assume that we're using a server and speak in terms of that. Now we just talked about transformation functions. These functions, they transform operations so that you can have one operation taken to affect the effect of another operation. But there's one other piece that you need. We still need to know which operations to transform, like when we need to transform operations. And to figure that out, the control algorithm needs to know if two documents are the same so that we can tell if two operations happen at the same time. Because remember that's what we mean by happen at the same time, they came from the same document state. But since we're talking to a server, this is really easy. We can just give each state of the document a unique version number and we can say two operations are the same if they came from the same version of the document. Once we have a version on the document, we can also put a version on each operation. And then it becomes really easy to know when two operations happen at the same time. You just compare versions and you say, okay, there's a collision here and we need to do some transformation. But what would happen if you went a little bit further before you sunk up? Like what if each client ran two operations before they collaborated together instead of just one? This is gonna get a little complicated so first have this cat. Once again, it's easier if you draw a square so you can really see what's happening. Just like before, we have some arrows on the top and some arrows on the left. And you wanna complete that square by getting the arrows on the right and the arrows on the bottom. You can use the same transformation functions that we already wrote to do each of these steps. But there's a little bit of a trick here because this isn't just one square, it's actually four. And this is really important because the right side of one square becomes the new left side of the next square. Like the end of one stage is the beginning of the next stage. You have to carry this stuff over. Now this is the most brain bending part of the talk so don't worry if it doesn't really sink in at first. It actually took people years to figure out that this was a problem and how to solve it. Just remember that you need to fill in every one of those arrows. That you can only have one operation on each arrow and that for each one of those squares if you take the top arrow and the right arrow you need to end up at the same state as if you took the left arrow and the bottom arrow. So your control algorithm, the thing that would transform these groups of operations, looks a little bit like this. You have your two lists of operations, your top list and your left list. And two empty lists, the right list and the bottom list. You transform top and left to get your transformed left and your bottom and then you put the bottom operation onto your bottom list. The transformed left, you gotta hold on to for this next step because now you're going to transform the top, the next top operation against that one you were holding on to. You take the bottom result of that and push that onto the bottom list and then you do the same thing with the right operation. If you have more, you hold on to it for the next step. If you don't, that becomes your new right operation. You push that onto your right list and you move on to the next row. So now we have one element in our right list and a full row of bottom operations. So for the next row, our bottom list becomes the new top list and we just keep doing the same thing. Again, this part is by far the most complicated thing you'll see in the entire talk. So don't worry if it doesn't really sink in at first. And it might also help to see it in code. So the first thing we will do is make sure we're only dealing with arrays because it just makes some of our life simpler for the rest of this function and for the rest of our control algorithm we'll only be thinking about arrays. Next, we'll handle some simple cases. Like if either our left list or our top list is empty, there's nothing to transform. That's like I walked away from my desk, you did a bunch of stuff, there's nothing that's going to interfere with that. If you're only transforming one operation against one other operation, this is the exact same function that we saw earlier where you have a single square and you're just trying to get the right operation, the bottom operation off of that. Now for the tricky part. We have multiple operations in a row to deal with. So first we'll create our empty lists that will be our return values. Next, if you remember, we'll work on one row at a time. So for the first row, we go through each operation on the top in our inner loop. We transform those by calling this function recursively. This is almost always going to hit one of those two easy cases, so don't think too hard about that. Just say, I have a transform function, it's going to give me the right and bottom. It's fine. This is going to return those new transformed operations which we'll hold on to. We get back our right operation and remember we use that as the left the next time through this loop. So we'll set that here. And we'll take the bottom operation we got back and we'll add it to the bottom list. Once we're done with the whole row, the last operation that we get is the one that we'll end up with. So we'll add it to our right list. This says left up up here, but if you look up, left up and right up are equal at this point and it's a little bit easier to get a hold of left up at this point. All right, or yeah, left up at this point. And then for the next time through our loop, the bottom list becomes the new top list and we just keep going through until we reach the end. And then when we're all finished with this, we return the right and the bottom list back to the user and we have fully transformed that two by two state that we saw. So, piece of cake, right? Now one plus side about this is that you're probably never going to have to write a function like this yourself and the reason why is because nothing in that function was specific to any kind of app. It spoke in terms of operations but didn't care at all about what those operations actually did. All I was talking about is when do I need to transform them and how do I need to transform them? How do I need to call these functions to transform them? So that leaves the question of what should your operations actually do? Like what should your operations be? And the answer is anything your app wants. Like, as long as you can write transformation functions that fulfill those properties for them, you can invent new operations all day. And this is great because with more specific operations you can get closer and closer to representing what a user is actually doing with your app. But like everything, there's a trade-off because if you have more detailed operations, you tend to have more operations and if you have more operations, you end up needing to write a whole lot more transformation functions. When I was working on this problem, I had 13 different operations which doesn't seem like a whole lot but it meant that I had to write over 100 different transformation functions to deal with them all. I could rely on a whole lot of helpers and things like that even so, like that's a lot of work. But it did mean that we could keep some really strong user intent when they were modifying the document. Like, we could know exactly what they were trying to do and make sure that we were trying to preserve that as best as we could. And that brings us to what kind of decisions make collaboration easier and which decisions, if you make them, are going to make collaboration nearly impossible to build into your app. The first thing is, if you're planning to transform operations, you have to speak in terms of operations. Like, you have to talk about what actions a person is taking, not what state the document is in. If you're only storing full document states, you might have a tough road ahead of you. There are ways around this, like sometimes you can look at diffs of your document and infer actions from that, which is a good worst case scenario, but like I said at the beginning, you lose a lot of intent that way and there are some operations that you can't really represent in that way. So first, think in terms of operations. Like, think insert T at position zero, not the document changed from A to at. You can take this even further. Like, there's a text editor out there called Quill that actually represents documents as just lists of operations. So like, a document would be insert hello, switch to bold, insert world, just a list of actions. Number two, if you can, keep your document state, like what actually is a document, keep it linear. By that I mean it's awesome if you can treat your document as like an array of characters or an array of objects or whatever. It's really easy to transform array indexes and transformation functions. It's a lot harder if your stake gets a little bit more complicated. You can sometimes represent trees linearly too. Like if you have items in your array that mean enter subtree or exit subtree or add formatting, remove formatting. This is still easy to transform but it's a little bit harder to work with when you're actually working with your document. If you have to transform trees, it's not the worst thing in the world. Instead of using indexes, you can use arrays of indexes. Like this node could be reached by one comma one, child one of child one. When I was working on this, trees actually made it much easier to model the kinds of documents that I wanted. And so this turned out to be the best way to handle it even though it made transformation a little bit harder. Finally, keep your actual data as transformable or as mergeable as possible. Like strings are pretty easy to transform. Numbers are pretty easy to merge like you add them or whatever. If you have conflicts on like a custom object, then you have a lot tougher decisions to make and it becomes a lot harder to make sure that you're keeping that user intent and keeping those things together. So taking a step back, what does all of this look like from a higher level? How does this all fit together? So we have document states. We'll call them arrays of characters to keep things simple. And documents also have a version. Each client and the server each have a copy of the document at a specific version. You apply an operation on your own document right away and then you also send that to the server so that the server can send it to other clients and they can see what you did. Sometimes the server will say, hey, that's cool. I haven't seen any new operations yet. Like my version that I have is the same as your version. It'll acknowledge your version, you update your version and now you're back in sync together. You're just basically back at that original state. Other times it'll say, no, I can't actually take that version because I've already seen something that's a newer version. But here are all of the operations between that version and yours so you can catch up. When that happens, you transform their operations against yours because yours has already happened and so like you can't really reorder them and you apply those transformed operations to your document. Then you do the opposite. You transform your operation against all of theirs because from the server's perspective, yours hasn't happened yet, but all of these have and then you send your transformed one to the server. Hopefully this time the server will accept it and once it does, everything is consistent again and we're back up to that original state. So with these, we can build an editor that can handle multiple people editing it at the same time but that's not quite enough to have a really great collaborative experience and I'll show you two reasons why. First, if you have two people editing the same document at the same time, it looks like these letters and words are just kind of appearing out of nowhere. You have no idea where to expect them or what to expect until it happens. So it'd be nice if you could see the cursors of other people editing your documents so you could see who else is there and what they're doing. Second, let's say you make a mistake and you hit undo. You now have a question to answer. Should you undo the last thing that you did or the last change that anybody made? We'll call the one where you only undo your own changes, local undo and the one where you can change undo, anybody's changes, global undo. And if you try editors with both of these different styles of undo, it becomes pretty obvious pretty quickly that local undo is the one that feels natural. Like if I type in a character, I undo should undo that regardless of what anybody else has done in the meantime. So to have a great collaborative editor, we need to add cursor reporting and local undo. We'll start with cursor synchronizing. Now what is a cursor, really? Oh, if we're treating our document as an array of things, you can think of a cursor as a place in that array. Like if I have a document, hello, and my cursor is at before the E, you can say my cursor is at position one. If it's after the O, you can say that it's at position five. So your cursor is just an index into that array. How about other people's cursors? These cursors can also be numbers, but you probably also want to know who's is whose so you can show like a name or something. So you can attach some kind of identifier to them, we'll just call that a client ID. So you have two numbers for remote cursors, a position and a client ID. This makes our document a little bit more complicated, but it's not too bad. You have our array of things, a version, our own cursor, and a list of remote cursors. And with this, when you render your document, you have enough information that you could actually render all those cursors however you want, like just put like a trans player and offset on the page or something like that. But what would happen when you typed in a character or you deleted a character? Well, going back to our document cart, let's say we're looking at our screen and client two left the cursor at position two, so in between A and R. And then when we run the operation insert H at position one, now our document says chart. And where should it draw a client two's cursor? Well, it would make the most sense to keep it where it was before, so in between the A and the R. And so we can pretend that place cursor in this position is an operation and we can transform it against the action that we just took. That looks like this. We inserted a character before the cursor and so we needed to move it over by one. So anytime you perform an operation, you need to transform all of the cursors that you know about against that operation to keep them in the same place. These transformations tend to be really easy. They're almost exactly the same as the insert text because in both of them you're doing the same kind of thing, you're just moving a cursor position one way or another. When another client sends you their cursor though, you need to know one other piece of information and that's which version of the document did that cursor come from? If the cursor is a position on a document that you haven't seen yet, then you can't draw it yet because you might not actually have that position in your document. Like if a client on version two sent you a position 15, but your document only has 10 characters in it, like how are you supposed to draw that? So you can either hold onto that cursor for later until you see that document or if your cursors are being sent all the time, you can just drop it and hope that it comes back later. If it's from an older version, it also might not apply to your document. And in this case, you have enough information like you could transform it all the way from the version that it came from up to your document and then draw it. But again, like if you have clients that are just sending cursors back and forth all the time, it's probably gonna come back soon enough and you can also just drop it. If it's on the same version, you might think it's all clear and it is, but only if the server has acknowledged all of your operations. And that's because if you're on version 15 and another client is also on version 15, but you have an operation that the server can't have seen yet, like this, you have to transform that cursor when it comes in against any operations that you haven't sent yet to keep it in the right place. What about sending your cursor? Well, kind of the same way, you can send your cursor anytime you want, but only if you haven't done anything you haven't told the server about yet, only if the server knows about every action that you've taken. You can only send it when nothing is pending. Otherwise, like you can see here, your cursor might not make sense to any other clients and they wouldn't be able to draw it in the right place. Once the server confirms that operation, then the other clients can start drawing it again. What about undo? Well, just like cursors, to understand how local undo would work, it makes the most sense to think about how undo would work in general. Remember, since we're thinking in terms of operations, like insert A at position three, when you think about how you wanna undo that, you think remove A at position three. If you wanna redo it, that operation turns into insert A at position three again. These operations are inverses of each other. They cancel each other out. Like if you run an operation and then you run its inverse, it acts as if that original operation never happened, which is exactly what you want undo to do. And undo works like a stack. The last thing you did is the first thing you'll undo. So if our editor wasn't collaborative, here's how applying an operation with undo would work. You would perform an operation like insert HB4 position one. You'd invert that operation so it becomes remove H at position one and then you'd put that inverted operation onto your undo stack. What about when you undo? Well, you'd pop the operation, remove H at position one off the stack. You'd apply it as if you were performing it to begin with. And then if you wanna support undo, you need to invert it again and then push it onto your reuse stack. That's a falseness so that you don't end up pushing undoes onto your undo stack and end up with like weird e-max style undo. So let's see how this breaks when other people are doing things. So you run insert S at position four and that pushes remove S at position four on your undo stack and then you'd send your insert to the server so that other clients can run it on their copy of the document. A little bit later, the server sends you the operation insert H at position one. This isn't happening simultaneously with anything so you don't need to transform it before you apply it and it turns our state, makes our state become charts. But now look at our undo stack. If you undo right now, it would try to remove S at position four but there is no S at position four, right? Is anybody here going to be surprised if I say that we need to do another transformation? So we're missing a step. When you get the operation from the server, you also need to transform your entire undo stack against that operation. So up here, the undo stack is remove S at position four. We receive insert H at position one and we shift that over by one spot so it becomes remove S at position five. And now when you undo, it becomes remove S at position five. We remove the S at position five and everything is good. So when you receive an operation, you have to transform your entire undo and redo stack against that operation. Luckily we already have a function, that big transform one that we wrote earlier that's really good at transforming lists of operations. So we can just use that. So here's local undo. When you perform an operation, you invert it and you push it on the stack. When you receive an operation, you transform the stack against it.  And this works but it isn't perfect. In fact, this can actually violate some rules that you would normally expect with undo. Like if everybody on the document undid their most recent change and then redid them in a different order, you would expect the document to end up the way that it was before and this doesn't guarantee that. But this turns out to be a pragmatic balance between being complex and having good enough behavior in most cases. And I'm not the only one who thinks so. Almost every collaborative editor I've used including Google Docs ends up having strange undo edge cases like this. Turn, if you can't think of anything, that probably means it's working. And now this is actually enough to make collaboration work with almost any kind of app. You start with a document which can be as simple as an array of things, a cursor, a version, a list of remote cursors and an undo and redo stack. You have operations which act in that state like insert character and remove character. These operations know which version of the document they came from. You have a set of transformation functions which take two operations that happen at the same time and they transform them so that they can be run one after the other in any order. You have a control algorithm which can take two lists of operations and transform each side against each other to come up with documents that end up in the same place. You have functions to transform cursors and functions to send and receive cursors and transform them as they come in. And you have an undo stack and a redo stack which hold inverted operations that get transformed whenever a remote operation comes in. When you perform an operation, putting this all together, you apply it to your document, you transform all your cursors, you send it to the server, you add the inverse to your undo and redo stack and you send your current selection once everything calms down. When you receive an operation, you transform your pending operations against it to complete that transformation square. You apply their transformed operations to your document and you send your transformed operations to their document. You transform all of the cursors that you know about against it and you transform your undo stack against it. When you change your cursor position and you don't have any pending operations like let's say you're hitting the arrow keys or clicking the mouse, you send your current selection. When you get a cursor from somebody else, if it's for an older version, you can ignore it or you can transform it up to your current version. If it's for the current version, you just have to transform it against anything that the server hasn't acknowledged yet. And if it's for a future version, you can also ignore it or you can hold onto it until you see that version of the document. I have an open source demo that puts all this together. You can hit it at this URL and I'll also show this URL again at the end of the talk so I don't feel like you need to rush to type it in. And by the way, it only supports Chrome. Just kidding, it only supports Safari. There are a lot of ways to build collaborative apps and I really like this one. It works for all different kinds of applications. It's extremely flexible and it's not too hard to build. But it's a model that you'll see a lot of different kinds of apps use but it's not perfect. This model needs a server to work or it just doesn't. There are some edge cases especially around undo and redo that would take a lot of complexity if you wanted to fix them. And if you're building specific kinds of apps, there are other kinds of collaboration methods that might be easier or more correct for your case. If you wanna build peer-to-peer collaboration that doesn't depend on the central server, I would highly recommend looking into conflict-free replicated data types. Same thing if you're dealing with just plain text, CRDTs tend to be really well understood and really great at that kind of thing. It's CRDTs are a newer collaboration method that fits some situations really well and are only expanding the scope of things that they can be used well in. If you're using operational transformation, you don't want to write the server or control algorithm yourself and I don't blame you. There's a program called sharedDB that is that kind of transformation server and control algorithm. And if you wanna check out CRDTs, YJS, GunJS, and AutoMerge are all really, really neat projects to go check out. Now I've heard a lot in the past two days about the beauty of remote work and I love that we can do our jobs remotely. Like working remote is one of the greatest changes that I made in my life in the last year. But it does make some things harder. We lose something when we make it harder to work together on a project. And the worst part is when things become harder, they sometimes don't happen at all. I like being able to get a group together to accomplish things that are bigger than any one of us. But that doesn't happen if I'm worried that making a small change is going to wreck your entire day. Collaborative editing is like a magical experience though because all of a sudden it stops feeling like my document or your document, it starts feeling like our document, something we're building together. And I'm confident in making whatever changes I need to because I know that my changes aren't going to conflict with yours. And I want that magic to be there everywhere I go, even if I'm not necessarily using it all the time. So if you're working on a project and you can share things and multiple people can modify things at the same time, like please consider adding this kind of thing to your app because people working on the same thing at the same time should make it better, it shouldn't make it worse. And there's one last thing I wanna mention. Ever since joining AHA, I've worked on some of the most interesting projects that I've ever thought about. And I've only worked on some of the many, many, many projects that we have going on at AHA. So if you like solving cool problems for great customers and you wanna work for a growing, remote, bootstrapped company, come talk to me, we're hiring and I would love to meet you. My email address is up here, use it. I love getting email and responding to email. And if you're going to wanna save one slide, like this is the slide that you wanna take a picture of, it has a link to the demo app, the source code, the slides, a bunch of interesting websites and papers about this topic and lots and lots of other things that I would have loved to talk about but would have turned this into a 37-hour talk. So I did have a ton more that I wanted to talk about but just couldn't fit in. So please, if you see me walking around, come ask me about any of this stuff, ask me questions, tell me about your experiences with collaborative editing, or even just say like, hey Justin, tell me about CRDTs and I will talk your ear off for 37 hours. So thank you so much again for coming and have an awesome day.


Hi, welcome to CES 2020. My name is Julio Sanchez. I'm the technical product marketing engineer for low power RF Today we are presenting our newest BLE family member the Blue Energy LP Which is Bluetooth 5.0 compliant SOC running on a Cortex M0 Plus to 56k of flash 64k of RAM. It complies fully with the standard We have the 2 megabit per second We have the coded 5 and we have the advertising extension In this particular demo we're Presenting or showing the coded 5 functionality What we have set up is for receivers here and the transmitters we put one on the next room and This one all the way to the back in a it's about a hundred meter distance And what and what we're doing is that we're comparing the LP the 5.0 BLE to our legacy part the blue energy dash 2 which is a 4.2 And what we're doing here is that we're sending a hundred packets every 10 seconds And we are measuring the packet error rate Usually when we talk about coded 5 the first thing that comes to our mind is long distance solutions But that totally changes when we're in an indoor environment, and that's what we're showing here so for a short distance So Application in an indoor environment. We can see that using a BLE 4.2 in this case a blue energy dash 2 They see The connection link is kind of weak Here we're seeing that we out of every three packages that we send we're basically losing more than two in average Compared to the blue energy LP in which we are using the coded 5 we can see that Basically, we are receiving for from every three packets. We're receiving actually two or more Which makes a really robust? Connection link Now if we move to a farther distance as I mentioned, it's about 80 to 100 meters and we do the same situation of Course on the blue energy dash 2 we're using a 20 dBm PA to be able to reach that far But even though with the PA You can see that the connection link is really weak compared to the coded 5 with the blue energy LP So imagine in an office building where you have a lot of people a lot of blocking objects a lot of 2.4 signals signal interference You can use this kind of Setup and you can use a blue energy LP with the coded 5 feature to improve the connectivity in this type of environments So apart from the point-to-point connection that we just showed with our blue energy LP We're also presenting our blue energy mesh for industrial building automation, which is sick certified our BLE mesh stack works with all of our BLE devices From 4.1 to 5.0 one of the things that we have done That makes this unique is that we added intelligence to the edge What does this mean in the past what we used to do is that all the information that we got from the sensors an MCU or the cloud used to process it adding latency in the case of the cloud or Having the our customers having them to go to a bigger microcontroller with more features more cost in this case ST is the only one that has this Technology so when an event happens in this case if we have a machinery and we detect the machinery is starting to vibrate All this processing is done inside the MEMS and the only thing that we get is an interrupt and this interrupt or changing state Goes through our mesh networks and fires an alarm. So this is really important another key aspect of Bluetooth mesh and in this case our stack is that we have the most complete the stack in the industry Apart from that we can apply it to different applications. For example in a smart building solution We can use it to control the lights using proximity or gesture recognition one important thing that we have also added is our embedded provisioner in the case of industrial applications You're not going to be using your your smart tablet or iPhone or Android phone to be connecting and managing your network You need something more Appropriate in this case the embedded provisioner gives you that You're able to use a user interface and the embedded provisioner to you know secure way Manage all your network another functionality that we that we have is voice over BLE We're capable of sending voice commands through BLE To the tablet and from the tablet using Wi-Fi to Google's cloud in this case Translate that into a text command compare it to the Google dictionary and get a response Let's test that turn on the lights Turn off the lights Having if we have in mind the demo that I just did on the BLE point 5.0 and our Bluetooth mesh ST has a complete solution for Industrial and building automation If you have more questions, you can talk contact our sales offices or visit our website at st.com


Hey guys, I thought it would be appropriate since I'm going to talk a little bit about hair that you see mine in its sort of natural state. I've made no secret of the fact that my hair is thinning and that I've been on a quest to try to find products that will help thicken my hair and just kind of like safeguard and keep the hair I have, which to me is the biggest battle, you know, the biggest battle. But so one of the things that I just wanted to mention that I've been doing on my hair, because I oftentimes you guys ask how is it going because I wear a lot of the wigs in my videos and and as you can see my own hair, I mean I still have hair for sure. It's just that it's thin and, you know, I can wear it up a lot. I can do a lot of different styles. Sometimes I wear a fake little bun, put it up on top of my head, you know, and stuff like that and and that works out okay. But in general day in, day out, it's really hard sometimes for me to, especially when I want to go in a video and I want my hair to look really good, you know, sometimes it's hard to fix it like right now. You're seeing it in a I would never go on video like this and here I am on video, right? So it's kind of like wacky. But anyways, I think what I wanted to, you know, really stress to Gigi. Anyways, there's a couple things that I've been doing and a number of you have asked me, am I still derma rolling my scalp? So the answer to the derma rolling my scalp is yes. I derma roll maybe once every two weeks or so. I don't derma roll a lot. And when I derma roll, I have three products that I normally alternate on my hair. So one is that I'm and I use the equate version of the ladies' row gang. This one is a dropper you do twice a day. I also have the foam that's a little bit stronger. The foam is the one that you can just use once. So really all depends on your hair. And then I also have the Nutriox 3C the hair serum. These are the three serums that I've been using on my hair. So when I derma roll, I make sure that in usually I am going to be using this version because this has the I believe this has the most proven ingredients in it that I know of for helping hair. And you have to be very consistent with it. So I normally will do that. I'll put that on my hair after I derma roll on my scalp after I derma roll. And then in the morning I might use the foam depending on you know when I put the foam on my part and I'm not a fan of how the foam really works but I put it on my part and then I kind of massage it in and then I alternate with the Nutriox serum. Those are the three treatments that I've been doing for my hair. Those are the three treatments that I've been doing for my hair. And the other thing is that I used to be afraid to brush my hair all the time because every time I brush my hair I would see hair come out. I still see hair come out.  So I use this brush this is a 100% Bors head brush and I think I'm just sort of like the old-fashioned way that we used to do it you know I used to hear oh brush your hair 100 strokes a night well I don't do 100 strokes a night but I brush my hair with this brush every single night and sometimes in the morning I am I'm really good with brushing my hair because I believe that when you use products especially if you use products that you know like sort of if you're going to be using a topics type thing where you want to darken your scalp so it doesn't look so bad or if you use a sort of a fill-in powder like I have right here that I put all over here to cover my gray so if you use a fill-in powder like this you know sometimes even though you're putting it on your hair it's getting on your scalp it's getting on your scalp there's just no if and buts about it so anyways that's what that's what I've been doing you know been brushing my hair like crazy instead of worrying that the brush is going to make it all fall out I haven't worried about that at all I've just been really brushing it very very consistently I've been using the you know the equate version of the woman's Rogaine and I still use the Nutriac serum alternate now for shampoos I have for many years and these are empty so this kind of combination of what I'm tossing these are empty so for many years I have used the this is their color lover volume boost conditioner volume boost shampoo I get this from my hairdresser this is from frame Z that's the name of it and this is what I've been using for a long long time in fact I just ran out and this is what I get from her now so I've got the big bottle I'm going to refill the smaller one put them in the camper and this is what I'm going to be doing so I alternate my my shampoos that I use so I've been using that I have also been using the Nutriac system however I find that extremely drying I don't like how my hair feels with it at all so a while back I was contacted by a company called Sol Toscano and they have this awakening condition or an awakening shampoo I'm going to list all the information below for you guys to see this is a this is a beautiful beautiful formulation I really love how this shampoo makes my hair feel now last time I said yes to trying a shampoo I tried an all-natural shampoo that many other content creators also reviewed at the same time and loved and I looked I it to me it stunk to high heaven I couldn't stand the smell of it whatsoever it was just absolutely awful this smells really nice has a faint smell but more importantly it actually feels really good now it doesn't lather up a lot at least I have hot water so maybe if you have more of a softer water or something I don't know but I have very hot water it doesn't super lather easy so I have to make sure that my hair is really really wet and then I put it on and then I work it through and it feels really really good so what I've been doing is like every third shampoo or fourth I use this I also use a clarifying shampoo as well but I have found with this I actually like how my hair feels with this afterwards and they have the conditioner and the shampoo the ingredients are really really nice it's made with some essential oils but it's really my glasses on maybe I can actually read the ingredients cypress lemon mint and essential oils awakening basomo energetic this is an organic it's made in Italy and it's a whole bunch of different things on the ingredients but it's a natural organic and it's really I will have to tell you for an organic shampoo and my only experience has been a real negative one with an organic shampoo I was pleasantly surprised with this I really like this a lot I like this so much that when I'm empty I'm gonna buy these I'm gonna buy these these were sent to me this company had reached out to me and asked me if I wanted to you know try their shampoo I said sure and no obligation not being sponsored I I'll leave the information below I can't remember if there's any kind of a coupon code if there is I'll include that I just can't remember but I'm liking the shampoo a lot I really want the other thing I wanted to talk about that was sent to me this was actually sent to me by the octaline network and I'll include I'll insert the little clip here well I'm looking forward to trying this and they also sent me three products to go with it oh my gosh I think it looks very interesting I think it's time to sit down and read the directions octaline network this is the chi spin and curl and I'm gonna see if I can make it work but it I'm a little nervous because I've been reading the directions this is big and my hair is short so I'm like uh oh but I'm gonna give it a whirl a twirl spin and curl a spin so I'm gonna give it a try many of you use this my hair is so short I'm not too sure I'm going to be this is bigger than I anticipated I think maybe both my daughter-in-laws have long hair and I'm thinking maybe one of them could be a ideal model for this I don't know we'll see I hope anyways so now I'm back I tried this I I think my hair is too short to make this work I had a really hard time because my hair is so short I'm going to use this on one of my daughter-in-laws I have two daughter-in-laws they both have long hair I'm going to use it on one of them I did get a texturizing spray I have not tried that yet I am going to try that because next time I'm going to talk about texturizing sprays and then also a heat defiant or heat protectant sort of a you know an iron guard as they call it and then this one a silk infusion I think that's really nice I like I like being able to put stuff like that in my hair so this for me this was all sent to me via the Octoly network from Chi and this is their spin and curl and I know I just showed this in the clip I inserted I really like this I think the concept is good but for me on my short hair I could not really get this I think it's too big for my shorter hair I could do the end but I couldn't do the whole thing I like the idea I like the whole concept but I think I'm going to have one of my daughter-in-laws give this a try and see if it works for them they both have super long hair I'll let you know how that works the other thing since we're on here Marge is it Brickle Brickle anyways Marge has a Facebook group she's also on Instagram she also has a YouTube channel I think she's more active on Facebook and Instagram than YouTube but quite a while ago last year sometime she made a recommendation that I try the living proof perfect hair day body builder I bought this stuff I used it in my niece's wedding I absolutely loved it it's expensive and I'm thinking oh my gosh you know but you can adjust the nozzle you know so you can go from more of a heavier to a lighter spray depending on your needs but I really liked how it made my hair feel it was like Varum then she said to me Monica you gotta check this stuff out you could only get it at a certain you know on their website or you can get it at your hair um well maybe at a hairdresser's but anyways this is the Helium Fiberflex multi function styling spray finishing spray fix define and lift it's you pay about the same amount of money for one of these as you do for one of these but you get like what three times the product this stuff is like unreal unreal it is like unreal I mean I'm not styling my hair right now but take a look at what this did can you imagine if I put in like a few curls if I styled it if I'm gonna lift it up this does a fabulous fabulous job and you can go a little bit heavier you can lift underneath usually I go upside down and at the price of this you don't mind spraying a little bit more anyways at the point is that Ma just asked me a couple of times how I'm liking the helium and I have to you know I mean I have to really use something a while before I feel super super comfortable with it and let me tell you I'm liking the helium a lot I like the helium better than I like the living proof I really do the helium is better than living proof now even with this because my hair is so fine and so baby soft I need hairspray I could not I mean I could this is awesome for styling awesome for giving me some fluff some body some texture I could simply not walk around without hairspray so I always have to use some hairspray now this this is sent to me I haven't tried it yet I'm like a little hesitant because because I already have the other stuff in there so it isn't going to be any kind of a good it has a little bit when I sprayed it has a little bit of a I don't know what kind of I'm gonna have to check the ingredients out has a little bit of a smell but nothing bad where this oh boy so my battery died so I have no I had to switch cameras I just I couldn't find a battery so this is a different camera I don't even remember what I left off but I think I was saying that I have to try something a little bit longer before I can really feel comfortable with it and that is the you know the case with sprays or anything else of that nature so you know with my hair being as fine as it is I need all the help I can get so I just wanted to share this with you thanks to Marge who turned me on to it this is replacing my living proof you can't go wrong with the money you really can't I absolutely love it and my brush if you if you have thinning hair and you're afraid to really brush your hair don't be afraid to brush your hair because all of the powder stuff that we put on our hair to you know to kind of like give it more volume give it some depth this this would be something that I would put on to you know make my scalp not show up as much as you can see I see my scalp which you can see a lot of my grays right now too so you know it's going to be it's going to be hard it's hard for me now maybe you guys will think oh monarchy you're out in left field but it was like super hard for me to go and really think about brushing my hair to that extreme every single day and my hairdresser said if you do that especially before you hop in the shower and you shampoo your hair you will lift all of the products off of your scalp and you'll also remove a lot of it from your hair fibers themselves but more importantly making sure that your hair follicles follicles are clear of any of that debris and this feels wonderful J1 Khan picked up my brush and and he was like oh I like this he says oh I like this I said keep your paws off of it it's my brush it's my brush so I just wanted to share those things with you a couple of things like I said this is really nice oh almost dropped it really nice shampoo conditioner and I really appreciate that they have two different color lids because when you're in the shower I don't shower with my glasses so it's hard for me to look at something that's identical and say what is what but I know when I use this the darker color is going to be the shampoo it's the dark color it's going to be getting everything off and the lighter color is going to give me the conditioner and the fluff and all that stuff after the fact that's how I remember it in the shower so I really like that I wish all shampoo bottles were able you able to distinguish them in the shower in water without any glasses on without any glasses on and this I really wish I could I'm going to play with it a little longer and see I really wish I could get this to work for me but I bet it'll work for my daughter-in-law one of my daughter-in-law's so anyways that's a quick hair video I wanted to just talk about my hair and some of the products that I'm using I'm also going to share with you some skincare stuff that I've been using that I switched up a little bit I've had a couple of requests for an updated skincare video so I think I'll probably end up tackling that along with some more empties so anyways guys thanks so much for hanging in there and I will catch you all later bye bye


He asks you your opinion. Recently, my girlfriend was telling me some skincare products that I might want to consider to rejuvenate my face a little bit. And I was asking her opinion on this. I want to get her take. Women tend to be better in this process of things like that. So when a man asks you or your opinion, he seeks to get your help or advice, that's a great sign that he is at least committed to want to exploring something beyond the surface. And yet sadly, these days, most people spend too much time on the surface in the, how's your day going? Did you have a good day? I hope you had a good day. You know, we got to stop talking about our day and start digging deeper roots if you want to have a fully committed relationship that goes the distance.


Hey everyone, this is Dan. The stock price of Taiwan semiconductor, TSM, hit the all-time high of $142 back in February. After a recent downgrade by Morgan Stanley, the stock price has been running below $120 in the last few weeks. Morgan Stanley was concerned about the capital expenditures of TSM. I studied the financial reports and found that Morgan Stanley was way too pessimistic. I am therefore still bullish about TSM. Let's get into the details. First of all, let me review my price target for TSM in my May 27 video. At the time, I set the target at $160 a share by the end of 2021. On May 27, TSM was at $115. Today is July 12, and the last trading day was last Friday, July 9, and TSM closed at $120.65, definitely the price moved in the right direction that I predicted, although it is still quite a distance from my target at $160. In light of what's been happening lately, especially with regard to capital expenditures, will we need to set a new price target? I will explain more in the next few minutes. There are three main events that have been affecting the stock price of TSM. Number one, it's a recent drought in Taiwan, and as you probably know, most of the manufacturing facilities for TSM are located on the island of Taiwan. And also, there's been a recent surge of COVID cases in Taiwan. And then there's this proverbial capital spending concerns. Let's look at them one by one. On April 20, it was reported that there's been a very severe drought on the island of Taiwan. Actually, it was the worst drought in 56 years. And the government started to restrict water usage for farmlands, residential buildings, and even businesses, although the production operations at TSM never got interrupted, even during the worst period of the drought. Recently, on June 5, it was reported that since Taiwan has been getting a lot more rainwater, the government has lifted all the water restrictions. So life is back to normal again in Taiwan. drought is no longer a problem. How about COVID? According to the BBC report, this is a collection of reports from May 16 to May 26. Because of the surge of new COVID cases, schools and public places have been shut down. Most recently, as you can see from this chart, the daily new cases have been coming down. As of today, there are only 24 cases. Now for an island with 23 million people, 24 cases a day is minimal. That's why the pandemic is no longer an issue in Taiwan. However, there's a caveat. Let me explain more. Why? Because only 0.3% of the population in Taiwan is fully vaccinated. They are running behind as far as vaccination. As compared to the US, where 48% of the people have been fully vaccinated. So Taiwan needs to catch up with vaccinating their people. Have they ordered vaccines already? Yes, they did. If you look at the vaccines they've ordered, they ordered a total of 35 million doses from AstraZeneca, Moderna, Johnson & Johnson. And then these two vaccines are produced, developed in Taiwan. They're still in phase two. But then they also have BioNTech vaccines on order. All together they should have about 35 million doses. And with the population of 23 million, that's just about enough for the total population except they have to wait for the doses to be delivered or produced. So far, they've received only 7 million doses. The government has been containing the spread of the virus by implementing very stringent quarantine procedures and also with very rigorous contact tracing. So far it's been working and the production operations at the Taiwan semiconductor plants have not been affected. And actually I'm pretty optimistic about their being able to control the COVID virus. At this point, if you like what you've seen so far, I'd like to encourage you to click the like, subscribe and notification button. That'll encourage me to produce more videos like this in the future. Let's talk about capital spending concerns. On June 20th, Morgan Stanley downgraded Taiwan semiconductor. They downgraded the stock from a target of $655 Taiwan dollars to $580 Taiwan dollars. And they downgraded the rating to neutral because the stock is traded both at the Taiwan exchange as well as new stock exchange, $580 Taiwan dollars at the Taiwan stock exchange is equivalent to $120 at the US exchange, $120 is just about a price where TSM is being traded now. And actually that's been a pretty strong resistance in the last few weeks. Also today, July 12th, the price broke above $121 and let's hope it'll stay above that line. It's a very important movement today for the price to finally break above $121. And that's a bullish sign. Also March 31st, Taiwan semiconductor announced that they decided to raise the full year 2021 capital expenditure to be around $30 billion. I've been digging into the financial reports published by TSM, especially this very important report 20F, which is the annual report they are required to file with the SEC in the United States. The 20F is equivalent to the 10K report of an American company. TSM is required to file the 20F instead of 10K because it's a foreign company, but they follow the international accounting standards, which are very similar to the US accounting standard. First of all, let's look in their revenues for 2021. The company has been very transparent about the monthly revenues. And this is from the company's monthly update that you can see January year to year that revenues increased by 22% compared to January of last year and then February and so on. So the average to six months from January through June of 2021, the revenues on the average went up by 20%. So that's an important number, 20% going forward. We got to use that as an assumption. And then in the 20F annual report, I pull out the revenue numbers for the four years plus the 12 month revenue number and then the net income and then the depreciation number. Basically, the net income is the revenue minus the expenses. And in this case, I simplified the calculation by using the actual depreciation expense.  That's basically all the other lines in the income statement added together. So this number is real from the report, that's real and that's real and that's a derived number. It's an aggregate of all the other numbers on the TSM report. And then I translate these numbers, these are in Taiwan dollars, by the way, I translate them into percentages. If the revenue is 100%, then we can factor in these three that should add up to the revenue, the expenses and the income. And that's why for the most recent 12 month period, the depreciation is 26% of the revenue and the other expense, it's 35.5% of the revenue and net income is 38% of the revenue. And this 35.5% is pertinent, we'll be using that number later on. I'll talk more about that. And finally, let's look at depreciation and amortization. This is from the quarterly report from TSM, especially the first quarter report. They reported a net revenue of $362 billion, that's Taiwan dollars. And then for 4Q, $361 billion, the first quarter, 2020 and $310 billion, Taiwan dollars. And then for depreciation and amortization, these are the numbers. And from these two lines, I can calculate depreciation as a percentage of revenue, which is 27.8% for the first quarter of 2021. And then for this quarter is 27% and this quarter 22% and not surprisingly, because they've increased their capital spending that this year, first quarter percentage wise, the depreciation is higher than 2020. So this year, we're at 27.8% of the revenue. That's an important number. We'll use that later on in our calculation. So let's carry all these pieces of information into 2021. Here we go. First of all, what will be the revenue for 2021? Since we are averaging a 20% increase, I just take the 2020 revenues, increase it by 20%. Here we go, at $1.6 trillion, Taiwan dollars for estimated total revenues for 2021. And then for depreciation, remember here, we got a 27.8% of the revenue for depreciation first quarter 2021, use that number, multiply that with this projected revenue. Here's our depreciation for 2021 for the entire year. And the other expenses, since we arrived at this 35.5%, from this calculation, again, we take this number multiplied by 35.5%, we have $570 billion of other expenses. And with these, then we can subtract these two numbers from the top, get $589 billion Taiwan dollars for net income for 2021. If you look at this number compared to 2020 number, it's a 15.5% increase from the 2020 net income. Is that number good enough? Let's compare that with the estimate from Yahoo Finance. Yahoo Finance uses an average aggregate number from the various analysts, they track. They're saying that for the next quarter, their expected increase in earning per share should be 13.3%. It's actually even lower than the 15.5% here. Although for the next five years, they project a higher number at 15.9%, which is just about the same as the 15.5%. And that's why if TSM does arrive at this net income number for the entire year of 2021, it'll be right on target. That means they're not going to create any positive or negative earnings surprise. And that that's good. Let's look at what the analysts are actually saying about TSM. Well, let's recap a little bit. The July 9th closing price, it's $120.56. My target was $160. That might be subject to change. Yahoo Business gives them a buy rating 1.9 out of 5, which is a good rating, a smaller number to better. The high target is 1.77, definitely way above the 1.20, even above my 1.60 average target 1.40, low target 1.05. Who's never really an overall B rating, which is a buy rating, pretty positive. TipRanks.com, it's a moderate buy, high target 1.50, average 1.27, low 1.05. And then CNMoney, it's a buy rating, high, 200, medium, 1.47, low 1.05. The street.com is a buy rating, target is 1.54.7. All these targets are higher than the Morgan Stanley target of about $120. And that's why I do believe that Morgan Stanley has been too pessimistic about TSM. What's my own calculation? I took all the leading semiconductor companies, lined them up, and looked at their P-E ratios and decided that an average forward P-E ratio of 27 is reasonable for a talent semiconductor. And incidentally, the current P-E ratio is 32.65. So 27 is a very reasonable assumption, actually is on a conservative side. The earning growth is 15.9%. And that's from Yahoo Finance. Remember my calculation earlier was 15.5%, 15.5%, 15.9%, it's about the same. So using these two assumptions, and I start to use the 2020 financial figures for TSM, I apply the P-E ratio and the earning growth. And I arrive at the stock prices for 2021, 2022, and 2023. And with these three numbers, I decided it's reasonable, actually very conservative, if I set the price target to $140 to be reached by the end of October 2021. It's $20 less than the 160 target, however, the 160 was aimed to be achieved by the end of 2021, but now I'm accelerating the achievement of my target to the end of October, except at only $140. And by then, we will already have the third quarter earning from TSM. And then I'll be in the position to fix a higher target, hopefully, at that time. So the new target is $140. I'd like to also encourage you to subscribe to my Twitter account, because if there's any news development, if I buy or sell TSM stock, I will definitely share that information with my subscribers by way of my Twitter account. And you're welcome to also send me your comments, questions and suggestions to my Twitter account as well as to my YouTube channel. Actually, I would very much appreciate your input, so I can make my videos even better, more informative, more interesting. Again, let me recap, my new target is $140 a share by the end of October 2021. The current price is at $120.65. That's the end of last Friday, today's Monday. Actually, today's closing price is already $122 in change. So it's definitely moving the direction that I'm predicting. Again, I'd like to encourage you to click a like, subscribe and the notification button. I'd like to remind you that I'm not a financial advisor. I share my stock trading strategies for educational purpose only. If you want to buy or sell stocks, you should make your own decisions and you should definitely consult with the financial advisors before you do so. This is about rest of my video for now. I will chat with you again in the next few days. In the meanwhile, I'd like to wish you the very best of luck with your financial investments.


I mean, I'm not gonna be hitting drunk, but I might as well have a glass of wine. You know what I'm saying? Hey guys, welcome back to my sadly realistic portrait of what a 30 year old lady's life is like. He's just got a nice little lady. A lot of you guys have been tweeting me pictures of things that you would like me to do, mostly to my face. Apparently that's all I am. It's just a face to glue things on. But I realized that a lot of them were actually sort of like face painting, which I've never done before. So I went out and I bought this very fancy kit of grease paint, which I tested and just seems god-awful quality.  And I think it looks fun and funky and I want to try it. Ugh. Yeah. Are you okay? This is what a grown woman does in her free time. She has a glass of wine and she buys a face painting kit with some stickers in it. That's so funny. You laughed a little too hard at me, Debbie. That's why I'm buying some. There's a way you said it. It was like really derpy. I don't know. What was derpy? Knock, knock. I'm knocking louder. Who was there?  So the first look that I'm going to create was this picture that someone tweeted at me of someone turning their mouth into an eyeball. Which I feel like I can handle. Except I was reading these instructions. Listen to this. It says warning, do not use red, pink, yellow, or orange in the eye area. Do not use blue, yellow, or green on the lips. Bitch, this is face paint. Why is any of this not safe for some part of your face? Just know that I'm putting my life at risk for you, for us. What's that mouth I do? Ugh. So they gave me this base white color, which I'm going to put all over my mouth. Should I make my eye like a little bigger? I have like little lips, so I can't just like make it the size of my lips. Girl, ain't nobody's eye that little. I think you're good like that. Just, I think that's... So I haven't watched any tutorials. I don't know how to do this. I'm literally just looking at the picture and trying to create something magical. Peanut butter and crack sandwiches! So of course this kit came with these nasty little sponges in here.  We're just going to dip into the black here. Don't squint at me. Wait. Oh my God. It's not looking like an eye at all. It's like a cloud mouth. Expectation versus reality. I have enough lip to do this? Girl. Okay, so we got to make one side like the wing and the lashes. I did too many lashes.  Okay, you know, this really is an awesome great start. Really? Oh my God. Does it look like an eye or not? It actually does. It looks like an eye with a hole in it. Oh my God. It lacks all of the detail that makes this one good. Like how does that one have so much detail?  It looks like you did colored pencils. Honestly, it's probably a fake picture. I look like I know every move to the industrial dance.  It's my third eye. It's just reversed. It's on the bottom. God. Smash your pants. Get them next time, champ. Can't win them all. Thanks, coach. You win some.  The next face painting that someone tweeted at me was actually a picture of this. But I asked Julian if he'd be willing to participate and he said, no, I'm not shaving my beard. Good news is I... Oh my God. I found this picture, which allows me to accomplish that in one face. So I think what we gotta do, we gotta get to white on here. So I'm trying to figure out the order of operations here without watching anything because I don't feel like it. But I think maybe I put on the colors and then outline it, you know? So, here we go. What is that fish's name? It looks like a Frederick. That's a look. So then he has like a fin that goes up here.  That's not even... Not even bad. Orange isn't going to work. It's too pale because I am the color of that. Should I try like a pink? Yeah, pink, maybe. Oh, she low-key thick though. Imimi!  You still can't exactly tell what's going on in my face.  It looks a little... What about this says blowjob to you?  You're actually pretty good. If you draw it in your face, I couldn't do this. I guess it's because you're going to make up and it turns like... I'm not even that good at makeup. Love having compliments, rejecting happy in my face. Oh!  They're eating each other. No, they're kissing!  Turn to your right. Do you know what right is? Usher right. Okay, now go like this. Oh, that's terrifying. It looks like Marvel's going... No, do it the other way. This could have been us.  But I'm playing. But you're what? This could have been us, but I'm playing. If you saw me... I mean, it's cool looking. Hi, my name's Jenna. I'm 30 years old. I have a hamster and I have a basketball game tomorrow. It's in my face. I look terrifying. From what I can see from here, I just... I look like... It's very Harvey then. It's pretty cool. Ooh, they nasty. They all look like real fish. Do you like it or not? You're going to take me on a date like this? Yeah. Where would you take me? The aquarium? I think you're my carer. You know, like, Ela takes care of Ethan? I am your carer. You're my carer. I tried to use coconut oil to take this off, but you can still see the pink fish. It dyed my skin.  Look at this. Girl. What is... You don't... Okay, I'm going to do it. I don't think I have enough space here to create another... another eye. Like, it actually looks like there's so much space. I think she just shaved the berms. Shaved, like, up to here. I think it'll help me to do the hair. So she's like this. That is not enough space to put an eye!  Nice. I got the whites done. Is that even in line with my own? Yeah, it's actually pretty straight. Look at me. Yeah, it looks good. Let's fix my eyebrow and make it backwards. Yo, that shit's fire though. Backwards brows. It's going to catch on. Oh, no. That's too much brow. I don't have enough room.  Is that good? Does that look like enough? Yeah, it does. How's that? That's better. Looks like really good.   Let's give her some liquid eyeliner.  Now we got to do her mouth. There's a cockroach right there. Oh, my God. Oh, my God.   Should I just vacuum it? I know. I got to get out.  It's going to work. It's going to work. Stop. Cockroach lips because we can't find it. All right. Let's give her like a beautiful red lip. She does kind of look like me. All right, Julian. Oh, my actual God. It looks horrifying. Hey, girl. It's me. How you doing? All you need is help. You're pushing like this. Like, why don't you dump it? Oh, my God. Okay.  How long?  Batman logo-y, but good. Are you the art police? That's fine. What's her name? Yeah, Torpol. Her name is Torpol? Torpol. Torpol? Torpol. P-R-P-V-L-E. Torpol. That's not a name. What happened to you? Torpol, look at me. You understand, don't you? E-P-P. So creepy. How you doing, baby? Oh, my God, no. E-P-P. Oh, Torpol, no. E-P-P. E-P-P. Come give Torpol a kiss. That got me a new man.  And we gotta burn it down. It's time to burn the house down, Julie. When we sleep tonight, he'll probably just be crawling in our mouths. Oh, my God. I'm not gonna be able to sleep. Torpol doesn't have to worry about that. Torpol's mouth doesn't open. You guys tweeted the pictures of me, so I did them, and I hope they are pleased with yourselves. Make sure you subscribe to my channel. I'll put on a video on Wednesday slash Thursday. I'll see you guys next week. Is Torpol your new girlfriend? She's just literally and figuratively my side piece. Oh. Sips wine, walks hamster on a lead. No, I'm not embarrassed. I'm just painting the face from the fish that I painted on it because I'm Jenna and I'm Thorny. I have a hamster and I have a basketball game tomorrow.