diff --git a/Makefile b/Makefile
index 4392e8c..e3e03c4 100644
--- a/Makefile
+++ b/Makefile
@@ -3,8 +3,12 @@ lint:
.PHONY: test
test:
- clojure -A:test:runner
+ clojure -M:test:runner
+.PHONY: uberjar
uberjar:
- clj -A:uberjar
+ clj -M:uberjar
+.PHONY: deploy
+deploy:
+ clj -M:deploy
diff --git a/README.md b/README.md
index adf1cc2..8159100 100644
--- a/README.md
+++ b/README.md
@@ -43,10 +43,15 @@ The result of *actual value* approximation to a given scale provides:
* *Favorite Number* expresses some common language names for certain numbers. A `0.25` is a favorite number in that that it has the name - `a quarter`.
A full approximation result returns three such approximation data structures for a *given value* which is:
-* **smaller** than the *actual value* on the scaled number range.
-* **greater** than the *actual value* on the scaled number range.
-* **around** the *actual value* on the scaled number range. For this a is chosen from the above two which is closer to the *actual value*.
-
+* **less** than the *actual value* on the scaled number range.
+* **more** than the *actual value* on the scaled number range.
+* **around** the *actual value* on the scaled number range. For this 'less' or 'more' value closer to the *actual value* is chosen.
+
+Lastly the number formatting can be specified:
+* **words** - spell out the number in words (110 -> hundred and ten).
+* **bites** - spell out the number using bite size style shortening (1022 -> 1k).
+* **numbers** - report number as is.
+
## Languages
Numeric approximation has two functionality points which are language dependent
@@ -63,10 +68,12 @@ Currently supported languages:
## Usage
-Number Words exposes approximation functionality through `approximations` function which takes on the following parameters:
-* `language` - `:de` or `:en`
+Number Words exposes approximation functionality through `numeric-expression` function which takes on the following parameters:
* `actual-value` - the number to approximate
* `scale` - at which the approximation is to be performed.
+* `language` - use two letter language code (like :pt), default is :en
+* `relation` - what kind of relation to between actual and given value to use (valid values specified in `:numberwords.domain/relation`)
+* `formatting` - which number formatting should be used (valid values specified in `:numberwords.domain/formatting`)
### Installation
@@ -76,12 +83,12 @@ Number Words exposes approximation functionality through `approximations` functi
_Leiningen_
```
-[ai.tokenmill.numberwords/numberwords "1.0.2"]
+[ai.tokenmill.numberwords/numberwords "1.1.0"]
```
_deps.edn_
```
-ai.tokenmill.numberwords/numberwords {:mvn/version "1.0.2"}
+ai.tokenmill.numberwords/numberwords {:mvn/version "1.1.0"}
```
Usage example:
@@ -89,23 +96,19 @@ Usage example:
```
(require '[numberwords.core :as nw])
-(nw/approximations :en 0.258 1/4)
+(numeric-expression 144 10 :en :numberwords.domain/around :numberwords.domain/words)
+=>
+"around one hundred forty"
+
+(numeric-expression 144 10 :de :numberwords.domain/less :numberwords.domain/numbers)
=>
-#:numwords{:around
- #:numwords{:hedges #{"approximately" "about" "around"},
- :text "zero point two five",
- :given-value 1/4,
- :favorite-number #{"a quarter"}},
- :more-than
- #:numwords{:hedges #{"over" "more than"},
- :text "zero point two five",
- :given-value 1/4,
- :favorite-number #{"a quarter"}},
- :less-than
- #:numwords{:hedges #{"nearly" "under" "less than"},
- :text "zero point five",
- :given-value 1/2,
- :favorite-number #{"a half"}}}
+"weniger als 150"
+
+;; with defaults
+(numeric-expression 144 10)
+=>
+"around 140"
+
```
### Java
@@ -126,7 +129,7 @@ Or as a _Maven_ dependency
ai.tokenmill.numberwords
numberwords
- 1.0.2-SNAPSHOT
+ 1.1.0
```
@@ -136,10 +139,9 @@ Usage example:
import ai.tokenmill.numberwords.NumberWords;
NumberWords nw = new NumberWords();
-nw.approximations("en", 1.22, 0.1);
+nw.numericExpression(1.22, 0.1, "en", "more", "numbers");
```
-
## Configuration
Hedges, favorite numbers can be modified and new languages added via changes to a configuration file - `resources/numwords.edn`
diff --git a/deps.edn b/deps.edn
index 6c948d7..8b0b53c 100644
--- a/deps.edn
+++ b/deps.edn
@@ -7,10 +7,10 @@
"-m" "ai.tokenmill.numberwords.NumberWords"
"--app-group-id" "ai.tokenmill.numberwords"
"--app-artifact-id" "numberwords"
- "--app-version" "1.0.2"]}
- :deploy {:extra-deps {deps-deploy {:mvn/version "RELEASE"}}
+ "--app-version" "1.1.0"]}
+ :deploy {:extra-deps {deps-deploy/deps-deploy {:mvn/version "RELEASE"}}
:main-opts ["-m" "deps-deploy.deps-deploy" "deploy"
- "target/numberwords-1.0.2.jar"]}
+ "target/numberwords-1.1.0.jar"]}
:test {:extra-paths ["test"]
:extra-deps {org.clojure/test.check {:mvn/version "RELEASE"}}}
:runner {:extra-deps {com.cognitect/test-runner
diff --git a/pom.xml b/pom.xml
index f559037..490115f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
numberwords
- 1.0.2
+ 1.1.0
numberwords
@@ -94,13 +94,19 @@
-
+
+
+
clojars
+
Clojars repository
+
https://clojars.org/repo
+
+
diff --git a/resources/numwords.edn b/resources/numwords.edn
index 33b18c3..dd6cb61 100644
--- a/resources/numwords.edn
+++ b/resources/numwords.edn
@@ -1,48 +1,48 @@
-{:en {:hedges {:equal #{"exactly"}
- :around #{"around" "approximately" "about"}
- :more #{"more than" "over"}
- :less #{"less than" "under" "nearly"}}
- :favorite-numbers {1/4 #{"a quarter"}
- 1/3 #{"a third"}
- 1/2 #{"a half"}}}
+{:en {:hedges {:equal ["exactly"]
+ :around ["around" "approximately" "about"]
+ :more ["more than" "over"]
+ :less ["less than" "under" "nearly"]}
+ :favorite-numbers {1/4 ["a quarter"]
+ 1/3 ["a third"]
+ 1/2 ["a half"]}}
- :de {:hedges {:equal #{"genau"}
- :around #{"ungefähr" "etwa"}
- :more #{"mehr als" "über"}
- :less #{"weniger als" "unter"}}
- :favorite-numbers {1/4 #{"Viertel"}
- 1/3 #{"Drittel"}
- 1/2 #{"Hälfte"}}}
+ :de {:hedges {:equal ["genau"]
+ :around ["ungefähr" "etwa"]
+ :more ["mehr als" "über"]
+ :less ["weniger als" "unter"]}
+ :favorite-numbers {1/4 ["Viertel"]
+ 1/3 ["Drittel"]
+ 1/2 ["Hälfte"]}}
- :pt {:hedges {:equal #{"exatamente"}
- :around #{"cerca de" "aproximadamente"}
- :more #{"mais de" "acima de"}
- :less #{"menos de" "abaixo de"}}
- :favorite-numbers {1/4 #{"um quarto"}
- 1/3 #{"um terço"}
- 1/2 #{"metade"}}}
+ :pt {:hedges {:equal ["exatamente"]
+ :around ["cerca de" "aproximadamente"]
+ :more ["mais de" "acima de"]
+ :less ["menos de" "abaixo de"]}
+ :favorite-numbers {1/4 ["um quarto"]
+ 1/3 ["um terço"]
+ 1/2 ["metade"]}}
- :lt {:hedges {:equal #{"lygiai"}
- :around #{"apie" "apytiksliai"}
- :more #{"daugiau nei" "virš"}
- :less #{"mažiau nei" "mažiau"}}
- :favorite-numbers {1/4 #{"ketvirtis"}
- 1/3 #{"trečdalis"}
- 1/2 #{"pusė"}}}
+ :lt {:hedges {:equal ["lygiai"]
+ :around ["apie" "apytiksliai"]
+ :more ["daugiau nei" "virš"]
+ :less ["mažiau nei" "mažiau"]}
+ :favorite-numbers {1/4 ["ketvirtis"]
+ 1/3 ["trečdalis"]
+ 1/2 ["pusė"]}}
- :fr {:hedges {:equal #{"exactement"}
- :around #{"environ"}
- :more #{"plus de"}
- :less #{"moins de"}}
- :favorite-numbers {1/4 #{"un quart"}
- 1/3 #{"un tiers"}
- 1/2 #{"un demi"}}}
+ :fr {:hedges {:equal ["exactement"]
+ :around ["environ"]
+ :more ["plus de"]
+ :less ["moins de"]}
+ :favorite-numbers {1/4 ["un quart"]
+ 1/3 ["un tiers"]
+ 1/2 ["un demi"]}}
- :ru {:hedges {:equal #{"ровно"}
- :around #{"около" "примерно"}
- :more #{"больше чем" "более"}
- :less #{"меньше чем" "менее"}}
- :favorite-numbers {1/4 #{"четверть"}
- 1/3 #{"треть"}
- 1/2 #{"пол" "половина"}}}
+ :ru {:hedges {:equal ["ровно"]
+ :around ["около" "примерно"]
+ :more ["больше чем" "более"]
+ :less ["меньше чем" "менее"]}
+ :favorite-numbers {1/4 ["четверть"]
+ 1/3 ["треть"]
+ 1/2 ["пол" "половина"]}}
}
diff --git a/src/numberwords/number_ops.clj b/src/numberwords/approx_math.clj
similarity index 58%
rename from src/numberwords/number_ops.clj
rename to src/numberwords/approx_math.clj
index 038684c..c22cc13 100644
--- a/src/numberwords/number_ops.clj
+++ b/src/numberwords/approx_math.clj
@@ -1,4 +1,4 @@
-(ns numberwords.number-ops)
+(ns numberwords.approx-math)
(defn nat-num? [x] (not (neg? x)))
@@ -25,3 +25,17 @@
[(rationalize (- rational-av m))
(rationalize (- (+ rational-av scale) m))]))
+(defn distances-from-edges
+ [actual-value scale]
+ (let [[start end] (bounding-box actual-value scale)]
+ [[start (delta start actual-value)]
+ [end (delta end actual-value)]]))
+
+(defn unreliable?
+ "Actual value and scale values which will generate unreliable results:
+ - actual-value much bigger that the scale
+ - scale is bigger than the actual value"
+ [actual-value scale]
+ ;; 1000x difference between the scale and value is a random choice
+ (or (< 1000 (/ actual-value scale))
+ (and (< 1 scale) (< actual-value scale))))
diff --git a/src/numberwords/config.clj b/src/numberwords/config.clj
index ca6f914..47f13b3 100644
--- a/src/numberwords/config.clj
+++ b/src/numberwords/config.clj
@@ -10,6 +10,10 @@
:favorite-numbers (fn [given-value]
(get-in config [:favorite-numbers given-value]))})))
+(defn numwords-config []
+ (with-open [r (io/reader (io/resource "numwords.edn"))]
+ (edn/read (PushbackReader. r))))
+
(defn supported-langauges []
(with-open [r (io/reader (io/resource "numwords.edn"))]
(-> (PushbackReader. r)
diff --git a/src/numberwords/core.clj b/src/numberwords/core.clj
index 1954f6c..0714615 100644
--- a/src/numberwords/core.clj
+++ b/src/numberwords/core.clj
@@ -1,120 +1,113 @@
(ns numberwords.core
(:require [clojure.spec.alpha :as s]
- [clojure.string :as string]
- [numberwords.number-ops :as no]
+ [numberwords.approx-math :as math]
[numberwords.config :as cfg]
- [numberwords.text :as text]))
+ [numberwords.formatting.bitesize :as bitesize]
+ [numberwords.formatting.text :as text]
+ [numberwords.domain :as nd]))
-;;the value for which numeric expression is to be calculated
-(s/def :numwords/actual-value (s/and number? no/nat-num? no/not-inf?))
+(def config (cfg/numwords-config))
-;;textual expression of the number
-(s/def :numwords/text (s/and string? #(not (string/blank? %))))
-;;textual hedge describing the relation between given and actual value
-(s/def :numwords/hedges (s/coll-of string? :kind set?))
-;;given value - the value given by the numeric expression calculation as the one
-;;rounding the actual value
-(s/def :numwords/given-value (s/and number? no/nat-num?))
-;;in case there are favorite expressions for a given number, spell it out
-(s/def :numwords/favorite-number (s/coll-of string? :kind set?))
+(defn numeric-relations
+ "Construct numeric relations for the actual value to the numbers on a scale
+ * actual-value - a number which has to be expressed
+ * scale - specifies the granularity of the rounding:
+ 1/10 for one decimal point, 10 for rounding to tenths, and so on."
+ [actual-value scale]
+ (let [[[num> delta>] [num< delta<]] (math/distances-from-edges actual-value scale)
+ closest-num (min num> num<)
+ equal-to (cond (= delta> 0.0) num>
+ (= delta< 0.0) num<
+ :else nil)]
+ (cond
+ equal-to {::nd/equal equal-to}
+ (math/unreliable? actual-value scale) {::nd/around closest-num}
+ :else {::nd/around closest-num
+ ::nd/more num>
+ ::nd/less num<})))
-;;numeric approximation provides all the info describing how an actual
-;;value can be described in an approximate manner
-(s/def :numwords/num-approximation (s/keys :req [:numwords/text :numwords/hedges :numwords/given-value]
- :upt [:numwords/favorite-number]))
+(s/fdef numeric-relations
+ :args (s/cat :actual-value ::nd/actual-value
+ :scale ::nd/scale)
+ :ret ::nd/given-value-relations)
-;;main resulting data structure with three branches
-;; :equal in case actual value is equal to given value
-;; :unreliable will be provided when working with huge numbers and where scale is
-;; larger than actual value
-;; :unequal main case when we will have all three numeric expressions generated
-(s/def :numwords/numeric-expressions
- (s/or :equal (s/map-of #{:numwords/equal} :numwords/num-approximation)
- :unreliable (s/map-of #{:numwords/around} :numwords/num-approximation)
- :unequal (s/map-of #{:numwords/around :numwords/more-than :numwords/less-than}
- :numwords/num-approximation :min-count 3)))
+(defn number->text
+ "Translate number to text in a given language"
+ [number language] (text/number->text language number))
-;;rounding (snapping) scale to use when calculating values which will be
-;;provided as numeric expressions
-(s/def :numwords/scale (s/or :fraction (s/and ratio? #(and (> % 0)
- (> (denominator %)
- (numerator %))))
- :natural-num (s/and number? pos-int?)))
+(s/fdef number->text
+ :args (s/cat :num ::nd/actual-value :lang ::nd/language)
+ :ret string?)
-;;supported languages
-(s/def :numwords/language (cfg/supported-langauges))
+(defn number->bitesize
+ "Translate number to bite style number formatting"
+ [number] (bitesize/number->bitesize number))
-(defn distances-from-edges
- [actual-value [start end]]
- [[start (no/delta start actual-value)]
- [end (no/delta end actual-value)]])
+(s/fdef number->bitesize
+ :args (s/cat :num ::nd/actual-value)
+ :ret string?)
-(defn build-expr
- "Build resulting spec conformant numeric expression description"
- [hedges fav-numbers text given-value]
- (cond-> {:numwords/hedges hedges
- :numwords/text text
- :numwords/given-value given-value}
- fav-numbers (assoc :numwords/favorite-number fav-numbers)))
+(defn hedge
+ "List of words describing the relation between given and actual value"
+ [relation language]
+ ;;FIXME how to do simple keyword given namespaced one?
+ (let [relation-kw (keyword (name relation))]
+ (-> config language :hedges relation-kw)))
-(defn unreliable?
- "Actual value and scale values which will generate results which are unreliable:
- - actual-value much bigger that the scale
- - scale is bigger than the actual value"
- [actual-value scale]
- ;; 1000x difference between the scale and value
- ;; is chosen completely randomly
- (or (< 1000 (/ actual-value scale))
- (and (< 1 scale) (< actual-value scale))))
+(s/fdef hedge
+ :args (s/cat :relation ::nd/relation :lang ::nd/language)
+ :ret (s/coll-of string? :kind set?))
-(defn approximations
- "Numeric approximations translate given numeric value to a set of simplified
- approximations of that number. Function parameters:
+(defn favorite-number
+ "List of phrases which can be used instead of the number. Like `a half`"
+ [value language] (-> config language :favorite-numbers (get value)))
- * actual-value - a number which has to be expressed
- * language - to be used for text generation
- * scale - specifies the granularity of the rounding:
- 1/10 for one decimal point, 10 for rounding to tenths, and so on.
+(s/fdef favorite-number
+ :args (s/cat :relation ::nd/given-value :lang ::nd/language)
+ :ret (s/or :has-fav-nums (s/coll-of string? :kind set?)
+ :no-fav-nums nil?))
- Resulting approximation provides:
+(defn number-with-precision [num scale]
+ (if (ratio? num)
+ (double num)
+ num))
- * given-value - a number which is a closest approximation of the actual value
- * relation - how given-value relates to actual value: equal, more, less, or around
- * hedges - a list of words describing the relation
- * text - given-number translated to text"
- [language actual-value scale]
- (let [{:keys [hedges favorite-numbers]}
- (cfg/numwords-for language)
- text (partial text/number->text language)
- value-range (no/bounding-box actual-value scale)
- [[num> delta>] [num< delta<]] (distances-from-edges actual-value value-range)
- closest-num (min num> num<)
- equal-to (cond (= delta> 0.0) num>
- (= delta< 0.0) num<
- :else nil)]
- (if equal-to
- {:numwords/equal (build-expr (hedges :equal)
- (favorite-numbers equal-to)
- (text equal-to)
- equal-to)}
- (let [around (build-expr (hedges :around)
- (favorite-numbers closest-num)
- (text closest-num)
- closest-num)]
- (if (unreliable? actual-value scale)
- {:numwords/around around}
- {:numwords/around around
- :numwords/more-than (build-expr (hedges :more)
- (favorite-numbers num>)
- (text num>)
- num>)
- :numwords/less-than (build-expr (hedges :less)
- (favorite-numbers num<)
- (text num<)
- num<)})))))
+(defn possible-relation
+ "Get the relation which is possible in the current given value approximations.
+ If we have regular case with all three (less,more,equal) detected then return it
+ else first check if we have 'equal' relation, if this is not present go for 'around'"
+ [given-val-relations requested-relation]
+ (let [relation-types (set (keys given-val-relations))]
+ (or
+ (get relation-types requested-relation)
+ (get relation-types ::nd/equal)
+ (get relation-types ::nd/about))))
+
+(defn numeric-expression
+ ([actual-value scale]
+ (numeric-expression actual-value scale :en ::nd/around ::nd/bites))
+ ([actual-value scale relation formatting]
+ (numeric-expression actual-value scale :en relation formatting))
+ ([actual-value scale language relation formatting]
+ (let [relations (numeric-relations actual-value scale)
+ actual-relation (possible-relation relations relation)
+ given-value (get relations actual-relation)
+ fav-num (first (favorite-number given-value language))]
+ (format "%s %s"
+ (first (hedge actual-relation language))
+ (condp = formatting
+ ::nd/numbers (number-with-precision given-value scale)
+ ::nd/words (or fav-num (number->text given-value language))
+ ::nd/bites
+ ;;FIXME this part is not good, plus revisit
+ ;; `number-with-precision` it has to work for `numbers`
+ (if (or (rational? given-value)
+ (ratio? given-value)
+ (< scale 1))
+ (number-with-precision given-value scale)
+ (number->bitesize given-value)))))))
-(s/fdef approximations
- :args (s/cat :language :numwords/language
- :actual-value :numwords/actual-value
- :scale :numwords/scale)
- :ret :numwords/numeric-expressions)
+(s/fdef numeric-expression
+ :args (s/cat :num ::nd/actual-value :scale ::nd/scale :lang ::nd/language
+ :relation ::nd/relation :formatting ::nd/formatting)
+ :ret string?)
diff --git a/src/numberwords/domain.clj b/src/numberwords/domain.clj
new file mode 100644
index 0000000..94f2c59
--- /dev/null
+++ b/src/numberwords/domain.clj
@@ -0,0 +1,42 @@
+(ns numberwords.domain
+ (:require [clojure.spec.alpha :as s]
+ [numberwords.config :as cfg]
+ [numberwords.approx-math :as math]))
+
+;;the value for which numeric expression is to be calculated
+(s/def ::actual-value (s/and number? math/nat-num? math/not-inf?))
+
+;;textual expression of the number
+;; (s/def ::text (s/and string? #(not (string/blank? %))))
+;;textual hedge describing the relation between given and actual value
+(s/def ::hedges (s/coll-of string? :kind set?))
+;;given value - the value given by the numeric expression calculation as the one
+;;rounding the actual value
+(s/def ::given-value (s/and number? math/nat-num?))
+;;in case there are favorite expressions for a given number, spell it out
+(s/def ::favorite-number (s/coll-of string? :kind set?))
+
+(s/def ::relation #{::around ::more ::less :equal})
+
+;;Given value relation to the actual value - a number on a scale grid
+;; :equal in case actual value is equal to given value
+;; :unreliable will be provided when working with huge numbers and where scale is
+;; larger than actual value
+;; :unequal main case when we will have all three numeric expressions generated
+(s/def ::given-value-relations
+ (s/or :equal (s/map-of #{::equal} ::given-value)
+ :unreliable (s/map-of #{::around} ::given-value)
+ :unequal (s/map-of #{::around ::more ::less}
+ ::given-value :min-count 3)))
+
+;;rounding (snapping) scale to use when calculating values which will be
+;;provided as numeric expressions
+(s/def ::scale (s/or :fraction (s/and ratio? #(and (> % 0)
+ (> (denominator %)
+ (numerator %))))
+ :natural-num (s/and number? pos-int?)))
+
+;;supported languages
+(s/def ::language (cfg/supported-langauges))
+
+(s/def ::formatting #{::words ::bites ::numbers})
diff --git a/src/numberwords/formatting/bitesize.clj b/src/numberwords/formatting/bitesize.clj
new file mode 100644
index 0000000..28672a6
--- /dev/null
+++ b/src/numberwords/formatting/bitesize.clj
@@ -0,0 +1,23 @@
+(ns numberwords.formatting.bitesize
+ (:require [clojure.spec.alpha :as s]))
+
+(def sizes '("" "k" "M" "B" "T"))
+
+(defn invalid? [n] (or (Double/isNaN n) (Double/isInfinite n)))
+
+(defn number->bitesize
+ "Algorithm taken from:
+ https://programming.guide/java/formatting-byte-size-to-human-readable-format.html "
+ [n]
+ (if (invalid? n)
+ {::number n}
+ (loop [number (Math/abs n)
+ number-letters sizes]
+ (cond
+ (nil? (peek number-letters)) (str n)
+ (= 0 (quot number 1000)) (str number (peek number-letters))
+ :else (recur (quot number 1000) (pop number-letters))))))
+
+(s/fdef number->bitesize
+ :args (s/cat :number number?)
+ :ret string?)
diff --git a/src/numberwords/text.clj b/src/numberwords/formatting/text.clj
similarity index 94%
rename from src/numberwords/text.clj
rename to src/numberwords/formatting/text.clj
index 6bf436d..64d1916 100644
--- a/src/numberwords/text.clj
+++ b/src/numberwords/formatting/text.clj
@@ -1,4 +1,4 @@
-(ns numberwords.text
+(ns numberwords.formatting.text
(:import com.ibm.icu.text.RuleBasedNumberFormat
java.util.Locale))
diff --git a/src/numberwords/java.clj b/src/numberwords/java.clj
index 787cfc2..81bb772 100644
--- a/src/numberwords/java.clj
+++ b/src/numberwords/java.clj
@@ -1,17 +1,41 @@
(ns numberwords.java
(:require [clojure.pprint :as pp]
- [numberwords.core :as nw]))
+ [numberwords.core :as nw]
+ [numberwords.domain :as nd]
+ [clojure.string :as string]))
(gen-class :name ai.tokenmill.numberwords.NumberWords
:main true
:prefix NW-
- :methods [[approximations [String Double Double] java.util.Map]])
+ :methods [[numericExpression
+ [Double Double String String String]
+ java.lang.String]])
-(defn NW-approximations [_ language actual-value scale]
- (nw/approximations (keyword language) actual-value (rationalize scale)))
+(def label->relation {"equal" ::nd/equal
+ "around" ::nd/around
+ "less" ::nd/less
+ "more" ::nd/more})
-(defn NW-main [& [language actual-value scale]]
+(def label->format {"words" ::nd/words
+ "bites" ::nd/bites
+ "numbers" ::nd/numbers})
+
+(defn NW-numericExpression [_ actual-value scale language
+ relation formatting]
+ (nw/numeric-expression actual-value scale (keyword language)
+ (get label->relation relation)
+ (get label->format formatting)))
+
+(defn NW-main [& [actual-value scale language relation formatting]]
(pp/pprint
- (NW-approximations nil language
- (Double/parseDouble actual-value)
- (Double/parseDouble scale))))
+ (NW-numericExpression
+ nil
+ (if (string/includes? actual-value ".")
+ (Double/parseDouble actual-value)
+ (Integer/parseInt actual-value))
+ (if (string/includes? scale ".")
+ (Double/parseDouble scale)
+ (Integer/parseInt scale))
+ language
+ relation
+ formatting)))
diff --git a/test/numberwords/core_test.clj b/test/numberwords/core_test.clj
index f012eab..ec165c0 100644
--- a/test/numberwords/core_test.clj
+++ b/test/numberwords/core_test.clj
@@ -1,94 +1,41 @@
(ns numberwords.core-test
(:require [clojure.spec.test.alpha :as st]
- [clojure.spec.alpha :as s]
- [clojure.test :refer [is are deftest]]
- [clojure.test.check.clojure-test :refer [defspec]]
- [clojure.test.check.properties :as prop]
- [numberwords.core :refer [approximations] :as nw]))
+ [clojure.test :refer [deftest are]]
+ [numberwords.domain :as nd]
+ [numberwords.core :as nw]))
-(st/instrument `approximations)
-
-(defspec check-approximations 200
- (prop/for-all
- [lang (s/gen :numwords/language)
- value (s/gen :numwords/actual-value)
- scale (s/gen :numwords/scale)]
- (let [result (approximations lang value scale)]
- (cond
- (= [:numwords/around] (keys result))
- ;;this branch is here to handle big numbers where
- ;;calcs become unreliable due to floating point operations
- (let [gv (get-in result [:numwords/around :numwords/given-value])]
- (is (or (< 1000 (/ gv scale))
- (<= (Math/abs (double (- gv value)))
- (* 2 (Math/ceil scale))))))
-
- (= [:numwords/equal] (keys result))
- (is (= (double value)
- (double
- (get-in result [:numwords/equal :numwords/given-value]))))
-
- :else
- (is (<= (get-in result [:numwords/more-than :numwords/given-value])
- (rationalize value)
- (get-in result [:numwords/less-than :numwords/given-value])))))))
-
-(defn given-vals [result]
- [(get-in result [:numwords/around :numwords/given-value])
- (get-in result [:numwords/less-than :numwords/given-value])
- (get-in result [:numwords/more-than :numwords/given-value])])
-
-(defn fav-nums [result]
- [(get-in result [:numwords/around :numwords/favorite-number])
- (get-in result [:numwords/less-than :numwords/favorite-number])
- (get-in result [:numwords/more-than :numwords/favorite-number])])
-
-(defn hedges [result]
- [(get-in result [:numwords/equal :numwords/hedges])
- (get-in result [:numwords/around :numwords/hedges])
- (get-in result [:numwords/less-than :numwords/hedges])
- (get-in result [:numwords/more-than :numwords/hedges])])
+(-> (st/enumerate-namespace 'numberwords.core) st/check)
(deftest hedge-identification
- (are [result lang value step] (= result (hedges (approximations lang value step)))
- [#{"exactly"} nil nil nil]
- :en 0 1/4
-
- [nil #{"around" "approximately" "about"} #{"less than" "under" "nearly"} #{"more than" "over"}]
- :en 0.54M 1/10
-
- [nil #{"apie" "apytiksliai"} #{"mažiau nei" "mažiau"} #{"virš" "daugiau nei"}]
- :lt 0.54M 1/10
-
- [nil #{"etwa" "ungefähr"} #{"unter" "weniger als"} #{"über" "mehr als"}]
- :de 0.54M 1/10
-
- [nil #{"cerca de" "aproximadamente"} #{"menos de" "abaixo de"} #{"mais de" "acima de"}]
- :pt 0.54M 1/10))
+ (are [result relation language] (= result (nw/hedge relation language))
+ ["exactly"] ::nd/equal :en
+ ["around" "approximately" "about"] ::nd/around :en
+ ["apie" "apytiksliai"] ::nd/around :lt
+ ["ungefähr" "etwa"] ::nd/around :de
+ ["cerca de" "aproximadamente"] ::nd/around :pt))
(deftest actual-values-at-extremes
- (are [result lang value step] (= result (get-in (approximations lang value step)
- [:numwords/equal :numwords/given-value]))
- 0 :en 0 1/4
- 1 :en 1 1/100
- 110 :en 110 10))
+ (are [result value scale] (= result (nw/numeric-relations value scale))
+ {::nd/equal 0} 0 1/4
+ {::nd/equal 1} 1 1/100
+ {::nd/equal 110} 110 10))
(deftest given-values-at-favorite-numbers
- (are [result lang value step] (= result (given-vals (approximations lang value step)))
- [1/2 3/5 1/2] :en 0.54M 1/10
- [0 1/4 0] :en 1/10 1/4
- [0 1/4 0] :en 0.1M 1/4)
-
- (are [result lang value step] (= result (fav-nums (approximations lang value step)))
- [#{"a half"} nil #{"a half"}] :en 0.54M 1/10
- [nil #{"a quarter"} nil] :en 1/10 1/4
- [nil #{"a quarter"} nil] :en 0.1M 1/4
- [nil #{"ketvirtis"} nil] :lt 0.1M 1/4
- [nil #{"Viertel"} nil] :de 0.1M 1/4
- [nil #{"um quarto"} nil] :pt 0.1M 1/4))
-
-(deftest non-special-given-values
- (are [result lang value step] (= result (given-vals (approximations lang value step)))
- [3/4 1 3/4] :en 0.8 1/4
- [1/10 1/5 1/10] :en 0.14 1/10
- [50 60 50] :en 54 10))
+ (are [result value language] (= result (nw/favorite-number value language))
+ ["a half"] 1/2 :en
+ ["ketvirtis"] 1/4 :lt))
+
+(deftest possible-relations-in-prefered-order
+ (are [result value scale relation]
+ (= result (-> value
+ (nw/numeric-relations scale)
+ (nw/possible-relation relation)))
+ ::nd/equal 120 10 ::nd/less
+ ::nd/less 122 10 ::nd/less))
+
+(deftest full-expression
+ (are [result actual-value language scale relation formatting]
+ (= result
+ (nw/numeric-expression actual-value language scale relation formatting))
+ "around zero point two" 0.23 1/10 :en ::nd/around ::nd/words
+ "around 0.2" 0.23 1/10 :en ::nd/around ::nd/numbers))
diff --git a/test/numberwords/formatting/bitesize_test.clj b/test/numberwords/formatting/bitesize_test.clj
new file mode 100644
index 0000000..4989b6d
--- /dev/null
+++ b/test/numberwords/formatting/bitesize_test.clj
@@ -0,0 +1,9 @@
+(ns numberwords.formatting.bitesize-test
+ (:require [numberwords.formatting.bitesize :refer [number->bitesize]]
+ [clojure.test :refer [deftest are]]))
+
+(deftest bitesize-formatting
+ (are [results value] (= results (number->bitesize value))
+ "1" 1
+ "1k" 1999
+ "100000000000000000" 100000000000000000))
diff --git a/test/numberwords/text_test.clj b/test/numberwords/formatting/text_test.clj
similarity index 60%
rename from test/numberwords/text_test.clj
rename to test/numberwords/formatting/text_test.clj
index 54aeecc..4a6c3b0 100644
--- a/test/numberwords/text_test.clj
+++ b/test/numberwords/formatting/text_test.clj
@@ -1,5 +1,5 @@
-(ns numberwords.text-test
- (:require [numberwords.text :refer [number->text]]
+(ns numberwords.formatting.text-test
+ (:require [numberwords.formatting.text :refer [number->text]]
[clojure.test :refer [deftest is]]))
(deftest number-conversion