Explore a simple Java-based technique for visiting the past and the future Many people are fascinated with the possibility of traveling through time. Over the years, a considerable number of television shows and movies have been created to exploit this fascination. Two of these movies, along with a third movie that appears to be under development, focus on two alleged time-travel events occurring in 1943 and 2000:The Philadelphia Experiment: In 1943, the United States Navy supposedly tested technology for making a warship invisible to radar and floating mines. The ship used in the test was the U.S.S. Eldridge. During the test, the U.S.S. Eldridge disappeared and reappeared about four hours later. Some of the sailors were found fused to the ship, some sailors were on fire, some had heart attacks, others appeared to be insane, and a few sailors seemed to disappear without ever being seen again. Legend has it that this ship traveled forty years into the future and ended up in Montauk, New York, prior to its return. Learn more about the Philadelphia Experiment by visiting Resources. Also, you might want to watch the 1984 The Philadelphia Experiment and 1993 Philadelphia Experiment II movies.John Titor’s arrival: On November 2, 2000, someone calling himself Timetravel_0, and later on John Titor, began posting on a public forum that he was a time traveler from the year 2036. He posted pictures of his time-travel device and its operations manual, answered various questions about life in his time, and made various predictions of future events, including a woman president in 2008—some of these predictions have already manifested themselves. John mentioned visiting his parents and his younger self, and, on March 21, 2001, announced his upcoming departure back to the future. John has not been heard from since. If you Google the Internet, you will discover many Websites, including the official John Titor Website (see Resources for a link), that are devoted to this unusual character. The John Titor Website mentions that a movie about John Titor is in the works.Scientists believe you need a wormhole (or some other exotic device) to travel through time. But in 2002, I discovered another way to visit the past and any one of the many possible futures. All this computer-based technique requires is an appropriate programming language, such as Java. I prefer to use Java because Java’s support for big integers and buffered images greatly facilitates the implementation of this technique.It’s all about picturesYou have probably heard someone refer to a picture as being worth a thousand words. Pictures save people time by sparing them from having to accurately describe, via many spoken words, what those pictures express. Perhaps even more importantly, pictures are windows into the past. Whenever you look at a picture, you are looking back in time to the moment when that picture was taken. From that perspective, you are traveling back in time. Confused? Sit back, close your eyes, and think about this concept before continuing to read this article. NoteWhile you are thinking about pictures and the past, you might want to think about this: if you create a picture of some item from some possible future, are you also looking back in time to the moment when this picture was created, whenever you look at the picture? A picture appears on a computer screen as a rectangular grid of colored pixels, an image. I believe that generating all possible images from a solid black image to a solid white image will reveal the past and all possible futures. All it takes to accomplish this task is a simple algorithm. The following algorithm, which I have expressed in pseudocode, uses constants NROWS and NCOLS to describe each image’s pixel resolution, and assumes the maximum number of colors per pixel to be 224 or 16,777,216 colors: LET NROWS = 100 ' 100 rows in image LET NCOLS = 100 ' 100 columns in image DIM image [NROWS][NCOLS] ' Create 100x100 array of 32-bit integers. Each entry ' represents a color, ranging from 0 (black) through ' 16777215 (white). Each entry defaults to 0 (black). ' Generate and display all possible images for the 100x100 resolution and the ' 2^24 possible colors per pixel. ' The stopping condition is the lower-right pixel -- image[NROWS-1][NCOLS-1] ' -- exceeding the white color. This is analogous to setting up a counter that ' counts all possible values from 0 through 99. When the counter reaches 100, ' you know that it has gone through all 100 values (0 through 99). ' For each iteration of the while loop, we increment the color in image[0][0], ' the upper-left pixel. When this color reaches one past white (16777216, in ' other words), we have the equivalent of a mathematical carry. We propagate ' this carry to the pixel to the right of image[0][0], by incrementing ' image[0][1]. If we go past white, we propagate to image[0][2], and continue ' until we reach the pixel in the lower-right corner. Eventually, the while ' loop will come to an end, having generated every possible color combination. ' Think of this algorithm as performing roughly the same task as adding two or ' more numbers (with a carry each time a digit column sum exceeds 9). WHILE image [NROWS-1][NCOLS-1] != 16777216 DISPLAY image INCREMENT image [0][0] IF image [0][0] == 16777216 THEN LET image [0][0] = 0; FOR index = 1 TO NROWS*NCOLS-1 INCREMENT image [index/NROWS][index%NCOLS] IF image [index/NROWS][index%NCOLS] != 16777216 THEN BREAK ELSE IF index != NROWS*NCOLS-1 THEN LET image [index/NROWS][index%NCOLS] = 0 ENDIF ENDIF ENDFOR ENDIF ENDWHILE The algorithm reveals many meaningless images between the solid black and solid white extremes; they exhibit a randomness much like “snow” that often appears on a television screen when a channel’s signal is lost. Other images are meaningful, and reveal people’s faces, city skylines, animals, and so forth. Many of those images are part of the real past (the Titanic sinking in the early part of the twentieth century), an imagined past (President Al Gore), or a possible future (human beings living on the moon).The algorithm also reveals something unusual: for any given pixel resolution and maximum number of colors per pixel, there is a finite number of images that can be created. This would seem to indicate that our universe is constrained to an upper limit—a very large upper limit—on the number of distinct faces that can be seen, the number of distinct movies that can be watched, and so on. Of course, you can increase the pixel resolution or number of colors to obtain more detail. But at some point, wouldn’t we reach our visual system’s limits? There is a problem with the algorithm. For significant pixel resolutions (even 100-by-100 pixels), our Sun will be long dead before the algorithm finishes. Why? There are 224 different color combinations per pixel. Multiply that value by itself for the total number of image pixels and the result is staggering. For just three pixels, the total number of three-pixel images that can be displayed is 224 x 224 x 224—or 272 images. When you consider that 232 is approximately 4 billion, we are talking about a significant number of images (and loop iterations).Note There is another problem with this (or any other) image-generation algorithm. If you encounter a meaningful image of something you have never seen before, does that image describe something from the real past, from an imaginary past, or from some future? Because the algorithm is useless from a practical perspective, I have decided not to present a demonstration applet that implements the algorithm. Instead, back in 2002, I developed a GRI (Generate Random Image) applet that creates and presents a random image (where each pixel is assigned a randomly-selected color) each time the applet’s button is clicked. Essentially, this GRI applet presents the possibility of viewing a meaningful image whenever you click its button. Listing 1 presents the applet’s source code.Listing 1. GRI.java // GRI.javaimport java.awt.*; import java.awt.event.*;public class GRI extends java.applet.Applet implements ActionListener { Picture pic; public void init () { setLayout (new BorderLayout ()); Image im = createImage (200, 150); pic = new Picture (im); Panel p = new Panel (); p.add (pic); add (p, BorderLayout.NORTH); Button b = new Button ("Draw"); b.addActionListener (this); p = new Panel (); p.add (b); add (p, BorderLayout.SOUTH); } public void actionPerformed (ActionEvent e) { pic.draw (); } }class Picture extends Canvas { private Image buffer; private Graphics context; private int height, width; Picture (Image buffer) { this.buffer = buffer; context = buffer.getGraphics (); width = buffer.getWidth (this); height = buffer.getHeight (this); } public void draw () { for (int row = 0; row < height; row++) for (int col = 0; col < width; col++) { context.setColor (new Color (rnd (256), rnd (256), rnd (256))); context.drawLine (col, row, col, row); } repaint (); } public Dimension getPreferredSize () { return new Dimension (width, height); } public void paint (Graphics g) { g.drawImage (buffer, 0, 0, this); } public void update (Graphics g) { paint (g); } private int rnd (int limit) { return (int) (Math.random () * limit); } } Although I never commented this old GRI applet’s source code, it shouldn’t prove too difficult to understand. The applet creates a GUI consisting of Picture and button components. Each time the button is clicked, its listener invokes Picture‘s public void draw() method to draw a new image based on randomly-chosen colors for the image’s pixels. The Picture class employs double buffering and overrides the public void update(Graphics g) method to prevent screen flicker.Now that you understand the applet’s essence, compile GRI.java. Before you can run the resulting GRI.class and Picture.class classfiles, you must describe the applet to appletviewer via Listing 2’s HTML.Listing 2. GRI.html <applet code="GRI.class" width=300 height=200> </applet> Invoke appletviewer gri.html; you see a white surface with a Draw button centered near the bottom of this surface. Click Draw; a randomly-generated 200-by-150-pixel image appears above the button. Figure 1 reveals one of GRI‘s randomly-generated images.Figure 1. You never know what image will appear when you click the buttonIf you click the Draw button a sufficient number of times, will you discover a meaningful image? This situation reminds me of placing 100 monkeys and typewriters in a room, having those monkeys continuously bang away at the keys, and hoping that a monkey eventually produces some kind of masterpiece. I guess the answer to this question might have to do with which side of the evolution versus intelligent design debate you prefer, but that discussion is outside the scope of this article.Needles in a haystackI don’t know about you, but I’m not satisfied with waiting x number of years for an algorithm to reveal all possible images at a certain pixel resolution and maximum number of colors per pixel. Nor am I satisfied with continuously clicking a button x number of times and waiting who knows how long (possibly many years) before seeing a meaningful image. There has to be better way to find non-snowy images. In this section, I present a technique for (hopefully) discovering these images. From the mathematical perspective, an image is an integer. Think about it: If you stretch out an image’s rows of bits into a straight line and then convert from binary to decimal, you end up with an integer. This integer is unique because two different images have two different combinations of binary digits. This integer also can be very large (often with thousands of binary digits), so it’s convenient to use a symbol (such as X) to represent it. And it’s also convenient to place these symbols on Figure 2’s number line of images.For a specific pixel resolution, the number line of images identifies every possible image ranging from solid black (0) to solid white (one less than the maximum number of images, as determined by the pixel resolution). Some of the integer X<sub>i</sub>s, such as X<sub>i-1</sub>, X<sub>i</sub>, and X<sub>i+1</sub>, represent (and are) meaningful images. I believe these images appear every so often, with lots of snow in between. If you were to approach one of these X<sub>i</sub>s from either side, you would slowly see a meaningful image emerge out of the snow.To clarify this concept, let’s calculate the total number of images existing on the number line of images for a 640-by-427-pixel resolution; assume each image is composed of 24-bit RGB pixels. The total number of pixels for each image is 640 x 427, or 273,280. At 24 bits per pixel, the total number of bits occupied by each image is 273,280 x 24, or 6,558,720. This is also the number of bits occupied by each integer X<sub>i</sub> (assume leading zeroes). If we raise 2 to this number of bits, the total number of images is 26558720. Earlier in the article, I mentioned that 232 is approximately 4 billion. Imagine how much larger is 26558720. To give you some idea, I believe the total number of atoms in the universe is less than 26558720. Given the immensity of this number, how would you find a meaningful image on the number line of images ranging from 0 through 26558720-1? Consider starting with a randomly-generated integer that lies between those extremes and then apply various combinations of mathematical operations to that integer.To accommodate this article, I have generated an integer and stored that integer in a file named x.dat. The 819,840-byte integer stored in this file represents a 640-by-427-pixel, 24-bit image. If you multiply 819,840 by 8, you will discover 6,558,720 to be the integer’s size, in terms of bits. Later in the article, I will tell you how I generated the integer. For now, I want to show you the snowy image represented by this integer. That image appears in Figure 3.Although finding an image relative to the meaningless image in Figure 3 is a daunting task, I decided to write a Search applet to see what kind of meaningful image I could find along the 640-by-427-pixel resolution (and 24-bit RGB) number line of images. That applet takes the integer described by the image and uses this integer as the starting point in a search. If the applet gets “lucky,” some kind of meaningful image will be found. The Search applet’s source code is presented in Listing 3. Listing 3. Search.java // Search.javaimport java.awt.*; import java.awt.event.*; import java.awt.image.*;import java.io.*; import java.math.*; import javax.swing.*;public class Search extends JApplet { BufferedImage biX; public void init () { try { File f = new File ("x.dat"); final byte [] image = new byte [(int) f.length ()]; FileInputStream fis = new FileInputStream (f); fis.read (image); fis.close (); biX = new BufferedImage (640, 427, BufferedImage.TYPE_INT_RGB); int [] pixels = new int [image.length / 3]; for (int i = 0, j = 0; i < pixels.length; i++, j += 3) pixels [i] = (image [j] & 255) << 16 | (image [j+1] & 255) << 8 | (image [j+2] & 255); biX.setRGB (0, 0, 640, 427, pixels, 0, 640); addMouseListener (new MouseAdapter () { public void mouseClicked (MouseEvent e) { BigInteger bi = new BigInteger (image); String [] divisors = { "11111111111111111111111111111", "22222222222222222222222222222", "33333333333333333333333333333", "44444444444444444444444444444", "55555555555555555555555555555", "66666666666666666666666666666", "77777777777777777777777777777", "88888888888888888888888888888", "99999999999999999999999999999" }; for (int i = 0; i < divisors.length; i++) { BigInteger bi2; bi2 = new BigInteger (divisors [i]); bi = bi.divide (bi2); } byte [] image2 = bi.toByteArray (); System.arraycopy (image2, 0, image, 0, Math.min (image.length, image2.length)); biX = new BufferedImage (640, 427, BufferedImage.TYPE_INT_RGB); int [] pixels = new int [image.length / 3]; for (int i = 0, j = 0; i < pixels.length; i++, j += 3) pixels [i] = (image [j] & 255) << 16 | (image [j+1] & 255) << 8 | (image [j+2] & 255); biX.setRGB (0, 0, 640, 427, pixels, 0, 640); repaint (); } }); } catch (Exception e) { System.out.println (e); } } public void paint (Graphics g) { if (biX != null) g.drawImage (biX, 0, 0, this); } } Once again, I haven’t bothered to comment the source code. Instead, I will explain the main parts of the code to you. As you can see, almost all of the work takes place in the applet’s public void init() method, beginning with a code sequence that reads the contents of file x.dat into an image byte array. If you are startled to see file I/O occurring within an applet context, recall the first article in this column, where I showed you how to satisfy the JVM’s security manager.The init() method stores image in a java.awt.image.BufferedImage, so the image can be drawn. That class describes an image with a width, a height, and a type. I’ve hard-coded 640 and 427 instead of using constants because this throwaway applet will not be reused. I’ve given the buffered image type TYPE_INT_RGB because image contains 24-bit RGB numbers. These numbers are copied into a pixels array of 32-bit integers, and pixels is stored in the buffered image. Now that image is in the BufferedImage, it’s only a matter of time before the applet’s public void paint(Graphics g) method is invoked to draw that image. In the meantime, there is one more chore for the init() method to tackle: register a mouse listener to respond to mouse clicks. Click a mouse button and that listener’s public void mouseClicked(MouseEvent e) method is invoked. This is where the applet gets interesting.When you click one of the mouse’s buttons, mouseClicked() manipulates image‘s integer with the goal of finding a meaningful image. Because the integer is far too big to fit in a long, Java’s java.math.BigInteger class is used. That class can accommodate integers of any practical length. The integer within image is stored in a BigInteger object by invoking that class’s public BigInteger(byte[] val) constructor.Following the BigInteger‘s creation, its public BigInteger divide(BigInteger val) method is invoked from within a loop to perform several divisions. Each division requires its own BigInteger, which is initialized to one of the entries in a divisors array. After these divisions, BigInteger‘s public byte[] toByteArray() method is called to return an array of bytes that (after some processing) will be stored in a BufferedImage. The result of the divisions is another integer that may or may not reveal something interesting. When the call is made to toByteArray(), to return that integer, a new byte[] array is returned. Because BigInteger records a sign bit, the returned array’s length may not be the same as the original array’s length. Therefore, when copying from the new byte array to the original byte array, care is taken to ensure that an ArrayIndexOutOfBoundsException is not thrown.Moving on, mouseClicked() copies the original array’s contents into a pixels array of 32-bit integers and then stores pixels in a newly created buffered image. After these tasks have been accomplished, this method completes its execution by invoking repaint() to update the applet’s window. Now comes the moment of truth: Is a meaningless snowy image revealed? Judge for yourself by looking at the image presented in Figure 4.Figure 4. Who would have guessed that my little cat (her name is Baby) is lurking in the snow. This image is based on a picture taken in 2003. Click on thumbnail to view full-sized image.I have a confession to make: the previous example is contrived. Before I created Search.java, I created a MakeX application that creates x.dat, starting with the contents of a JPEG file named baby.jpg. Because I’ve only recently started to explore the number line of images and haven’t yet found a meaningful image in time for this article, I decided to work backwards to provide you with an idea of how big integers and buffered images could be used in a search for meaningful images. ConclusionWilliam Shakespeare’s Hamlet play includes the following quotation: “There are more things in heaven and earth, Horatio, than are dreamt of in your philosophy.” This quotation certainly seems fitting if you have never thought about traveling through time by viewing computer-generated images. It is also fitting when you realize that you can also experience time travel by focusing on sounds (including music and speech). For example, an algorithm that reveals all combinations of sounds eventually presents conversations from people not yet born.Whether or not you agree with my technique for traveling through time, I hope that you will send me feedback. Perhaps you can share some mathematical insight into more effectively locating images along the number line of images. Or if you believe I have overlooked something that invalidates my time-travel technique, I also want to know about that. Ultimately, if enough people work together to find meaningful images (or even conversations), incredible discoveries may result … discoveries made possible from using Java to travel through time.Jeff Friesen is a freelance software developer and educator specializing in C, C++, and Java technology. JavaComputers and PeripheralsTechnology Industry