1. Lost in Localisation - Ordering a Taxi while on Holiday

    I was on holiday in England this summer. I arrived in Worthing by bus from London, had a full day there, and wanted to take the train the following day to Bristol. The bus route from my BnB to the station was inconvenient and expensive. Ordering a taxi was my next idea. I wanted to do that without human interaction the evening before. This is the story of how that did not work.

    I name the taxi company in this article. This is not to shame them. I hope they just fix the issues, which I believe present in many applications, because localisation is hard.


    Website screenshot promising easy access via mobile app

    Quite a Promise

    The Mobile App

    Before analyzing why ordering a taxicab through their website did not work, I wanted to try the mobile app. I was hopeful, considering their promise that I could "book a car in seconds and don't even need to ring us!".

    As it turns out, this app is not available through the German Google Play Store. This means, you won't find it when you search for it and if you have the correct URL, you can't install the app through the web interface on a device that is connected to the German Google Play Store (so all of mine).

    But this is not a problem since you can download any APK that is available from any app store through a third-party service like APKPure (unendorsed). So knowing the name of the app in the Play Store I did that and installed the app.

    Screenshot of the app

    That is the App

    You're presented with a registration screen where you have to enter your phone number to receive a confirmation SMS. My (international) phone number didn't take and I was not able to complete the process. We will later see why. Being unable to register via the app, I return to the web site.

    Screenshot of the website

    The website looks quite modern

    The Web Site

    Order Form

    The first part of the order form

    As you can see, to order a taxi you first have to enter where you want to be picked up and where you want to go. The system then calculates the distance between both points, estimates the cost, and then you can go on to the next step.

    In order to calculate the distance, the web site uses the Google Maps API to create a route between both points and calculates a cost. Here's the corresponding code from the site:

    journey['distance'] = response.routes[0].legs[0].distance;
    journey['duration'] = response.routes[0].legs[0].duration;
    journey['miles'] = ( journey['distance'].value * 0.000621371192 );
    journey['price'] = CurrencyFormatted(2.90);
    journey['total'] = CurrencyFormatted( journey['price']
                                  + 1.90 * ( journey['miles'] - 0.2 ));
    
    journey['start_address'] = response.routes[0].legs[0].start_address;
    journey['end_address'] = response.routes[0].legs[0].end_address;
    

    This looks okay. It is to note that the site takes the addresses returned by google as the canonical start and end of the journey. So in principle, you could enter the name of a shop or restaurant as the target location of your taxicab ride and as long as google knows it, the taxi service is booked to the actual and correct address.

    Then there's another step of validation: Since the taxicab company only operates in Worthing in the south of England, they do not want to accept rides that are outside a specific area. They want to achieve this by parsing the post code from the canonical google response and compare it to a set of given post codes that correspond to the Worthing area: journey['start_postcode'] = journey['start_address'].match(/([A-Z]?\d(:? \d[A-Z]{2})?|[A-Z]\d{2}(:? \d[A-Z]{2})?|[A-Z]{2}\d(:? \d[A-Z]{2})?|[A-Z]{2}\d{2}(:? \d[A-Z]{2})?|[A-Z]\d[A-Z](:? \d[A-Z]{2})?|[A-Z]{2}\d[A-Z](:? \d[A-Z]{2})?),\s*UK$/)[0]. That's quite a large regular expression and I give you a visualisation powered by Regexper:

    Visualisation of the regular expression

    This does correspond in some fashion (but not exactly [1]) to the valid UK post codes followed by ", UK" and the end of the input.

    I give you part of the google response for a ride between some random house and the train station and let you figure out why this step fails for me:

    { "routes" : [
          { "legs" : [
                {
                   "distance" : {
                      "text" : "1,7 km",
                      "value" : 1654
                   },
                   "duration" : {
                      "text" : "5 Minuten",
                      "value" : 292
                   },
                   "end_address" : "Worthing BN11 1AA, Vereinigtes Königreich",
                   "start_address" : "Harrow Rd, Worthing BN11 4RB, Vereinigtes Königreich"
                }
          ]}
    ]}
    

    Google does return the post code correctly, but since my browser is set to German, Google's response is in German as well. The regular expression only matches strings ending in "UK", while my German response ends in "Vereinigtes Königreich" ("United Kingdom" in German).

    The fix seems easy enough. Just set the browser language to UK English and do the whole thing again and there we go:

    Form containing the quote of about 6 pounds

    Success?

    Final part of the booking form asking for the name and phone number

    Okay, good. Let's book the taxi.

    Now we just have to enter our name, email address, and phone number and we're good and finally can go by taxi wherever we want to go (within the greater Worthing area). As I enter my phone number, starting with +49 or 0049 for Germany, followed by three digits for the service provider [2], and seven or eight digits for the user, it dawns on me that it might not be that easy (as if it had been up to this point) and I take another glance at the code:

    function validateTelephone(telephone){
      var re = /^(?:\W*\d){11}\W*$/;
      return re.test(telephone);
    }
    

    Oh oh. Okay. Yeah. About that...

    My phone number does not fit that scheme (while 00000000000 does). If I get any confirmation code I need to respond to, or they want to call back for some additional questions, they won't be able to reach me. This is probably also the reason why the app did not work and why I couldn't receive an SMS with a confirmation code.

    I won't be able to order a taxi online. To recap the two main reasons why:

    • Because my browser is set to something else than English, the website can not verify that the cab journey takes place within the serviced area. Solution: Google provides GPS coordinates for the legs of the journey. One can easily check whether they are contained within a rectangle or convex polygon.
    • Because I have an international phone number, I can't interact with the parts of the process that depend on my cellphone. Solution: Allow for international phone numbers or make the process not depend on them.

    In the end I called them by phone, which took a whopping 25 seconds and the taxi arrived on time to bring me to the train station.


    [1]According to Wikipedia, the UK government has provided a regular expression that can be used for the purpose of validation: ^([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9]?[A-Za-z])))) [0-9][A-Za-z]{2})$
    [2]It's a little bit more complicated but sufficient for this story.

  2. Prolog Programs, Order of Evaluation, and Functional Equivalents

    While teaching Prolog this semester I had a few thoughts about Prolog programs and their equivalent programs in a functional programming language like Haskell.


    The Peano Naturals

    The Peano numbers are a unary number system for positive natural numbers where the zero is represented by a Z (or 0, or any atom - it doesn't matter) and every successor of a natural number N is the function S applied to N. We would therefore represent the natural number 3 with the term S(S(S(Z))). To look at it differently: in the Peano axioms, the naturals are defined as the set that contains zero and a closure over the successor function. In haskell we would define the type of the naturals like this:

    data N = Z | S N
    

    In Prolog we don't need a type definition for the naturals, as terms are generated without types. Now we want a predicate that checks whether our input is a natural number as we've defined it. The idea is that the term is a natural, iff either the term is a Z or it has an S as its root and its parameter is a natural. In Prolog we would write it intuitively like this:

    naturals(z).
    naturals(s(X)) :- naturals(X).
    

    And indeed this checks whether our input is a natural number. The query naturals(s(s(z))). returns true while naturals(foo). does not. Writing down this program in Haskell is not very useful since the type system does not allow us to have anything else but a natural as the function parameter but let's pretend:

    naturals v = case v of
                  S x -> naturals x
                  Z -> True
                  _ -> False
    

    And of course, calling naturals $ S $ S Z in haskell has the correct type and returns true. But this haskell program only corresponds to the case where we actually call the Prolog predicate with a bound parameter. We could call the prolog predicate with a free binding pattern like naturals(X). and get bindings for X for every solution, so every natural number. The corresponding Haskell programm would be:

    naturals_f  = [ Z ] ++ map S naturals_f
    

    This might need some explaining: The list corresponds to some notion of non-determinism. In Prolog the non-determinism is baked into the language due to backtracking from failed predicate evaluations and us asking for more answers. The Prolog program essentially says: Z is a solution. Everything with an S is a solution, if the "content" of the S is a solution as well. That's the same for our haskell programm. In our list of solutions we know that Z is in it and for every solution we have, adding an S leads to another one. And evaluating naturals_f starts to calculate the infinite list of natural numbers.

    Now consider the following Prolog program where we switched the order of the clauses:

    naturals(s(X)) :- naturals(X).
    naturals(z).
    

    This still works nicely for the bound case where we call naturals(s(s(z)). But if we now call naturals(X) we quickly get ERROR: Out of local stack. The natural intuition might be that the order of clauses does not matter but it does in this case. Writing down the equivalent Haskell programm we are able to see why:

    naturals_f  = map S naturals_f ++ [ Z ]
    

    We have the recursion before we have any solution. Well, the solution is easy: just write the simple case in the first line and the recursive case in the second. But let's consider another problem.

    Repeated Printing

    Consider the problem of repeatedly printing a character:

    repeat(0, _).
    repeat(N, C) :- write(C), NN is N - 1, repeat(NN, C).
    

    This looks very correct and is very wrong.

    ?- repeat(5, f).
    fffff
    true ;
    ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff...
    

    Now we see why it's nice to represent the naturals als Peano numbers. After hitting 0 there's a choice point left and asking for another solution leads to the negatives and printing our character in an infinite loop. So let's augment the second rule to prevent us from going into the negatives.

    repeat(0, _).
    repeat(N, C) :- N > 0, write(C), NN is N - 1, repeat(NN, C).
    % query:
    ?- repeat(5, f).
    fffff
    true ;
    false.
    

    This looks better but we still have a choice point left over which might be not so nice. The problem is that the matched patterns are not disjoint and after the end of the recursion there's still a pattern left over that fits. We can't encode that N should be larger than 0 in the head of the rule. Remember that we can match naturals larger than 0 (or any other number) in the rule head using Peano numbers - that's why they are so cool.

    But how do we fix our problem with the left-over choice point? There are two ways we can do it. We can either introduce a cut or reorder the rules.

    % Variant 1a: The order doesn't matter but we introduce a cut
    repeat(0, _) :- !.
    repeat(N, C) :- N > 0, write(C), NN is N - 1, repeat(NN, C).
    
    % Variant 1b: If we promise to be good and only call repeat with a positive numeric argument, we can even leave out the range check
    repeat(0, _) :- !.
    repeat(N, C) :- write(C), NN is N - 1, repeat(NN, C).
    
    % Variant 2: The order does matter
    repeat(N, C) :- N > 0, write(C), NN is N - 1, repeat(NN, C).
    repeat(0, _).
    

    I personally think that the variant without the cut is nicer. So for terminating rules, it might be better to have the "smaller" rule last as to not leave choice-points hanging around. Of course, this only works if the matched terms in the rules are proper subsets of each other so that we actually can order the rules in that fashion. In general, that might not be possible.

    Conclusion

    • The order of rules can (in theory) not affect termination. But if our rules are already non-terminating for our query, the order and timeliness of results does matter. Then order does matter.
    • Disjoint rule heads are important to not have left-over choice points.
    • If rules are terminating but the rule heads are not disjoint, they still should be ordered carefully.
    • Implicit conditions (N > 0) can lead to non-obvious non-termination if they are not made explicit.
    • By encoding data properties in the term structure we can make some implicit conditions explicit and make rule-heads disjoint.
    • Having numerical conditions in rule-heads would be really nice. Maybe some Prolog implementation in the future allows for that.

  3. Clojure and the Mandelbrot set on LEGO Mindstorms EV3

    The LeJOS operating system for LEGO Mindstorms EV3 is, as I said before, a small Linux with an embedded JVM.


    Running Clojure:

    To run Clojure on the EV3 I had to download the Clojure runtime, unzip it and throw away everything but the actual jar-file clojure-1.8.0.jar.

    Then the jar-file needs to be transfered to the EV3 (via scp for example) and can be executed via ssh with jrun -jar clojure-1.8.0.jar (I already have an alias in my ~/.ssh/config for the EV3). After about two minutes we have a Clojure repl that we can use to answer the important questions of our time.

    Takes about two minutes to start the repl

    Actually coding something:

    The first thing we have to notice is, that the leJOS menu stays visible when we opened the clojure repl. This is a problem insofar, that when we use the Clojure code to interact with (for example) the display, both the leJOS menu and our application want to use the display and their interactions will overlap in unpredictable ways (for me both images were "fighting", but the system might as well crash).

    So I created a shell script that kills the existing java process and opens it again once our command ran.

    #!/bin/sh
    killall java
    jrun # ... we will look at that later
    ~/lejos/bin/startmenu &
    

    Now we want to actually write Clojure code where we calculate members of the Mandelbrot set. The Mandelbrot set is the set of all numbers \(c\) for which \(z_n\) with \(z_0 = 0\) and \(z_{n+1} = z_{n}^2 + c\) does not diverge (when \(n\) goes to infinity). This is calculated in the complex plane. The numbers for which this doesn't diverge are usually drawn in black while the diverging numbers remain white.

    I looked for a Java-based library for the complex numbers and found one with Apache. This library was insofar underwhelming, that taking a complex number to the power of an arbitrary Integer doesn't work as one expects. The library always uses the following equivalence: \(y^x = exp(x \times log(y))\) which is fine in general but doesn't work if \(y\) is zero, which is the base case but also no problem with positive integer powers. Took me an hour because that is not at all well documented (for the integer-case). So the first thing is, to write our own pow function: (defn pow [y, x] (if (.equals y Complex/ZERO) y (.pow y x)))

    Now we can define both \(z_n\) and whether any \(z_n\) diverges:

    (defn zn [n, c] ( if (== n 0) Complex/ZERO (.add c (pow (zn (- n 1) c) 2.0 ) ) ))
    (defn diverges [x] (or (.isNaN x) (.isInfinite x) (< 2.0 (.abs x))))
    

    And the idea is, to set the whole display to black and evaluate for every black pixel \(z_1\) and then for every remaining black pixel \(z_2\). Once a pixel was set to white, we no longer need to evaluate it, because we already know it diverges.

    So here's the full code for mb.clj:

    (import [org.apache.commons.math3.complex Complex])
    (import [lejos.hardware.lcd LCD])
    (import [lejos.utility Delay])
    
    (defn pow [y, x] (if (.equals y Complex/ZERO) y (.pow y x) ))
    (defn zn [n, c] ( if (== n 0) Complex/ZERO (.add c (pow (zn (- n 1) c) 2.0 ) ) ))
    (defn diverges [x] (or (.isNaN x) (.isInfinite x) (< 2.0 (.abs x))))
    
    (defn scale_x [x] ( - (/ (* 2.5 x) LCD/SCREEN_WIDTH ) 2.0 ))
    (defn scale_y [y] ( - (/ (* 2.0 y) LCD/SCREEN_HEIGHT) 1.0 ))
    
    (doseq [x (range  LCD/SCREEN_WIDTH )
            y (range  LCD/SCREEN_HEIGHT)] (LCD/setPixel x y 1))
    
    (doseq [rep (range 1 1000)]
      (doseq [x (range  LCD/SCREEN_WIDTH )
              y (range  LCD/SCREEN_HEIGHT)]
              (
                  if (== 1 (LCD/getPixel x y))
                  (if (diverges (zn rep (Complex. (scale_x x) (scale_y y)))) (LCD/setPixel x y 0))
              )
      )
    )
    

    We don't need a delay at the end because this will take long enough.

    But now we have to think about running this. Basically we need to include every jar we use into the java classpath and then run the clojure main with our script as the parameter. For org.apache.commons.math3 we need the commons-math3-3.6.1.jar from Apache and for the lejos namespace we need the ev3classes.jar from leJOS (which is not included on the system because it is usually compiled into the finished jar).

    Once this is all done we can basically run our application with jrun -cp "../ev3classes.jar:./commons-math3-3.6.1.jar:../clojure-1.8.0.jar" clojure.main mb.clj.

    After a few hours, it will look like this

    I am pretty sure it is possible to compile the whole jar with the correct main using eclipse/ant and whatnot. But that's the first successful experiment in this direction. Here's a timelapse of the program in action.


  4. Upgrading Fedora 24 to 25

    Fedora 25 was released a few days ago.

    I've upgraded one of my machines. The others are soon to follow. Fedora 25 switches from X11 to Wayland as a default. For my laptop I had to switch last release because my laptop's touchpad was nowhere to be found in X. For this release I am looking forward to not getting to play games on my main desktop. I am sure to blog about any problems I happen upon.

    If you're running Fedora 24 you can easily upgrade to Fedora 25 running the following console commands. But before that, make sure your system is fully upgraded and rebooted, especially if you installed some kernel updates.

    sudo dnf install dnf-plugin-system-upgrade
    sudo dnf system-upgrade download --releasever=25 --allowerasing
    

    The --allowerasing argument allows some packages to be deleted during the process. I had some dangling haskell packages, that needed to be deleted. No problem there.

    Once all the downloads are done, type sudo dnf system-upgrade reboot to reboot your system starting the actual upgrading process.


    And yeah, I broke my work setup that used proprietary NVidia drivers to get a second monitor running.


« Page 2 / 7 »

links

social

Theme based on notmyidea