Wednesday, October 22, 2014

Git Hooks: One Size Unfortunately Does Not Fit All

Was disheartened to find out recently that despite all the progress in the world's software development community, even when everyone is using git, you cannot rely on a standard set of hooks to enforce commit rules and perform other actions.

Some links to show what I'm talking about:

The problem isn't that something like hooks can't be accomplished across IDEs and platforms in 2014, but rather that it is a pain. Hooks shouldn't require this much lock-in.

Wednesday, October 15, 2014

Which Programming Language or Architecture Is Most Popular?

Although my favorite way of judging programming language or architecture popularity is indeed.com's trends, it might also be helpful to take a look at Tiobe's stats. Other gauges include GitHub's trending projects and architectures and programming language showcase.

Also, here's a mid-2014 post that analyzes GitHub, showing trends in language usage, and another showing Javascript framework popularity.

Saturday, September 20, 2014

Should I Get the 2 Large or the 3 Medium Pizza Special from Papa John's?

As of 2014, the Papa John's large pizza is 14 inches and the medium is 12 inches. They have a special running for 2 large or 3 medium, and if you're only concerned about the size without the crust that is ~11 inches diameter for the medium or ~13 in for the large.

3 medium = 3 x 95.03 = 285.09 sq. in.

2 large = 2 x 132.73 = 265.46 sq. in.

So, the medium is the better deal for the same price. However, the large special included a specialty pizza, so that's what I got.

Friday, September 12, 2014

Java Fun

It's Friday as I'm writing this, so for fun, here are some neat facts about Java.

  • hashCode() of strings are a hash int value of the contents of the string which can be 0 or at the integer limits.
  • Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE
For some information about hashCode() strings, see this answer in StackOverflow.

Some examples in beanshell:

% System.out.println("actuation conjugant".hashCode());
2147483647
% System.out.println("bequirtle zorillo".hashCode());
0
% System.out.println("polygenelubricants".hashCode());
-2147483648
% System.out.println(Integer.MIN_VALUE);
-2147483648
% System.out.println(Math.abs("polygenelubricants".hashCode()));
-2147483648

I quickly hacked up a simple Java app to process OS X's /usr/share/dict/words. It's suboptimal but works for a test:

import java.io.*;
import java.util.ArrayList;

public class Test {
    public static final void main(String args[]) throws IOException {
        ArrayList words = new ArrayList();
        try(BufferedReader br = new BufferedReader(new FileReader("words"))) {
            for(String line; (line = br.readLine()) != null; ) {
                words.add(line);
            }
        }

        String s;
        for (int i = 0; i < words.size(); i++) {
            s = words.get(i);
            test(s);
            test(s.toUpperCase());
            test(s.toLowerCase());
            for (int j = 0; j < words.size(); j++) {
                s = words.get(i) + words.get(j);
                test(s);
                test(s.toUpperCase());
                test(s.toLowerCase());
                s = words.get(i) + " " + words.get(j);
                test(s);
                test(s.toUpperCase());
                test(s.toLowerCase());
            }
        }
    }

    public static final void test(String s) {
        int h = s.hashCode();
        if (h == Integer.MIN_VALUE || h == 0 || h == Integer.MAX_VALUE) {
            System.out.println("" + h + " = " + s);
        }
    }
}

Some strings with hashCode() of Integer.MAX_VALUE, 2147483647 (some of the following I found with a different version of the code):

ABASEDNESS RESEMBLANCERESEMBLANT
actuation conjugant
addlepate Burushaski
actuation conjugant
addlepate Burushaski
AIRCREWBRUTALIZATION
ANALYZATIONDIANCECHT
andrewsite sruti
apatetic libber
attached retranslate

Some strings with hashCode() of 0:

accismus toddlertoddy
acephalisthematolysis
ACETOMORPHINE ACHENE
acousmatic gymnarchidaegymnarchus
ALVEOLODENTAL WALDENSES
APODAN UNCONTENDED
auraadmonitorial
BALSAMROOT SQUARENESS

Some strings with hashCode() of Integer.MIN_VALUE, -2147483648:

accompt hypercriticism
acetothienoneruledom
achroite unenviablyunenvied
adventitiouslyoniscoidea
AFFORCEMISBAPTIZE
aggressquadrifurcate
bamboozlercastaway

I modified it to wait until the end to output and show progress, e.g.

rate = 186ms/word set. Estimated 730 min left. 1 found
Here's the code with the modification:

import java.io.*;
import java.util.ArrayList;

public class Test {
    public static final void main(String args[]) throws IOException {
        ArrayList<String> words = new ArrayList<String>();
        try(BufferedReader br = new BufferedReader(new FileReader("words"))) {
            for(String line; (line = br.readLine()) != null; ) {
                words.add(line);
            }
        }

        String s;
        int counter = 0;
        ArrayList<String> results = new ArrayList<String>();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < words.size(); i++) {
            s = words.get(i);
            test(s, results);
            test(s.toUpperCase(), results);
            test(s.toLowerCase(), results);
            for (int j = 0; j < words.size(); j++) {
                s = s + words.get(j);
                test(s.toUpperCase(), results);
                test(s.toLowerCase(), results);
                s = words.get(i) + " " + words.get(j);
                test(s.toUpperCase(), results);
                test(s.toLowerCase(), results);
            }
            counter++;
            long rate = (System.currentTimeMillis() - startTime) / counter;
            long minLeft = ((words.size() - counter) * rate) / 1000 / 60;
            System.out.println("rate = " + rate + "ms/word set. Estimated " + minLeft + " min left. " + results.size() + " found");
        }
    }

    public static final void test(String s, ArrayList<String> results) {
        int h = s.hashCode();
        if (h == Integer.MIN_VALUE || h == 0 || h == Integer.MAX_VALUE) {
            results.add("" + h + " = " + s);
        }
    }
}

Then I made it multi-threaded which helped it to go a bit faster:

rate = 86ms/word set. Estimated 337 min left. 0 found
Here's the code with the suboptimal multi-threading modifications:

import java.io.*;
import java.util.ArrayList;

public class Test {
    public static final int SPLIT = 3;
    public long startTime;
    public int counter;
    public ArrayList<String> results;

    public static final void main(String args[]) throws IOException, InterruptedException {
        (new Test()).test();
    }

    public void test() throws IOException, InterruptedException {
        processWords(readWords());
    }

    private ArrayList<String> readWords() throws IOException {
        System.out.println("Reading file 'words'");
        ArrayList<String> allWords = new ArrayList<String>();
        try(BufferedReader br = new BufferedReader(new FileReader("words"))) {
            for(String line; (line = br.readLine()) != null; ) {
                allWords.add(line);
            }
        }
        return allWords;
    }

    private void processWords(ArrayList<String> allWords) throws InterruptedException {
        System.out.println("" + SPLIT + " threads starting");
        ArrayList<ArrayList<String>> wordLists = split(allWords, SPLIT);

        this.results = new ArrayList<String>();
        this.counter = 0;
        this.startTime = System.currentTimeMillis();

        for (ArrayList<String> words : wordLists) {
            (new Thread(new WordHashCodeChecker(words, allWords, this))).start();
        }
        
        while(counter < allWords.size()) {
            Thread.sleep(1000);
            if (counter > 0) {
                long rate = (System.currentTimeMillis() - this.startTime) / counter;
                long minLeft = ((allWords.size() - counter) * rate) / 1000 / 60;
                System.out.println("rate = " + rate + "ms/word set. Estimated " + minLeft + " min left. " + this.results.size() + " found");
            }
        }

        for (String result : results) {
            System.out.println(result);
        }

    }

    public ArrayList<ArrayList<String>> split(ArrayList<String> originalList, int numLists) {
        int usualMaxSize = originalList.size() / numLists;
        ArrayList<ArrayList<String>> lists = new ArrayList<ArrayList<String>>();
        ArrayList<String> current = new ArrayList<String>();
        for (int i=0; i<originalList.size(); i++) {
            current.add(originalList.get(i));
            if (current.size() >= usualMaxSize && lists.size() < numLists) {
                lists.add(current);
                current = new ArrayList<String>();
            }
        }
        return lists;
    }

    class WordHashCodeChecker implements Runnable {
        
        ArrayList<String> words;
        ArrayList<String> allWords;
        Test test;

        public WordHashCodeChecker(ArrayList<String> words, ArrayList<String> allWords, Test test) {
            this.words = words;
            this.allWords = allWords;
            this.test = test;
        }

        public void run() {
            String s;
            for (int i = 0; i < words.size(); i++) {
                s = words.get(i);
                checkHashCode(s.toUpperCase());
                checkHashCode(s.toLowerCase());
                for (int j = 0; j < allWords.size(); j++) {
                    s = words.get(i) + allWords.get(j);
                    checkHashCode(s.toUpperCase());
                    checkHashCode(s.toLowerCase());
                    s = words.get(i) + " " + allWords.get(j);
                    checkHashCode(s.toUpperCase());
                    checkHashCode(s.toLowerCase());
                }
                this.test.counter++;
            }
        }

        public final void checkHashCode(String s) {
         int h = s.hashCode();
            if (h == Integer.MIN_VALUE || h == 0 || h == Integer.MAX_VALUE) {
                this.test.results.add("" + h + " = " + s);
            }
        }
    }
}

Friday, August 29, 2014

Buying and Using a Used Verizon Phone

To buy a used Verizon phone, you first want to find a vendor. You could use a directory and intermediary service like Swappa. Swappa basically provides a directory listing and they ask the seller to try to ensure the phone is clean before sale. However, it does not absolve you from needing to ask that the phone has a non-zero and clean (not reported stolen) IMEI and is able to be activated on the Verizon network without additional work. If the phone is rooted be very careful, as it might have an IMEI of 0, which will be a problem. If you decide to go forward with the purchase, Swappa charges the buyer a certain amount (e.g. $10) as part of the sale as well as charging you from the seller the remaining amount of the sale, such that on your Paypal bill you would see the bill for Swappa and the charge from the seller. You can track the device during shipping and when you receive it, you must first check the IMEI to ensure it is not 0, and then try activating it. If you have any problem with your device don't mark the device as received; you would then need to comment on the sale page and then manually send a note in the resolution area of Paypal against the Seller (not Swappa) stating that the seller needs to reimburse you because you are sending the phone back, and then Swapper and seller must then credit you that amount they charged back. So, what Swappa really is doing here is just acting as a directory service and arbitrator for the sale. If you'd rather, you could try buying the phone on eBay or Craiglist, etc. If you do that, take the same precautions asking the seller if the device has a clean, non-zero IMEI that can be used on the Verizon network (e.g. CDMA, 3G (old/slow) or 4G LTE). You may need to go get a new SIM card from Verizon in order to activate your device. Unfortunately for the hearing impaired or those using chat, the Verizon chat support cannot help to the same extent that the phone support staff can, and they are much slower. Newer 4G LTE Verizon devices can automatically register on the network as part of the process, though you may have to reset your network settings on some devices if you are doing something like changing the phone number associated with the SIM. SIMs can vary in size! Older devices will need to call *228 to register on the Verizon network. This involves having to enter "1" on the phone to register after it is answered. The old device must either be off or the battery or even the SIM removed if the phone cannot be turned off (if the screen is badly damaged). To ensure your new device is both registered and able to use the Verizon network, you can call #832. I do not work for Verizon, so don't ask me questions in the comments- just call Verizon for assistance. Good luck!

Tuesday, August 26, 2014

Newer Workaround for set -e Bug in bash with RVM

Just found out that with the newer RVM (noticed in v1.25.28 and 29 (stable) but not sure which versions are affected), the old fix that we used in our bash script:

# Load RVM into a shell session *as a function*
if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
  # First try to load from a user install
  source "$HOME/.rvm/scripts/rvm"
elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
  # Then try to load from a root install
  source "/usr/local/rvm/scripts/rvm"
else
  printf "ERROR: An RVM installation was not found.\n"
fi

That allowed use of so that RVM wouldn't break when using both set -e and cd'ing into a project directory that has a .ruby-version/.ruby-gemset:

set -e
cd /some/path/to/project

Will now break the script, exiting when you call the cd command (issue #3017).

The workaround provided by Michal Papis is to use the builtin cd and rvm use instead which seems to work:

set -e
builtin cd /some/path/to/project
rvm use .

Edited 2014-09-04 to provide solution from Michal Papis.

Tuesday, August 19, 2014

Apple Won't Merge Your Multiple Apple IDs

Just had a few hour long conversation with Apple through 3 levels of their tech support discussing something very simple: moving the purchase history from one Apple ID to another.

Both Apple IDs belong to me, and both IDs are similar, except that one was created prior to Apple using email addresses as IDs and the other was created as an email address, because the device at the time would not allow me to login with the non-email address Apple ID. Each account has purchase history that I'd rather not lose.

The only two solutions the Apple representatives stated that you can use if you find yourself in this situation are:

  1. Login as each account and download your purchases, and/or
  2. In order to avoid what seems sometimes to be a mandate to convert your old non-email address to an email address Apple ID, just get another email address and rename the non-email address account to that other email address.

The primary reason that Apple provided for why this is the case is that merging Apple ID accounts would be a "security issue". They did not describe what that issue was.

I specifically stated that I only require them to move the purchase history from the original account to the new account. I also successfully authenticated both online and over the phone with the representative to both accounts. For me, it is not a security issue. It is a customer service issue.

A greater concern is that two of the representatives said that this was a frequent cause of customer complaints. It was like I was hearing Bill Clinton say, "I feel your pain-", but over and over (and over) again. If it is such a pain, why haven't they done anything about it?

What reason is there to disallow the move of purchase history from one Apple ID to another? How simple would it have been and how much money would Apple be saving in customer service calls if they were to only develop a tool that customers could use to transfer ownership of an app from one account to another? It could even restrict the move to require that both accounts share the same primary email address.

In the meantime, maybe one of the workarounds Apple provided above might help you. They didn't really help me.

Note: this was written August 19, 2014. Hopefully at some point Apple will come to their senses and resolve this, so please contact them if you need support.