 This is a little informal. I don't know if somebody was supposed to introduce me, but it's 1041 and I like to be punctual, so let's go. I'm really excited to be here. It's my first time at RubyConf, first time speaking here too, so should be pretty cool. Plus I've been listening to hip hop all morning to get pumped up for this talk, so. So my name is Micah Adams. I work for Modeset, which is a software consultancy based out of Denver. We focus on early stage startups, so we do development for them, and our goal is usually to get our startups from series A to series B funding. Most recently we worked with LO, which is a social network that's ad-free, if anybody's on that, so pretty cool. So the title of my talk is not so new on the matrix, and of course you're probably wondering why matrices. There's probably two or three groups in this talk right now. Some of you probably use matrices all the time, and you're awesome at it, so I ask you to be kind to me during the Q and A session, and don't grill me too hard or put my feet in the fire too hard. Others of you might not use matrices at all. Maybe you're newer to programming, maybe you don't have such a formal background in programming, things like that. And then some of you might fall into the third camp, which is very much like myself, where actually matrices became like this new revelation because I had just forgotten about how much I'm using them in my code. So for me, this reintroduction happened during kind of a short 4A into OpenGL programming on Android. So I had a client who wanted us to develop a virtual reality application using the cardboard headset that Google and the SDK that Google came out with. And I did that. It was a really, really quick project. It was only a couple of weeks long. We just slammed out some code for that. But I was getting all battered minehuff, and I started seeing matrices everywhere. I started seeing matrices in my sleep. I started seeing it down the street when I was walking. I started seeing an old code when I would review old code that I'd contributed to. I started seeing matrices then. And it became a little bit of an obsession to me, and I also realized how much I use it versus how much I'm aware of when I use it. So I formulated kind of a hypothesis. And the hypothesis is kind of not that mind-bending. It's pretty straightforward. The matrices are useful in all sorts of situations. And not just in the OpenGL application I was working on, right? Matrices are used everywhere for all kinds of things and all kinds of different domains. So I really wanted to explore that hypothesis with this talk. So what I found out was I had forgotten how prevalent that data structure is. I use matrices constantly, but without really stopping to consider why I'm using that data structure. And I deeply repressed all memories of calculating the inverse of a three-by-three matrix by hand. I need therapy for that stuff. Has anybody done that? Any math majors or any? Yeah, so brutal. Like the amount of calculations you have to do, you just thank your lucky stars that computers exist to do this for us, right? The other thing I found out is that Dungeons & Dragons is a great domain for me to explore new domain patterns. I am a ultra mega Dungeons & Dragons nerd. I've been playing since I was like seven years old. I own every edition of the game. I've considered getting a Gary Gygax tattoo. I mean, it's really part of my life. And I'm a domain expert in Dungeons & Dragons. So when you're trying to explore new programming concepts, using your domain expertise is a great way to flesh out those ideas, right? So we're gonna talk a lot about Dungeons & Dragons in this. And I have a little interlude. Yeah, I have a little interlude for people here who might not be into Dungeons & Dragons, but, you know, we'll figure it out. So what I hope you as the audience get from experiencing this talk is a renewed interest in matrices, awareness of when you're using them in your code, but not only awareness, but when you should be just removing it, right? Because as we're gonna see in one of the code examples I have, a matrix in that example is really just brute forcing code. And there's times when it's not appropriate, there's times where it is appropriate. And when you get like me, you get obsessed with the concept. Sometimes you try and insert it everywhere, get too excited about it and put it in places it doesn't belong. So with that, the caveat is your mileage is gonna vary with some of the code examples here. Okay, so the aim of the talk is not to tell you how to use matrices, but it's to make you more aware of matrices and how they might be used. But if you don't need it, like I said before, if you don't need it, please, please, please leave it out because there are certain times where it's just not very performant. So the tool I didn't read talks of, somebody needs coffee and they're like really not interested in this. You can watch this slide and you can leave and I won't be hurt, okay? Too long didn't read. Matrices are pretty much just multi-dimensional arrays. Vectors are pretty much just arrays. I just blew your mind right now. So those are interesting factoids, right? But the context around them is what kind of fleshes out this talk. So we're gonna explore the context of that to a greater degree. So to get more context on those, we're gonna talk about fields, some of the mathematical concepts around fields, talk about vectors and matrices, and then we're gonna go through our three code examples, kind of flesh that stuff out. So what the heck is a matrix anyway? Well, simply put, it's array of elements that have been organized into rows and columns. Very simple stuff. We can think of a matrix as being composed of vectors, so a column in a matrix is a vector, a row in a matrix is a vector, and the elements of a matrix can be considered as being over what we call over a field. So that begs the question of when should you use a matrix? The snarky, grumpy developer in me wants to say, well, it depends. But one way to actually figure that out is when it's appropriate, is to look at some places where matrices and vectors and mathematics around them are actually being used in programming, right? So the first thing that everybody thinks about when they think of a matrix or a matrix manipulation is in the context of graphics or graphical programming, right? And this literally is just, in some way, shape or form, using mathematical primitives, polygons, and then manipulating them. So I don't want to get too much into three-dimensional programming because it's way out of the scope of this talk, so let's talk a little bit about how vectors and vector manipulation can be used on two-dimensional shapes as a quick and easy introduction to this stuff. So what we see here is a little animation of a triangle and it's being scaled, right? It's kind of like throbbing, scaling over time, it's on a timer, right? How is this actually being achieved? Well, we have some interesting parts of the triangle called vertices, right? Which make up the three points of the triangle. And then we're applying a transition on that triangle to make it grow and shrink. And that transformation is being achieved by multiplying the vertices of the polygon by a vector, right? So if we see, if we look at this, assuming that one of the vertices in the above triangle is zero and one, scaling it is multiplicative and we can just increase its size in that way. That's scaling, so another transformation we can do is translation, which is the same concept, except you're moving the triangle around an X and Y coordinates in a coordinate space, right? You could also rotate the triangle's vertices by applying this transformation to it, which would rotate it, and you're actually multiplying this matrix of sine and cosine to actually rotate that vertex 90 degrees. So like I said before, there's a lot more to say about this and the domain of 3D programming and 2D programming. The purpose of this talk is to more demystify matrices and I don't wanna get too sidetracked on that. Maybe we can talk a little bit more about it if there's people out there who are graphics gurus. We can talk more about it in the Q and A section, but I think that's pretty good for the scope of this talk. So what are some other applications for matrices and vectors that we might not think about all the time? Well, you can represent probability distributions in a matrix, right? So this is a Markov matrix. So the first row, the way you read this is you can read through the first row and it breaks out a probability distribution of whether it will be rainy or dry. Each row adds up to one, which is a little factoid about that. So that's kind of a representation of a state of a system. You can also use matrices in cryptography. Cyphers, get it? That adds terrible one. A cypher can be considered in terms of a matrix though, right? So here we have a cypher. This is actually, anybody know what cypher this is? This is a Caesar's substitutive cypher with a shift value of three. So given an input A, I would be able to shift it. That letter gets shifted to D, et cetera. This is also the paleolithic age of cryptography, right? If you guys are storing passwords like this in a database, leave now. We can also visualize or understand relational data in terms of matrices, right? So this could be used, this is a force directed graph. We care about this kind of stuff in things like social networking where we're trying to represent relationships between people. So the way you read this is that you have a node of one and one has an edge that's corollary to four. You kind of use the matrix like you would use a map coordinate. So if I wanted to look at four's relationships, I look on the left-hand side, the column of four, and I see that four only has a self-referential relationship. We use this quite a bit. I mean, this is a representation, not really gonna go too much into detail on the operations on graph structures, but know that eventually you're gonna store this in some way, shape, or form in a matrix. So those are applications of matrices and vectors. So let's get into some more mathematics or mathematical concepts around this. So let's talk about fields. So a field is a collection of numbers that have the properties of addition, subtraction, multiplication, and division. You can think of a field as a class that obeys an interface. The interface defines the methods for addition, subtraction, multiplication, and division. And I apologize for inserting Java pseudo code into this talk. I think there's somebody in the back who will hand out pitchforks and torches to route me after the end of the presentation. But as you all know, there's no really true interface style construction ruby, so Java makes sense. Anyway, so if we consider the field of real numbers, you know, a real number can be rational or irrational, it can be positive, negative, or zero, and it implements those methods for lack of a better term, of multiplication, subtraction, division, and addition. There are infinite numbers in this field, so it's kind of hard for us to understand it in terms of a matrix because you have infinite elements in the field. What does that really mean for me? Also, as a side note, when you think about the field, you can also think about the field of real numbers as what's on a number scale, right, or a number line. So there's a better example of a field that we use a lot in computing. We call this a finite field, or a Galois field, and it's a field that contains a finite number of elements in it. So anybody have a guess of what finite field I'm thinking of? What's that? It is, thinking of specifically two integers. Okay, good, no hardcore mathematicians here, I don't think, because the answer I'm looking for is Galois field two. We use that for binary representation, so Galois field two is a finite field with only two elements, zero and one, right? If you know that, and you're being mum, please be kind to me, because I'm not a classically trained mathematician. So we can talk more about that in the Q and A section if you want. So let's digress for a minute and talk about using the finite field, because of the properties of a Galois field with limited numbers of elements, there's some really interesting things we can do with it in computation. So this is addition on Galois field two as represented by a matrix, and this actually has a specific Boolean property, does anybody know what that is? Any takers? Exclusive OR is what that operation is, so you can flip bits basically when you are reduced to two elements in your field. Another application is encryption with Exclusive OR, right, so if you want to read through this, so the column denoted by P would be a plain text bit, right? The column K is our key and the column C is the cipher bit that would be encrypted or that would be the output of encrypting the plain text with the key. So if you look, the interesting fact of this is that the first row, given a plain text bit of zero and I encrypt it with a key of zero, the cipher bit that comes out is zero. On the last row, the plain text bit is one, the key is one and I also get a zero. So, if we assume that that key is truly randomly generated, we can say that Exclusive OR has the property of perfect secrecy, and perfect secrecy in quotes because we all know that things get brute-forced all the time, but walking backwards, I can't, if I don't know the key and I have no idea what's going on with the key and how it was generated, I don't really, I can't really use the cipher text to work backwards and derive the plain text. So this is kind of beyond, that's kind of beyond the paleolithic age of substitutive ciphers and this is more mathematical ciphers that are used in cryptography. So we digress a little bit and talked about the field and the finite field. So let's move on to vectors. So since we know what a field is, we can assume the definition of a vector which is a list of scalars over a specific field. They're also understood as components of matrices which we'll look at. So this is an example of a vector over the field of real numbers. This would be, in our terms in Ruby, that's just an array. Here's a vector over the field of Galois field two. Vectors can also be understood in terms of a function. We use this concept all the time in programming, right? So you can refer to things by their index. So there's a mathematical concept around that. And Ruby's matrix library has a vector class as well. So if you need to do more intensive computation on vectors, you can use Ruby's matrix library for that. So we talked about fields. We know what that is. We know what fields are. We talked about vectors. So what is a matrix then? Well, in code, we often just use multidimensional arrays to represent matrices, right? Or we can use Ruby's matrix library. We have a code example in this talk. Give you an example on how you can use that, right? And using Ruby's matrix library gives you some extra methods that you can utilize for more intense calculations. There's also a couple contributed or open source libraries out there, like N matrix that you can use for that. But now I think we should move on to code examples. So the first code example is my favorite is I get to talk ad nauseam about Dungeons and Dragons. So this is how you would use matrices and vectors for relational data or subtitle how D&D always teaches me interesting code concepts. So who knows what D&D is? Everybody, lots of people. All right, so those poor unfortunate people who don't know what Dungeons and Dragons is, this is what it is in about five seconds. It's like World of Warcraft except with pen and paper. Who doesn't know what World of Warcraft is? Okay, I won't call you out, I'm sorry. That's the main thing to do. Anyway, if you don't know what World of Warcraft is, it's like playing make-believe with stats, dice, pen, and paper. And so when you're playing D&D, you have a group of characters that play, you have a dungeon master that runs the game, right? And D&D is really composed of a lot of static data that has some inherent relationships in it that apply to your character. So as you adventure through your campaign in D&D, you gain levels, right? You get better at your job, and your job is killing orcs and getting loot, right? You get better at that. So that's a function of how many experience points you have. And the abilities your character possesses are related to what character class you have, right? So if you're a rogue, you're really good at sneaking, you can sneak around, you can backstab people. If you're a mage, you can cast spells, things like that. The same thing with fighting, if I'm a mage and I'm level 10, I'm terrible at fighting compared to a warrior who's level 10. There's a lot of different reasons for that, like my strength as a mage. I might not build up my strength because I need intelligence as a mage to cast spells and things like that. So all those things are interrelated. So when I was exploring this concept, I was like, I think it'd be kind of fun to work on a character generation tool for D&D, and specifically a character generation tool that just pumps out character sheets and encourages you to play the pen and paper game. I wasn't trying to reinvent D&D in some kind of digital format, right? So basically what the idea is that you fill out a web form and it generates a nice PDF for your character. You can snapshot your character at different levels, at different points in their adventuring career, and you can preview your character's abilities and their attributes at any point in their career, and also at character creation to where you can say, do I really want to be a wizard with these stats? So the challenges for this project was one that I felt like it was way too small for like postgres or something on the back end, right? I didn't want to use like a full rail stack really for it, and I didn't really want to use anything too persistent because it's really just lookup tables. And I wanted to be able to snapshot characters at different points in their career, and I wasn't interested in developing like an engine. So I didn't want to build a game engine and say, I don't know, I can run games virtually in this environment. So like we were talking about before, I started with how do I model a character's level and their experience points just using data structures, right? So in D&D, a character's level is related to the number of experience points they have. So here we have a table, tabular representation of their level versus experience points versus proficiency bonus. Proficiency bonus for those who don't know is a modifier that you apply to when you roll a die in D&D. So the other thing to note is that this relationship really never changes. Like when you get to 900 experience points, you are level two until you get 2,700 experience points. So as I looked at that table and I kind of took it apart, I saw that it was just a matrix. It's just a multi-dimensional array. And mathematically, this is not extra interesting, but it is very pragmatic way for us to organize that data in a matrix structure, right? So we're just gonna model the static data using this in code. So my kind of first blast at this was like, well, if I know what levels I have and I know what experience points I need to be at a level and I know my proficiency bonus, I can literally just zip these up into a complex array and now I have a lookup table. Unfortunately, you have to know how I organize the table and enable to really pull data from it though, right? I mean, what stinks about multi-dimensional arrays is that you have to know the column and the row number in order to really pull data back in an explicit way. Or you have to iterate over those columns and rows to pull data back, which we'll see in a later example is pretty slow algorithmically. So you could also just pivot that table and turn it into a hash, which is nice for us as humans. So we don't have to keep track of so many numbers, right? We have a reference point to what the row of level zero would be in that table. So I can create a hash, I could iterate over it and pivot that table and then put it into a hash and now I have a complex data structure that I can use to refer to my character. But what if we wanna get a little bit more refined with this or what if we have a little bit more complex data? So yeah, it's fine if you just wanna represent a table, right? But what if we have interrelated data in glasses? What if we wanna use composition for this sort of domain pattern? So say I wanna represent a character at any given point in their career. I started going through and just breaking out the character into their subsequent static class parts, right? So for example, the vector of abilities that are allowed or the ability scores that you have in Dungeons and Dragons are strength, dexterity, constitution, intelligence, wisdom, and charisma. So I said, hey, why not just make that a vector of symbols, right? Same thing with a level. I mean, a level is really just a vector from zero to 20. And then experience points. I didn't wanna work backwards and figure out how what the math is around going through experience levels. I'm lazy, I didn't wanna derive that algorithm. So I just put it in a lookup table or a lookup list and I vectorize that. So now given those classes that I built, I can just build my character advancement table by zipping up my level vector and my experience vector. And it becomes really easy to refer to later on. So obviously this isn't gonna give you the kind of robust querying you need in a relational database, right? You can't do like joins or certain things like that. But if you're just using something as a reference point, you can use just composition and classes to build that. So this little code snippet shows a method that I built for the character class, given ability score rolls, zip them up and put them into a key value pair and now you have your ability scores. So what's a use case for this pattern? So you have a bunch of static lookup tables and you have classes that build vectors. Well, modeling complex business logic is actually a lot of times we're gonna use a pattern like this. Depending on how complex that business logic can get, I don't know if you would always wanna keep that in code or in a Ruby process in memory, but it can reduce a lot of cruft. And I mean, at the end of the day, this is really just the composition pattern, right? It's the composition pattern of classes relying on a very simple data structure to refer to. So let's move on to a more mathematical approach, which would be solving systems of equations with matrices. And this is a little bit more focused on mathematics and using the Ruby matrix library for things. I think not a lot of people will probably get a lot of mileage from this code example, but it's interesting to see it work. And it will be good therapy for people like me who remember inverting three by three matrices. So let's assume we have these three equations. We wanna solve for the variables in those equations, right? Well, Ruby has a pretty cool matrix library that we can use for this sort of thing. So we can use matrix algebra to solve this system. And we're gonna do it with the Ruby matrix library. So the first step, if you remember, you're solving systems of linear equations from school or college or whatever, is you remove coefficients and place them into a coefficient matrix, right? So two, 10, and eight become the first row in our coefficient matrix. Seven and seven are for Y and Z. We don't have an X value, so we assign that zero in the second row. And then we move our third row, our three fives go into third row there. In code, it's just setting up a multi-dimensional array. We collect those values and apply typecasting to a rational in the rows because we don't wanna have rounding errors when we actually calculate this stuff. So then we set up our matrix of coefficients in the last line there. So then we pull out the constants and place them into a matrix. So 54, 30, and 35 go into their own matrix. In code, I was lazy, so I just explicitly typecast each one. There's probably fancier ways to do it. We can play code golf later. So now solve. Thankfully, computers are awesome at this kind of tedious arithmetic. Solving by hand is much more complicated. You have to get the inverse of that matrix. It's a very intensive operation. I think I counted at least 11 major steps with three or four sub steps on each step, and it's really complex. Then once we've derived that inverse matrix, we would multiply by the constants matrix, which actually can get pretty burly too when you're looking at a three by three matrix. But what's so great for us who have done this on paper is that in code, this is just so beautiful and simple. I just tell the coefficients matrix to invert itself and I multiply by the constants and now I have my solution. So use cases for this pattern. Anytime you're using linear equations to model a problem, which actually might happen more often than you think. I will say though, it was hard for me if I was thinking about my recent experience and my experience overall as a developer. Can't really say one time when I've literally said, ah, that's a system of linear equations. I'm totally using that. But I'm sure I have. And anytime you need to compare those values in an equation form, you're pretty much using that pattern. So I'd be interested to see if other people have used this. Maybe we can talk about that later. And this only really scratches the surface of matrix operations, but it's a good simple way of understanding what you can do with matrix operations and how you can solve those equations with that. So let's move on to what's gonna be our third example. And we're gonna use matrix as a data structure internal to our algorithm. So we're gonna use a matrix in this algorithm to calculate edit distance between strings. This has a specific name. Everybody know what it is? It's a Levenstein distance algorithm. So we're gonna calculate a score. We're gonna take a source string and we're gonna try and convert it to a target string. And we're gonna calculate a score for that. And that's gonna be a distance score, which is a function of the number of deletions, insertions or substitutions required to transform that source string into a target string. So the greater the edit distance score, the more dissimilar those strings are. So the steps of this algorithm are pretty simple. You build up a matrix using the lengths of the source and target strings. That informs the overall number of rows and columns you need to compare. Then you iterate over each string. And then based on the differences between those two values, you assign a score. That score is stored in the salad position X and Y, where X is the position of where we're on the source string and Y is where we are in the target string. Interestingly enough, the scores that we care about make up the diagonal of the matrix that we build up in the actual algorithm. So I put together a quick little visualization of what this sequentially looks like as we step through. So as we step through and we compare these strings, you can see the diagonal of the matrix starting to build up. And in that first case, you can see that we've assigned the score of one. What we'll see when we actually look through the code is that the score is actually the minimum of these three values plus an insertion, substitution and deletion score that we assign it. And as we iterate through each one, we just build on the next calculation. So given that we're in a row and column of the source string index or the target strings index, we insert the minimum of the following vector in the position we're currently at. So we build up a little formula in our algorithm to derive an edit score value. And that is the minimum of the above cell plus the deletion score, that diagonal cell plus the substitution score and the left cell plus the insertion score. So if we, let me step back and look at that. There it is. We look through the code for that. So we have our insertion and our substitution score. We weight our deletion score a little bit more and I know that more complex or more robust implementations of Levenstein edit distance allow you to assign scores in different ways and weight scores differently. So our source string is my name and Micah and our target string is Alice. We initialize a matrix that we're gonna hold our rows and columns in and then we start building up that first row and then we fill out the rest of the matrix. So we iterate through our target length, target strings length and we start building up our matrix. And then we do the burly nasty part which we're gonna see is probably not the most elegant solution. So we're doing a nested for loop here. We iterate through our source string and then we iterate through our target string. We look at where we are in our matrix and the row and column that we're in and then we calculate our score which is that minimum kind of computation. It's the minimum of those of that vector essentially. And then we can retrieve the last row and last column and we get that value and that's our edit distance score. So what can we say about this algorithm? Well, it's not really the most performant data structure there is. You're at the mercy of the length of the two strings that you're comparing and this implementation of 11 Stein is probably not the fastest but and probably not the most optimized but it is pretty simple to understand. So what I like to call this use case for is when you're forcing a problem and until you can come up with a better, more elegant solution, sometimes it's worth committing the effort into the brute force implementation and figuring out how do I get this to work better? So wrapping up, so what do we go over and what do we talk about? Well, talked about the field, right? Which is a collection of numbers that have properties of addition, subtraction, multiplication and division. Talked about field of real numbers. We talked about the Galois field which is of specific interest to us in computing. Talked about exclusive or in terms of Galois field and addition in terms of Galois field. We talked about vectors which mind blown is pretty much just an array and Ruby. It's a list of scalars, symbols or functions over a specific field. So we showed that vectors can be over Galois field two or the field of real numbers. In Ruby, for all intents and purposes, a hash is really also a vector. It's a list of values. And then we talked about matrices. So an array of vectors over a field organized into rows and columns, right? We looked at a little bit of matrix manipulation in terms of graphics, graphical processing. We looked at some other elements with matrices including systems of linear equations and our Levenstein edit distance. So when you would use a matrix as an internal data structure. So what are they good for? So what are the conclusions after this hypothesis that I came up with? Well, they're good for many things. I think lowest barrier to entry to understanding matrices though, specifically, you can use them as a way to structure data in order to emphasize its relationships and tease out those relationships. They're a useful tool for solving systems of linear equations and other operations. So like we had talked about in graphics processing or graphical representations, matrices make scaling, moving and transforming those things very easy. And they are often used as a data structure for procedures and algorithms, which sometimes could be a brute force tactic and is not the most elegant solution for an algorithmic structure, but it sometimes gets the job done as well. So I wanna open it up for questions in the time that we have kind of remaining if people have questions or we can just talk about D&D too, that's fine. So the question, so I'm supposed to repeat the question. So the question was, is the matrix library part of the standard library? And yes, it is actually. And actually it's interesting when I started working with that matrix library, I fist fought it quite a bit. So there's some kind of idiosyncrasies with the matrix library that you're gonna probably come up against. But as always, the documentation for it's pretty good, so. So the question was, what is the expected general performance when you're doing things with matrix math, right? As opposed to linear solving for a system of linear equations? And the answer is I have no idea. That sounds awesome. So what's the name of the gem? It's called color. So he's working on a color math gem. I'm actually, that sounds really interesting. So performance-wise, I think there's a really good library called NMatrix that could probably help who you could include in that. And I'll check it out. I don't think NMatrix works with JRuby, unfortunately. But I would totally love to check out that. Let's get together after the talk, I'll check out that gem, sounds cool. Anybody else? Sure, so the question was, or the comment and question was, you can use GPU acceleration or things like that to enhance performance of matrices, right? And he was asking if there's any specific time when I've used that. And honestly, I really haven't. My first foray into true graphics programming or matrix optimized programming was OpenGL. And a lot of that is done for you. It's unfortunate because Ruby, in a lot of ways, it's great for things like scoping out math, but the mathematics libraries are not super intensive. And I feel like at a certain level, you're gonna start getting down to like more C++ or like Chrome level code to be able to maximize those things. Right, so the next question was, would you expect, or did you have an instance of a matrix manipulation that just totally came out of left field and you didn't expect it? And honestly, no, ultimately the other day, it was kind of like, of course, this uses a matrix. The question was, with the D&D stuff, could I do some predictive or did I find a way to do predictive modeling? And the matrixes that I used. And the answer is I didn't go down that path, but it is good to note that matrices are used quite a bit for things like machine learning algorithms. Also, if you're looking at genomic data or keeping cancer data, that can be also put into a matrix and analyzed. So I think if I wanted to start modeling data that way, yes, there's definitely a use case for that going further down the road. Sorry, my glass prescription is super old, so I'm like staring like really hard at you guys, sorry. All right. Thank you.