summaryrefslogtreecommitdiff
path: root/docs/design.org
blob: 383a707216238ad75b0b01b51d068a77043773ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#+TITLE: 7Units Design Document
#+SUBTITLE: For version 0.3.1
#+DATE: 2021 August 24
#+LaTeX_HEADER: \usepackage[a4paper, lmargin=25mm, rmargin=25mm, tmargin=25mm, bmargin=25mm]{geometry}
#+LaTeX_HEADER: \usepackage{xurl}

* Introduction
  7Units is a program that can convert between units.  This document details the internal design of 7Units, for current and future developers.

  The frontend code is currently subject to change, so it is not included in the current version of this document.
* Unit System Design
  Any code related to the backend unit system is stored in the ~sevenUnits.unit~ package.
** Dimensions
** Unit Classes
** Prefixes
* Utility Classes
  7Units has a few general "utility" classes.  They aren't directly related to units, but are used in the units system.
** ObjectProduct
   An ~ObjectProduct~ represents a "product" of elements of some type.  The units system uses them to represent coherent units as a product of base units, and dimensions as a product of base dimensions.

   Internally, it is represented using a map mapping objects to their exponents in the product.  For example, the unit "kg m^2 / s^2" (i.e. a Joule) would be represented with a map like ~[kg: 1, m: 2, s: -2]~.
** ExpressionParser
   The ~ExpressionParser~ class is used to parse the unit, prefix and dimension expressions that are used throughout 7Units.  An expression is something like "(2 m + 30 J / N) * 8 s)".  Each instance represents a type of expression, containing a way to obtain values (such as numbers or units) from the text and operations that can be done on these values (such as addition, subtraction or multiplication).  Each operation also has a priority, which controls the order of operations (i.e. multiplication gets a higher priority than addition).

   ~ExpressionParser~ has a parameterized type ~T~, which represents the type of the value used in the expression.  The expression parser currently only supports one type of value per expression; in the expressions used by 7Units numbers are treated as a kind of unit or prefix.  Operators are represented by internal types; the system distinguishes between unary operators (those that take a single value, like negation) and binary operators (those that take 2 values, like +, -, * or /).

   Expressions are parsed in 2 steps:
   1. Convert the expression to [[https://en.wikipedia.org/wiki/Reverse_Polish_notation][Reverse Polish Notation]], where operators come *after* the values they operate on, and brackets and the order of operations are not necessary.  For example, "2 + 5" becomes "~2 5 +~", "(1 + 2) * 3" becomes "~1 2 + 3 *~" and the example expression earlier becomes "~2 m * 30 J * N / + 8 s * *~".  This makes it simple to evaluate - early calculators used RPN for a good reason!
   2. Evaluate the RPN expression.  This can be done simply with a for loop and a stack.  For each token in the expression, the progam does the following:
      - if it is a number or unit, add it to the stack.
      - if it is a unary operator, take one value from the stack, apply the operator to it, and put the result into the stack.
      - if it is a binary operator, take two values from the stack, apply the operator to them, and put the result into the stack.
      After evaluating the last token, there should be one value left in the stack - the answer.  If there isn't, the original expression was malformed.
** Math Classes
   There are two simple math classes in 7Units:
   - ~UncertainDouble~ :: Like a ~double~, but with an uncertainty (e.g. \(2.0 \pm 0.4\)).  The operations are like those of the regular Double, only they also calculate the uncertainty of the final value.  They also have "exact" versions to help interoperation between ~double~ and ~UncertainDouble~.
   - ~DecimalComparison~ :: A static utility class that contains a few alternate equals() methods for ~double~ and ~UncertainDouble~.  These methods allow a slight (configurable) difference between values to still be considered equal, to fight roundoff error.
** Collection Classes
   The ~ConditionalExistenceCollections~ class contains wrapper implementations of ~Collection~, ~Iterator~, ~Map~ and ~Set~.  These implementations ignore elements that do not pass a certain condition - if an element fails the condition, ~contains~ will return false, the iterator will skip past it, it won't be counted in ~size~, etc. even if it exists in the original collection.  Effectively, any element of the original collection that fails the test does not exist.