SWAG a J/EXCEL/GIT Personal Cash Flow Forecasting Mob

While browsing in a favorite bookstore with my son, I spotted a display of horoscope themed Christmas tree ornaments. The ornaments were glass balls embossed with golden birth signs like Aquarius, Gemini, Cancer, et cetera, and a descriptive phrase that “summed up” the character of people born under a sign. Below my birth sign golden text declared, “Imaginative and Suspicious.”

I said to my son, “I hate it when astrological rubbish is right.”

I am imaginative and suspicious; it’s a curse. When it comes to money my “suspicious dial” is permanently set on eleven. I assume everyone is out to cheat and defraud me until there is overwhelming evidence to the contrary. Paranoia is generally crippling but when it comes to cold hard cash it’s a sound retention strategy.

Prompted by an eminent life move, I found myself in need of a cash flow forecasting tool. Normal people deal with forecasting problems by buying standard finance programs or cranking up spreadsheets; imaginative and suspicious people roll their own.

SWAG

SWAG, (Silly Wild Ass Guess), is a hybrid J/EXCEL/GIT mob1 that meets my eccentric needs. I wanted a tool that:

  1. Abstracted away accounting noise.
  2. Was general and flexible.
  3. Used highly portable, durable, and version control friendly inputs and outputs.
  4. Reflected what ordinary people, not tax accountants, actually do with money.
  5. Is open source and unencumbered by parasitic software patents.

Amazingly, my short list of no-brainer requirements eliminates most standard finance programs. Time to code!

SWAG Inputs

The bulk of SWAG is a JOD generated self-contained J script. You can peruse the script here. SWAG inputs and outputs are brain-dead simple TAB delimited text tables. Inputs consist of monthly, null-free, numeric time series tables, scenario tables, and name cross-reference tables. Outputs are simple, null-free, numeric time series tables. Input and output time series tables have identical formats.

A few examples will make this clear. The following is a typical SWAG input and output time series table.

 Date        E0      E1       E2      E3  E4     E5  E6  E7  E8  EC  EF  Etotal   I0       I1  I2  I3  I4  I5  IC  Itotal   R0         R1        R2  R3  Rtotal     D0  D1  D2  D3  D4  Dtotal  BB       NW         U0         U1  U2  U3
 2015-09-01  912.00  1650.00  100.00  0   50.00  0   0   0   0   0   0   2712.00  4800.00  0   0   0   0   0   0   4800.00  130000.00  25000.00  0   0   155000.00  0   0   0   0   0   0       2088.00  157088.00  155000.00  0   0   0 
 2015-10-01  912.00  1656.88  100.00  0   50.00  0   0   0   0   0   0   2718.88  4806.00  0   0   0   0   0   0   4806.00  130054.17  25062.50  0   0   155116.67  0   0   0   0   0   0       2087.13  159291.79  0          0   0   0 
 2015-11-01  912.00  1663.78  100.00  0   50.00  0   0   0   0   0   0   2725.78  4812.01  0   0   0   0   0   0   4812.01  130054.17  25062.50  0   0   155116.67  0   0   0   0   0   0       2086.23  161378.02  0          0   0   0 
 2015-12-01  912.00  1670.71  100.00  0   50.00  0   0   0   0   0   0   2732.71  4818.02  0   0   0   0   0   0   4818.02  130054.17  25062.50  0   0   155116.67  0   0   0   0   0   0       2085.31  163463.33  0          0   0   0 
 2016-01-01  912.00  1677.67  100.00  0   50.00  0   0   0   0   0   0   2739.67  4824.05  0   0   0   0   0   0   4824.05  130054.17  25062.50  0   0   155116.67  0   0   0   0   0   0       2084.37  165547.70  0          0   0   0 
 2016-02-01  912.00  1684.66  100.00  0   50.00  0   0   0   0   0   0   2746.66  4830.08  0   0   0   0   0   0   4830.08  130054.17  25062.50  0   0   155116.67  0   0   0   0   0   0       2083.41  167631.12  0          0   0   0 
 2016-03-01  912.00  1691.68  100.00  0   50.00  0   0   0   0   0   0   2753.68  4836.11  0   0   0   0   0   0   4836.11  130054.17  25062.50  0   0   155116.67  0   0   0   0   0   0       2082.43  169713.55  0          0   0   0 

The first header line is a simple list of names. The first name “Date” heads a column of first of month dates in YYYY-MM-DD format. The SWAG clock has month resolution and dates are the only nonnumeric items. Names beginning with “E” like E0, E1, …, are aggregated expenses. Names beginning with “I” like I0, I1, I2 … are income totals. “R” names are reserves: basically savings, investments, equity and so forth. “D” names are various debts. BB is basic period balance, NW is period net worth and “U” names are utility series. Utility series facilitate calculations. Remaining names are self-explanatory totals. Be aware that this table has been formatted for this blog. Examples of raw input and output tables can be found here.

The next ingredient in the SWAG stew is what many call a scenario. A scenario is a collection of prospective assumptions and actions. In one scenario you buy a Mercedes and assume interest rates remain low. In another, you take the bus and rates explode. When forecasting I evaluate five basic scenarios, grim, pessimistic, expected, optimistic, and exuberant. Being a negative Debbie Downer type I rarely invest time in exuberant scenarios. I concentrate on grim and pessimistic scenarios because once you are mentally prepared for the worst anything better feels like a lottery win.

The following is a typical SWAG scenario table. Scenario tables, like time series tables, are simple TAB delimited text files.

 Name         Scenario On Group       Value  OnDate     OffDate    Method   MethodArguments                                                Description                                                                     
 reservetotal s0          assumptions 0      2015-09-01 2015-10-01 assume   RSavings=. 0.5 [ RInvest=. 3 [ REquity=. 3 [ ROther=. 1        annual nominal percent reserve growth or decline during period                  
 car          s0                      50     2015-09-01 2035-08-01 history                                                                 annualized car maintenance until first death                                    
 house        s0                      912    2015-09-01 2016-08-01 history  BackPeriods=.1                                                 current rent until move                                                         
 insurance    s0                      100    2015-09-01 2035-08-01 history                                                                 car insurance                                                                   
 living       s0                      1650   2015-09-01 2044-01-01 history  YearInflate=.5                                                 normal monthly living expenses                                                  
 salary       s0                      4800   2015-09-01 2016-08-01 history  BackPeriods=.4 [ YearInflate=.1.5                              maintain net monthly income until move                                          
 reservetotal s0                      25000  2015-09-01 2015-10-01 reserve  Initial=.1 [ RInvest=. 1                                       stock value at model start                                                      
 reservetotal s0                      130000 2015-09-01 2015-10-01 reserve  Initial=.1                                                     savings at model start                                                          
 salary       s0                      4200   2016-08-01 2023-07-01 history  BackPeriods=.4 [ YearInflate=.1.5                              reduced net income after move until retirement                                  
 house        s0          move        2000   2016-08-01 2016-09-01 history                                                                 moving expenses                                                                 
 house        s0                      100    2016-08-01 2044-01-01 history                                                                 incidental housing expenses after move                                          
 house        s0                      100    2016-08-01 2044-01-01 history                                                                 home owners association payments                                                
 house        s0                      150    2016-08-01 2044-01-01 history                                                                 property taxes                                                                  
 reservetotal s0          buy house   110000 2016-08-01 2016-09-01 reserve  Initial=.1 [ REquity=. 1                                       down payment added to house equity initial setting prevents double spend        
 loan         s0          buy house   150000 2016-08-01 2023-02-01 borrow   Interest=. 4.5 [ YearTerm=. 30 [ DHouse=.1  [ LoanEquity=.1    30 year mortgage rate on house until inheritance                                
 reservetotal s0          buy house   110000 2016-08-01 2016-09-01 spend                                                                   down payment on house from savings                                              
 annuities    s0                      250    2018-07-01 2035-08-01 history                                                                 monthly retirement and other annuity payments end date is unknown               
 annuities    s0                      50     2018-07-01 2035-08-01 history                                                                 any government pension payments                                                 
 reservetotal s0          assumptions 0      2020-01-01 2044-01-01 assume   RSavings=. -2.5 [ RInvest=. -5.0 [ REquity=. -5.0 [ ROther=. 2 market tanks government introduces negative interest                            
 loan         s0          buy car     10000  2020-07-01 2025-07-01 borrow   Interest=. 5 [ YearTerm=. 5 [ DCar=.1                          pay balance of car at 5% for 5 years                                            
 reservetotal s0          buy car     7000   2020-07-01 2020-08-01 spend                                                                   car down payment from savings                                                   
 reservetotal s0          inherit     180000 2023-01-01 2023-02-01 reserve  Initial=.1                                                     inheritance to savings                                                          
 reservetotal s0          buy house   150000 2023-03-01 2023-04-01 transfer Fee=. 1500 [ DHouse=.1                                         pay off remaining mortgage balance after inheritance fee is closing cost        
 salary       s0                      1400   2023-07-01 2035-08-01 history                                                                 estimated social security payments spread over expected life                    
 insurance    s0                      700    2023-07-01 2024-12-01 history                                                                 medical insurance in the gap between retirement and spouse medicare eligibility 
 annuities    s0                      100    2024-12-01 2044-01-01 history                                                                 any retirement pension payments to spouse                                       
 annuities    s0                      100    2035-08-01 2044-01-01 history                                                                 any us social security survivor benefit after first death                       

Again the first header row is a simple list of names. Most scenario names are self-explanatory but four OnDate, OffDate, Method, and MethodArguments merit some explanation. SWAG series methods assume, history, reserve, transfer, borrow, and spend are modeled on what people typically do with cash.

  1. assume sets expected interest rates and other global assumptions for a given time period. SWAG series methods operate over a well-defined time period. The period is defined by OnDate and OffDate.
  2. history looks at past periods and estimates a numeric value that is projected into the future. Currently, history computes simple means but the underlying code can use arbitrary time series verbs.
  3. reserve manages savings, investments, equity and other cash-like instruments.
  4. borrow borrows money and sets future loan payments. borrow supports simple amortization loans but is also capable of reading an arbitrary payment schedule that can be used for exotic2 loans.
  5. transfer moves money between reserves, debts, expenses and income series.
  6. spend does just what you expect.

SWAG series methods adjust all the series affected by the method. As you might expect SWAG arguments methods are detailed. MethodArguments uses a restricted J syntax to set SWAG arguments. Argument order does not matter but only supported names are allowed. Many examples of SWAG MethodArguments can be found in the EXCEL spreadsheet tp.xlsx. I use EXCEL as a scenario editor. By setting EXCEL filters, you can manage many scenarios.

The final SWAG input is a name cross-reference table. It is another TAB delimited text file that defines SWAG names. You can inspect a typical cross-reference table here.

Running SWAG

To run SWAG you:

  1. Prepare input files.
  2. Start J, any front-end jconsole, JQT or JHS will do, and load the Swag script.
  3. Execute RunTheNumbers.
  4. Open the EXCEL spreadsheet swag.xlsx, click on the data ribbon and then press the “Refresh All” button.

Let’s work through the steps.

Prepare Input Files

By far the most difficult step is the first. Here you review your financial status which means checking bank balances, stock values, loan balances and so on. Depending on your holdings this could take anywhere from minutes to hours. I call this updating actuals. Not only is updating actuals the most difficult and time-consuming step it is also the most valuable. Money that is not closely watched leaks away.

I store my actuals in a simple tabbed spreadsheet. Each tab maintains an image of a text file. I enter my data and then cut and paste the sheets into a text editor where I apply final tweaks and then save the sheets as TAB delimited text files.

Monthly income, expenses and debts are easy to update but some of my holdings do not offer monthly statements. The verb RawReservesFromLast in Swag.ijs fills in missing months with the last known values. When I’m finished preparing input files I’m left with four actual TAB delimited files, RawIncome.txt, RawExpenses.txt, RawReserves.txt, and RawDebts.txt. You can inspect example actual files here.

Start J and load the Swag script.

The SWAG script is relatively self-contained. It can be run in any J session that loads the standard J profile. Load Swag with the standard load utility.

 load 'c:/pd/fd/swag/swag.ijs'

Here SWAG is loaded in JHS.

jhsswag

Execute RunTheNumbers

RunTheNumbers sets the SWAG configuration, loads scenarios, copies actuals to each scenario, and then evaluates each scenario. Scenarios are numbered. I use positive numbers for “production” scenarios and negative numbers for test scenarios. It sounds more complicated that it is. This is all you have to do to execute RunTheNumbers

 RunTheNumbers 0 1 2 3 4 

The code is simple and shows what’s going on.

RunTheNumbers=:3 : 0

NB.*RunTheNumbers v-- compute all scenarios on list (y).
NB.
NB. monad:  blclFiles =. RunTheNumbers ilScenarios
NB.
NB.   RunTheNumbers 0 1 2 3 4

NB. parameters sheet is the last config sheet
ModelConfiguration_Swag_=:MainConfiguration_Swag_
parms=. ".;{:LoadConfig 0
scfx=. ScenarioPrefix

ac=. toHOST fmttd ActualSheet 0
ac write TABSheetPath,'MainActuals',SheetExt

sf=. 0$a:
for_sn. y do.
  ac write TABSheetPath,scfx,(":sn),'Actuals',SheetExt
  sf=. sf , parms Swag sn [ LoadSheets sn
end.

sf 
)

RunTheNumbers writes a pair of TAB delimited forecast and statistics files for each scenario it evaluates.

Open swag.xlsx and press “Refresh All”

The spreadsheet swag.xlsx loads SWAG TAB delimited text files and plots results.3 I plot monthly cash flow, estimated net worth and debt/equity for each scenario. The following is a typical cash flow plot. It estimates mean monthly cash balance over the scenario time range.

meanbalance

The polynomial displayed on the graph is an estimated trend line. Things are looking bleak.

Here’s a typical net worth plot.

networth

In this happy scenario, we die broke and leave a giant bill for the government.4

So far SWAG has met my basic needs and forced me to pay more attention to the proverbial bottom line. As I use the system I will fix bugs, refine rough spots, and add strictly necessary features. Feel free to use or modify SWAG for your own purposes. If you find SWAG useful please leave a note on this blog or follow the SWAG repository on GitHub.


  1. What do you call dis-integrated collections of programs that you use to solve problems? Declaring such dog piles “systems” demeans the word “system” and gives the impression that everything has been planned. This is not how I roll. “Mob” is far more appropriate. It conveys a proper sense of disorder and danger.
  2. When borrowing money you should always plan on paying it all back. Insist on a complete iron clad repayment schedule. If such a schedule cannot be provided run like hell or prepare for the thick end of a baseball bat to be rammed up your financial ass.
  3. It may be necessary to adjust file paths on the EXCEL DATA ribbon to load SWAG TAB delimited text files.
  4. They can see me in Hell to collect.

Jacks Repository

The other day I attempted to browse a J script described in an old blog post only to find that my employer’s network monkeys had blocked the file sharing service. I’ve railed about IT control freaks in the past. They will not rest until it’s impossible to do useful work. I fumed and grumbled until I perceived a bigger problem. I have so many references to program code in this blog that it’s getting tedious tracking them down. Wouldn’t it be nice if my hacks were neatly organized in one coherent repository?

Let me introduce jacks. jacks, or “J-hacks”, organizes the J related code referenced in this blog into a single GitHub repository. Most of the scripts in jacks are one-offs but some have proven so useful that it makes sense to store them in a repository and track changes. From now on jacks will be the first place to look for code from this blog. You pull the contents of jacks into a new Git repository with the commands:

git init
git remote add jacks https://github.com/bakerjd99/jacks.git
git pull jacks master

It took me a few moments to settle on the name “jacks.” I considered “jokes” because programmers often take their code too seriously and “jocks” because J programmers are wild out of control convention eschewing code jocks but jacks won out when I remembered the refrain “jack be nimble, jack be quick, jack jump over” whatever coding problem is pissing you off.

More about JHS with the DHTMLX Grid

I have resolved my DHTMLX standard edition row data extraction problem. The standard edition does not serialize grids or track user cell changes. You have to pay for such luxuries. Because I’m a foul software Grinch and this is just an exploratory hack I had to roll my own. I am posting the relevant JavaScript because I could not find similar examples. Here is how you can fetch rows from standard edition DHTMLX grids and save them as JSON in a hidden textarea element. Eric Iverson suggested hidden textareas and they work like a charm.

function ev_saveme_click(){

  if ('undefined' != typeof grid0){
  
    if (0 == grid0.getRowsNum()){
      jbyid("rerowcnt").innerHTML = "No rows to save"; 
      return;
    }

    var st = new Date().getTime(),  // start time  
        ids = grid0.getAllRowIds(","),
        ccnt = 1 + grid0.getColumnsNum();  // includes id
      
    ids = ids.split(",");  
    var rcnt = ids.length,
        tab = new Array(rcnt);
    
    // header row - tab[0][0] cell ignored
    tab[0] = new Array(ccnt);  
    for (var i = 1; i < ccnt; i++) {
      tab[0][i] = grid0.getColumnLabel(i-1,0); 
    }
     
    // cells with leading row id
    for (var i = 0 , si = 1 ; i < rcnt; i++ , si++) {
      tab[si] = new Array(ccnt);
      for (var j = 1; j < ccnt; j++) {
        tab[si][j] = grid0.cells((+ids[i]),j-1).getValue();
      }
      tab[si][0] = ids[i];
    }
  
    // prefix row column counts 
    var pfx = (rcnt+1) + " " + ccnt + "*";
    jbyid("gridchgs").innerHTML = pfx + JSON.stringify(tab);
    jdoajax(["gridchgs","tout"],"");
  
    var et = new Date().getTime() - st;  // end time    
    jbyid("rerowcnt").innerHTML= " row count= " + grid0.getRowsNum() +  
        ",  JavaScript ms= " + et; 
        
  } else {
  
    jbyid("rerowcnt").innerHTML= "Nothing to save";     
  }
}

Passing data back to J is fast but the J JSON addon convert\json burps on large datasets. For this demo I substituted a simple table oriented parser that is much faster.

JHS with the DHTMLX Grid

Grids are the most important GUI user object. It’s hard to think of a user-friendly data munching application that doesn’t have a grid beating at its heart. Consequently, any serious GUI interface contender must support grids. My previous post showed how to use MathJax with JHS. MathJax is an impressive and important JavaScript library; it clearly demonstrates the potential of CHJ1 GUI interfaces but let’s face it, mathematical typesetting will not win many consulting contracts. Grids won’t seal the deal either but their absence is a huge “next” signal. To support serious business and technical applications JHS needs grids.

Fortunately, the JavaScript world is grid saturated. The difficulty is not finding a grid but choosing among dozens of candidates. For this demo I Googled around and found DHTMLX. According to this probably biased article the DHTMLX grid performs well on large inputs and, more importantly, there is an open source version.

You have to start somewhere so I opted to use DHTMLX to build a simple CSV file editor. The CSV files I am going to edit are TAB delimited text files. Each file has a fixed number of columns with column names in the first row. Here is an example TAB delimited file. The idea is to load the file data into the grid. Tweak a few rows and save the result. By increasing the size of the CSV file we can gauge the performance of the grid. Let’s get started.

Using the DHTMLX grid requires some preparation.

  1. Create a local directory and edit J’s ~config/folders.cfg to reference the directory with the name GridDemo. jpath '~GridDemo' should return the full directory path.
  2. Download the files in the GridDemo folder and copy them to ~GridDemo.
  3. Download the Standard Edition (Version 3.5) of DHTMLX. The distribution file dhtmlxGrid.zip contains the grid source and supporting files.
  4. Extract the /dhtmlxGrid/codebase/ directory from dhtmlxGrid.zip and copy the entire directory tree to ~GridDemo.
  5. Also extract /dhtmlxGrid/samples/common from dhtmlxGrid.zip and copy the directory to ~GridDemo.

When you’re finished the top-level of ~GridDemo will look like the following where names without extensions are directories.

    calendar           dhtmlxgrid.js         GridDemo.ijs   t100rows.txt
    common             dhtmlxgrid_skins.css  imgs           t5000rows.txt
    dhtmlxcommon.js    excells               jodoval.png
    dhtmlxgridcell.js  ext                   skins
    dhtmlxgrid.css     favicon.ico           t1000rows.txt

The main J script is ~GridDemo\GridDemo.ijs. Start JHS and load this file.

    load '~GridDemo/GridDemo.ijs'

Then browse to this site.

    http://127.0.0.1:65001/GridDemo

If all goes well you will see the following GridDemo page after pressing the Edit Grid button.

Screenshot of GridDemo running on Chrome

Screenshot of GridDemo running on Chrome

To load and edit files enter their fully qualified names in the Input and Output boxes and press Edit Grid. To edit a cell double-click it. To save changes press Save Grid.2 There are more sophisticated ways to pick files on JavaScript pages. It’s easy to pop up standard host OS file dialogs but it’s not particularly easy to determine host directory paths. This post outlines the demons web programmers must slay to select host files. JHS circumvents these difficulties by asking the J server, which is a typically a local console process, to do the dirty work. JavaScript’s access to local files is limited for security reasons but J has no such restrictions. Use the force Luke!

Three test files t100rows.txt, t1000rows.txt, and t5000rows.txt are included with the demo. On my test machines load times vary from fractions of a second for the smaller files to nine seconds for the largest. This is competitive with the basic C# grid control and fast enough for serious work.

In subsequent posts I will explore JavaScript/JHS graphics options and start the process of integrating, grids, graphs and MathJax with JHS.


  1. CSS, HTML and JavaScript.
  2. The freebie version of DHTMLX does not support grid serialization. Here is how to roll your own.

JHS meets MathJax

With the release of J 7.01 Jsoftware “deprecated” COM. J 6.02 can run as a COM automation server but J 7.01 cannot.1 Throwing COM under the bus is hardly radical. Microsoft, COM’s creator, has been holding COM’s head underwater for years. Many .Net programmers cringe when they hear the word “COM” and the greater nonwindows2 world never really accepted it. COM is a complex, over-engineered, proprietary dead-end. Yet despite its bloated deficiencies a lot of useful software is COM based. So with COM going away, at least for J programmers, the hunt is on for viable replacements and while we’re hunting let’s rethink our entire approach to building J GUI applications.

J GUI applications are traditional desktop applications. They’re built on native GUIs like Windows Forms and GTK and when done well they look and act like GUI applications coded in other languages. This is all good but there is a fundamental problem with desktop GUIs. There are many desktop GUIs and they do not travel well. Programmers have spent many dollars and days creating so-called cross-platform GUIs but, if you wander off the Windows, Mac and Linux reservation, the results are not particularly portable. And, as portable GUIs rarely outperform native alternatives, programmers tend to stick in their tribal silos. GUI programming is a bitch, has always been a bitch and will always be a bitch. It’s time to divorce the desktop GUI bitch.

All divorces, even the geeky GUI variety, are hard. When you finally cut the knot you’re not entirely sure what you’re doing or where you’ll end up. All you know is that there is a better way and with respect to J GUI applications I believe that JHS is that way. JHS leverages the large CSS, HTML and JavaScript (CHJ) world and in recent years some impressive browser-based applications have emerged from that world. The application that changed my mind about JavaScript and browser-based applications in general is something called MathJax.

MathJax typesets mathematics. It renders both LaTeX and MathML using fully scalable browser fonts. This is better than what WordPress does. The following Ramanujan identity taken from MathJax examples renders on WordPress as an image.

\frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} =  1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}}  {1+\frac{e^{-8\pi}} {1+\ldots} } } }

MathJax renders the same expression with scalable fonts and supports downloading the expression as LaTeX or MathML text. This is pretty impressive for browser JavaScript. I wondered how hard it would be to use MathJax with JHS and was pleased to find it’s easy peasy.

Writing a basic JHS application is a straightforward task of setting up three JHS J nouns HBS, CSS and JS.3 HBS is a sequence of J sentences where each sentence yields valid HTML when executed. JHS generates a simple web page from HBS and returns it to the browser. MathJaxDemo HBS is:

 HBS=: 0 : 0
 navul''           
 '<hr>','treset' jhb 'Reset'

 '<hr>',jhh1 'Typeset with MathJax and J'
 configjax
 oltypeset''
        
 '<hr>',jhh1 'Typeset Random Expression Tables'
 tabledesc
 '<br/>','ttable' jhb'Typeset Random Expression Array' 
 '<br/>','restable' jhspan''        
 )

CSS is exactly what you expect: CSS style definitions. Finally, JS is application specific JavaScript. MathJaxDemo JS matches HBS page events with corresponding JHS server handlers. This demo uses ajax for all event handlers.

JS=: 0 : 0

 function ev_ttable_click(){jdoajax([],"");}
 function ev_tquad_click(){jdoajax([],"");}
 function ev_tmaxwell_click(){jdoajax([],"");}
 function ev_tramaujan_click(){jdoajax([],"");}
 function ev_tcrossprod_click(){jdoajax([],"");}
 function ev_treset_click(){jdoajax([],"");}

 function ev_ttable_click_ajax(ts){jbyid("restable").innerHTML=ts[0]; MathJax.Hub.Typeset();}
 function ev_tquad_click_ajax(ts){jbyid("resquad").innerHTML=ts[0]; MathJax.Hub.Typeset();}
 function ev_tmaxwell_click_ajax(ts){jbyid("resmaxwell").innerHTML=ts[0]; MathJax.Hub.Typeset();}
 function ev_tramaujan_click_ajax(ts){jbyid("resramaujan").innerHTML=ts[0]; MathJax.Hub.Typeset();}
 function ev_tcrossprod_click_ajax(ts){jbyid("rescrossprod").innerHTML=ts[0]; MathJax.Hub.Typeset();}

 function ev_treset_click_ajax(ts){
   jbyid("restable").innerHTML=ts[0]; jbyid("resquad").innerHTML=ts[0];
   jbyid("resmaxwell").innerHTML=ts[0]; jbyid("resramaujan").innerHTML=ts[0];
   jbyid("rescrossprod").innerHTML=ts[0];
 }

Running the JHS MathJaxDemo is a simple matter of:

  1. Downloading the J scripts in the MathJaxDemo folder to a local directory.
  2. Editing J’s ~config/folders.cfg file and pointing to your download directory with the name MathJaxDemo. You are set up correctly if jpath '~MathJaxDemo' returns your path.
  3. Loading the demo: load '~MathJaxDemo/MathJaxDemo.ijs'
  4. Browsing to the site: http://127.0.0.1:65001/MathJaxDemo

It’s not hard to use JHS as a general application web server. JHS provides many common controls right out of the box but to compete with desktop applications it’s necessary to supplement JHS with JavaScript libraries like MathJax. In coming posts I will explore how to use industrial strength JavaScript grids and graphics with JHS.

MathJaxDemo Screen Shot

Screen shot of JHS MathJaxDemo running on Ubuntu.


  1. On a purely numerical basis there is no greater nonwindows world.
  2. To learn about JHS programming study the JHS demos and the JHS browser application.
  3. Bill Lam has pointed out that J 7.01 can function as a COM client. The JAL addon tables/wdooo controls OpenOffice using Ole Automation which is one of the many manifestations of COM.

J 7.01 Now Playing in your Browser

Big changes are afoot for J programmers.  With the release of J 7.01 JSOFTWARE has taken the risky step of deprecating two well established user IDE’s and replacing them with brand new GTK and Web browser-based interfaces.  As we all know new software is not necessarily better software, (remember that risky bit), but in this case the changes make sense.

J 7.01 running JHS on Chrome

J 7.01 running JHS on Chrome

Early J systems were largely Windows-Centric.  Yes, J has run perfectly well on strains of UNIX/Linux and the Mac for over a decade but I think it’s fair to say that J IDE’s were biased toward Windows.  JSOFTWARE was well aware of this and tried to offer the features of their Windows IDE with a portable Java based IDE. Unfortunately the Java IDE, like Java itself, failed to live up to expectations.

Now that the many, if not the majority of  J programmers, run J on non-Windows, (mostly Linux and the Mac), systems it makes no sense to treat them like second class citizens.  So how do you provide a state of the art portable user IDE that runs well on Windows, Linux, the Mac, smart phones and various IGadgets.  There is no single answer so JSOFTWARE wisely decided to offer two IDEs: JHS and JGTK.

The JGTK IDE is a desktop application that is similar to the older Windows IDE.  JGTK is somewhat biased toward Linux  but this his doesn’t mean it doesn’t work well on other systems.  I run it on WinXP and Win7 machines all the time.  JGTK takes longer to load than the older Windows IDE but once it’s up and running I don’t see significant performance differences.  The JGTK editor is superior to the older Windows editor and I love the side bar and UNIX style code tagging.  For a first version JGTK is a nice bit of work that will only improve as J programmers and users pound away on it.

JGTK is nice but JHS is radical.  JHS abandons the desktop and turns your favorite web browser into a stripped down J programming environment.  I was skeptical when I first heard about JHS but it has won me over.  JHS is amazingly effective under Google’s Chrome browser.   It comes up in a flash and is as zippy or faster than the older Windows IDE.  Even better this webby goodness imposes minimal burdens. The JHS web server is often one of the smaller processes running on my machines.  I have been doing the bulk of my JOD update  work with JHS and I frequently forget I’m using a browser!  Good user interfaces, unlike politicians, get out of your face!

As for my J addon JOD: J 7.01 introduced a number of system script changes that break JOD 0.8.0.  I am working on JOD 0.9.0, (a beta version is available in the files box sidebar of this blog), that fixes these problems and implements some planned changes.  The biggest change so far: I have moved the JOD interface out of the z locale into it’s own ijod interface locale.  How’s this for a marketing slogan?  JOD 0.9.x – now with less z stomping!