From c64ceeb60f1dc9ca71c5465cfe5ba2fc3c67a6e4 Mon Sep 17 00:00:00 2001
From: Andreas Zwinkau <qznc@web.de>
Date: Wed, 14 Jan 2015 20:56:00 +0100
Subject: [PATCH 1/5] Fix makefile dependencies

---
 makefile | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/makefile b/makefile
index b4fccf0..deede85 100644
--- a/makefile
+++ b/makefile
@@ -3,8 +3,9 @@ INCLUDE_PATHS := -Ilibdparse/src
 DMD_FLAGS := -g -w $(INCLUDE_PATHS)
 LDC_FLAGS := -g -w -oq $(INCLUDE_PATHS)
 
-dmd: $(SRC)
-	dmd $(DMD_FLAGS) $(SRC) -ofbin/dfmt
+.PHONY: dmd ldc test
+
+dmd: bin/dfmt
 
 ldc: $(SRC)
 	ldc2 $(LDC_FLAGS) $(SRC) -ofbin/dfmt
@@ -12,3 +13,7 @@ ldc: $(SRC)
 
 test: bin/dfmt
 	cd tests && ./test.sh
+
+bin/dfmt: $(SRC)
+	dmd $(DMD_FLAGS) $(SRC) -ofbin/dfmt
+

From 8dfce773d186f143a3fedc0c4b09c9f77fde7a82 Mon Sep 17 00:00:00 2001
From: Andreas Zwinkau <qznc@web.de>
Date: Wed, 14 Jan 2015 20:56:21 +0100
Subject: [PATCH 2/5] always a space after 'return'

---
 src/dfmt.d | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/dfmt.d b/src/dfmt.d
index 71944ae..7b44ac0 100644
--- a/src/dfmt.d
+++ b/src/dfmt.d
@@ -141,6 +141,11 @@ private:
                     formatStep();
             }
         }
+        else if (current.type == tok!"return")
+        {
+            writeToken();
+            write(" ");
+        }
         else if (current.type == tok!"switch")
             formatSwitch();
         else if (current.type == tok!"for" || current.type == tok!"foreach"

From 158c679349dc1ae88ebb379c0092c05204228e46 Mon Sep 17 00:00:00 2001
From: Andreas Zwinkau <qznc@web.de>
Date: Wed, 14 Jan 2015 21:16:53 +0100
Subject: [PATCH 3/5] no doubleNewline at end of file

---
 src/dfmt.d            | 2 +-
 tests/frontpage.d.ref | 1 -
 tests/hello.d.ref     | 1 -
 3 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/dfmt.d b/src/dfmt.d
index 7b44ac0..c2e8f81 100644
--- a/src/dfmt.d
+++ b/src/dfmt.d
@@ -383,7 +383,7 @@ private:
                     newline();
                 write("}");
                 depth--;
-                if (index < tokens.length &&
+                if (index < tokens.length-1 &&
                     assumeSorted(astInformation.doubleNewlineLocations)
                     .equalRange(tokens[index].index).length)
                 {
diff --git a/tests/frontpage.d.ref b/tests/frontpage.d.ref
index 654056d..640ab33 100644
--- a/tests/frontpage.d.ref
+++ b/tests/frontpage.d.ref
@@ -12,4 +12,3 @@ void main()
     }
     writeln("Average line length: ", lines ? sumLength / lines:0);
 }
-
diff --git a/tests/hello.d.ref b/tests/hello.d.ref
index 3894114..0beb9aa 100644
--- a/tests/hello.d.ref
+++ b/tests/hello.d.ref
@@ -4,4 +4,3 @@ void main()
 {
     writeln("Hello, world without explicit compilations!");
 }
-

From 8983dc8b155c60c1fc6d95e2263ce585dd0fb6e9 Mon Sep 17 00:00:00 2001
From: Andreas Zwinkau <qznc@web.de>
Date: Wed, 14 Jan 2015 22:39:45 +0100
Subject: [PATCH 4/5] tuning whitespace around comments

---
 src/dfmt.d           | 22 ++++++++++++++++++++--
 tests/comments.d     |  6 ++++++
 tests/comments.d.ref |  7 +++++++
 3 files changed, 33 insertions(+), 2 deletions(-)
 create mode 100644 tests/comments.d
 create mode 100644 tests/comments.d.ref

diff --git a/src/dfmt.d b/src/dfmt.d
index c2e8f81..341c91e 100644
--- a/src/dfmt.d
+++ b/src/dfmt.d
@@ -115,8 +115,25 @@ private:
         assert (index < tokens.length);
         if (current.type == tok!"comment")
         {
+            const i = index;
+            if (i > 0)
+            {
+                if (tokens[i-1].line < tokens[i].line)
+                {
+                    if (tokens[i-1].type != tok!"comment"
+                        && tokens[i-1].type != tok!"{")
+                        newline();
+                }
+                else
+                    write(" ");
+            }
             writeToken();
-            newline();
+            if (i >= tokens.length-1)
+                newline();
+            else if (tokens[i+1].line > tokens[i].line)
+                newline();
+            else if (tokens[i+1].type != tok!"{")
+                write(" ");
         }
         else if (isStringLiteral(current.type) || isNumberLiteral(current.type)
             || current.type == tok!"characterLiteral")
@@ -245,7 +262,8 @@ private:
             case tok!";":
                 tempIndent = 0;
                 writeToken();
-                newline();
+                if (current.type != tok!"comment")
+                    newline();
                 break;
             case tok!"{":
                 writeBraces();
diff --git a/tests/comments.d b/tests/comments.d
new file mode 100644
index 0000000..80f5313
--- /dev/null
+++ b/tests/comments.d
@@ -0,0 +1,6 @@
+int /*sneaky*/ foo( /*comments*/ ) /*everywhere*/ {
+    // comment on its own line
+    foo() // comment on same line
+        .bar(); // also on same line
+    /* again */ // same line
+}
diff --git a/tests/comments.d.ref b/tests/comments.d.ref
new file mode 100644
index 0000000..3b0fb36
--- /dev/null
+++ b/tests/comments.d.ref
@@ -0,0 +1,7 @@
+int /*sneaky*/ foo( /*comments*/ ) /*everywhere*/
+{
+    // comment on its own line
+    foo() // comment on same line
+    .bar(); // also on same line
+    /* again */  // same line
+}

From 718f3a533156dc4c8907e95ed2bee77e40f29419 Mon Sep 17 00:00:00 2001
From: Andreas Zwinkau <qznc@web.de>
Date: Wed, 14 Jan 2015 22:40:31 +0100
Subject: [PATCH 5/5] more (failing) tests

---
 tests/guessnumber.d     | 31 ++++++++++++++++++++++++++
 tests/guessnumber.d.ref | 36 +++++++++++++++++++++++++++++++
 tests/heronian.d        | 45 ++++++++++++++++++++++++++++++++++++++
 tests/heronian.d.ref    | 48 +++++++++++++++++++++++++++++++++++++++++
 tests/higherorder.d     |  9 ++++++++
 tests/higherorder.d.ref | 12 +++++++++++
 6 files changed, 181 insertions(+)
 create mode 100644 tests/guessnumber.d
 create mode 100644 tests/guessnumber.d.ref
 create mode 100644 tests/heronian.d
 create mode 100644 tests/heronian.d.ref
 create mode 100644 tests/higherorder.d
 create mode 100644 tests/higherorder.d.ref

diff --git a/tests/guessnumber.d b/tests/guessnumber.d
new file mode 100644
index 0000000..45b470a
--- /dev/null
+++ b/tests/guessnumber.d
@@ -0,0 +1,31 @@
+import std.stdio, std.random, std.typecons, std.conv, std.string,
+       std.range;
+ 
+void main() {
+    immutable interval = tuple(1, 100);
+    writefln("Guess my target number that is between " ~
+             "%d and %d (inclusive).\n", interval[]);
+    immutable target = uniform!"[]"(interval[]);
+ 
+    foreach (immutable i; sequence!q{n}) {
+        writef("Your guess #%d: ", i + 1);
+        immutable txt = stdin.readln.strip;
+ 
+        Nullable!int answer;
+        try {
+            answer = txt.to!int;
+        } catch (ConvException e) {
+            writefln("  I don't understand your input '%s'", txt);
+            continue;
+        }
+        if (answer < interval[0] || answer > interval[1]) {
+            writeln("  Out of range!");
+            continue;
+        }
+        if (answer == target) {
+            writeln("  Well guessed.");
+            break;
+        }
+        writeln(answer < target ? "  Too low." : "  Too high.");
+    }
+}
diff --git a/tests/guessnumber.d.ref b/tests/guessnumber.d.ref
new file mode 100644
index 0000000..9ff51c1
--- /dev/null
+++ b/tests/guessnumber.d.ref
@@ -0,0 +1,36 @@
+import std.stdio, std.random, std.typecons, std.conv, std.string, std.range;
+
+void main()
+{
+    immutable interval = tuple(1, 100);
+    writefln("Guess my target number that is between "
+        ~ "%d and %d (inclusive).\n",
+        interval[]);
+    immutable target = uniform!"[]"(interval[]);
+    foreach (immutable i; sequence!q{n})
+    {
+        writef("Your guess #%d: ", i + 1);
+        immutable txt = stdin.readln.strip;
+        Nullable!int answer;
+        try
+        {
+            answer = txt.to!int;
+        }
+        catch(ConvException e)
+        {
+            writefln("  I don't understand your input '%s'", txt);
+            continue;
+        }
+        if (answer < interval[0] || answer > interval[1])
+        {
+            writeln("  Out of range!");
+            continue;
+        }
+        if (answer == target)
+        {
+            writeln("  Well guessed.");
+            break;
+        }
+        writeln(answer < target ? "  Too low.":"  Too high.");
+    }
+}
diff --git a/tests/heronian.d b/tests/heronian.d
new file mode 100644
index 0000000..ffe6334
--- /dev/null
+++ b/tests/heronian.d
@@ -0,0 +1,45 @@
+import std.stdio, std.math, std.range, std.algorithm, std.numeric, std.traits, std.typecons;
+ 
+double hero(in uint a, in uint b, in uint c) pure nothrow @safe @nogc {
+    immutable s = (a + b + c) / 2.0;
+    immutable a2 = s * (s - a) * (s - b) * (s - c);
+    return (a2 > 0) ? a2.sqrt : 0.0;
+}
+ 
+bool isHeronian(in uint a, in uint b, in uint c) pure nothrow @safe @nogc {
+    immutable h = hero(a, b, c);
+    return h > 0 && h.floor == h.ceil;
+}
+ 
+T gcd3(T)(in T x, in T y, in T z) pure nothrow @safe @nogc {
+    return gcd(gcd(x, y), z);
+}
+ 
+void main() /*@safe*/ {
+    enum uint maxSide = 200;
+ 
+    // Sort by increasing area, perimeter, then sides.
+    //auto h = cartesianProduct!3(iota(1, maxSide + 1))
+    auto r = iota(1, maxSide + 1);
+    const h = cartesianProduct(r, r, r)
+              //.filter!({a, b, c} => ...
+              .filter!(t => t[0] <= t[1] && t[1] <= t[2] &&
+                            t[0] + t[1] > t[2] &&
+                            t[].gcd3 == 1 && t[].isHeronian)
+              .array
+              .schwartzSort!(t => tuple(t[].hero, t[].only.sum, t.reverse))
+              .release;
+ 
+    static void showTriangles(R)(R ts) @safe {
+        "Area Perimeter Sides".writeln;
+        foreach (immutable t; ts)
+            writefln("%3s %8d %3dx%dx%d", t[].hero, t[].only.sum, t[]);
+    }
+ 
+    writefln("Primitive Heronian triangles with sides up to %d: %d", maxSide, h.length);
+    "\nFirst ten when ordered by increasing area, then perimeter,then maximum sides:".writeln;
+    showTriangles(h.take(10));
+ 
+    "\nAll with area 210 subject to the previous ordering:".writeln;
+    showTriangles(h.filter!(t => t[].hero == 210));
+}
diff --git a/tests/heronian.d.ref b/tests/heronian.d.ref
new file mode 100644
index 0000000..f7334ff
--- /dev/null
+++ b/tests/heronian.d.ref
@@ -0,0 +1,48 @@
+import std.stdio, std.math, std.range, std.algorithm, std.numeric, std.traits,
+    std.typecons;
+
+double hero(in uint a, in uint b, in uint c) pure nothrow @safe@nogc
+{
+    immutable s = (a + b + c) / 2.0;
+    immutable a2 = s * (s - a) * (s - b) * (s - c);
+    return (a2 > 0) ? a2.sqrt : 0.0;
+}
+
+bool isHeronian(in uint a, in uint b, in uint c) pure nothrow @safe @nogc
+{
+    immutable h = hero(a, b, c);
+    return h > 0 && h.floor == h.ceil;
+}
+
+T gcd3(T)(in T x, in T y, in T z) pure nothrow @safe @nogc
+{
+    return gcd(gcd(x, y), z);
+}
+
+void main() /*@safe*/
+{
+    enum uint maxSide = 200;
+    // Sort by increasing area, perimeter, then sides.
+    //auto h = cartesianProduct!3(iota(1, maxSide + 1))
+    auto r = iota(1, maxSide + 1);
+    const h = cartesianProduct(r, r, r)
+        //.filter!({a, b, c} => ...
+        .filter!(t => t[0] <= t[1] && t[1] <= t[2] && t[0] + t[1] > t[2] &&
+                      t[].gcd3 == 1 && t[].isHeronian)
+        .array.schwartzSort!(t => tuple(t[].hero, t[].only.sum, t.reverse))
+        .release;
+
+    static void showTriangles(R)(R ts) @safe
+    {
+        "Area Perimeter Sides".writeln;
+        foreach (immutable t; ts)
+            writefln("%3s %8d %3dx%dx%d", t[].hero, t[].only.sum, t[]);
+    }
+
+    writefln("Primitive Heronian triangles with sides up to %d: %d", maxSide,
+        h.length);
+    "\nFirst ten when ordered by increasing area, then perimeter,then maximum sides:".writeln;
+    showTriangles(h.take(10));
+    "\nAll with area 210 subject to the previous ordering:".writeln;
+    showTriangles(h.filter!(t => t[].hero == 210));
+}
diff --git a/tests/higherorder.d b/tests/higherorder.d
new file mode 100644
index 0000000..56d2c35
--- /dev/null
+++ b/tests/higherorder.d
@@ -0,0 +1,9 @@
+int hof(int a, int b, int delegate(int, int) f) {
+    return f(a, b);
+}
+ 
+void main() {
+    import std.stdio;
+    writeln("Add: ", hof(2, 3, (a, b) => a + b));
+    writeln("Multiply: ", hof(2, 3, (a, b) => a * b));
+}
diff --git a/tests/higherorder.d.ref b/tests/higherorder.d.ref
new file mode 100644
index 0000000..be9b953
--- /dev/null
+++ b/tests/higherorder.d.ref
@@ -0,0 +1,12 @@
+int hof(int a, int b, int delegate(int, int) f)
+{
+    return f(a, b);
+}
+
+void main()
+{
+    import std.stdio;
+
+    writeln("Add: ", hof(2, 3, (a, b) => a + b));
+    writeln("Multiply: ", hof(2, 3, (a, b) => a * b));
+}