Showing posts with label Scala. Show all posts
Showing posts with label Scala. Show all posts

20 March 2021

11 Years of Prime Factors Kata

In this post I want to celebrate the Prime Factors Code Kata. Prime Factors is a small coding exercise first used by Robert C. Martin in 2005. It is my favourite code kata and it has been almost nine years since I last wrote about it - time to change that. Weird enough, the first code kata I ever worked on - outside of university assignments that turned out to be katas later - was Roman Numerals in 2007. The first time I did the Prime Factors was during Christmas holidays 2009 in Java and Ruby:
import java.util.ArrayList;
import java.util.List;

public class PrimeFactors {
  public static List<Integer> generate(int n) {
    List<Integer> primes = new ArrayList<Integer>();

    for (int candidate = 2; candidate <= n; candidate++) {
      for (; n % candidate == 0; n /= candidate) {
        primes.add(candidate);
      }
    }

    return primes;
  }
}
Now the Java code is exactly the code Robert Martin showed, I was following his example. The Ruby version from back then looks pretty similar, just using while instead of for.
module PrimeFactors
  def generate(n)
    prime_factors = []

    candidate = 2
    while n > 1 do
      while n % candidate == 0 do
        prime_factors << candidate
        n /= candidate
      end
      candidate += 1
      candidate = n if candidate > Math.sqrt(n) # performance fix
    end

    prime_factors
  end
end
If you are wondering how I am still able to find the code, I organise my code katas to allow lookup and comparison. Since then I did the kata 141 times and it has many uses.

Learn a language
Prime Factors is one of the first pieces of code I write - Test Driven of course - when revisiting old languages, like Commodore BASIC or looking at a new language, like Forth, using Gforth 0.7:
: prime_factors ( n -- n1 n2 n3 n4 )
  DUP 1 > IF           \ test for ?DO
    DUP 2 ?DO
      BEGIN
        DUP I MOD 0 =  \ test candidate I
      WHILE
        I SWAP I /     \ add candidate, reduce number
      REPEAT
    LOOP
  THEN
  DUP 1 = IF DROP THEN ;

T{ 1 prime_factors -> }T
T{ 2 prime_factors -> 2 }T
T{ 3 prime_factors -> 3 }T
T{ 4 prime_factors -> 2 2 }T
T{ 6 prime_factors -> 2 3 }T
T{ 8 prime_factors -> 2 2 2 }T
T{ 9 prime_factors -> 3 3 }T
Gforth came with a modified testing framework based on John Hayes S1I's tester.fs, defining the functions T{, -> and }T for testing. Note that the given function prime_factors is not realistic as the number of returned arguments is not known by the caller.

When I had a look at Scala, of course I did Prime Factors:
import java.lang.Math.sqrt

object PrimeFactors {
  def generate(number: Int): List[Int] = {

    def fold(current: Pair[Int, List[Int]], candidate: Int): Pair[Int, List[Int]] = {
      if (current._1 % candidate == 0)
        fold((current._1 / candidate, candidate :: current._2), candidate)
      else
        current
    }

    val (remainder, factors) =
      (2 to sqrt(number).intValue).foldLeft((number, List[Int]()))(fold)

    if (remainder > 1)
      (remainder :: factors).reverse
    else
      factors.reverse
  }
}
This is crazy. The code creates a sequence of all candidate primes and folds it starting from left by dividing by the candidate recursively, appending to the begin of the list, which is cheap. Because of that the list is reversed at the end. I have no idea why I created this, probably I was playing around with foldLeft. This is not a good example, please do not copy it. After all these years, the Forth solution seems easier to grasp. ;-)

So which languages are missing? PowerShell looks much like my PHP (shown below) and my Python Prime Factors looks similar too, just with Python specific range(2, number + 1) and //= inside. And of course JavaScript is missing:
PrimeFactors = function() {
  this.factors = [];
};

PrimeFactors.prototype.generate = function(num) {
  var candidate;
  for (candidate = 2; candidate <= num; candidate += 1) {
    num = this.tryCandidate(num, candidate);
  }
  return this.factors;
};

PrimeFactors.prototype.tryCandidate = function(num, candidate) {
  while (num % candidate === 0) {
    num = this.reduceByFactor(num, candidate);
  }
  return num;
};

PrimeFactors.prototype.reduceByFactor = function(num, factor) {
  this.factors.push(factor);
  return num / factor;
};
Isn't that lovely? Again this is not good code, please do not copy it. At least I showed some creativity using prototype functions.

Learn a testing framework
Using TDD to write some known code is a perfect way to learn more about a testing framework. So I explored XSLTunit using Prime Factors in XSLT or NUnit in C#:
using NUnit.Framework;

[TestFixture]
public class PrimeFactorsTest
{
  [TestCase(new int[0], 1)]
  [TestCase(new int[] { 2 }, 2)]
  [TestCase(new int[] { 3 }, 3)]
  [TestCase(new int[] { 2, 2 }, 4)]
  [TestCase(new int[] { 2, 2, 2 }, 8)]
  [TestCase(new int[] { 3, 3 }, 9)]
  public void TestFactors(int[] expected, int number)
  {
    CollectionAssert.AreEqual(expected, PrimeFactors.Generate(number));
  }

  [Test]
  [Timeout(100)]
  public void TestLarge()
  {
    CollectionAssert.AreEqual(new int[] { int.MaxValue },
                              PrimeFactors.Generate(int.MaxValue));
  }
}
Test your own testing framework
Sometimes, when I need to create my own unit testing framework, e.g. TPUnit for old Turbo Pascal, assert-scm (Scheme R5RS) or ASM Unit for Windows Assembly, I use Prime Factors as test cases:
_prime_factors:
  mov     esi, 0          ; esi = number of factors
  mov     edi, ebx        ; edi = address of factors
  mov     ecx, eax        ; ecx = current number
  mov     ebx, 1          ; ebx = candidate

  jmp .not_diviseable

.loop_over_candidates:
  inc     ebx             ; next candidate

.break_if_candidate_is_larger_than_square:
; if candidate * candidate <= number then try candidate
  mov     eax, ebx
  mul     ebx
  cmp     eax, ecx
  jbe     .try_candidate

; else number is a (large) prime and we are done
  mov     [edi + esi * register_size], ecx
  add     esi, 1
  jmp     .done

.try_candidate:
; if number % candidate == 0 then add candidate
  mov     eax, ecx
  xor     edx, edx
  div     ebx
  cmp     edx, 0          ; remainder is 0
  jne     .not_diviseable

.is_diviseable:
  mov     [edi + esi * register_size], ebx
                          ; store candidate in factors
  add     esi, 1          ; we found a factor
  mov     ecx, eax        ; number is remainder of division
  jmp     .try_candidate  ; try current candidate again

.not_diviseable:
; if number > 1 then try next candidate
  cmp     ecx, 1
  jne     .loop_over_candidates

.done:
; return number of factors
  mov     eax, esi
  ret
This piece of assembly calcultes the prime factors of the number passed in EAX into in the dword array address EBX.

TDD Demo
In 2012 I started practising Prime Factors as kata performance, minimising the number of keys I pressed. I ran it around 50 times. In the end I used the practice to calm down when I was anxious - it was like mediation. I still have to perform it somewhere, adding music and all... I have used it demoing TDD in uncounted presentations - actually around 40: during my university guest lectures, user group meetings and at my clients. Most demos were in Java and some in PHP,
<?php
class PrimeFactors {
  static function generate($n) {
    $factors = [];
    for ($candidate = 2; $candidate <= $n; $candidate += 1) {
      while ($n % $candidate == 0) {
        $factors[]= $candidate;
        $n /= $candidate;
      }
    }
    return $factors;
  }
}
and a single demo of test driving R code,
primeFactors <- function(number) {
  factors <- vector(mode="integer")

  candidate <- 2
  while (candidate <= sqrt(number)) {
    while (number %% candidate == 0) {
      factors <- c(factors, candidate)
      number <- number / candidate
    }
    candidate = candidate + 1
  }

  if (number > 1) {
    factors <- c(factors, number)
  }

  factors
}
It seems, most programming languages look the same. The last sentence is not true for NATURAL, Cobol's cousin, which is ugly. I will not show it here as it would destroy this lovely post.

Conclusion
By writing this post, I learned that I still need to create Prime Factors in the programming languages Go, Kotlin, OpenOffice Basic, Oracle PL/SQL and of course TypeScript - I could - and I will, it is just a matter of time. So Prime Factors - in fact any small enough code kata - is a great tool for exploring, studying and practising programming languages, testing frameworks, IDE tools and Test Driven Development in general. I will leave you with my latest addition to my collection of Prime Factors, using C99. Have fun!
#include <math.h>

typedef struct {
  unsigned char count;
  unsigned int values[31];
} PrimeFactors;

void PrimeFactors_init(PrimeFactors* factors)
{
  (*factors).count = 0;
}

void PrimeFactors_add(PrimeFactors* factors, const unsigned int factor)
{
  int count = (*factors).count;
  (*factors).values[count] = factor;
  (*factors).count = count + 1;
}

void generate(const unsigned int number, PrimeFactors* factors)
{
  PrimeFactors_init(factors);

  unsigned int remaining = number;
  for (unsigned int candidate = 2; candidate <= sqrtl(remaining); candidate += 1) {
    while (remaining % candidate == 0) {
      PrimeFactors_add(factors, candidate);
      remaining /= candidate;
    }
  }

  if (remaining > 1) {
    PrimeFactors_add(factors, remaining);
  }
}

21 January 2011

For You Scala Enthusiasts

On my way to a Jelly at sector 5 I walked past several streets which I had never seen before and stumbled upon a Scalagasse (Scalaalley or Scalalane):

Scalagasse in Wien MargaretenI didn't know that Martin had already whole streets dedicated to his golden egg :-)

(In fact the alley is named after the Viennese priest Johann Scala (1816–1888), who was a member of the district council.)

17 October 2010

Android Browser Bookmarks

In my previous post I already mentioned my Android phone. Soon after I got it, I wanted to import my bookmarks from my desktop PC to its standard browser. So being green I copied my favourites to the sdcard and tried to open them. Well it didn't work.

Google AndroidWorking Around
If I lived in "Google-land", I could use Google Bookmarks. But I don't and had to look for alternatives. After some searching I found a possible way to import bookmarks:
  1. Upload your single html file of exported bookmarks to a document on Google Docs.
  2. In your browser navigate to said page.
  3. Manually click on each link and save as bookmark.
Noooo, you must be kidding me. This is hideous. Then I found a nice instruction how to
import the bookmarks directly into the browser's database. That would be cool. But I'm not bold enough to root my new phone and "play with that, probably break it a bit - and then cry about it later." (Ward from androidcommunity.com)

My Solution
On several places I read hints that some Android apps were able to import bookmarks, but I couldn't find any. Instead I found Matthieu Guenebaud's Bookmarks Manager. It's able to backup and restore the browser bookmarks and uses a plain zip file to store them.
Viewing .ZIP: Bookmarks_2010-09-10_14-11-24.zip

Length  Method Size  Ratio    Date    Time   Name
------  ------ ----- -----    ----    ----   ----
  2273 DeflatN   710 68.8% 09.10.2010  2:11p bookmarks.xml
   526 DeflatN   531  0.0% 09.10.2010  2:11p 23.png
   764 DeflatN   769  0.0% 09.10.2010  2:11p 24.png
   326 DeflatN   331  0.0% 09.10.2010  2:11p 51.png
   684 DeflatN   689  0.0% 09.10.2010  2:11p 57.png
   239 DeflatN   238  0.5% 09.10.2010  2:11p 69.png
   541 DeflatN   546  0.0% 09.10.2010  2:11p 90.png
  1266 DeflatN  1271  0.0% 09.10.2010  2:11p 198.png
   490 DeflatN   495  0.0% 09.10.2010  2:11p 164.png
   304 DeflatN   309  0.0% 09.10.2010  2:11p 124.png
   408 DeflatN   413  0.0% 09.10.2010  2:11p 229.png
------         ----- -----                   ----
  7821          6302 19.5%                     11
The file bookmarks.xml has a simple XML structure of <bookmark>s inside a <bookmarks> element. Yes, that's something I can use.
  1. Backup the bookmarks, even if empty, to get an initial file.
  2. Unpack the archive.
  3. Insert bookmarks into the existing XML structure.
  4. Repack the archive.
  5. Restore from the modified zip file.
  6. Enjoy your new wealth of bookmarks.
In case you don't feel like editing the bookmarks.xml by hand, here is some Scala code.:
val bookmarkXml = scala.xml.XML.loadFile(targetFolder + "/bookmarks.xml")
val lastOrder = Integer.parseInt((bookmarkXml \\ "order").last.text)
val oldNodes = bookmarkXml \\ "bookmark"
val newNodes = to_xml_list(bookmarks, lastOrder + 1000)
val root = <bookmarks>{ oldNodes }{ newNodes }</bookmarks>
scala.xml.XML.save(targetFolder + "/bookmarks.xml", root, "UTF8", true, null)
Thanks to Scala's excellent XML support, reading and modifying the bookmarks file is easy. The method to_xml_list() iterates all favourites and creates XML fragments for each one using the following method.
def to_xml(bm: Favorite, order: Int) = {
  <bookmark>
    <title>{ bm.name }</title>
    <url>{ bm.url }</url>
    <order>{ order }</order>
    <created>{ bm.fileDate.getTime }</created>
  </bookmark>
}
Favorite is a class representing an Internet Explorer favourite that I wrote long ago. (Yeah baby, code reuse!) Value order is the number Bookmark Explorer uses for sorting bookmarks. See the complete source of GuenmatBookmarks.scala.

9 January 2010

Scala DSL for BASIC

Sweet holidays - two weeks without work (but unfortunately without any connection to the internet). So what did I do? I did some code katas in Java and Ruby and some reading. Masterminds of Programming is a fine book. After reading the chapter about BASIC, which was the first language I used in the 80ties, I spent some time thinking about the old days of Commodore 64 when GOTO was still state of the art ;-) Winter Holidays

"How about some fun coding or even performing a kata in BASIC?" Without any connection to the internet gwbasic.exe or any other interpreter was out of reach. But BASIC v2 (aka Commodore BASIC) was quite simple. So I started implementing an interpreter myself. The question was if it is possible to implement BASIC v2 as an internal DSL in Scala. DSLs are quite popular in Scala, even for other languages, e.g. Daniel Spiewak's DSL for calling JRuby from Scala. Let's see how far I got...

Hello World
First I want to compile
10 PRINT "Hello World"
RUN
That shouldn't be difficult using implicit conversions.
class Command(val lineNumber:Int, code: =>Unit) {
   def execute = code
}
class NewStatement(line:Int) {
   def PRINT(msg:String) =
      addToMemory(new Command(line) { println(msg) })
}
implicit def Int2NewStmt(line:Int) = new NewStatement(line)

def RUN ...
Note that there has to be a blank after the line numbers. This is not necessary for BASIC. Method addToMemory adds the Command instance containing the code to run (println(msg)) to a list for later execution by the mixed in RUN method. (In case you are not familiar with Scala, the code that finally gets compiled for line number 10 looks like new NewStatement(10).PRINT("Hello World"). Scala is not strict about dots and braces.) By adding PRINT(num:Int) to NewStatement
10 PRINT 12 + 12
20 PRINT 144/12
work as well. Unfortunately I am not able to implement PRINT's concatenating features, i.e.
40 PRINT "a" ; "b"
50 PRINT "a" , "b"
Commodore 64 because ; is the empty statement in Scala and parameter lists containing , need braces.

My old friend where have you gone?
One of the first examples in the Commodore 64 Manual is
10 ? "Commodore 64"
20 GOTO 10
? is just a shortcut for PRINT. So add
class NewStatement(line:Int) {
   ...
   def ?(msg:String) = PRINT(msg)
   def GOTO(target:Int) =
      addToMemory(new Command(line) { lineCounter = target })
}
The implementation of RUN uses lineCounter to keep track of executing line numbers. When the Command with GOTO's code is executed, it changes the number of the next line to be run. Tada! Now I can GOTO Scala ;-)

Assignment and Evaluation
Let's tackle some basic assignments.
10 X% = 15
20 X = 23.5
30 X$ = "SOME STRING"
40 PRINT X
50 PRINT X$
Using the same approach as before (with some refactoring of addToMemory()):
class NewStatement(line:Int) {
   ...
   def X_=(d:Double) = {
      addToMemory(line) { variables.X = d }
   }
   def X:Double = throw new Error()

   def X$_=(s:String) = {
      addToMemory(line) { variables.X$ = s }
   }
   def X$:String = throw new Error()
}
The empty getters are necessary for Scala to recognise the methods as overridden bean properties and are never called. Although % is a valid Scala method name, it's not possible to suffix method names with it. So integer "variables" like def X%_=(i:Int) can't be defined and line 10 will never compile. (One could escape `X%`, but then the BASIC code would have to look like 10 `X%` = 15.)

For evaluation I need a method without parameters called X, in the scope of the current code, like RUN. The method must not return the value of the variable because that would be too early. It should return code that yields the value of the variable when called (yes that's a function ;-).
def X:Function0[Double] = new Function0[Double] {
   override def apply = variables.X
}
def X$:Function0[String] = new Function0[String] {
   override def apply = variables.X$
}
The container variables holds the values of all variables during "BASIC runtime", i.e. when RUN is called. To use the variables in PRINT, it must accept these functions as arguments as well.
class NewStatement(line:Int) {
   ...
   def PRINT(msg:Function0[String]) = {
      addToMemory(line) { println(msg()) }
   }
}
Basic Instinct Variables
To allow arbitrary variables all variable names have to be predefined for assignment and for evaluation. That's impossible. But fortunately early basic used only two letters for variables. [A-Z] and [A-Z][A-Z0-9] yield 962 valid names for each Double and String variables. So all these getters and setters could be generated. The generated trait VariableAssignment with all setters is mixed into NewStatement and the generated trait VariableEvaluation with all getters is mixed into the main code. (Unfortunately the Scala compiler crashes with StackOverflowError when mixing in the trait with its nearly 4000 methods.) The container variables is better replaced with two maps, one for Double and one for String variables.

Express Yourself
To add two variables, i.e. to add the values of two variable evaluations, I change all literals and Function0s to expressions (read custom function instances). This is a major refactoring but Scala makes it easy for me. Literals are still covered by implicit conversions.
abstract class DoubleExpression {
   def value:Double

   def +(d:DoubleExpression) =
      new DoubleExpression {
         override def value = DoubleExpression.this.value + d.value
      }
   def *(d:DoubleExpression) =
      new DoubleExpression {
         override def value = DoubleExpression.this.value * d.value
      }
}
class LiteralDoubleExpression(val value:Double) extends DoubleExpression

implicit def Double2DoubleExpression(value:Double) =
   new LiteralDoubleExpression(value)
implicit def Int2DoubleExpression(value:Int) =
   new LiteralDoubleExpression(value.toDouble)

trait VariableAssignment {

   def assign(name:String, d:DoubleExpression) = {
      addToMemory(line) { memory.set(name, d.value) }
   }
   def unused = throw new Error("never used")

   def X_=(d:DoubleExpression) = assign("X", d)
   def X:DoubleExpression = unused
   ...
}
class NewStatement(line:Int) extends VariableAssignment {
   ...
   def PRINT(msg:StringExpression) =
      addToMemory(line) { println(msg.value) }
}
trait VariableEvaluation {

   class VariableDoubleExpression(val fromVariable:String)
      extends DoubleExpression {
      override def value = memory.get(fromVariable)
   }
   def X = new VariableDoubleExpression("X")
   ...
}
The code above shows the changes needed for Double. Similar classes and methods are needed to cover StringExpressions. Now let's examine what happens for 10 X = X + 1: The method call X of trait VariableEvaluation returns a VariableDoubleExpression (which will yield the value of "X" when called) on which + is invoked with the implicitly converted new LiteralDoubleExpression(1.0). The resulting DoubleExpression is the argument for the setter method X_= of trait VariableAssignment in NewStatement.

Let it Flow (Control)
Till now the Scala BASIC lacks any flow control, e.g. a conditional GOTO like
40 IF X < 5 THEN 20
class NewStatement(line:Int) {
   ...
   def IF(expr: => Boolean) = {
      class ConsumeThen(expr: => Boolean) {
         def THEN(target:Int) = addToMemory {
            if (expr) ... // goto target
         }
      }
      new ConsumeThen(expr)
   }
}
Method IF consumes the boolean expression (as a function) and returns some instance of a class that only allows the following THEN. Method THEN in turn adds the code to execute the GOTO to the list of Commands. BASIC comparison operators are different from Scala's and I have to implement them in DoubleExpression:
abstract class DoubleExpression {
   ...
   def <(d:DoubleExpression) = value < d.value
   def <>(d:DoubleExpression) = value != d.value
}
The implementation of FOR is a bit more complex mainly because the BASIC "runtime" has to keep track of the current loop, so NEXT statements are executed proper.
10 FOR X = 1 TO 5
20 PRINT "COMMODORE 64"
30 NEXT X
To allow 1.TO(5) I define a LoopRangeExpression that handles the boundaries of the loop:
class LoopRangeExpression(val lower:DoubleExpression) {
   var upper:DoubleExpression = null
   def TO(u:DoubleExpression) = {
      upper = u
      this
   }
}
implicit def Int2LoopRangeExpression(value:Int) =
   new LoopRangeExpression(new LiteralDoubleExpression(value.toDouble))
Scala compiles the loop statement into new NewStatement(10).FOR().(X()) = new LoopRangeExpression(new LiteralDoubleExpression(1.0)).TO(new LiteralDoubleExpression(5.0)). That's why method TO has to return its instance, to match the update method (used for setting array elements). So FOR has to return an object that defines update(variable: VariableDoubleExpression, range: LoopRangeExpression). Inside method update all necessary code to run the loop is put into the BASIC runtime part. Both FOR and NEXT use DoubleExpression to capture the variable names but never evaluate these expressions.
class NewStatement(line:Int) {
   ...
   def FOR = {
      new Object() {
        def update(variable:VariableDoubleExpression,
                   range:LoopRangeExpression) = {
           val usedVariable = variable.fromVariable
           addToMemory {
              ... // set usedVariable to range.lower.value
           }
           addLoop(usedVariable) {
              ... // increment and check condition
           }
        }
      }
   }
   def NEXT(d:VariableDoubleExpression) = addToMemory {
      ... // perform next for name d.fromVariable
   }
}
Functions
Support for functions like INT() or LEN$() is simple to add. I use another trait BasicFunctions, that defines the functions in terms of expressions, e.g.
def INT(d:DoubleExpression) =
   new DoubleExpression {
      override def value = d.value.floor
   }
Time for Refactoring
Although I factored out the traits of getters, setters and functions there is still too much stuff in the main class. Parsing BASIC should be separated from the logic that stores and executes the lines of code, the "runtime". I call this logic the BasicMemory.
Scala DSL for BASIC class hierarchyTrait BasicParser is the main parser, which mixes in VariableEvaluation and BasicFunctions. It contains the NewStatement and all implicit conversions. Trait BasicMemory contains the program code as list of Commands and the logic needed to run them. The parser communicates with the runtime by using a small interface LinesOfCode for adding commands, runtime and memory (read variable) operations. BasicApplication is just for convenience, so BASIC programs may be written as
object Demo extends org.codecop.basic.BasicApplication {

   10 PRINT "Hello World"
   RUN
}
The Whole Beast
The shown code was developed with Scala 2.7.7. Download the full source here. There is more implemented than covered in this post, e.g. INPUT, END or STEP. This little DSL has been an experiment. It's far from complete. It lacks important features like DATA/READ or GOSUB/RETURN. I reckon that supporting DIM or DEFFN would be difficult as well. And there are things that can't be done in Scala, e.g. the shown problem with %. Still if this is interesting to people, I may do a proper release into an OSS project somewhere later.

23 March 2009

Resources to Start Scala

The Scala Programming Language has been around some time and started getting popular in 2007. A good place to start are the tutorials included in the distribution, which are also available online: A Scala Tutorial (very short 15 pages), Scala By Example (already 145 pages) and The Scala Language Specification (full 180 pages).

Early multimedia resources that picked my interest were Martin Odersky's talk, The Scala Experience at JavaOne 2007 and the 62th episode from Software Engineering Radio. Further Martin Odersky gave another talk at JavaOne 2008 and JAX'08.

In January 2008 Ted Neward began his busy Java developer's guide to Scala, which started with stuff from the Scala Tutorial but went into greater detail later. Another blogger that is definitely worth mentioning is Daniel Spiewak, who wrote the nice Scala for Java Refugees as well as on some special topics like Integrating Scala into JRuby. Another piece worth recommending is Dean Wampler's blog titled the The Seductions of Scala. James Iry has to be mentioned for exploring more theoretical stuff in nice little chunks.

After spending some time with Scala, I went for the only book available, Programming in Scala, a comprehensive step-by-step guide with massive 754 pages. Unfortunately it did not ship for almost 9 (!) months and I do not like ebooks. (I know - I should have read the whole page when ordering; that it was not printed, not even finished back then.) However, now it's printed. Since January it's standing on my book shelf and torturing my conscience.

I have to pull myself together and finally start reading it!