aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--challenge-132/tyler-wardhaugh/clojure/README.md14
-rw-r--r--challenge-132/tyler-wardhaugh/clojure/bb.edn4
-rw-r--r--challenge-132/tyler-wardhaugh/clojure/deps.edn3
-rw-r--r--challenge-132/tyler-wardhaugh/clojure/pom.xml13
-rw-r--r--challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/core.clj12
-rw-r--r--challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/t1.clj35
-rw-r--r--challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/t2.clj62
-rw-r--r--challenge-132/tyler-wardhaugh/clojure/test/tw/weekly/c132_test.clj28
8 files changed, 157 insertions, 14 deletions
diff --git a/challenge-132/tyler-wardhaugh/clojure/README.md b/challenge-132/tyler-wardhaugh/clojure/README.md
index 064e23e11f..74b8669aa3 100644
--- a/challenge-132/tyler-wardhaugh/clojure/README.md
+++ b/challenge-132/tyler-wardhaugh/clojure/README.md
@@ -1,7 +1,7 @@
-# tw.weekly.c130
+# tw.weekly.c132
-The Weekly Challenge - #130 - Tyler Wardhaugh
+The Weekly Challenge - #132 - Tyler Wardhaugh
## Usage
@@ -9,7 +9,7 @@ Clojure ([installation instructions](https://clojure.org/guides/getting_started#
Run the project directly (shows default output from both tasks):
- $ clojure -M -m tw.weekly.c130.core
+ $ clojure -M -m tw.weekly.c132.core
# ... or ...
$ bb run both
@@ -21,15 +21,13 @@ Run the project's tests (which are samples from the task descriptions):
Run Task #1 with input
- $ clojure -M -m tw.weekly.c130.t1 N
- # ... or ...
- $ bb run task-1 N
+ $ clojure -M -m tw.weekly.c132.t1 D
Run Task #2 with input:
- $ clojure -M -m tw.weekly.c130.t2 D S
+ $ clojure -M -m tw.weekly.c132.t2 H1 H2 I1 I2
# ... or ...
- $ bb run task-2 D S
+ $ bb run task-2 H1 H2 I1 I2
View available tasks Babashka can run:
diff --git a/challenge-132/tyler-wardhaugh/clojure/bb.edn b/challenge-132/tyler-wardhaugh/clojure/bb.edn
index 4cd11817b8..a0e8b96a48 100644
--- a/challenge-132/tyler-wardhaugh/clojure/bb.edn
+++ b/challenge-132/tyler-wardhaugh/clojure/bb.edn
@@ -63,7 +63,9 @@
:task (run-task :t1 *command-line-args*)}
task-1-bb {:doc "Run Task 1 (via Babashka)"
- :task (run-task-bb :t1 *command-line-args*)}
+ :task (binding [*out* *err*]
+ (println "error: can't run Task 1 via Babashka because it depends on some incompatible libraries.")
+ (System/exit 1))}
task-2 {:doc "Run Task 2 (via clojure)"
:task (run-task :t2 *command-line-args*)}
diff --git a/challenge-132/tyler-wardhaugh/clojure/deps.edn b/challenge-132/tyler-wardhaugh/clojure/deps.edn
index 5b1400b27e..e821835450 100644
--- a/challenge-132/tyler-wardhaugh/clojure/deps.edn
+++ b/challenge-132/tyler-wardhaugh/clojure/deps.edn
@@ -1,5 +1,6 @@
{:paths ["src" "resources"]
- :deps {org.clojure/clojure {:mvn/version "1.10.3"}}
+ :deps {org.clojure/clojure {:mvn/version "1.10.3"}
+ clojure.java-time/clojure.java-time {:mvn/version "0.3.3"}}
:aliases
{:test {:extra-paths ["test"]
:extra-deps {org.clojure/test.check {:mvn/version "1.1.0"}
diff --git a/challenge-132/tyler-wardhaugh/clojure/pom.xml b/challenge-132/tyler-wardhaugh/clojure/pom.xml
index b1039ce744..ca7a3f1f2a 100644
--- a/challenge-132/tyler-wardhaugh/clojure/pom.xml
+++ b/challenge-132/tyler-wardhaugh/clojure/pom.xml
@@ -2,11 +2,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>tw.weekly</groupId>
- <artifactId>tw.weekly.c131</artifactId>
+ <artifactId>tw.weekly.c132</artifactId>
<version>0.1.0-SNAPSHOT</version>
- <name>tw.weekly.c131</name>
- <description>Challenge #131</description>
- <url>https://github.com/tw.weekly/tw.weekly.c131</url>
+ <name>tw.weekly.c132</name>
+ <description>Challenge #132</description>
+ <url>https://github.com/tw.weekly/tw.weekly.c132</url>
<licenses>
<license>
<name>Eclipse Public License</name>
@@ -24,6 +24,11 @@
<artifactId>clojure</artifactId>
<version>1.10.3</version>
</dependency>
+ <dependency>
+ <groupId>clojure.java-time</groupId>
+ <artifactId>clojure.java-time</artifactId>
+ <version>0.3.3</version>
+ </dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
diff --git a/challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/core.clj b/challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/core.clj
new file mode 100644
index 0000000000..4115f99d9b
--- /dev/null
+++ b/challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/core.clj
@@ -0,0 +1,12 @@
+(ns tw.weekly.c132.core
+ (:require [tw.weekly.c132.t1 :as t1])
+ (:require [tw.weekly.c132.t2 :as t2])
+ (:gen-class))
+
+(defn -main
+ "Run all tasks"
+ [& _]
+ (println "Task #1:")
+ (t1/-main)
+ (println "\nTask #2:")
+ (t2/-main))
diff --git a/challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/t1.clj b/challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/t1.clj
new file mode 100644
index 0000000000..b24255c143
--- /dev/null
+++ b/challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/t1.clj
@@ -0,0 +1,35 @@
+(ns tw.weekly.c132.t1
+ (:require [java-time :as j]
+ [clojure.pprint :refer [cl-format]]))
+
+;;;
+; Task description for TASK #1 › Mirror Dates
+;;;
+(def DEFAULT-TODAY "2021/09/22")
+(def DEFAULT-INPUT ["2021/09/18" DEFAULT-TODAY])
+(def DATE-FORMAT "yyyy/MM/dd")
+
+(defn parse-date
+ [s]
+ (j/local-date DATE-FORMAT s))
+
+(defn format-date
+ [d]
+ (-> (j/formatter DATE-FORMAT)
+ (j/format d)))
+
+(defn mirror-date
+ [origin today]
+ (let [delta (j/time-between :days origin today)
+ before (j/minus origin (j/days delta))
+ after (j/plus today (j/days delta))]
+ (list before after)))
+
+(defn -main
+ "Run Task 1 with a given input N, defaulting to the first example from the
+ task description."
+ [& args]
+ (let [[origin today] (map parse-date (or args DEFAULT-INPUT))]
+ (->> (mirror-date origin today)
+ (map format-date)
+ (cl-format true "~{~a~^, ~}~%"))))
diff --git a/challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/t2.clj b/challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/t2.clj
new file mode 100644
index 0000000000..0f0930b838
--- /dev/null
+++ b/challenge-132/tyler-wardhaugh/clojure/src/tw/weekly/c132/t2.clj
@@ -0,0 +1,62 @@
+(ns tw.weekly.c132.t2
+ (:require [clojure.edn :as edn]
+ [clojure.pprint :refer [cl-format]]))
+
+;;;
+; Task description for TASK #2, Hash Join
+;;;
+(def MAXSIZE "The maximum number of build relations to process at one time." 3)
+(def DEFAULT-INPUT
+ [[[20, "Alex" ]
+ [28, "Joe" ]
+ [38, "Mike" ]
+ [18, "Alex" ]
+ [25, "David" ]
+ [18, "Simon" ]]
+ [["Alex", "Stewart"]
+ ["Joe", "Root" ]
+ ["Mike", "Gatting"]
+ ["Joe", "Blog" ]
+ ["Alex", "Jones" ]
+ ["Simon","Duane" ]]
+ 1
+ 0])
+
+(defn butnth
+ "Returns all values except the one at index."
+ [coll index]
+ (keep-indexed (fn [i v] (when (not= i index) v)) coll))
+
+;;; Classic Hash Join
+;; Algorithm description:
+; https://en.wikipedia.org/wiki/Hash_join#Classic_hash_join
+;; Notes:
+; - We proactively batch the smaller relation (build) into MAXSIZE chunks to
+; ensure it can fit into memory.
+; - Order of output relation is not guaranteed.
+;;;
+(defn hash-join
+ [a b a-index b-index]
+ (let [[build build-index probe probe-index]
+ (if (<= (count a) (count b))
+ [a a-index b b-index]
+ [b b-index a a-index])]
+ (-> (comp
+ (partition-all MAXSIZE)
+ (map (fn [batch] (group-by #(nth % build-index) batch)))
+ (mapcat
+ (fn [build-map]
+ (keep (fn [row]
+ (when-let [ks (build-map (nth row probe-index))]
+ (map #(concat % (butnth row probe-index)) ks)))
+ probe)))
+ cat)
+ (sequence build))))
+
+(defn -main
+ "Run Task 1 with a given input H1, H2, I1, and I2, defaulting to the first
+ example from the task description."
+ [& args]
+ (let [[H1 H2 I1 I2] (or (some->> args (map edn/read-string)) DEFAULT-INPUT)]
+ (->> (hash-join H1 H2 I1 I2)
+ (cl-format true "~:{~a, ~s, ~s~%~}"))))
diff --git a/challenge-132/tyler-wardhaugh/clojure/test/tw/weekly/c132_test.clj b/challenge-132/tyler-wardhaugh/clojure/test/tw/weekly/c132_test.clj
new file mode 100644
index 0000000000..00edee2c66
--- /dev/null
+++ b/challenge-132/tyler-wardhaugh/clojure/test/tw/weekly/c132_test.clj
@@ -0,0 +1,28 @@
+(ns tw.weekly.c132-test
+ (:require [clojure.test :refer [deftest is testing]]
+ [tw.weekly.c132.t1 :as t1]
+ [tw.weekly.c132.t2 :as t2]))
+
+(def today (t1/parse-date t1/DEFAULT-TODAY))
+
+(deftest task-1
+ (testing "Task 1, Mirror Dates"
+ (let [today (t1/parse-date t1/DEFAULT-TODAY)]
+ (is (= (map t1/parse-date ["2021/09/14" "2021/09/26"])
+ (t1/mirror-date (t1/parse-date "2021/09/18") today)))
+ (is (= (map t1/parse-date ["1929/10/27" "2067/09/05"])
+ (t1/mirror-date (t1/parse-date "1975/10/10") today)))
+ (is (= (map t1/parse-date ["1912/07/08" "2076/04/30"])
+ (t1/mirror-date (t1/parse-date "1967/02/14") today))))))
+
+(deftest task-2
+ (testing "Task 2, Hash Join"
+ (is (= (set '((20 "Alex" "Stewart")
+ (18 "Alex" "Stewart")
+ (28 "Joe" "Root")
+ (38 "Mike" "Gatting")
+ (28 "Joe" "Blog")
+ (20 "Alex" "Jones")
+ (18 "Alex" "Jones")
+ (18 "Simon" "Duane")))
+ (set (apply t2/hash-join t2/DEFAULT-INPUT))))))