Saturday, March 18, 2017

Amazon Dash Buttons

Amazon makes lots of interesting hardware gadgets. This one, the Amazon Dash Button, is of questionable value. Amazon has a variety of dash buttons, branded with Tide, Glad, Charmin, etc. The consumer buys the Button for $5, peels off the sticky tape, and fastens the Button in some convenient location. Then, when he runs out of plastic bags or dog food, he pushes the appropriate button, and a corresponding order is placed automatically. Your first order is discounted by $5, so the Button is effectively free.

The product works fine, and is a nice piece of engineering. The problem is, people like to push buttons. I put a Tide button near my laundry machine, and people pushed it. Do you know how much laundry detergent I have?

I would prefer to have the dash button add the corresponding item to my shopping list, which would act as a reminder to order more.

I can think of lots of great uses for such "Internet Buttons." I'd like to have one that turns on my recirculating hot water, one that orders a pizza, etc. Fortunately, Amazon offers a generic AWS IoT Button that can be custom programmed. It's not free, of course, but costs $20.

With my interests in all things IoT, I'm a big fan of the Dash button, of course. In fact, I invented my own. This Amazon Dash button is for purchasing Amazon Dash buttons. When you run low on Amazon Dash buttons, simply press the button, and it will order another one for you.


A configuration option allows you to order two Buttons with a single press.

Thursday, January 26, 2017

Trump Impeachment Day: Jan 21, 2018


From U.S. Constitution, Amendment XXII, Section 1:
no person who has held the office of President, or acted as President, for more than two years of a term to which some other person was elected President shall be elected to the office of President more than once
The Republican Senate will wait until Jan 21, 2018 to convict President Trump of impeachment. The House can impeach the president before then, but it's important that V.P. Pence become president less than two years before the end of the term. This way, Pence is still eligible for an additional eight years. Imagine that! Ten years of President Pence after two years of President Trump!

Here's a thought. It takes 2/3 of the Senate to convict, and the Republicans have only 52 seats. What if the Democrats decide to leave Trump in office? From their perspective, in many ways, Pence is worse. Also, the Democrats will have lots of leverage to make a deal with Trump before allowing him to stay in office. Trump will be happy to oblige because his thin skin will feel horribly slighted by the Republicans.

Net, net: No chance Trump will be impeached.


Saturday, August 02, 2014

Sprint Overcharges for Overages

The new Sprint Framily plans are very inexpensive. It's a trade off. Sprint's service is mediocre and declining. The new LTE roll out was a disaster, with large areas (e.g., the entire downtown Sunnyvale) having no service for extended periods. The service seems to have stabilized now, and the very fast LTE is available in many areas.

The LTE service seems to cover much smaller regions than their 3G. If you happen to be in an LTE area, you will get terrific, very fast service. Outside of those smallish areas, you still have 3G, which performs quite fine. The problem is when you find yourself at the boundary of the LTE area, still inside the 3G. The LTE signal is too weak to provide service but strong enough to prevent the phone from switching to 3G. Thus, in the middle of a perfectly useful 3G signal, I often get no service.

I might blame the device, which should determine that LTE isn't strong enough and automatically switch to 3G. My device is a Samsung Galaxy 4S, the most popular phone in the world. It would surprise me if this most popular device were at fault. Doesn't matter, it's a problem.

To get 3G coverage when I am at the edge of LTE, I change the phone settings. (Settings / More Networks... / Mobile networks / Network mode / CDMA.) This change is inconvenient, as the phone has to reboot. Switching back is another reboot, so I find myself leaving the phone in 3G most of the time. I can stream Netflix in 3G no problem, so maybe LTE isn't that important.

Why not switch to AT&T, Verizon, or T-Mobile? Sprint has been less expensive than the others for a long time. And the new Family plans are priced even much lower. I now pay $25/mo/line, which includes unlimited voice, unlimited text, and 1 GB data. For 2 lines, I pay an extra $10 or $20, for 3 GB data or unlimited data.

Sprint bill is much lower than it would be with Ting, which is known as a low cost leader. If they were less expensive, I'd switch to Ting. Problem is, they haven't lowered their prices since they launched.

Most lines have only 1 GB of data. Not enough, you might think, given that people use a lot of data these days. Surprisingly, Sprint is very reasonable about overages these days. If you use more than the 1 GB but not enough to justify paying the extra $10/mo to get 3 GB, they charge for the overages at a reasonable amount.

How much do they charge? $15/GB. If you use 1.5 GB, then you are charged an overage of about $7.50. My overages this month was $13.06.

I have two points to make about the $15/GB overage charge. First, that amount is mentioned in the contract, but only in the context of the tablet plan and the 3 GB plan. There is no overage amount mentioned for the 1 GB default.

Second, whenever you are approaching your 1 GB limit, Sprint sends text messages:


These messages state that the overage amount is $0.01/MB (= $10/GB). According to these text messages, Sprint overcharges for the overages.

I called them to ask for my refund. They are sure the overage price is $0.015/MB. We refer to the contract. They are surprised that the overage amount isn't mentioned (for the 1 GB plan). I then quote the messages they send me.

Casey A.: I do believe you and we are seeing it here on our end. It's just that these notifications can only have two decimal digits that's why it's only $0.01.
You: not my problem
You: I make decisions based on the information you provide me
You: You can't expect any reasonable person to agree that a company with the technical sophistication to deploy a cell phone system is technically incompetent, as you describe.
Casey A.: I have been approved for an adjustment for $4.14 on the account. We will create a report for this one to have these notifications corrected. We do apologize for the inconvenience.

They then provide the refund. (My refund this month was $4.14. Not worth my time, I know, but it's the principle that motivates me.)

One final note. Sprint defines a MB as 210 bytes, so when requesting a refund, remember to divide the KB by 1024, not 1000, and you'll get a few more pennies back.

Saturday, July 05, 2014

WolframAlpha Boils Wrong

Wolfram Alpha is very good at knowing or computing the answer to a wide range of questions, especially math and science related. I love Wolfram Alpha and use it all the time. Siri does too, and uses it to answer many arbitrary questions.

We learn in high school chemistry class that the boiling point of water at sea level is 100°C. At elevations above sea level, where pressure is reduced, the boiling point drops. For example, at 10,000 feet above sea level, water boils at 90°C.

I live in Los Altos, CA, at 157 feet above sea level. I expect water to boil at very close to 100°C, but I am calibrating a thermometer, so I want to be accurate. To my surprise, Wolfram Alpha gets it wrong:

For water to boil at 100.1°C, Los Altos would have to be below sea level, which we know is not true, and so does Wolfram Alpha:


Wolfram Alpha, I am disappointed in you!

While we are at it, why is Wolfram Alpha so slow?

Friday, June 06, 2014

Fast Alphanum Algorithm

The Alphanum algorithm sorts strings "the right way". By default, most sorting is done lexicographically, which means that you sort two strings based on the left-most character in each string that do not match. Ted comes before Tim because e comes before i. Also, A100 comes before A2 because 1 comes before 2. You've probably seen files sorted this way before:

    A1
    A10
    A11
    A2
    A3
    A4
    A5
    A6
    A7
    A8
    A9

You want to see it sorted this way:

    A1
    A2
    A3
    A4
    A5
    A6
    A7
    A8
    A9
    A10
    A11

That's where the Alphanum algorithm comes in. It compares two strings in a smarter way, so that you get the second result above, not the first.

Now ask yourself how should these strings be sorted?

    x3
    xy
    x
    x3
    x2
    x23
    000
    01
    009
    01
    2x
    2
    #2x
    #20
    #2
    40003
    4000000000002
    0000000000003
    00003
    000034
    xyz40003q
    xyz4000000000002q
    xyz0000000000003q
    xyz00003q
    xyz000034q
    00000
    00000000000000000000000
    0
    (empty string)

Which comes first, 0, 00000, or 00000000000000000000000 ? Let's say that numbers representing the same value should be sorted shortest first.

I wrote my alphanum algorithm a long, long time ago. It was slow, so I rewrote it, and now it's really fast. I published it, in the sense that it is used in various websites, but I never wrote it up. Why write it up now?

I just came across various implementations of alphanum collected by Dave Koelle at http://www.davekoelle.com/alphanum.html. So it's out there. And his versions seem to be pretty popular. There are a great many discussions about his collection.

Unfortunately, the code is pretty slow. The implementations use a "chunkify" approach, which converts each string into separate alpha and numeric parts. That's a whole lot of computation and memory management, which is slow.

My first implementation of alphanum did something similar, but I eventually moved past it. I found it is possible to write alphanum to do a single pass over the data, comparing the two strings in place, which is much faster. (In fact, this version is O(N), the same as lexicographic string comparison, so this version could potentially run the same speed as lexicographic string comparison.) Comparing to Dave's JavaScript version, mine runs about 9 times faster. (More on that later.) The code is also simpler, at least until the final optimized version. Here it is.
    function alphanum(a, b) {

        function isdigit(ch) {
            return '0' <= ch && ch <= '9';
        }

        var min = Math.min(a.length, b.length);

        for (var i = 0; i < min; i++) {
            if (a.charCodeAt(i) !== b.charCodeAt(i)) break;
        }

        if (i == min) return a.length - b.length;
        var cmp = a.charCodeAt(i) - b.charCodeAt(i);

        if (isdigit(a[i - 1]) || isdigit(a[i]) && isdigit(b[i])) {
            for (;; i++) {
                var ai = isdigit(a[i]);
                var bi = isdigit(b[i]);
                if (ai !== bi) return ai - bi;
                if (!ai || !bi) break;
            }
        }

        return cmp;
    }


Try it here: http://jsfiddle.net/SpP65/. The example uses the data set from Dave's page.

One problem: there is an important case that my code does not handle. See if you can figure it out. (It's tricky.)









==== SPOILER ====


I found Dave's page and saw that the algorithm was slow. However, my version didn't handle a significant case properly: numbers with leading zeros. How should frame2, frame3, and frame002 sort? It's obvious that frame002 belongs between the other two, but the code above doesn't do that.

For years I have been pondering how to fix my code while keeping it simple and fast. Today I figured it out. Unfortunately, I didn't manage to keep it simple. But the change to support leading zeros made the code even faster!

The approach: given all the edge conditions, I decided to simply blow out the code into a bunch of special cases, to deal with each edge condition separately. This way, you get lots of code, but only one code path runs for each case, so it's very fast. Here it is.
    function alphanum(a, b) {
        
        function isdigit(ch) {
            return '0' <= ch && ch <= '9';
        }
        
        var min = Math.min(a.length, b.length);
        
        for (var i = 0; i < min; i++) {
            if (a.charCodeAt(i) !== b.charCodeAt(i)) break;
        }
        
        if (i == min) return a.length - b.length;
        
        if (isdigit(a[i])) {
            if (isdigit(b[i])) {
                if (a[i] === '0' || b[i] === '0') {
                    for (var z = i - 1; z > 0; z--) {
                        if (a[z] !== '0') break;
                    }
                    if (!isdigit(a[z])) z++;
                    if (a[z] === '0' || b[z] === '0') {
                        for (var j = i; ; j++) {
                            if (b[j] !== '0') break;
                        }
                        for (;; i++) {
                            if (a[i] !== '0') break;
                        }
                        if (!isdigit(a[i])) {
                            if (isdigit(b[j])) {
                                return -1;
                            } else {
                                return i - j;
                            }
                        } else if (!isdigit(b[j])) {
                            return 1;
                        } else {
                            var cmp = a.charCodeAt(i) - b.charCodeAt(j);
                            for (i++, j++;; i++, j++) {
                                if (!isdigit(a[i])) {
                                    if (isdigit(b[j])) {
                                        return -1;
                                    } else {
                                        if (cmp) return cmp;
                                        return i - j;
                                    }
                                } else if (!isdigit(b[j])) {
                                    return 1;
                                }
                            }
                        }
                    }
                }
                
                var cmp = a.charCodeAt(i) - b.charCodeAt(i);
                for (i++;; i++) {
                    if (!isdigit(a[i])) {
                        if (isdigit(b[i])) {
                            return -1;
                        } else {
                            return cmp;
                        }
                    } else if (!isdigit(b[i])) {
                        return 1;
                    }
                }
            } else if (isdigit(a[i - 1])) {
                return 1;
            } else {
                return a.charCodeAt(i) - b.charCodeAt(i);
            }
        } else if (isdigit(b[i])) {
            if (isdigit(b[i - 1])) {
                return -1;
            } else {
                return a.charCodeAt(i) - b.charCodeAt(i);
            }
        } else {
            return a.charCodeAt(i) - b.charCodeAt(i);
        }
    }
    
    $('#a').text([
        '1000X Radonius Maximus',
        '10X Radonius',
        '200X Radonius',
        '20X Radonius',
        '20X Radonius Prime',
        '30X Radonius',
        '40X Radonius',
        'Allegia 50 Clasteron',
        'Allegia 500 Clasteron',
        'Allegia 50B Clasteron',
        'Allegia 51 Clasteron',
        'Allegia 6R Clasteron',
        'Alpha 100',
        'Alpha 2',
        'Alpha 200',
        'Alpha 2A',
        'Alpha 2A-8000',
        'Alpha 2A-900',
        'Callisto Morphamax',
        'Callisto Morphamax 500',
        'Callisto Morphamax 5000',
        'Callisto Morphamax 600',
        'Callisto Morphamax 6000 SE',
        'Callisto Morphamax 6000 SE2',
        'Callisto Morphamax 700',
        'Callisto Morphamax 7000',
        'Xiph Xlater 10000',
        'Xiph Xlater 2000',
        'Xiph Xlater 300',
        'Xiph Xlater 40',
        'Xiph Xlater 5',
        'Xiph Xlater 50',
        'Xiph Xlater 500',
        'Xiph Xlater 5000',
        'Xiph Xlater 58'
    ].sort(alphanum).join("\n"));
    
    $('#b').text([
        'x3', 'xy', 'x', 'x3', 'x2', 'x23',
        '000', '01', '009', '01',
        '2x', '2', '#2x', '#20', '#2',
        '40003', '4000000000002', '0000000000003', '00003', '000034',
        'xyz40003q', 'xyz4000000000002q', 'xyz0000000000003q', 'xyz00003q', 'xyz000034q',
        '00000', '00000000000000000000000', '0', ''
    ].sort(alphanum).join("\n"));
Try the new code: http://jsfiddle.net/nz6uU/3/

I find it fascinating how complicated the code got. There is nothing surprising in the code, just a lot of it. To understand it, pick various cases and follow the code through.

And how fast is it? See the benchmark: http://jsperf.com/alphanum/4. The first is Dave's alphanum. The second, named "timkay" is the broken (but simple) version, and the last, named "timkay 2" is the optimized version.


The version that handles leading zeros correctly also runs over 10x faster than the chunkify version. Sweet!


Saturday, May 10, 2014

Target Screen Size for Web Design

Most new screens are HD resolution. These screens are very wide to be a target width for web page design. Instead, many websites still target a minimum width of 1024 pixels. The IBM XGA display was 1024x768 pixels, and nobody has seen an XGA monitor for decades. Never the less, I am thrilled that the de facto standard has survived for one simple reason. I like using three monitors. I have a 1920x1200 monitor in the middle and two 1280x1024 monitors on the sides. The latter are in portrait mode, so they are 1024 pixels wide. They are perfect for viewing most web pages.

Occasionally, I want to see four windows, so I split the large middle screen. Microsoft Windows supports "gestures", that lets you perform certain actions. If you jam a window against the top of the screen, for example, it maximizes on that screen. If you jam the window against the left or right side of the screen, it fills the left or right half of the screen. On the middle of the screen, there is no left or right. (The pointer moves to the next screen.) Instead, you can use Windows-Left and Windows-Right to get the same effect.

It's useful to split the big screen. The problem is that HD divided by two is 1920 / 2 = 960, which is slightly less than 1024, the target width for most websites. It means that you can't see a full web page most of the time.

I propose that website designers no longer target 1024 pixels width, but instead target 960 pixels width. It's 6% fewer pixels, which will make negligible difference in practice, and it will allow two full web pages side-by-side on an HD screen.

Friday, May 09, 2014

HP - Best Service Ever! - NOT

I wrote the following glowing report based on what HP said they would do. Turns out my little mistake completely scrambled their system. They still have my working unit, and I still have the broken one. Now we need to place a third order because they canceled the first order. I have no idea what happened, except that the representative whom I spoke highly about below appears to have made a messy situation even worse.



I bought HP Folio 13 notebook computers for my kids. I started buying the first. The price was really good because it was refurbished. I chose this model because of the good price and the SSD, which makes a computer run much, much faster. My daughter liked it so much that I bought a second one for my son. They have been working really well for both kids.

One day my wife needed a notebook computer. I've had good luck with these, so I bought a third. This unit worked well for a few months. She was quite happy with it. Then it turned into a brick. No lights at all.

According to the HP site, the unit is still under warranty, which is not something I had considered. That's excellent! I called HP. They placed a repair order and sent me a box with prepaid shipping. It would take about a week for them to receive it, fix it, and return it to me. All at no cost to me! That's excellent service. I shipped the unit 10 days ago, so where is it?

To my dismay, my son's unit stopped working 2 days after I sent off the first unit. Exactly the same symptoms. Hmm. Maybe there is a design flaw. His is also under warranty, so I am going to file a second repair order. I just haven't gotten around to it.

Today I received a call from HP. Apparently the unit they received has a serial number that does not match the repair order. It didn't take us long to realize that I sent in the wrong unit. My son must have switched them somehow! And, no, there isn't a design flaw. We didn't have two units die in the same time frame with exactly the same symptoms!

I'm thinking, this is our mistake. I'll have HP return the one unit, and I'll have to pay for shipping for the other unit. Instead he says, as long as they have that one, they should check it out to make sure it's performing properly. Then they'll return it. As well, he is placing another repair order for the broken one. Wow! That is excellent service! My mistake, and they still fix it without charging me.

I will buy HP from now on.