Purpose
Plurimath provides a common data model for mathematical representation languages and allows conversion between various math representation languages.
Plurimath aims to streamline the process of converting mathematical expressions between different representation languages. This facilitates easier integration across different systems and platforms, ensuring that mathematical content remains consistent and accurate regardless of the format it is presented in.
Supported math representational languages:
-
LaTeX math
-
Microsoft Office Math Markup Language (OMML) "OfficeMath"
Supported units representation languages:
Benefits
- Suitability
-
Convert mathematical content to the required format for different tools and platforms.
- Correctness
-
Ensure mathematical expressions are correctly formatted for print and digital publications.
- Interoperability
-
Facilitate the integration of mathematical expressions across various software systems and platforms.
- Accessibility
-
Convert mathematical content into formats that are more accessible for screen readers and other assistive technologies.
Installation
Add this line to your application’s Gemfile:
gem "plurimath"
And then execute:
$ bundle install
Or install it yourself with:
$ gem install plurimath
Command Line Interface (CLI) usage
General
Plurimath provides a Command Line Interface (CLI) tool for converting between different math formats.
|
Note
|
Before continuing, please ensure you have the gem installed. |
To convert math equations between formats, use the following command followed by appropriate options.
plurimath convert [options]
Options available are:
-i,--input <INPUT>-
Specifies the input math equation. Should be provided within double quotes.
-f,--input-format <FORMAT>-
Specifies the input format of the equation. Defaults to
asciimath. -p,--file-path <FILE_PATH>-
Reads input from a file instead of the command line input. Use this for larger inputs or when input contains special characters.
-t,--output-format <FORMAT>-
Specifies the output format type. Defaults to
mathml. -m,--math-rendering-
Renders the converted equation as a math zone display tree. Boolean option (
true,false). -d,--display-style-
Specifies the DisplayStyle for OMML and MathML outputs only. Boolean option (
true,false). -s,--split-on-linebreak-
Splits MathML and OMML output into multiple equations. Boolean option (
true,false).
plurimath convert -i "sqrt(x^2 + y^2)" -f asciimath -t mathml
plurimath convert -i "equation" -f omml -t mathml -d true
plurimath convert -p <file_path> -f asciimath -t unicodemath
For more detailed information and additional options, use:
plurimath help convert
Ruby API
The central data model in Plurimath is the Plurimath::Formula class, which
allows you to transform any math representation language into any other
representation language.
Conversion examples
AsciiMath Formula example
Localized decimal input
AsciiMath, LaTeX, HTML, and UnicodeMath input can use a locale decimal marker
from Plurimath::Formatter::SupportedLocales::LOCALES when parsing numbers.
Pass locale: to Plurimath::Math.parse for one parse, or use
Plurimath.configure for persistent defaults.
For AsciiMath, LaTeX, and HTML, the configured marker is the decimal marker.
When a locale uses a non-full-stop marker, 1.2 is parsed as 1, ., and
2, not as one decimal number.
When the locale decimal marker is a comma, unspaced digit-comma-digit input is read as a decimal number.
1,2 # decimal number
1 , 2 # comma separator
AsciiMath uses commas for expression and table separators, so separator commas between digit tokens should be written with spaces around them when the configured decimal marker is a comma.
HTML can use the same configured decimal marker inside plain text or tags.
1,2
<span>1,2</span>
Locales are not limited to full stop and comma markers. For example, Arabic and Persian locales use the Arabic decimal separator.
LaTeX ordinary spaces are removed during parser preprocessing, so 1, 2 is
equivalent to 1,2 for this parser. With a comma decimal marker, only
digit-comma-digit is parsed as a decimal number; a leading comma remains a comma
symbol followed by a number. To write a comma separator between two digits,
wrap the comma or the following value in a group, such as 1{,}2 or 1,{}2.
1,2 % decimal number
1{,}2 % comma separator
1,{}2 % comma separator
UnicodeMath is different: it keeps its existing full stop and comma decimal
grammar, including leading markers such as .2 and ,2, and also accepts the
configured locale marker.
Localized decimal parsing preserves the parsed number text, such as 1,2 or
1٫2. Number formatter input remains canonical decimal input; locale decimal
and grouping symbols are applied when formatting output.
MathML Formula example
UnicodeMath Formula example
OMML Formula example
omml = <<~OMML
<m:oMathPara xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">
<m:oMath>
<m:f>
<m:fPr>
<m:ctrlPr />
</m:fPr>
<m:num>
<m:r>
<m:t>sin</m:t>
</m:r>
</m:num>
<m:den>
<m:r>
<m:t>1</m:t>
</m:r>
</m:den>
</m:f>
</m:oMath>
</m:oMathPara>
OMML
formula = Plurimath::Math.parse(omml, :omml)
Converting to other formats
Once you have a Plurimath::Math::Formula object, you can convert it to
AsciiMath, MathML, LaTeX, UnicodeMath, or OMML by calling the respective
conversion function on the Formula object.
AsciiMath output conversion
formula.to_asciimath
# => "sin(1)"
|
Note
|
AsciiMath doesn’t support certain symbols that LaTeX does. During conversion from LaTeX to AsciiMath, if a symbol is not supported in AsciiMath, the LaTeX symbol will be returned. |
LaTeX output conversion
formula.to_latex
# => "\\sin1"
MathML output conversion
formula.to_mathml
# => "<math xmlns='http://www.w3.org/1998/Math/MathML'><mstyle displaystyle='true'><mi>sin</mi><mn>1</mn></mstyle></math>"
UnicodeMath output conversion
formula.to_unicodemath
# => "sin(1)"
OMML output conversion
formula.to_omml
# => "<m:oMathPara xmlns:m=\"http://schemas.openxmlformats.org/officeDocument/2006/math\"><m:oMath><m:f><m:fPr><m:ctrlPr /></m:fPr><m:num><m:r><m:t>sin</m:t></m:r></m:num><m:den><m:r><m:t>1</m:t></m:r></m:den></m:f></m:oMath></m:oMathPara>"
Complex mathematical expressions
Plurimath is capable of handling complex mathematical expressions with nested functions and operators.
This feature is particularly useful for application that requires consistent and accurate conversion of intricate mathematical content.
Example. Consider the following complex LaTeX expression:
\frac{\sqrt{a^2 + b^2}}{\sin(\theta) + \cos(\theta)}
You can parse and convert this complex expression with Plurimath:
complex_latex = "\\frac{\\sqrt{a^2 + b^2}}{\\sin(\\theta) + \\cos(\\theta)}"
formula = Plurimath::Math.parse(complex_latex, :latex)
# Convert to AsciiMath
asciimath = formula.to_asciimath
# => "frac(sqrt(a^2 + b^2))(sin(theta) + cos(theta))"
# Convert to MathML
mathml = formula.to_mathml
# => "<math xmlns='http://www.w3.org/1998/Math/MathML'><mfrac><msqrt><mrow><msup><mi>a</mi><mn>2</mn></msup><mo>+</mo><msup><mi>b</mi><mn>2</mn></msup></mrow></msqrt><mrow><mi>sin</mi><mo>(</mo><mi>θ</mi><mo>)</mo><mo>+</mo><mi>cos</mi><mo>(</mo><mi>θ</mi><mo>)</mo></mrow></mfrac></math>"
# Convert to UnicodeMath
unicodemath = formula.to_unicodemath
# => "frac(√(a^2 + b^2))(sin(θ) + cos(θ))"
# Convert to OMML
omml = formula.to_omml
# => "<omml representation of the expression>"
Number formatting
Introduction
Number formatting is an essential aspect of presenting numerical data in a way that is consistent with regional conventions and user preferences. There are myriad number formatting conventions and standards that are widely used in various cultures and fields.
To address these needs, Plurimath now allows precise control over how numbers are presented through its number formatting feature.
Plurimath’s number formatter allows users to format numbers based on locale, ensuring that the formatting adheres to regional conventions and enhances both readability and precision.
For more details, please refer to the blog post Number formatting support in Plurimath
Existing conventions
Traditional conventions
Different cultures, orthographies and organizations have different conventions for formatting numbers.
These include practices on how to represent decimal points, digit grouping, digit grouping separators, and various mathematical notations.
- Decimal point symbol
-
In the United States, a full stop (
.) is used as the decimal point separator, while in many European countries, a comma (,) is used instead. - Digit grouping delimiter
-
In the United States, numbers are often grouped in sets of three digits using commas, such as 1,234,567.89. In some European countries, numbers are grouped using periods, such as 1.234.567,89, or a thin space, such as 1 234 567,89.
- Digit grouping practices
-
In Western cultures, numbers ahead of the decimal are often grouped threes. Numbers behind the decimal are less standardized, but are often grouped in sets of two or three.
- Mathematical notation
-
In scientific and engineering contexts, numbers are often formatted using scientific notation, which expresses numbers as a coefficient multiplied by a power of 10. For example, the number 123,456,789 can be expressed in scientific notation as 1.23456789 x 10^8.
Standardized conventions
Standardization organizations have established standards for number formatting to ensure uniformity and accuracy.
The SI system (International System of Units), by the BIPM (Bureau International des Poids et Mesures), specifies rules regarding the decimal point symbol, digit grouping delimiter and digit groupings.
ISO 80000-2, the international standard for quantities and units, used by all ISO and IEC standards, also provides guidelines for number formatting in a different manner than the SI system.
Using the number formatter
General
The number formatting feature is implemented in the Plurimath::NumberFormatter
class, which allows users to re-use a single formatter class for formatting
multiple numbers.
A simple two-step process to format numbers:
-
Create a new
Plurimath::NumberFormatterobject, passing the desired locale and overriding options as arguments. -
Call the
localized_numbermethod on the formatter object, passing the number to be formatted as a string and any additional options.
The final formatted number is formatted according to the following configuration priority, ordered from highest to lowest precedence:
-
The
formathash given toPlurimath::NumberFormatter#localized_number -
The
localize_numberstring in the creation of aPlurimath::NumberFormatter -
The
localizer_symbolshash in the creation of aPlurimath::NumberFormatter -
The default configuration of the locale of the
Plurimath::NumberFormatter
precision is resolved separately: the precision: keyword passed to
localized_number, which defaults to the formatter instance’s precision
value, takes precedence over any precision provided through the formatter
symbols or format hash.
formatter = Plurimath::NumberFormatter.new(:en)
formatted_number = formatter.localized_number(
"1234567.89",
format: {
group_digits: 2,
# other support options
}
)
# => "1,23,45,67.89"
Creating a number formatter
The NumberFormatter is used to format numbers based on the locale and the
formatting configuration provided.
Syntax:
Plurimath::NumberFormatter objectformatter = Plurimath::NumberFormatter.new(
<locale-symbol>, # mandatory <b class="conum">(1)</b>
localize_number: <localize-string>, # optional <b class="conum">(2)</b>
localizer_symbols: <format-hash>, # optional <b class="conum">(3)</b>
precision: <precision-number>, # optional <b class="conum">(4)</b>
)
-
Locale to be used for number formatting.
-
String pattern to define the number formatting.
-
Hash containing relevant options for number formatting.
-
Number of decimal places to round.
Where,
<locale-symbol>-
(optional, default
:en) The locale to be used for number formatting. Accepted values are listed in thePlurimath::Formatter::SupportedLocales::LOCALESconstant. localize_number: <localize-string>-
(optional, default
nil) A string containing a specific sequence of characters that defines the number formatting. Use eitherlocalize_numberorlocalizer_symbolsto set the number formatting pattern.See
localize_numberfor details. localizer_symbols: <format-hash>-
(optional, default
{}) A hash containing the relevant options for number formatting. Use eitherlocalize_numberorlocalizer_symbolsto set the number formatting pattern.See format options hash for details.
precision: <precision-number>-
(optional, default
nil) Number of decimal places to round. Accepts an integer value.Example 5. Specifying a precision of 6 digits"32232.232" ⇒ "32232.232000"
Plurimath::NumberFormatter object using the :en localeformatter = Plurimath::NumberFormatter.new(:en)
# => #<Plurimath::NumberFormatter:0x00007f8b1b8b3b10 @locale=:en>
Configuring the number formatter
The Plurimath::NumberFormatter object can be configured using either the
localize_number or localizer_symbols options.
Plurimath::Formatter::Standard exposes the same configuration through the
string_format and options initializer arguments.
Via "format options" using localizer_symbols
The localizer_symbols key is used to set the number formatting pattern
through a Hash object containing specified options.
This Hash object is called the "format options Hash".
Available options are explained below.
|
Note
|
Each option takes an input of a certain specified type. Using an input type other than the specified type will result in errors or incorrect output. |
The values passed to localizer_symbols persist as long as the initialized
NumberFormatter instance is accessible. It is therefore useful in scenarios
when configuration will be static or changes are not required very often.
The same option keys can be passed through localizer_symbols, through the
per-call format: hash, or through Plurimath::Formatter::Standard’s
`options: argument.
decimal-
(
Stringvalue) Symbol to use for the decimal point. Accepts a character.Example 7. Using the ',' "comma" symbol as the decimal point"32232.232" ⇒ "32232,232"
Example 8. Using the '.' "full stop" symbol as the decimal point"32232.232" ⇒ "32232.232"
digit_count-
(
Numericvalue) Total number of digits to render (integer + fractional), with the value truncated or rounded as necessary. Accepts an integer value.When
digit_countis less than or equal to the integer length, the fractional part is omitted entirely, and the integer is rounded based on whether the fractional value is >= 0.5 (or >= base/2 for non-decimal bases).Example 9. Specifying a total of 6 digits in rendering the number"32232.232" ⇒ "32232.2"
Example 10. When digit_count is smaller than the integer length"99999.999" with
digit_count: 3⇒ "100,000" (fractional part omitted, integer rounded up) "12345.123" withdigit_count: 3⇒ "12,345" (fractional part omitted, no rounding) group-
(
Stringvalue) Delimiter to use between groups of digits specified ingroup_digits. Accepts a character. (default is not to group digits.)Example 11. Using the unicode thin space (THIN SPACE, U+2009) as the grouping delimiter"32232.232" ⇒ "32 232.232"
group_digits-
(
Numericvalue) Number of digits to group the integer portion, grouping from right to left. Accepts an integer value. (default is 3 in most locales.)Example 12. Using the unicode thin space as the grouping delimiter, and grouping every 2 digits"32232.232" ⇒ "3 22 32.232"
number_sign-
(
SymbolorStringvalue) Controls whether positive numbers are rendered with an explicit sign. Use:plusor"plus"to add a leading+to positive values. Negative values keep their normal-sign. The sign is applied before base prefixes and before notation rendering.Example 13. Showing a positive number signWhen
baseis used, the input string is still the source decimal value;basecontrols the rendered output base."14236.39239" with
number_sign: :plus⇒ "+14,236.39239" "10" withbase: 2, number_sign: :plus, group_digits: 2, group: ","⇒ "+0b10,10" padding-
(
Stringvalue) Character to use when padding the integer portion. Defaults to0.Example 14. Using spaces as integer padding"32" with
padding_digits: 6, padding: " ", group: ""⇒ " 32" padding_digits-
(
Numericvalue) Minimum number of integer digits to render. Missing integer digits are inserted before grouping using the configuredpaddingcharacter. Do not use it together withpadding_group_digits.Padding is applied before integer grouping, so padding characters are grouped with the rest of the integer digits.
Example 15. Padding to a fixed integer width"32" with
padding_digits: 6, group: ""⇒ "000032" "32123" withpadding_digits: 6, group: ""⇒ "032123" "32" withpadding_digits: 6, group_digits: 3, group: " "⇒ "000 032" padding_group_digits-
(
Numericvalue) Pads the integer portion to the lowest multiple of the configured digit count. Missing integer digits are inserted before grouping using the configuredpaddingcharacter. Do not use it together withpadding_digits.Example 16. Padding to an integer width multiple"32" with
padding_group_digits: 4, group: ""⇒ "0032" "32123" withpadding_group_digits: 4, group: ""⇒ "00032123"
base-
(
Numericvalue) Sets the numeric base (radix) used to render both the integer and fractional parts of the number. Supported values are 2 (binary), 8 (octal), 10 (decimal, default) and 16 (hexadecimal). Passing any other value forbaseraisesPlurimath::Formatter::UnsupportedBase.
"10" with base: 2, group_digits: 2, group: "," ⇒ "0b10,10"
"9" with base: 8 ⇒ "0o11"
"255" with base: 16 ⇒ "0xff"
"10.75" with base: 2, group_digits: 2, group: "," ⇒ "0b10,10.11" (integer and fractional parts converted, with grouping)
base_prefix-
(
Stringornilvalue) Overrides the default base prefix for non‑decimal bases. When omitted, standard prefixes are used (0bfor base 2,0ofor base 8,0xfor base 16).nilor an empty string can be used to omit the prefix.
"255" with base: 16, base_prefix: "16#" ⇒ "16#ff"
"255" with base: 16, base_prefix: nil ⇒ "ff"
"255" with base: 16, base_prefix: "" ⇒ "ff"
base_postfix-
(
Stringornilvalue) If present withoutbase_prefix, a postfix is appended to the converted number instead of using the default base prefix. Whenbase_prefixis also provided, the prefix and postfix are both rendered. This is applied after digit grouping. Because the formatter checks whether thebase_postfixkey is present, setting it tonilor an empty string omits both the postfix and the default base prefix.
"255" with base: 16, base_postfix: "_16" ⇒ "ff_16"
"255" with base: 16, base_prefix: "16#", base_postfix: "_16" ⇒ "16#ff_16"
"255" with base: 16, base_postfix: nil ⇒ "ff"
"255" with base: 16, base_postfix: "" ⇒ "ff"
hex_capital-
(
Booleanor:numbers_onlyvalue) Controls uppercase rendering for hexadecimal output. Whentrueandbase: 16, all characters in the rangea-fin the converted output are rendered using uppercase letters. This includes both hexadecimal digits and any separators (such asdecimal,group,fraction_group) that happen to be letters in thea-frange. Thebase_prefixandbase_postfixvalues are never modified.Use
:numbers_onlyto uppercase generated hexadecimal digits without changing separator characters. It has no effect for other bases.
"48879" with base: 16, hex_capital: true, group_digits: 2 ⇒ "0xBE,EF"
"48879" with base: 16, hex_capital: :numbers_only, group_digits: 2, group: "e" ⇒ "0xBEeEF"
+
WARNING: If you use separator characters in the range a-f (such as decimal: "d" or fraction_group: "f"), those separators will also be uppercased when hex_capital: true. Choose separators outside this range (e.g., ".", ",", " ", "_") if you need them to remain unchanged.
+ .Separator uppercasing behavior
"48879" with base: 16, hex_capital: true, group_digits: 2, group: "g" ⇒ "0xBEgEF" (g remains lowercase)
"48879" with base: 16, hex_capital: true, group_digits: 2, group: "f" ⇒ "0xBEFEF" (f separator becomes F)
|
Note
|
When base is not 10, both the integer and fractional parts are converted to the specified base.
The decimal separator is rendered as configured by the decimal option.
|
+
NOTE: The fractional-part conversion treats the digits after the decimal point as a fractional value (for example, "10.75" is interpreted
as decimal 0.75 for the fractional part) and converts that real value to the requested base, honoring the configured precision.
===== Base conversion with other formatting options
Base conversion works seamlessly with other formatting options such as precision, digit_count,
fraction_group, fraction_group_digits, significant, and integer padding.
+
Integer padding is applied after digit_count rounding, so padding can add
integer digits after the total visible-digit budget has been resolved.
Controls the maximum number of fractional digits in the converted base (pads with zeros when needed and truncates repeating expansions to the configured precision):
+
"10.75" with base: 2, precision: 4 ⇒ "0b10,10.1100" (fractional part padded to 4 digits in base 2)
"0.5" with base: 16, precision: 6 ⇒ "0x0.800000" (fractional part converted to base 16 and padded to 6 digits)
Groups fractional digits in the converted base:
+
"10.75" with base: 2, fraction_group_digits: 1, fraction_group: " ", group_digits: 10 ⇒ "0b1010.1 1"
Limits total digits when using base conversion:
+
"14236.39239" with base: 10, digit_count: 6, group_digits: 3 ⇒ "14,236.4"
"10.75" with base: 2, digit_count: 7, group_digits: 10 ⇒ "0b1010.110"
"255.9" with base: 16, digit_count: 2, group_digits: 10 ⇒ "0x100" (fractional part omitted, integer rounded)
Applies significant digit rounding with base conversion:
+
"1234.56" with base: 10, significant: 4 ⇒ "1,235"
"1999" with base: 10, significant: 2 ⇒ "2,000"
fraction_group-
(
Stringvalue) Delimiter to use between groups of fractional digits specified infraction_group_digits. Accepts a character.
"32232.232131" ⇒ "32232.232 131".
fraction_group_digits-
(
Numericvalue) Number of digits in each group of fractional digits, grouping from left to right. Accepts an integer value.
"32232.232131" ⇒ "32232.23 21 31"
significant-
(
Numericvalue) Sets the number of significant digits to show, with the value rounded. Leading zeros before the first non-zero digit are not counted as significant.
"112" with significant: 2 ⇒ "110"
"1999" with significant: 2 ⇒ "2,000"
"0.1999" with significant: 3 ⇒ "0.200"
+
Significant-digit rounding is applied before localized rendering. This means
the formatter does not re-parse an already grouped or localized output string,
so decimal and group may use the same symbol without changing which digits
are rounded.
+ .Using significant digits with overlapping decimal and group symbols
"327428.7432878432992" with decimal: ",", group_digits: 3, fraction_group_digits: 2, fraction_group: "'", significant: 9
⇒ "327,428,74'3"
+
When base is not 10 and precision is omitted, Plurimath infers the
fractional precision needed in the target base where possible. That inference
is capped by the source number’s significant digits, so finite converted values
can keep required trailing zeros while repeating fractional conversions are not
expanded only to fill the requested count.
+ .Using significant digits with base conversion
"48879" with base: 16, significant: 3, group_digits: 2, group: "," ⇒ "0xbe,f0"
"123.25" with base: 16, significant: 5, group_digits: 10 ⇒ "0x7b.400"
"0.1" with base: 16, significant: 5, group_digits: 10 ⇒ "0x0.1"
notation-
(
Stringvalue) Specifies the mathematical notation to be used. Accepts the following values.e-
Use exponent notation.
1.23456789e8
scientific-
Use scientific notation.
1.23456789 × 10⁸
engineering-
Use engineering notation, where the exponent of ten is always selected to be divisible by three to match the common metric prefixes.
123.456789 × 10⁶
e-
(
Stringvalue) Symbol to use for exponents in E notation (default valuee). (used in the mode:eonly).
3.2232232e5
times-
(
Stringvalue) Symbol to use for multiplication where required by the notation (used in the modes:scientificandengineering). Defaults to×.
32.232232 · 104
exponent_sign-
(
SymbolorStringvalue) Whether to use a plus sign to indicate positive exponents, in exponent-based notation (used in the modes:e,scientific,engineering). Legal values are::plus,"plus"-
The
+symbol is used.
32.232232 × 10⁺⁴
These options are to be grouped under a single Hash object.
localizer_symbols{
decimal: ",", # replaces the decimal point with the passed string
group_digits: 2, # groups integer digits into groups of the passed size
group: "'", # places the string between grouped integer digits
fraction_group_digits: 3, # groups fraction digits into groups of the passed size
fraction_group: ",", # places the string between grouped fraction digits
number_sign: :plus, # adds a plus sign to positive numbers
digit_count: 6, # limits the total rendered digit count
significant: 4, # limits the rendered significant digit count
precision: 2, # controls rendered fractional precision
notation: :scientific, # renders the number using the selected notation
e: "E", # sets the exponent marker for E notation
times: "x", # sets the multiplication symbol for power notation
exponent_sign: :plus, # adds a plus sign to positive exponents
base: 16, # renders the number in the selected base
base_prefix: "0x", # prefixes non-decimal base output when no postfix key is present
base_postfix: "_16", # appends a postfix and takes precedence over base_prefix
hex_capital: :numbers_only, # uppercases generated hexadecimal digits only
}
Only include the keys needed for the formatter behavior being configured.
===== Via the localize_number option
The localize_number option accepts a formatting pattern specified as a string,
using the hash symbol # to represent a digit placeholder.
The localize_number option is useful when you want to format numbers in a
specific way that is not covered by the localizer_symbols option.
A sample value of #,##0.### ### is interpreted as the following
configuration in the format options hash:
group-
This parameter is set to the very first non-hash character before 0. If there is no non-hash character before
#+0, then the default group delimiter will be nil.In this example, it is
,. group_digits-
This parameter is set to the "count of all hashes + 1" (including the zero). Minimum 1 hash symbol is required.
In this example,
##0sets the value to 3. decimal-
This parameter is set to the character immediately to the right of
0. This is mandatory.In this example, it is
.. fraction_group_digits-
This parameter is set to "count of all the hashes right next to decimal". Minimum 1 hash symbol is required.
In our example, '###' sets the value to 3.
fraction_group-
This parameter is set to the first character after
fraction_group_digits. If there is no non-hash character afterfraction_group_digits, it is set to nil.In this example it is
' '(a space).
formatter = Plurimath::NumberFormatter.new(:en, localize_number: "#,##0.### ###")
formatter.localized_number("1234.56789")
# => "1,234.568 9"
==== Formatting a number using NumberFormatter
The localized_number method is used to format a number given a
NumberFormatter instance.
Syntax:
localized_numberformatter.localized_number(
<number>, # mandatory <b class="conum">(1)</b>
locale: <locale-symbol>, # optional <b class="conum">(2)</b>
precision: <precision-number>, # optional <b class="conum">(3)</b>
format: <format-hash> # optional <b class="conum">(4)</b>
)
-
The number to be formatted.
-
The locale to be used for number formatting.
-
The number of decimal places to round the number to.
-
Hash containing the relevant options for number formatting.
Where,
<number>-
(mandatory) The number to be formatted, as a
Stringcontaining the decimal representation of the value (for example,"1234.56789"). locale: <locale-symbol>-
(optional) The locale to be used for number formatting. Value is a symbol. Overrides the locale set during the creation of the
NumberFormatterobject. If not provided, the locale of theNumberFormatterinstance will be used. precision: <precision-number>-
(optional) The number of decimal places to round the number to. If not provided, the precision of the
NumberFormatterinstance will be used. format: <format-hash>-
(optional, default
{}) A Hash containing the relevant options for number formatting, that overrides thelocalizer_symbolsconfiguration of theNumberFormatter. Takes a Hash in the form of the format options hash. precision: <precision-number>-
Number of decimal places to round. Accepts an integer value.
"32232.232" ⇒ "32232.232000"
formatter = Plurimath::NumberFormatter.new(:en)
formatter.localized_number("1234.56789")
# => "1,234.56789"
formatter = Plurimath::NumberFormatter.new(:fr)
formatter.localized_number("1234.56789")
# => "1 234,56789"
The locale and precision set in the NumberFormatter can be overridden by
passing the locale and precision options to the localized_number method.
formatter = Plurimath::NumberFormatter.new(:en)
formatter.localized_number("1234.56789", locale: :de, precision: 6)
# => "1.234,567890"
==== Overriding specified NumberFormatter options using the format key
The format option is used to override the specified configuration of the
NumberFormatter object.
It expects a Hash in the form of the format options hash.
formatter = Plurimath::NumberFormatter.new(:en)
formatter.localized_number(
"1234.56789",
format: {
decimal: "x",
# other supported options
}
)
# => "1,234x56789"
formatter = Plurimath::NumberFormatter.new(:en)
formatter.localized_number(
"1234567.89",
format: {
group_digits: 2,
# other supported options
}
)
# => "1,23,45,67.89"
formatter = Plurimath::NumberFormatter.new(:en)
formatter.localized_number(
"1234.56789",
format: {
decimal: "x",
group_digits: 2,
group: "'",
fraction_group_digits: 3,
fraction_group: ","
}
)
# => "12'34x567,89"
=== Supported locales
Plurimath supports the following locales for number formatting. The locale values are sourced from the Unicode CLDR repository.
The list of locales and their values are given in the file
lib/plurimath/formatter/supported_locales.rb.
The locales and their values can be obtained through the following code.
Plurimath::Formatter::SupportedLocales::LOCALES[:en]
# => { decimal: ".", group: "," }
| Locale | Decimal delimiter | Group delimiter |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
=== Formatting numbers in a formula
==== General
Plurimath supports number formatting within formulas for all supported languages. This feature allows you to apply custom number formatting when converting formulas to any of the supported format.
|
Note
|
For details, check out our blog post: Number formatting now supported in formulas across all math representation languages. |
The steps to format numbers within a formula are:
-
Create a number formatter that can be configured;
-
Apply the number formatter to a formula through the
Formula.to_{format}method using aformatteroption, or configure a default formatter throughPlurimath.configure.
The formatter should be an instance of Plurimath::NumberFormatter or a custom
formatter derived from Plurimath::Formatter::Standard. Custom formatters can
define a format_number(formula, number) method to make context-aware
formatting decisions based on the surrounding formula (see
[Contextual number formatting]). Existing custom formatters that define
format(formula, number) remain supported.
The quick example below demonstrates how to format a number in a formula.
The following code applies number formatting to a LaTeX math formula.
formula = Plurimath::Math.parse('\sum_{i=1}^{10000} i^2121221', :latex) <b class="conum">(1)</b>
formatter = Plurimath::Formatter::Standard.new <b class="conum">(2)</b>
formula.to_latex(formatter: formatter) <b class="conum">(3)</b>
# => '\sum_{i = 1}^{10,000} i^{2,121,221}'
-
The formula is parsed into a
Formulaobject using thePlurimath::Math.parsemethod. -
A
Plurimath::Formatteris created. -
The
Formula.to_latexmethod is called with theformatteroption to format the formula.
formula = Plurimath::Math.parse("e^(i*pi) + 1.1 = 0.2", :asciimath)
custom_formatter = Plurimath::Formatter::Standard.new(
locale: :fr,
options: { number_sign: :plus },
precision: 3
)
print formula.to_mathml(formatter: custom_formatter)
# <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
# <mstyle displaystyle="true">
# <msup>
# <mi>e</mi>
# <mrow>
# <mi>i</mi>
# <mo>⋅</mo>
# <mi>π</mi>
# </mrow>
# </msup>
# <mo>+</mo>
# <mn>+1.100</mn>
# <mo>=</mo>
# <mn>+0.200</mn>
# </mstyle>
# </math>
Passing formatter: to a formula serialization method takes precedence over
the configured default formatter.
==== Deprecation notices
Plurimath exposes deprecation behavior through the shared configuration object. The current number formatter arguments documented above remain supported; this setting controls how Plurimath handles deprecation notices when a deprecated feature is used.
Plurimath.configure do |config|
config.deprecation.behavior = :collect
end
# Retrieve collected notices after parsing
Plurimath::Deprecation.notices.each do |notice|
puts notice.to_s # => "[plurimath][DEPRECATION] ..."
puts notice.severity # => :warning
puts notice.feature # => "feature_name"
end
Supported behaviors are:
collect-
Default behavior. Collects deprecation notices in
Plurimath::Deprecation.noticesfor the caller to retrieve. Each notice is aPlurimath::DeprecationErrorwithseverity,feature,replacement,since,remove_in, andto_s. raise-
Raises
Plurimath::DeprecationErrorimmediately when a deprecated feature is used. silence-
Suppresses deprecation notices entirely.
==== Defining a number formatter
A "number formatter" is a class that formats numbers in a specific way. It contains the configuration for formatting numbers, such as the number of digits in a group, the decimal separator, and the group separator.
Plurimath offers a standard formatter class called
Plurimath::Formatter::Standard that includes a comprehensive
standard configuration.
Plurimath::Formatter::Standard object> formatter = Plurimath::Formatter::Standard.new <b class="conum">(1)</b>
-
Creates a
Plurimath::Formatterobject that uses standard configuration.
The number formatting configuration can be changed in these ways:
-
Pass options to the
Plurimath::Formatter::Standardclass initializer (with options explained in the number formatter blog post). -
Create a custom formatter inheriting from the
Plurimath::Formatter::Standardclass.
==== Changing number formatting configuration
The typical way to change the number formatting configuration is to create a
Plurimath::Formatter::Standard object with the desired configuration options.
There are two types of number formatting configuration to change:
-
Arguments passed to the
Plurimath::Formatter::Standardclass initializer. -
Overriding options through the
optionsargument.
Plurimath::Formatter::Standard objectThe arguments are:
locale-
(default:
:enfor English) a symbol or string value. The supported locales are listed in the number formatter blog post. options-
(default: empty) a hash of options. This is the
Plurimath::Formatter::Standardalias forPlurimath::NumberFormatter’s `localizer_symbolsargument. string_format-
(default:
nil, disabled) a string value. This is thePlurimath::Formatter::Standardalias forPlurimath::NumberFormatter’s `localize_numberargument. precision-
(default:
nil, disabled) an integer value.
> options = {
fraction_group_digits: 2,
fraction_group: ".",
group_digits: 2,
decimal: ";",
group: ",",
}
> formatter = Plurimath::Formatter::Standard.new(locale: :hy, options: options, precision: 2)
# string_format: <string value> if provided
> Plurimath::Math.parse('2121221.3434', :latex).to_latex(formatter: formatter)
# => '2,12,12,21;34'
The precision = 2 option in the initializer causes the formatted value to have
decimal places truncated from 4 to 2.
==== Creating a custom formatter
In cases where the standard formatter’s available options do not meet the needs for number presentation, a custom formatter can be created to apply new mechanisms of formatting numbers.
The custom formatter is to be subclassed from Plurimath::Formatter::Standard.
class MyCustomFormatter < Plurimath::Formatter::Standard <b class="conum">(1)</b>
def initialize(locale: :en, precision: nil, options: {}, string_format: nil) <b class="conum">(2)</b>
super
end
end
-
The custom formatter class inherits from
Plurimath::Formatter::Standard. -
The arguments can be overridden in the
initializemethod.
The default options of the custom formatter are set using the
set_default_options method.
set_default_options methodclass MyCustomFormatter < Plurimath::Formatter::Standard
def initialize(locale: :en, precision: nil, options: {}, string_format: nil)
super
end
def set_default_options(options = {}) <b class="conum">(1)</b>
options = {
fraction_group_digits: 2,
fraction_group: ".",
...
}
end
end
-
The
set_default_optionsmethod is overridden to set the default options. The shown options are ones inherited from thePlurimath::Formatter::Standardclass, but additional ones understood by the class can be set.
It is used in the following manner.
class MyCustomFormatter < Plurimath::Formatter::Standard
def initialize(locale: :fr)
super
end
def set_default_options(options = {})
{
fraction_group_digits: 2,
fraction_group: ".",
group_digits: 2,
decimal: ";",
group: ",",
...
}
end
end
> formula = Plurimath::Math.parse('\sum_{i=1}^{1000.001} i^2121221.3434', :latex)
# => Plurimath::Math::Formula...
> formula.to_latex(formatter: formatter)
# => '\sum_{i = 1}^{10,00;00.1} i^{2,12,12,21;34.34}'
> formula.to_asciimath(formatter: formatter)
# => 'sum_(i = 1)^(10,00;00.1) i^(2,12,12,21;34.34)'
==== Contextual number formatting
In some cases, certain numbers within a formula should be formatted differently depending on their context. For example, a year like "2024" should not have digit grouping applied, while other numbers in the same formula should.
The format_number method on the formatter receives both the root
Plurimath::Math::Formula tree and the current Plurimath::Math::Number node
being processed, allowing context-aware formatting decisions.
format_number methoddef format_number(formula, number)
# formula: the root Plurimath::Math::Formula containing the full equation
# number: the Plurimath::Math::Number node currently being formatted
# returns: a formatted string
end
Formula rendering uses the first formatter method available in this order:
format_number(formula, number), then format(formula, number), then
localized_number(number.value.to_s). This keeps existing custom formatters
working while giving new custom formatters a method name specific to formula
number formatting.
New custom formatters should define format_number; the format fallback
exists for compatibility with existing custom formatters.
To implement contextual formatting, define a format_number method in a custom
formatter subclass.
class YearFormatter < Plurimath::Formatter::Standard
def format_number(formula, number) <b class="conum">(1)</b>
int_value = Integer(number.value, exception: false)
if int_value && int_value > 1800 && int_value < 2200 <b class="conum">(2)</b>
number.value.to_s
else
localized_number(number.value.to_s) <b class="conum">(3)</b>
end
end
end
formatter = YearFormatter.new
formula = Plurimath::Math.parse("2024 + 1000000", :asciimath)
formula.to_latex(formatter: formatter)
# => "2024 + 1,000,000" <b class="conum">(4)</b>
-
Define
format_numberto receive the formula tree and number node. -
Detect year-like numbers and return them unformatted.
-
Call
localized_numberto apply locale-based formatting for other numbers. -
"2024" is left as-is, while "1000000" is formatted with digit grouping.
=== Default number formatting configuration
The effective default configuration for Plurimath::Formatter::Standard is as
follows.
| Option key | Description | Value |
|---|---|---|
|
The locale used for number formatting |
|
|
The number of digits in each group of the fraction part |
|
|
The sign used for the exponent part of the number |
|
|
The sign used for positive numbers |
|
|
The character used to separate groups of digits in the fraction part |
|
|
The notation used for the number formatting |
|
|
The number of digits in each group of the integer part |
|
|
The character used to pad the integer part |
|
|
The minimum number of integer digits to display |
|
|
The multiple of integer digits to pad to |
|
|
The number of significant digits to display |
|
|
The number of digits to display |
|
|
The number of decimal places to display |
|
|
The numeric base used to render the number |
|
|
The prefix used for non-decimal bases |
standard prefix for the selected base |
|
The postfix used for non-decimal bases |
|
|
Whether hexadecimal output is capitalized |
|
|
The character used as the decimal separator |
|
|
The character used to separate groups of digits in the integer part |
|
|
The character used for multiplication |
|
|
The character used for E notation |
|
== Math parse trees
=== General
Plurimath allows you to display the math parse tree both as Formula objects
and in the math language of expression.
=== Displaying as Formula objects
You can display the parse tree as Formula objects to understand the structure
of the parsed mathematical expression.
=== Displaying in the math language of expression
You can also display the parse tree in the math language of expression to see how the expression is represented in that language.
formula = Plurimath::Math.parse("sin(1)", :asciimath)
formula.to_display(:asciimath)
# |_ Math zone
# |_ "sin(1)"
# |_ "sin" function apply
# |_ "1" argument
formula.to_display(:latex)
# |_ Math zone
# |_ "\\sin1"
# |_ "sin" function apply
# |_ "1" argument
formula.to_display(:mathml)
# |_ Math zone
# |_ "<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><mstyle displaystyle="true"><mrow><mi>sin</mi><mrow><mo>(</mo><mn>1</mn><mo>)</mo></mrow></mrow></mstyle></math>"
# |_ "<mrow><mi>sin</mi><mrow><mo>(</mo><mn>1</mn><mo>)</mo></mrow></mrow>" function apply
# |_ "sin" function name
# |_ "<mrow><mo>(</mo><mn>1</mn><mo>)</mo></mrow>" argument
# |_ "<mtext>1</mtext>" text
== Working with UnitsML
=== General
Plurimath supports UnitsML, a markup language used to express units of measure in a way that can be understood by humans and machines. This allows you to handle mathematical expressions involving units of measure seamlessly.
UnitsML can be used with the following math representation languages:
-
MathML
-
AsciiMath
For detailed information on supported units and symbols in UnitsML, refer to the UnitsML Supported Data documentation.
=== Parsing and Converting UnitsML Expressions
Plurimath can parse UnitsML expressions and convert them to other mathematical representation languages. Here’s an example of how to work with UnitsML in Plurimath.
=== Example: Parsing and Converting UnitsML
Consider the following UnitsML expression in AsciiMath syntax:
h = 6.62607015 xx 10^(-34) "unitsml(kg*m^2*s^(-1))"
==== Step-by-Step Customization
-
Parse the UnitsML Expression
-
Customize and Convert to AsciiMath
-
Customize and Convert to MathML
-
Customize and Convert to UnicodeMath
-
Customize and Convert to OMML
==== Parse the UnitsML Expression
First, parse the UnitsML expression using Plurimath:
==== Customize and Convert to AsciiMath
You can customize the output by modifying the resulting string after conversion:
asciimath = formula.to_asciimath
# Customization logic (if any)
puts asciimath
# Output: 'h = 6.62607015 xx 10^(-34) "unitsml(kg*m^2*s^(-1))"'
==== Customize and convert to MathML
To customize the MathML output, you can use additional attributes and options:
mathml = formula.to_mathml
# Customization logic (if any)
puts mathml
# Output: "<math xmlns='http://www.w3.org/1998/Math/MathML'><mrow><mi>h</mi><mo>=</mo><mn>6.62607015</mn><mo>×</mo><msup><mn>10</mn><mrow><mo>−</mo><mn>34</mn></mrow></msup><mtext>kg·m²·s⁻¹</mtext></mrow></math>"
==== Customize and convert to UnicodeMath
Similarly, customize the UnicodeMath output:
unicodemath = formula.to_unicodemath
# Customization logic (if any)
puts unicodemath
# Output: 'h = 6.62607015 × 10^(−34) kg·m²·s⁻¹'
==== Customize and convert to OMML
For OMML output, you can customize the XML structure:
omml = formula.to_omml
# Customization logic (if any)
puts omml
# Output: "<m:oMathPara xmlns:m='http://schemas.openxmlformats.org/officeDocument/2006/math'><m:oMath><m:r><m:t>h</m:t></m:r><m:r><m:t>=</m:t></m:r><m:r><m:t>6.62607015</m:t></m:r><m:r><m:t>×</m:t></m:r><m:sSup><m:sSupPr><m:ctrlPr /></m:sSupPr><m:e><m:r><m:t>10</m:t></m:r></m:e><m:sup><m:r><m:t>−34</m:t></m:r></m:sup></m:sSup><m:r><m:t>kg·m²·s⁻¹</m:t></m:r></m:oMath></m:oMathPara>"
=== Complete example code with customization
Here’s the complete code for parsing, converting, and customizing the UnitsML expression between different formats:
require 'plurimath'
# Step 1: Parse the UnitsML Expression
asciimath_unitsml = 'h = 6.62607015 xx 10^(-34) "unitsml(kg*m^2*s^(-1))"'
formula = Plurimath::Math.parse(asciimath_unitsml, :asciimath)
# Step 2: Convert to AsciiMath
asciimath = formula.to_asciimath
# Customization logic for AsciiMath (if needed)
puts "AsciiMath: #{asciimath}"
# Output: 'h = 6.62607015 xx 10^(-34) "unitsml(kg*m^2*s^(-1))"'
# Step 3: Convert to MathML
mathml = formula.to_mathml
# Customization logic for MathML (if needed)
puts "MathML: #{mathml}"
# Output: "<math xmlns='http://www.w3.org/1998/Math/MathML'><mrow><mi>h</mi><mo>=</mo><mn>6.62607015</mn><mo>×</mo><msup><mn>10</mn><mrow><mo>−</mo><mn>34</mn></mrow></msup><mtext>kg·m²·s⁻¹</mtext></mrow></math>"
# Step 4: Convert to UnicodeMath
unicodemath = formula.to_unicodemath
# Customization logic for UnicodeMath (if needed)
puts "UnicodeMath: #{unicodemath}"
# Output: 'h = 6.62607015 × 10^(−34) kg·m²·s⁻¹'
# Step 5: Convert to OMML
omml = formula.to_omml
# Customization logic for OMML (if needed)
puts "OMML: #{omml}"
# Output: "<m:oMathPara xmlns:m='http://schemas.openxmlformats.org/officeDocument/2006/math'><m:oMath><m:r><m:t>h</m:t></m:r><m:r><m:t>=</m:t></m:r><m:r><m:t>6.62607015</m:t></m:r><m:r><m:t>×</m:t></m:r><m:sSup><m:sSupPr><m:ctrlPr /></m:sSupPr><m:e><m:r><m:t>10</m:t></m:r></m:e><m:sup><m:r><m:t>−34</m:t></m:r></m:sup></m:sSup><m:r><m:t>kg·m²·s⁻¹</m:t></m:r></m:oMath></m:oMathPara>"
== Compatibility
=== General
Not every math representation language supports expressing all symbols and
primitives supported by another. For example, the backepsilon symbol is
supported by LaTeX and UnicodeMath, but not AsciiMath.
Plurimath implements a "compatibility wrapper" syntax for each math
representation language to allow all symbols usable by Plurimath to be expressed
in a side-effect-free wrapper in those languages. For example, in AsciiMath, the
"{symbol-name}" is side-effect-free because it is considered a single symbol
as a text string of "{symbol-name}". Plurimath can recognize it, but other
renderers or processors would treat it as a single symbol, which is accurate.
=== Usage of the compatibility wrapper
For a symbol like backepsilon.
In AsciiMath:
"__{backepsilon}"
In LaTeX:
"\\backepsilon"
In UnicodeMath:
"∍"
In MathML:
<mi>∍</mi>
== XML engines
Plurimath supports two XML engines:
-
Ox: (default) A fast XML parser
-
Oga: A pure Ruby XML parser
By default, Ox is used.
To switch to Oga, use the following syntax:
require "plurimath/xml_engines/oga"
Plurimath.xml_engine = Plurimath::XmlEngine::Oga
You can switch back to Ox similarly.
=== Supported content
=== General
Consult the following tables for details on supported symbols and parentheses:
The following table shows the classes that support MathML "intent" encoding:
|
Note
|
To regenerate these files, delete them and run:
bundle exec rake supported_symbols_list.adoc.
|
==== Supported Data Files
== Copyright and license
Copyright Ribose. BSD 2-clause license.