I would like to thank my colleagues Eric Parent & Zheng Shao for writing the Origin C scripts that made this post possible.

In the middle of a long scientific presentation in 1963, renowned mathematician and inventor Stanislaw Ulam (pronounced oo-lam) was doodling on a pad of paper and began listing integers starting with 1 in a counterclockwise spiral. Like so-

As he drew this spiral he began circling the prime numbers, only to notice this-

There seemed to be “lines” of prime numbers going outward in a distinct pattern. Primes which, by definition, are indivisible by any number but 1 and themselves, may seem to occur at random intervals at first, but there is in fact a certain logic behind this chaos, as we can see in the grid below.

Ulam spirals are largely mathematical art, however they have shown promise for illustrating not only the general behaviors of various number series, but also an older mathematic conjecture that may explain the frequency of prime numbers. In this blog, I’ll be showing you how a matrix of any size can be combined with a C script in Origin to generate a Ulam spiral. This spiral can be used to illustrate primes as well as any other number series expressed in OriginC.

Let’s begin by opening up the Code Builder and creating a new C++ file, which can be saved in either your User Files (by default) or anywhere you have user permissions.

One of our initial challenges is finding a way to place 1 in the center of a matrix of equal width and height, and count outward until it reaches the value of the total number of cells in the entire grid- regardless of the matrix’s size. Pasting the following code into our C++ file will generate this spiral. To understand what each section does, you can follow along with the //comments in the script below.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
#include <Origin.h> #include <GetNBox.h> //---------------------------------------------------------------------------- // CSpiral // The base class that does all the complicated stuff. // By itself it will do an Index spiral. // All other spirals should be derived from CSpiral. //---------------------------------------------------------------------------- class CSpiral { public: CSpiral() { } ~CSpiral() { } // Creates the CSpiral class to be called on by Prime and other related functions. int Run() { if( AttachActive() ) run(); return 0; } virtual int Equation() { m_M[m_row][m_col] = m_index; return 0; } // This takes the product of the rows and columns of the matrix and creates integers based on them protected: Matrix<double> m_M; int m_numRows, m_numCols; int m_index, m_row, m_col; BOOL AttachActive() { MatrixLayer ml = Project.ActiveLayer(); if( !ml.IsValid() ) return FALSE; return m_M.Attach(ml); } // This confirms that a matrix is present in which these values may be placed int run() { int left, right, top, bottom; run_init(); left = right = m_col; top = bottom = m_row; while( true ) { while( m_col <= right ) { Equation(); m_col++; m_index++; } if( m_col == m_numCols ) break; right++; while( m_row >= top ) { Equation(); m_row--; m_index++; } if( m_row < 0 ) break; top--; while( m_col >= left ) { Equation(); m_col--; m_index++; } if( m_col < 0 ) break; left--; while( m_row <= bottom ) { Equation(); m_row++; m_index++; } if( m_row == m_numRows ) break; bottom++; } return 0; } void run_init() { m_numRows = m_M.GetNumRows(); m_numCols = m_M.GetNumCols(); if( m_numRows != m_numCols ) { if( m_numRows < m_numCols ) m_numCols = m_numRows; else m_numRows = m_numCols; } m_row = m_numRows >> 1; m_col = m_numCols >> 1; if( (m_numCols & 1) == 0 ) m_col--; m_index = 1; } }; //This last section determines the placement of these values relative to aspect ratio of the matrix //if the matrix is not square then the spiral will form in the largest square that can be drawn //and will otherwise return the missing number sign (--). |

This code generates the spiral, however we need a way to call this as a function from Origin’s script window. To do this, in Code Builder we’ll add the following lines to the end of our script-

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
//---------------------------------------------------------------------------- // UlamSpiral // The main function for calling the different types of spirals. //---------------------------------------------------------------------------- enum { UlamSpiral_Help = 0, UlamSpiral_Index, UlamSpiral_Unknown }; // This creates an index allowing you to call the UlamSpiral help with UlamSpiral(0), generate // the Ulam Spiral itself with UlamSpiral(1), or return a null "unknown" error for // any other other value. int UlamSpiral(int type = 0) { switch( type ) { case UlamSpiral_Help: printf("%d = Help\n", UlamSpiral_Help); printf("%d = Index\n", UlamSpiral_Index); break; case UlamSpiral_Index: { CSpiral spiral; spiral.Run(); } break; } return 0; } // This section generates "help" for option 0, listing the 3 options, // runs the CSpiral function on our previous script for option 1, // and returns a text break for any other value representing an "unknown" function |

To use this in Origin, the next thing we need to do is right-click on the User [Autoload] file or another “User files” folder in the Codebuilder workspace window and select “Add Files…”. Locate the .cpp file in its folder, and click okay. Then select your file in the workspace, and go to Build>Build (F8). This will compile your code and should generate a working function that can be executed from Origin’s script window.

To test this function create a new matrix window, and then open the Script Window. In the script window type UlamSpiral(1), and you should see that the center cell is populated with 1 with values increasing by with each cell in a counter-clockwise order. If this didn’t work you may want to either copy/paste the script above or go back to the original file included in this post’s ZIP folder.

This is how you would generate the base values for a Ulam Spiral. In another post, we’ll be covering how to set it to display only primes as well as odd/even numbers or numbers divisible by different integers.