philihp.com

Mining Bitcoin in the Eligius pool using Phoenix2

I prefer to use the Eligius bitcoin mining pool because the pool takes no cut (compared to the larger, more popular BTC Guild, which takes 3-5%). The “getting started” for the Eligius Bitcoin mining pool doesn’t show how to configure Phoenix2, which is faster than, and supports more features over the older Phoenix 1.xx miner. Here’s a quick/dirty tutorial of how I set them up to work together on my machine.

NOTE: It’s cheaper to buy bitcoin on an exchange than to mine it. The cost of a dedicated machine will probably take 1-2 years to recoup.

Step 1: Download Phoenix 2

Extract it to a folder on your hard drive, like c:\phoenix\. Anywhere. Doesn’t really matter.

Step 2: Create phoenix.cfg

Create it in that same folder. It should contain something like this. Once running you can play around with some of the parameters.

[general]
    autodetect = +cl -cpu
    verbose = False
    backend = http://1PhiLHuzbozkqVkWYZEUiptKNGYNfne9Hb:x@mining.eligius.st:8337
    backups = http://rpcuser:rpcpassword@localhost:8332/
    failback = 600
    queuesize = 1
    ratesamples = 100
    logfile = False
[cl:0:0]
    name = GPU 0
    kernel = phatk2
    aggression = 1
    bfi_int = True
    fastloop = False
    goffset = True
    vectors = True
    vectors4 = False
    worksize = 128

Once running, you can tinker around with these last parameters to get the highest Mhash/s rate. You will want to change the username to be the backend property to be where you want your rewards to be sent when the pool finds a new block (ideally yourself). You will also need some luck for the pool to start finding Bitcoin. You can generate a higher chance of that happening by donating bitcoin to 1PhiLHuzbozkqVkWYZEUiptKNGYNfne9Hb

Step 3: There is no step 3. Just run phoenix.exe from that folder. Go find something to do while your computer makes you money.

Automatic Base SAS Library Assignments

If you stick a file named autoexec.sas in the directory where SAS is installed, it will run automatically when SAS starts up. By default, this place is C:\Program Files\SASHome\SASFoundation\9.3. This has worked since at least SAS 8.2, probably before.

I like to use my Windows desktop as a temporary staging area, so I have my autoexec.sas automatically assign a “DESKTOP” library with this code:

%let USERPROFILE=%sysget(USERPROFILE);
libname desktop "&USERPROFILE\Desktop\";

So when SAS starts up, the library is assigned and my log says this:

NOTE: Copyright (c) 2002-2010 by SAS Institute Inc., Cary, NC, USA.
NOTE: SAS (r) Proprietary Software 9.3 (TS1M2)
      Licensed to Microsoft Windows for x64 All Compatible Non-Plann, Site  XXXXXXX.
NOTE: This session is executing on the X64_7PRO  platform.
 
 
 
NOTE: Enhanced analytical products:
 
SAS/STAT 12.1, SAS/ETS 12.1, SAS/OR 12.2, SAS/IML 12.1, SAS/QC 12.1
 
NOTE: SAS initialization used:
      real time           0.65 seconds
      cpu time            0.63 seconds
 
 
NOTE: AUTOEXEC processing beginning; file is C:\Program
      Files\SASHome\SASFoundation\9.3\autoexec.sas.
 
NOTE: Libref DESKTOP was successfully assigned as follows:
      Engine:        V9
      Physical Name: C:\Users\philihp\Desktop
 
NOTE: AUTOEXEC processing completed.

Finding the Center of US Counties in SAS

I had a problem where I needed the center longitude and latitude of each US county. SAS comes with some datasets containing map data in the format of tables that trace a line around the borders of counties. I was interested in the MAPSGFK.US_COUNTIES dataset, which has data that looks like this:

The State and County numbers were FIPS codes, which were also stored in the US_COUNTIES_ATTR table, so those are easy enough to figure out. Segment seemed to be always 1. Resolution and Density were numbers that I figured had to do with how much detail the drawing would have if these points were used in a line drawing.

The longitude and latitude here are all points along the border. The simplest, and most incorrect way of getting the center of the county would be to take an average of them, which would be fine for rectangular counties, but for territories where one side is flat (and has few waypoints) and one side is jagged (such as a coastal border), the midpoint will be weighted to that side. So instead, use geometry.

In geometry, there’s a formula for the Centroid of a polygon. This is a magic formula that I just trust is correct, because everything on Wikipedia is true, without exception. Here was my first pass at an implementation of it in SAS:

proc sort data=mapsgfk.us_counties out=centroids;
  by state county;
run;
data centroids(keep=cx cy county state);
  retain yi yj xi xj a cx cy x0 y0 0;
  set centroids(keep=state county lat long rename=(lat=yj long=xj));
  by state county;
  if(first.county) then do;
    cx = 0;
    cy = 0;
    a = 0;
    x0 = xj;
    y0 = yj;
  end;
  else if(not first.county) then do;
    ta = (xi*yj - xj*yi);
    cx + ((xi+xj)*ta);
    cy + ((yi+yj)*ta);
    a + ta;
  end;
  if(last.county) then do;
    ta = (xj*y0 - x0*yj);
    cx + ((xj+x0)*ta);
    cy + ((yj+y0)*ta);
    a  = ta + a * 0.5;
    cx = cx / (6*a);
    cy = cy / (6*a);
    output;
  end;
  xi = xj;
  yi = yj;
run;

Here, xi and yi are always the coordinates from the previous point, and “if not first.county” prevents processing of the first point of a county because it didn’t have a previous point. The variables a, cx, and cy accumulate for every point. The variable ta is the area of the rectangle defined by the two points. Once all the points have been accumulated, the area is halfed so it’s the actual area, and the area is used in calculating the centroid (cx and cy).

But then I noticed the 2nd county was positioned somewhere in the mid-Atlantic. Something had to be up. Looking closer, this was Baldwin County, which had an island. Looking at the source data, this segment was drawn in two segments, which caused the centroid formula to choke, as it assumed contiguous shapes. So instead, in my second pass, I compute the centroid of every segment, then average them weighted by their areas.

proc sort data=mapsgfk.us_counties out=centroids;
  by state county segment;
run;
data centroids_temp;
  retain yi yj xi xj a cx cy x0 y0 0;
  set centroids(keep=state county segment lat long rename=(lat=yj long=xj));
  by state county segment;
  if(first.segment) then do;
    cx = 0;
    cy = 0;
    a = 0;
    x0 = xj;
    y0 = yj;
  end;
  else if(not first.segment) then do;
    ta = (xi*yj - xj*yi);
    cx + ((xi+xj)*ta);
    cy + ((yi+yj)*ta);
    a + ta;
  end;
  if(last.segment) then do;
    ta = (xj*y0 - x0*yj);
    cx + ((xj+x0)*ta);
    cy + ((yj+y0)*ta);
    a  = ta + a * 0.5;
    cx = cx / (6*a);
    cy = cy / (6*a);
    output;
  end;
  xi = xj;
  yi = yj;
run;
proc sql;
  create table centroid_weight as
  select
    state, county, sum(a) as sum
  from centroids_temp
    group by state, county;
quit;
proc sql;
  create table centroids as
  select a.state, a.county,
         sum(cx*(a / sum)) as lat,
         sum(cy*(a / sum)) as long
    from centroids_temp a
    inner join centroid_weight b
      on (a.state = b.state and a.county = b.county)
    group by a.state, a.county;
quit;
proc sql;
  drop table centroids_temp;
  drop table centroid_weight;
quit;

Here, I add two more steps. The first proc SQL block sums up the total area of each county, which is used in the second block to find the average centroid of each county’s centroids, weighted by the total area that centroid represents, which gives us correct centroids for counties drawn in multiple segments.

Bayesian Averaging in SAS

Hypothetical situation, lets say you’ve got a list of movies that you want to rank in a website or a report or something, and you have user-submitted ratings for them, but some are more popular than others, so your data looks like this:

  data ratings;
    input name $ rating;
    datalines;
  Lincoln 9
  Lincoln 8
  Lincoln 9
  Amour 9
  Argo 5
  Argo 10
  ;
  run;
Obs name rating
1 Lincoln 9
2 Lincoln 8
3 Lincoln 9
4 Amour 9
5 Argo 5
6 Argo 10

The easiest thing to do would be to calculate an average rating for each movie like this:

  proc sql;
    select distinct name, avg(rating) as average
      from ratings
      group by name
      order by average desc;
  run;
name average
Amour 9.00
Lincoln 8.67
Argo 7.50

But hey! That’s not cool. It looks like Amour wins, because its average rating is 9. Maybe we want to consider Lincoln as better because 3 people think it’s very high. A good way to deal with this is by instead taking a Bayesian Average.

This means we’re going to add in some “dummy” votes for each movie, who give each movie the average rating a movie gets. How many (C) is a judgement call, the more we add, the harder we make it for an obscure movie to be near the top. Likewise, if a movie’s first rating is low, it keeps it from suddenly dropping to the bottom of the list. If we expect thousands of ratings for each movie, a C=1000 might be appropriate. In this example, I use a small C of 10.

  proc sql;
    select avg(rating) into :average
      from ratings;
    select distinct
        name,
        (sum(rating) + &average * 10) / (count(*) + 10) as b_average
      from ratings
      group by name
      order by b_average;
  quit;
name b_average
Lincoln 8.41
Amour 8.39
Argo 8.19

And look! Lincoln is back on top, since its bayesian average more closely reflects a product of the number of ratings it has and what those ratings are.

How To Save Struts Messages After a Redirect

If you’ve got an Action in Struts 1 that leaves an error or message for the view, it does it somewhere in the class like this:

ActionMessages messages = getMessages(request);
messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("message.detail", "Normality has been restored."));
saveMessages(request, messages);

However if your Action later selects an ActionForward that redirects (possibly unbeknownst to the Action, since that is configured elsewhere), the Controller will redirect the browser to that new location and Struts will see that redirected call as an entirely new request, having lost anything in the request attributes (such as the message you just saved).

There are some other ways to do it, but here’s a really simple way to do it:

saveMessages(request.getSession(), messages);

Once they’re displayed in the view once they will automatically be removed from the session. In this fashion, it behaves like a Flash scoped variable in JSF, except you can be sure it’s displayed even if the next request fails, which is probably a good thing.

WARNING: Form ‘myForm’ not found for locale ‘en_US’

It looks like someone had this error in 2006, and it was never resolved. I am posting this, hoping that in 2018 someone will have the same issue again, and the wisdom of the ancients will not fail him.

Dec 10, 2012 1:41:05 AM org.apache.commons.validator.ValidatorResources getForm
WARNING: Form 'myFormBean' not found for locale 'en_US'

In Struts 1.3, when you see this error in your code, it’s because you have a ValidatorForm specified in your struts-config, and Struts is trying to instantiate it, and wondering what validator rules it follows. The validator is looking in your validation.xml file (the thing specified in the plug-in element in your struts-config.xml file), and not seeing any mention of your form. The bit about the locale is a red herring; every Form bean that extends ValidatorForm listed in struts-config.xml must have a corresponding form element in validation.xml. It doesn’t matter if your ValidatorForm does all of its own validation in validate(). Everything will work, but if that empty element isn’t in validation.xml, you will get this annoying warning, and if you treat warnings as errors, then you’re probably wondering how to fix it.

To fix it, add an empty form element to validation.xml, like this:

<form-validation>
	<formset>
		<form name="myFormBean" />
	</formset>
</form-validation>

Please use this knowledge to save us from the Ko-dan armada.

Getting the last element in an array in JSF and JSTL

I’d love it if JSTL had some sort of way to get the last element in an array. Ruby conveniently makes negative indexes start from the end of the array:

myarray[-1]

For some reason, lists and arrays in Java have “.length” and “.size()” accessors, instead of either getLength() or getSize(), and there isn’t a convenience method for this. And for some reason, this has never been corrected, and the old way deprecated. Who knows? Bad decisions from the start have a way of growing like cancer. In JSTL and JSF, which relies heavily on getters for property accessing, there’s a helper function for this: fn:length(). Using this, you can get the last element of an array like this:

${myArray[fn:length(myArray)-1]}

or in JSF

#{myArray[fn:length(myArray)-1]}

Maybe someone at Sun Oracle will see this and add it to Java 9?

Selecting Rows from the Last 5 Years in SAS

I was asked recently in a Q&A, “how to select the last 5 years worth of data from a table in SAS?” One way of doing it is by selecting the data with a Proc SQL query similar to the following, and something similar could also be done in a data step:

PROC SQL;
  create table mywindow as
  select ...myvariables...
    from mylib.mytable
    where year(mydate) between year(today())-5 and year(today());
QUIT;

Caveat emptor, this is really only going to include the last 4 complete years and whatever portion of the current year is in the dataset. This is because year() truncates out the month and day of the date.

Of course, this is just one way to do it. By using the intnx function, more advanced intervals can be done.

SAS Documentation:

Two Common Struts Null Pointer Exceptions

The First Problem

I have come across this problem at 2 times this weekend. Since I came across it twice, I assume a high likelihood that others will as well. Hopefully the people of the future will be able to Google the stack trace and find this post. The problem is that after mucking around with my struts-config.xml file’s hooks, I got this error when trying to visit a page:

javax.servlet.ServletException: java.lang.NullPointerException
	org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:286)
	org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
	org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:449)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
	com.philihp.weblabora.util.EntityManagerFilter.doFilter(EntityManagerFilter.java:32)
 
java.lang.NullPointerException
	org.apache.struts.config.FormBeanConfig.createActionForm(FormBeanConfig.java:289)
	org.apache.struts.config.FormBeanConfig.createActionForm(FormBeanConfig.java:357)
	org.apache.struts.chain.commands.CreateActionForm.execute(CreateActionForm.java:92)
	org.apache.struts.chain.commands.ActionCommandBase.execute(ActionCommandBase.java:51)
	org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
	org.apache.commons.chain.generic.LookupCommand.execute(LookupCommand.java:305)
	org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
	org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:283)
	org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
	org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:449)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
	com.philihp.weblabora.util.EntityManagerFilter.doFilter(EntityManagerFilter.java:32)

The problem was that the type of my form-bean couldn’t be instantiated, because it didn’t exist. My config looked like:

<form-bean name="loginForm" type="org.apache.struts.action.DynaValidatorForm">
	<form-property name="username" type="java.lang.String" />
	<form-property name="password" type="java.lang.String" />
</form-bean>

The problem was org.apache.struts.action.DynaValidatorForm does not exist (or wasn’t found on the classpath). Instead, the correct line was

<form-bean name="loginForm" type="org.apache.struts.validator.DynaValidatorForm">
	<form-property name="username" type="java.lang.String" />
	<form-property name="password" type="java.lang.String" />
</form-bean>

In another case, I typed “com.apache…” instead of “org.apache…”.

The Second Problem

This problem’s symptom was a stack trace like this:

java.lang.IllegalArgumentException: The path of an ForwardConfig cannot be null
	org.apache.struts.chain.commands.servlet.PerformForward.perform(PerformForward.java:70)
	org.apache.struts.chain.commands.AbstractPerformForward.execute(AbstractPerformForward.java:54)
	org.apache.struts.chain.commands.ActionCommandBase.execute(ActionCommandBase.java:51)
	org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
	org.apache.commons.chain.generic.LookupCommand.execute(LookupCommand.java:305)
	org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:191)
	org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:283)
	org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
	org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:462)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

The problem is that an Action with a ValidatorForm form was validating with errors, and didn’t know where to send the client back to. This can happen if both the validate and input attributes of the Action are omitted. It can be remedied by setting validate=”false” (if you don’t want it to validate) or input=”some path” (if you do want it to validate).

Struts needs to know where to send the user if there are errors in validation.

A Farmville 2 Profit/Experience Maximization Spreadsheet

I’ve been playing Farmville 2 since launch. At first it started out as a mix of nostalgia and curiosity, but after reaching level 5 and opening up the cookhouse, I discovered there was a fun supply-chain management problem in it, and I made this Google Docs spreadsheet. It glosses over a few things like the average number of fruit a tree produces, and the costs involved in farming livestock.


http://goo.gl/5oOAq

If you have any data or corrections you’d like to contribute, please email me.