Browse Source

Add mixed-unit conversion functions

David Peter 1 year ago
parent
commit
4476d1ec2a

+ 4 - 0
book/build.py

@@ -218,6 +218,10 @@ list_of_functions(
                 "title": "Chemical elements",
                 "modules": ["chemistry::elements"],
             },
+            {
+                "title": "Mixed unit conversion",
+                "modules": ["units::mixed"],
+            },
             {
                 "title": "Temperature conversion",
                 "modules": ["physics::temperature_conversion"],

+ 6 - 0
book/src/conversion-functions.md

@@ -15,6 +15,12 @@ now() -> tz("Asia/Kathmandu")
 # Convert a duration to days, hours, minutes, seconds
 10 million seconds -> human
 
+# Convert an angle to degrees, minutes, seconds (48° 46′ 32″)
+48.7756° -> DMS
+
+# Convert an angle to degrees, decimal minutes (48° 46.536′)
+48.7756° -> DM
+
 # Convert a number to its binary representation
 42 -> bin
 

+ 37 - 1
book/src/list-functions-other.md

@@ -1,6 +1,6 @@
 # Other functions
 
-[Error handling](#error-handling) · [Floating point](#floating-point) · [Quantities](#quantities) · [Chemical elements](#chemical-elements) · [Temperature conversion](#temperature-conversion)
+[Error handling](#error-handling) · [Floating point](#floating-point) · [Quantities](#quantities) · [Chemical elements](#chemical-elements) · [Mixed unit conversion](#mixed-unit-conversion) · [Temperature conversion](#temperature-conversion)
 
 ## Error handling
 
@@ -62,6 +62,42 @@ Get properties of a chemical element by its symbol or name (case-insensitive). F
 fn element(pattern: String) -> ChemicalElement
 ```
 
+## Mixed unit conversion
+
+Defined in: `units::mixed`
+
+### `DMS` (Degrees, minutes, seconds)
+Convert an angle to a mixed degrees, (arc)minutes, and (arc)seconds representation. Also called sexagesimal degree notation.
+More information [here](https://en.wikipedia.org/wiki/Sexagesimal_degree).
+
+```nbt
+fn DMS(alpha: Angle) -> String
+```
+
+### `DM` (Degrees, decimal minutes)
+Convert an angle to a mixed degrees and decimal minutes representation.
+More information [here](https://en.wikipedia.org/wiki/Decimal_degrees).
+
+```nbt
+fn DM(alpha: Angle) -> String
+```
+
+### `feet_and_inches` (Feet and inches)
+Convert a length to a mixed feet and inches representation.
+More information [here](https://en.wikipedia.org/wiki/Foot_(unit)).
+
+```nbt
+fn feet_and_inches(length: Length) -> String
+```
+
+### `pounds_and_ounces` (Pounds and ounces)
+Convert a mass to a mixed pounds and ounces representation.
+More information [here](https://en.wikipedia.org/wiki/Pound_(mass)).
+
+```nbt
+fn pounds_and_ounces(mass: Mass) -> String
+```
+
 ## Temperature conversion
 
 Defined in: `physics::temperature_conversion`

+ 36 - 0
examples/tests/mixed_units.nbt

@@ -0,0 +1,36 @@
+# Degrees, minutes, seconds (DMS)
+
+## United states capitol coordinates
+assert_eq(38° + 53′ + 23″, 38.8897°, 1e-4°)
+assert_eq(-(77° + 0′ + 32″), -77.0089°, 1e-4°)
+
+assert_eq(38.8897° -> DMS, "38° 53′ 23″")
+assert_eq(-77.0089° -> DMS, "-77° 0′ 32″")
+
+## Stuttgart
+assert_eq(48° + 46′ + 32″, 48.7756°, 1e-4°)
+assert_eq(9° + 10′ + 58″, 9.1828°, 1e-4°)
+
+assert_eq(48.7756° -> DMS, "48° 46′ 32″")
+assert_eq(9.1828° -> DMS, "9° 10′ 58″")
+
+# Degrees, decimal minutes (DM)
+
+assert_eq(38.8897° -> DM, "38° 53.382′")
+assert_eq(-77.0089° -> DM, "-77° 0.534′")
+
+# Feet and inches
+
+assert_eq(5.5 ft -> feet_and_inches, "5 ft 6 in")
+assert_eq(6.75 ft -> feet_and_inches, "6 ft 9 in")
+assert_eq(-5.5 ft -> feet_and_inches, "-5 ft 6 in")
+assert_eq(0 -> feet_and_inches, "0 ft 0 in")
+assert_eq(1 ft -> feet_and_inches, "1 ft 0 in")
+assert_eq(2.345 inch -> feet_and_inches, "0 ft 2.345 in")
+
+# Pounds and ounces
+
+assert_eq(5 lb -> pounds_and_ounces, "5 lb 0 oz")
+assert_eq(5.5 lb -> pounds_and_ounces, "5 lb 8 oz")
+assert_eq(6.75 lb -> pounds_and_ounces, "6 lb 12 oz")
+assert_eq(-5.5 lb -> pounds_and_ounces, "-5 lb 8 oz")

+ 27 - 0
numbat/modules/core/mixed_units.nbt

@@ -0,0 +1,27 @@
+use core::strings
+use core::lists
+
+# Helper functions for mixed-unit conversions. See units::mixed for more.
+
+fn _mixed_units_helper<D: Dim>(q: D, units: List<D>, names: List<String>, round_last: Bool) -> List<String> =
+  if is_empty(units)
+    then
+      []
+    else
+      cons(
+        if len(units) == 1
+          then
+            if round_last
+              then "{round(q / head(units))}{head(names)}"
+              else "{q / head(units)}{head(names)}"
+          else "{trunc(q / head(units))}{head(names)}",
+        _mixed_units_helper(
+          q - trunc(q / head(units)) * head(units),
+          tail(units),
+          tail(names),
+          round_last))
+
+fn _mixed_units<D: Dim>(q: D, units: List<D>, names: List<String>, round_last: Bool) -> String =
+  if q < 0
+    then str_append("-", _mixed_units(-q, units, names, round_last))
+    else join(_mixed_units_helper(q, units, names, round_last), "")

+ 1 - 0
numbat/modules/prelude.nbt

@@ -29,6 +29,7 @@ use units::fff
 use units::misc
 use units::humorous
 use units::partsperx
+use units::mixed
 
 use units::currency
 use units::bit

+ 27 - 0
numbat/modules/units/mixed.nbt

@@ -0,0 +1,27 @@
+use core::mixed_units
+use units::si
+use units::imperial
+
+@name("Degrees, minutes, seconds")
+@description("Convert an angle to a mixed degrees, (arc)minutes, and (arc)seconds representation. Also called sexagesimal degree notation.")
+@url("https://en.wikipedia.org/wiki/Sexagesimal_degree")
+fn DMS(alpha: Angle) -> String =
+  _mixed_units(alpha, [deg, arcmin, arcsec], ["° ", "′ ", "″"], true)
+
+@name("Degrees, decimal minutes")
+@description("Convert an angle to a mixed degrees and decimal minutes representation.")
+@url("https://en.wikipedia.org/wiki/Decimal_degrees")
+fn DM(alpha: Angle) -> String =
+  _mixed_units(alpha, [deg, arcmin], ["° ", "′"], false)
+
+@name("Feet and inches")
+@description("Convert a length to a mixed feet and inches representation.")
+@url("https://en.wikipedia.org/wiki/Foot_(unit)")
+fn feet_and_inches(length: Length) -> String =
+  _mixed_units(length, [foot, inch], [" ft ", " in"], false)
+
+@name("Pounds and ounces")
+@description("Convert a mass to a mixed pounds and ounces representation.")
+@url("https://en.wikipedia.org/wiki/Pound_(mass)")
+fn pounds_and_ounces(mass: Mass) -> String =
+  _mixed_units(mass, [pound, ounce], [" lb ", " oz"], false)