At a recent job interview I was asked to create an application that manipulated images, and allowed the user to move forward and backward through time, such that they could undo and redo changes to their image.
It wasn’t meant to be a fully working application, just enough to show that I understood certain software engineering fundamentals.
Before this gets too far along, it must be said that artificial problems in interviews produce artificial solutions, and I am not an expert on image manipulation in any language. Further this isn’t a dig at the interviewer, finding a way to see the depth of another engineer is an open and difficult problem.
The first part of the problem is that images have a variety of formats that they store their information as, there are RGBA
, YCbCr
, CMYK
and more.
This is important as it means that methods created to process one format may not necessarily work for any other format. Generic code, therefore, needs to detect the type of image data, then act accordingly.
On to the solution I was proposing.
Event logs are logs that capture the history of changes to the state of some data held by an application. There are two options available on the way the events are logged, either the function that was called can be captured, or a snapshot of the change (think diff, but it could also be the changed state).
The two options have strengths and weaknesses that must be taken into account when choosing which to store.
If you store the diff (git
does this, and so do video streams) then your event log will be storage heavy, that is, it takes a hang of a lot of disk, RAM, or network data to be able hold and transmit the log. However moving forward or backward through the changes is trivial (I’ll explain why in a moment).
If, on the other hand, you store the commands, or pathway, that are executed to produce the desired state, much less storage space is required, but it’s very CPU heavy, and difficult to go backwards in time because the direct reversal of time would be to apply the reciprocal to the function that is listed as getting to the current state, which may not be possible (consider func MakeCarYellow(){}
, in order to undo that function the previous state needs to be known, was the car red before, or blue).
Further, ensuring that the function stated is the same version as the function you currently have adds some overhead (if your MakeCarYellow() function changes the hue of Yellow it changes the car to, then it becomes difficult to run disparate systems on that event log that produce the same final state).
In both cases what often happens is a snapshot of the state is stored for a given moment in time, with changes to that snapshot being recorded in the event log (in video these snapshots are called key frames
).
Reversing time in the diff style log is trivial because the reciprocal function is clear, the diff is removed from the current state to discover the previous state. There is no ambiguity on states, or functions. Any function can be applied, as long as it achieves the change that the diff presents.
It should be clear, then, that my proposal was for an event log that stored the diffs as entries to the log, with one log per image. It would have been demanding of storage, but less demanding of the CPU.
Note: The Go colour package demonstrates multiple pixel storage formats and shows the need to create methods that deal with each format specifically, with the Color
interface defining a single method (RGBA
) that allows users to effectively store their images in a universal format.
Also, an excellent resource on logs can be found in the (free) ebook I heart logs by Jay Kreps