plurimath 0.8.19 → 0.8.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.adoc CHANGED
@@ -12,15 +12,15 @@ remains consistent and accurate regardless of the format it is presented in.
12
12
 
13
13
  Supported math representational languages:
14
14
 
15
- * MathML
16
- * AsciiMath
17
- * UnicodeMath
15
+ * https://www.w3.org/TR/MathML3/[MathML 3] (and https://www.w3.org/TR/mathml4/[MathML 4])
16
+ * https://www.asciimath.org[AsciiMath]
17
+ * http://unicodemath.org[UnicodeMath] (https://www.unicode.org/notes/tn28/UTN28-PlainTextMath-v3.2.pdf[UnicodeMath v3.2])
18
18
  * LaTeX math
19
- * OMML
19
+ * Microsoft Office Math Markup Language (OMML) https://devblogs.microsoft.com/math-in-office/officemath/["OfficeMath"]
20
20
 
21
21
  Supported units representation languages:
22
22
 
23
- * UnitsML
23
+ * https://www.unitsml.org[UnitsML]
24
24
 
25
25
 
26
26
  == Benefits
@@ -68,7 +68,7 @@ $ gem install plurimath
68
68
  Plurimath provides a Command Line Interface (CLI) tool for converting between
69
69
  different math formats.
70
70
 
71
- NOTE: Before continuing please ensure you have the gem installed.
71
+ NOTE: Before continuing, please ensure you have the gem installed.
72
72
 
73
73
  To convert math equations between formats, use the following command followed by
74
74
  appropriate options.
@@ -106,26 +106,33 @@ Splits MathML and OMML output into multiple equations. Boolean option (`true`,
106
106
  `false`).
107
107
 
108
108
 
109
- === Convert an AsciiMath equation to MathML
110
-
109
+ [example]
110
+ .Convert an AsciiMath equation to MathML
111
+ ====
111
112
  [source,bash]
112
113
  ----
113
114
  plurimath convert -i "sqrt(x^2 + y^2)" -f asciimath -t mathml
114
115
  ----
116
+ ====
115
117
 
116
- === Convert an OMML equation to MathML with DisplayStyle
117
-
118
+ [example]
119
+ .Convert an OMML equation to MathML with DisplayStyle
120
+ ====
118
121
  [source,bash]
119
122
  ----
120
123
  plurimath convert -i "equation" -f omml -t mathml -d true
121
124
  ----
125
+ ====
122
126
 
123
- === Convert equations from a file and output as UnicodeMath
124
-
127
+ [example]
128
+ .Convert equations from a file and output as UnicodeMath
129
+ ====
125
130
  [source,bash]
126
131
  ----
127
132
  plurimath convert -e <file_path> -t unicodemath
128
133
  ----
134
+ ====
135
+
129
136
 
130
137
  For more detailed information and additional options, use:
131
138
 
@@ -219,6 +226,7 @@ Once you have a `Plurimath::Math::Formula` object, you can convert it to
219
226
  AsciiMath, MathML, LaTeX, UnicodeMath, or OMML by calling the respective
220
227
  conversion function on the `Formula` object.
221
228
 
229
+
222
230
  ==== AsciiMath output conversion
223
231
 
224
232
  [source,ruby]
@@ -306,6 +314,983 @@ omml = formula.to_omml
306
314
  ----
307
315
 
308
316
 
317
+ == Number formatting
318
+
319
+ === Introduction
320
+
321
+ Number formatting is an essential aspect of presenting numerical data in a way
322
+ that is consistent with regional conventions and user preferences.
323
+ There are myriad number formatting conventions and standards that are
324
+ widely used in various cultures and fields.
325
+
326
+ To address these needs, Plurimath now allows precise control over how
327
+ numbers are presented through its number formatting feature.
328
+
329
+ Plurimath's number formatter allows users to format numbers based on locale,
330
+ ensuring that the formatting adheres to regional conventions and enhances both
331
+ readability and precision.
332
+
333
+ For more details, please refer to the blog post
334
+ link:https://www.plurimath.org/blog/2024-07-09-number-formatter/[**Number formatting support in Plurimath**]
335
+
336
+ === Existing conventions
337
+
338
+ ==== Traditional conventions
339
+
340
+ Different cultures, orthographies and organizations have different conventions
341
+ for formatting numbers.
342
+
343
+ These include practices on how to represent decimal points, digit grouping,
344
+ digit grouping separators, and various mathematical notations.
345
+
346
+ Decimal point symbol::
347
+ In the United States, a full stop (`.`) is used as the decimal point
348
+ separator, while in many European countries, a comma (`,`) is used instead.
349
+
350
+ Digit grouping delimiter::
351
+ In the United States, numbers are often grouped in sets of three digits using
352
+ commas, such as 1,234,567.89. In some European countries, numbers are grouped
353
+ using periods, such as 1.234.567,89, or a thin space, such as 1 234 567,89.
354
+
355
+ Digit grouping practices::
356
+ In Western cultures, numbers ahead of the decimal are often grouped threes.
357
+ Numbers behind the decimal are less standardized, but are often grouped in sets
358
+ of two or three.
359
+
360
+ Mathematical notation::
361
+ In scientific and engineering contexts, numbers are often formatted using
362
+ scientific notation, which expresses numbers as a coefficient multiplied by a
363
+ power of 10. For example, the number 123,456,789 can be expressed in scientific
364
+ notation as 1.23456789 x 10^8.
365
+
366
+
367
+ ==== Standardized conventions
368
+
369
+ Standardization organizations have established standards for number formatting
370
+ to ensure uniformity and accuracy.
371
+
372
+ The https://www.bipm.org/en/measurement-units[SI system (International System of Units)],
373
+ by the https://www.bipm.org[BIPM (Bureau International des Poids et Mesures)],
374
+ specifies rules regarding the decimal point symbol, digit grouping delimiter and
375
+ digit groupings.
376
+
377
+ https://www.iso.org/standard/64973.html[ISO 80000-2], the international standard
378
+ for quantities and units, used by all ISO and IEC standards, also provides
379
+ guidelines for number formatting in a different manner than the SI system.
380
+
381
+
382
+ === Using the number formatter
383
+
384
+ ==== General
385
+
386
+ The number formatting feature is implemented in the `Plurimath::NumberFormatter`
387
+ class, which allows users to re-use a single formatter class for formatting
388
+ multiple numbers.
389
+
390
+ A simple two-step process to format numbers:
391
+
392
+ . Create a new `Plurimath::NumberFormatter` object, passing the desired locale
393
+ and overriding options as arguments.
394
+
395
+ . Call the `localized_number` method on the formatter object, passing the
396
+ number to be formatted as a string and any additional options.
397
+
398
+ The final formatted number is formatted according to the following configuration
399
+ priority, ordered from highest to lowest precedence:
400
+
401
+ . The `format` hash given to `Plurimath::NumberFormatter#localized_number`
402
+ . The `localize_number` string in the creation of a `Plurimath::NumberFormatter`
403
+ . The `localizer_symbols` hash in the creation of a `Plurimath::NumberFormatter`
404
+ . The **default configuration** of the locale of the `Plurimath::NumberFormatter`
405
+
406
+
407
+ [example]
408
+ .Formatting a number to group every 2 digits
409
+ ====
410
+ [source, ruby]
411
+ ----
412
+ formatter = Plurimath::NumberFormatter.new(:en)
413
+ formatted_number = formatter.localized_number(
414
+ "1234567.89",
415
+ format: {
416
+ group_digits: 2,
417
+ # other support options
418
+ }
419
+ )
420
+ # => "1,23,45,67.89"
421
+ ----
422
+ ====
423
+
424
+
425
+ ==== Creating a number formatter
426
+
427
+ The `NumberFormatter` is used to format numbers based on the locale and the
428
+ formatting configuration provided.
429
+
430
+ Syntax:
431
+
432
+ .Syntax for creating a `Plurimath::NumberFormatter` object
433
+ [source,ruby]
434
+ ----
435
+ formatter = Plurimath::NumberFormatter.new(
436
+ <locale-symbol>, # mandatory <1>
437
+ localize_number: <localize-string>, # optional <2>
438
+ localizer_symbols: <format-hash>, # optional <3>
439
+ precision: <precision-number>, # optional <4>
440
+ )
441
+ ----
442
+ <1> Locale to be used for number formatting.
443
+ <2> String pattern to define the number formatting.
444
+ <3> Hash containing relevant options for number formatting.
445
+ <4> Number of decimal places to round.
446
+
447
+ Where,
448
+
449
+ `<locale-symbol>`:: (optional, default `:en`) The locale to be used for number formatting.
450
+ Accepted values are listed in the
451
+ `Plurimath::Formatter::SupportedLocales::LOCALES` constant.
452
+
453
+ `localize_number: <localize-string>`:: (optional, default `nil`) A string containing a specific
454
+ sequence of characters that defines the number formatting. Use either
455
+ `localize_number` or `localizer_symbols` to set the number formatting pattern.
456
+ +
457
+ See <<localize_number,`localize_number`>> for details.
458
+
459
+ `localizer_symbols: <format-hash>`:: (optional, default `{}`) A hash containing the relevant
460
+ options for number formatting. Use either `localize_number` or
461
+ `localizer_symbols` to set the number formatting pattern.
462
+ +
463
+ See <<localizer_symbols,format options hash>> for details.
464
+
465
+ `precision: <precision-number>`:: (optional, default `nil`)
466
+ Number of decimal places to round. Accepts an integer value.
467
+ +
468
+ .Specifying a precision of 6 digits
469
+ [example]
470
+ ====
471
+ "32232.232" => "32232.232000"
472
+ ====
473
+
474
+
475
+ .Creating a `Plurimath::NumberFormatter` object using the `:en` locale
476
+ [example]
477
+ ====
478
+ [source,ruby]
479
+ ----
480
+ formatter = Plurimath::NumberFormatter.new(:en)
481
+ # => #<Plurimath::NumberFormatter:0x00007f8b1b8b3b10 @locale=:en>
482
+ ----
483
+ ====
484
+
485
+
486
+ ==== Configuring the number formatter
487
+
488
+ The `Plurimath::NumberFormatter` object can be configured using either the
489
+ `localize_number` or `localizer_symbols` options.
490
+
491
+
492
+ [[localizer_symbols]]
493
+ ===== Via "format options" using `localizer_symbols`
494
+
495
+ The `localizer_symbols` key is used to set the number formatting pattern
496
+ through a Hash object containing specified options.
497
+
498
+ This Hash object is called the "format options Hash".
499
+
500
+ Available options are explained below.
501
+
502
+ NOTE: Each option takes an input of a certain specified type (`String` or
503
+ `Numeric`). Using an input type other than the specified type will result in
504
+ errors or incorrect output.
505
+
506
+ The values passed to `localizer_symbols` persist as long as the initialized
507
+ `NumberFormatter` instance is accessible. It is therefore useful in scenarios
508
+ when configuration will be static or changes are not required very often.
509
+
510
+
511
+ `decimal`:: (`String` value)
512
+ Symbol to use for the decimal point. Accepts a character.
513
+ +
514
+ .Using the ',' "comma" symbol as the decimal point
515
+ [example]
516
+ ====
517
+ "32232.232" => "32232,232"
518
+ ====
519
+ +
520
+ .Using the '.' "full stop" symbol as the decimal point
521
+ [example]
522
+ ====
523
+ "32232.232" => "32232.232"
524
+ ====
525
+
526
+ `digit_count`:: (`Numeric` value)
527
+ Total number of digits to render, with the value truncated.
528
+ Accepts an integer value.
529
+ +
530
+ .Specifying a total of 6 digits in rendering the number
531
+ [example]
532
+ ====
533
+ "32232.232" => "32232.2"
534
+ ====
535
+
536
+
537
+ `group`:: (`String` value)
538
+ Delimiter to use between groups of digits specified in `group_digits`. Accepts a
539
+ character. (default is not to group digits.)
540
+ +
541
+ .Using the unicode thin space (THIN SPACE, U+2009) as the grouping delimiter
542
+ [example]
543
+ ====
544
+ "32232.232" => "32 232.232"
545
+ ====
546
+
547
+
548
+ `group_digits`:: (`Numeric` value)
549
+ Number of digits to group the integer portion, grouping from right to left.
550
+ Accepts an integer value. (default is 3 in most locales.)
551
+ +
552
+ .Using the unicode thin space as the grouping delimiter, and grouping every 2 digits
553
+ [example]
554
+ ====
555
+ "32232.232" => "3 22 32.232"
556
+ ====
557
+
558
+ `fraction_group`:: (`String` value)
559
+ Delimiter to use between groups of fractional digits specified in
560
+ `fraction_group_digits`. Accepts a character.
561
+ +
562
+ .Using the unicode thin space as the fraction grouping delimiter
563
+ [example]
564
+ ====
565
+ "32232.232131" => "32232.232 131".
566
+ ====
567
+
568
+ `fraction_group_digits`:: (`Numeric` value)
569
+ Number of digits in each group of fractional digits, grouping from left to
570
+ right. Accepts an integer value.
571
+ +
572
+ .Using the unicode thin space as the fraction grouping delimiter, and grouping every 2 fraction digits
573
+ [example]
574
+ ====
575
+ "32232.232131" => "32232.23 21 31"
576
+ ====
577
+
578
+ `significant`:: (`Numeric` value)
579
+ Sets the number of significant digits to show, with the value rounded.
580
+
581
+ `notation`:: (`String` value)
582
+ Specifies the mathematical notation to be used. Accepts the following values.
583
+
584
+ `e`::: Use exponent notation.
585
+ +
586
+ .Example of using exponent notation
587
+ [example]
588
+ ====
589
+ 1.23456789e8
590
+ ====
591
+
592
+ `scientific`::: Use scientific notation.
593
+ +
594
+ .Example of using scientific notation
595
+ [example]
596
+ ====
597
+ 1.23456789 × 10⁸
598
+ ====
599
+
600
+ `engineering`::: Use engineering notation, where the exponent of ten is always
601
+ selected to be divisible by three to match the common metric prefixes.
602
+ +
603
+ .Example of using engineering notation
604
+ [example]
605
+ ====
606
+ 123.456789 × 10⁶
607
+ ====
608
+
609
+ `e`:: (`String` value)
610
+ Symbol to use for exponents in E notation (default value `E`). (used in the
611
+ mode: `e` only).
612
+ +
613
+ .Using the lowercase 'e' symbol as the exponent symbol
614
+ [example]
615
+ ====
616
+ ----
617
+ 3.2232232e5
618
+ ----
619
+ ====
620
+
621
+ `times`:: (`String` value)
622
+ Symbol to use for multiplication where required by the notation (used in the
623
+ modes: `scientific` and `engineering`). Defaults to `×`.
624
+ +
625
+ .Using the '·' "middle dot" symbol as the multiplication symbol
626
+ [example]
627
+ ====
628
+ ----
629
+ 32.232232 · 104
630
+ ----
631
+ ====
632
+
633
+ `exponent_sign`:: (`String` value)
634
+ Whether to use a plus sign to indicate positive exponents, in exponent-based
635
+ notation (used in the modes: `e`, `scientific`, `engineering`). Legal values
636
+ are:
637
+
638
+ `plus`::: The `+` symbol is used.
639
+ +
640
+ .Using the plus sign to indicate positive exponents
641
+ [example]
642
+ ====
643
+ ----
644
+ 32.232232 × 10⁺⁴
645
+ ----
646
+ ====
647
+
648
+ These options are to be grouped under a single Hash object.
649
+
650
+ .Format options Hash for `localizer_symbols`
651
+ [source,ruby]
652
+ ----
653
+ {
654
+ decimal: ",", # replaces the decimal point with the passed string
655
+ group_digits: 2, # groups integer part into passed integer
656
+ group: "'", # places the string between grouped parts of the integer
657
+ fraction_group_digits: 3, # groups fraction part into passed integer
658
+ fraction_group: ",", # places the string between grouped parts of the fraction
659
+ }
660
+ ----
661
+
662
+
663
+ [[localize_number]]
664
+ ===== Via the `localize_number` option
665
+
666
+ The `localize_number` option accepts a formatting pattern specified as a string,
667
+ using the hash symbol `#` to represent a digit placeholder.
668
+
669
+ The `localize_number` option is useful when you want to format numbers in a
670
+ specific way that is not covered by the `localizer_symbols` option.
671
+ // TODO When is that?
672
+
673
+ A sample value of `\#,\##0.\### \###` is interpreted as the following
674
+ configuration in the <<localizer_symbols,format options hash>>:
675
+
676
+ `group`::
677
+ This parameter is set to the very first non-hash character before 0.
678
+ If there is no non-hash character before `#`+`0`, then the default group
679
+ delimiter will be nil.
680
+ +
681
+ In this example, it is `,`.
682
+
683
+ `group_digits`::
684
+ This parameter is set to the "count of all hashes + 1" (including the zero).
685
+ Minimum 1 hash symbol is required.
686
+ +
687
+ In this example, `##0` sets the value to 3.
688
+
689
+ `decimal`::
690
+ This parameter is set to the character immediately to the right of `0`.
691
+ This is mandatory.
692
+ +
693
+ In this example, it is `.`.
694
+
695
+ `fraction_group_digits`::
696
+ This parameter is set to "count of all the hashes right next to decimal".
697
+ Minimum 1 hash symbol is required.
698
+ +
699
+ In our example, '\###' sets the value to 3.
700
+
701
+ `fraction_group`::
702
+ This parameter is set to the first character after `fraction_group_digits`.
703
+ If there is no non-hash character after `fraction_group_digits`, it is
704
+ set to nil.
705
+ +
706
+ In this example it is `' '` (a space).
707
+
708
+
709
+ .Formatting a number using the `localize_number` option
710
+ [example]
711
+ ====
712
+ [source,ruby]
713
+ ----
714
+ formatter = Plurimath::NumberFormatter.new(:en, localize_number: "#,##0.### ###")
715
+ formatter.localized_number("1234.56789")
716
+ # => "1,234.568 9"
717
+ ----
718
+ ====
719
+
720
+
721
+
722
+ ==== Formatting a number using `NumberFormatter`
723
+
724
+ The `localized_number` method is used to format a number given a
725
+ `NumberFormatter` instance.
726
+
727
+ Syntax:
728
+
729
+ .Syntax for `localized_number`
730
+ [source,ruby]
731
+ ----
732
+ formatter.localized_number(
733
+ <number>, # mandatory <1>
734
+ locale: <locale-symbol>, # optional <2>
735
+ precision: <precision-number>, # optional <3>
736
+ format: <format-hash> # optional <4>
737
+ )
738
+ ----
739
+ <1> The number to be formatted.
740
+ <2> The locale to be used for number formatting.
741
+ <3> The number of decimal places to round the number to.
742
+ <4> Hash containing the relevant options for number formatting.
743
+
744
+ Where,
745
+
746
+ `<number>`:: (mandatory) The number to be formatted. Value should be a Numeric,
747
+ i.e. Integer, Float, or BigDecimal. If not provided, an `ArgumentError` will be
748
+ raised.
749
+
750
+ `locale: <locale-symbol>`:: (optional) The locale to be used for number formatting.
751
+ Value is a symbol.
752
+ Overrides the locale set during the creation of the `NumberFormatter` object. If
753
+ not provided, the locale of the `NumberFormatter` instance will be used.
754
+
755
+ `precision: <precision-number>`:: (optional) The number of decimal places to round the
756
+ number to. If not provided, the precision of the `NumberFormatter` instance will
757
+ be used.
758
+
759
+ `format: <format-hash>`:: (optional, default `{}`) A Hash containing the relevant
760
+ options for number formatting, that overrides the `localizer_symbols`
761
+ configuration of the `NumberFormatter`.
762
+ Takes a Hash in the form of the <<localizer_symbols,format options hash>>.
763
+
764
+ `precision: <precision-number>`::
765
+ Number of decimal places to round. Accepts an integer value.
766
+ +
767
+ .Specifying a precision of 6 digits
768
+ [example]
769
+ ====
770
+ "32232.232" => "32232.232000"
771
+ ====
772
+
773
+
774
+ .Formatting a number using the `localized_number` method for the English locale
775
+ [example]
776
+ ====
777
+ [source,ruby]
778
+ ----
779
+ formatter = Plurimath::NumberFormatter.new(:en)
780
+ formatter.localized_number("1234.56789")
781
+ # => "1,234.56789"
782
+ ----
783
+ ====
784
+
785
+ .Formatting a number using the `localized_number` method for the French locale
786
+ [example]
787
+ ====
788
+ [source,ruby]
789
+ ----
790
+ formatter = Plurimath::NumberFormatter.new(:fr)
791
+ formatter.localized_number("1234.56789")
792
+ # => "1 234,56789"
793
+ ----
794
+ ====
795
+
796
+
797
+ The locale and precision set in the `NumberFormatter` can be overridden by
798
+ passing the `locale` and `precision` options to the `localized_number` method.
799
+
800
+ .Overriding locale and precision in `localized_number`
801
+ [example]
802
+ ====
803
+ [source,ruby]
804
+ ----
805
+ formatter = Plurimath::NumberFormatter.new(:en)
806
+ formatter.localized_number("1234.56789", locale: :de, precision: 6)
807
+ # => "1.234,567890"
808
+ ----
809
+ ====
810
+
811
+
812
+ ==== Overriding specified `NumberFormatter` options using the `format` key
813
+
814
+ The `format` option is used to override the specified configuration of the
815
+ `NumberFormatter` object.
816
+
817
+ It expects a Hash in the form of the <<localizer_symbols,format options hash>>.
818
+
819
+ .Using the `format` key to override the decimal point symbol with `x`
820
+ [example]
821
+ ====
822
+ [source,ruby]
823
+ ----
824
+ formatter = Plurimath::NumberFormatter.new(:en)
825
+ formatter.localized_number(
826
+ "1234.56789",
827
+ format: {
828
+ decimal: "x",
829
+ # other supported options
830
+ }
831
+ )
832
+ # => "1,234x56789"
833
+ ----
834
+ ====
835
+
836
+ .Using the `format` key to group numbers in 2 digits
837
+ [example]
838
+ ====
839
+ [source,ruby]
840
+ ----
841
+ formatter = Plurimath::NumberFormatter.new(:en)
842
+ formatter.localized_number(
843
+ "1234567.89",
844
+ format: {
845
+ group_digits: 2,
846
+ # other supported options
847
+ }
848
+ )
849
+ # => "1,23,45,67.89"
850
+ ----
851
+ ====
852
+
853
+ .Formatting a number using the `format` key in the `localized_number` method
854
+ [example]
855
+ ====
856
+ [source,ruby]
857
+ ----
858
+ formatter = Plurimath::NumberFormatter.new(:en)
859
+ formatter.localized_number(
860
+ "1234.56789",
861
+ format: {
862
+ decimal: "x",
863
+ group_digits: 2,
864
+ group: "'",
865
+ fraction_group_digits: 3,
866
+ fraction_group: ","
867
+ }
868
+ )
869
+ # => "12'34x567,89"
870
+ ----
871
+ ====
872
+
873
+
874
+ === Supported locales
875
+
876
+ Plurimath supports the following locales for number formatting. The locale
877
+ values are sourced from the https://cldr.unicode.org[Unicode CLDR] repository.
878
+
879
+ The list of locales and their values are given in the file
880
+ `lib/plurimath/formatter/supported_locales.rb`.
881
+
882
+ The locales and their values can be obtained through the following code.
883
+
884
+ .Getting the supported locales and their default values
885
+ [source,ruby]
886
+ ----
887
+ Plurimath::Formatter::SupportedLocales::LOCALES[:en]
888
+ # => { decimal: ".", group: "," }
889
+ ----
890
+
891
+ .Locales supported by Plurimath (delimiters wrapped in double quotes)
892
+ |===
893
+ | Locale | Decimal delimiter | Group delimiter
894
+
895
+ | `sr-Cyrl-ME` | `","` | `"."`
896
+ | `sr-Latn-ME` | `","` | `"."`
897
+ | `zh-Hant` | `"."` | `","`
898
+ | `en-001` | `"."` | `","`
899
+ | `en-150` | `"."` | `","`
900
+ | `pt-PT` | `","` | `" "`
901
+ | `nl-BE` | `","` | `"."`
902
+ | `it-CH` | `"."` | `"’"`
903
+ | `fr-BE` | `","` | `" "`
904
+ | `fr-CA` | `","` | `" "`
905
+ | `fr-CH` | `","` | `" "`
906
+ | `de-AT` | `","` | `" "`
907
+ | `de-CH` | `"."` | `"’"`
908
+ | `en-AU` | `"."` | `","`
909
+ | `en-CA` | `"."` | `","`
910
+ | `en-GB` | `"."` | `","`
911
+ | `en-IE` | `"."` | `","`
912
+ | `en-IN` | `"."` | `","`
913
+ | `en-NZ` | `"."` | `","`
914
+ | `en-SG` | `"."` | `","`
915
+ | `en-US` | `"."` | `","`
916
+ | `en-ZA` | `"."` | `","`
917
+ | `es-419` | `"."` | `","`
918
+ | `es-AR` | `","` | `"."`
919
+ | `es-CO` | `","` | `"."`
920
+ | `es-MX` | `"."` | `","`
921
+ | `es-US` | `"."` | `","`
922
+ | `fil` | `"."` | `","`
923
+ | `af` | `","` | `" "`
924
+ | `ar` | `"٫"` | `"٬"`
925
+ | `az` | `","` | `"."`
926
+ | `be` | `","` | `" "`
927
+ | `bg` | `","` | `" "`
928
+ | `bn` | `"."` | `","`
929
+ | `bo` | `"."` | `","`
930
+ | `bs` | `","` | `"."`
931
+ | `ca` | `","` | `"."`
932
+ | `cs` | `","` | `" "`
933
+ | `cy` | `"."` | `","`
934
+ | `da` | `","` | `"."`
935
+ | `de` | `","` | `"."`
936
+ | `el` | `","` | `"."`
937
+ | `en` | `"."` | `","`
938
+ | `eo` | `","` | `" "`
939
+ | `es` | `","` | `"."`
940
+ | `et` | `","` | `" "`
941
+ | `eu` | `","` | `"."`
942
+ | `fa` | `"٫"` | `"٬"`
943
+ | `fi` | `","` | `" "`
944
+ | `fr` | `","` | `" "`
945
+ | `ga` | `"."` | `","`
946
+ | `gl` | `","` | `"."`
947
+ | `gu` | `"."` | `","`
948
+ | `he` | `"."` | `","`
949
+ | `hi` | `"."` | `","`
950
+ | `hr` | `","` | `"."`
951
+ | `hu` | `","` | `" "`
952
+ | `hy` | `","` | `" "`
953
+ | `id` | `","` | `"."`
954
+ | `is` | `","` | `"."`
955
+ | `it` | `","` | `"."`
956
+ | `ja` | `"."` | `","`
957
+ | `ka` | `","` | `" "`
958
+ | `kk` | `","` | `" "`
959
+ | `km` | `","` | `"."`
960
+ | `kn` | `"."` | `","`
961
+ | `ko` | `"."` | `","`
962
+ | `lo` | `","` | `"."`
963
+ | `lt` | `","` | `" "`
964
+ | `lv` | `","` | `" "`
965
+ | `mk` | `","` | `"."`
966
+ | `mr` | `"."` | `","`
967
+ | `ms` | `"."` | `","`
968
+ | `mt` | `"."` | `","`
969
+ | `my` | `"."` | `","`
970
+ | `nb` | `","` | `" "`
971
+ | `nl` | `","` | `"."`
972
+ | `pl` | `","` | `" "`
973
+ | `pt` | `","` | `"."`
974
+ | `ro` | `","` | `"."`
975
+ | `ru` | `","` | `" "`
976
+ | `sk` | `","` | `" "`
977
+ | `sl` | `","` | `"."`
978
+ | `sq` | `","` | `" "`
979
+ | `sr` | `","` | `"."`
980
+ | `sv` | `","` | `" "`
981
+ | `sw` | `"."` | `","`
982
+ | `ta` | `"."` | `","`
983
+ | `th` | `"."` | `","`
984
+ | `tr` | `","` | `"."`
985
+ | `uk` | `","` | `" "`
986
+ | `ur` | `"."` | `","`
987
+ | `vi` | `","` | `"."`
988
+ | `xh` | `"."` | `" "`
989
+ | `zh` | `"."` | `","`
990
+ | `zu` | `"."` | `","`
991
+
992
+ |===
993
+
994
+
995
+
996
+
997
+ === Formatting numbers in a formula
998
+
999
+ ==== General
1000
+
1001
+ Plurimath supports number formatting within formulas for all supported
1002
+ languages. This feature allows you to apply custom number formatting when
1003
+ converting formulas to any of the supported format.
1004
+
1005
+ NOTE: For details, check out our blog post:
1006
+ link:https://www.plurimath.org/blog/2024-09-16-formula-number-formatting/[**Number formatting now supported in formulas across all math representation languages**].
1007
+
1008
+
1009
+ The steps to format numbers within a formula are:
1010
+
1011
+ . Create a number formatter that can be configured;
1012
+
1013
+ . Apply the number formatter to a formula through the `Formula.to_{format}`
1014
+ method using a `formatter` option, which serializes the formula into an math
1015
+ representation language.
1016
+
1017
+ The formatter should be an instance of `Plurimath::NumberFormatter` or a custom
1018
+ formatter derived from `Plurimath::Formatter::Standard`.
1019
+
1020
+ The quick example below demonstrates how to format a number in a formula.
1021
+
1022
+ [example]
1023
+ .Applying number formatting to a formula in LaTeX math
1024
+ ====
1025
+ The following code applies number formatting to a LaTeX math formula.
1026
+
1027
+ [source,ruby]
1028
+ ----
1029
+ formula = Plurimath::Math.parse('\sum_{i=1}^{10000} i^2121221', :latex) <1>
1030
+ formatter = Plurimath::Formatter::Standard.new <2>
1031
+ formula.to_latex(formatter: formatter) <3>
1032
+ # => '\sum_{i = 1}^{10,000} i^{2,121,221}'
1033
+ ----
1034
+ <1> The formula is parsed into a `Formula` object using the
1035
+ `Plurimath::Math.parse` method.
1036
+ <2> A `Plurimath::Formatter` is created.
1037
+ <3> The `Formula.to_latex` method is called with the `formatter` option to
1038
+ format the formula.
1039
+ ====
1040
+
1041
+ [example]
1042
+ .Applying number formatting to an AsciiMath formula in MathML
1043
+ ====
1044
+ [source,ruby]
1045
+ ----
1046
+ formula = Plurimath::Math.parse("e^(i*pi) + 1.1 = 0.2", :asciimath)
1047
+ custom_formatter = Plurimath::Formatter::Standard.new(
1048
+ locale: :fr,
1049
+ options: { number_sign: :plus },
1050
+ precision: 3
1051
+ )
1052
+ print formula.to_mathml(formatter: custom_formatter)
1053
+ # <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
1054
+ # <mstyle displaystyle="true">
1055
+ # <msup>
1056
+ # <mi>e</mi>
1057
+ # <mrow>
1058
+ # <mi>i</mi>
1059
+ # <mo>&#x22c5;</mo>
1060
+ # <mi>&#x3c0;</mi>
1061
+ # </mrow>
1062
+ # </msup>
1063
+ # <mo>+</mo>
1064
+ # <mn>+1.100</mn>
1065
+ # <mo>=</mo>
1066
+ # <mn>+0.200</mn>
1067
+ # </mstyle>
1068
+ # </math>
1069
+ ----
1070
+ ====
1071
+
1072
+
1073
+ ==== Defining a number formatter
1074
+
1075
+ A "number formatter" is a class that formats numbers in a specific way. It
1076
+ contains the configuration for formatting numbers, such as the number of digits
1077
+ in a group, the decimal separator, and the group separator.
1078
+
1079
+ Plurimath offers a standard formatter class called
1080
+ `Plurimath::Formatter::Standard` that includes a comprehensive
1081
+ link:#standard_configuration[standard configuration].
1082
+
1083
+ .Creating a standard `Plurimath::Formatter::Standard` object
1084
+ [source,ruby]
1085
+ ----
1086
+ > formatter = Plurimath::Formatter::Standard.new <1>
1087
+ ----
1088
+ <1> Creates a `Plurimath::Formatter` object that uses standard configuration.
1089
+
1090
+
1091
+ The number formatting configuration can be changed in these ways:
1092
+
1093
+ . Pass options to the `Plurimath::Formatter::Standard` class initializer
1094
+ (with options explained in the
1095
+ https://www.plurimath.org/blog/2024-07-09-number-formatter[number formatter blog post]).
1096
+
1097
+ . Create a custom formatter inheriting from the `Plurimath::Formatter::Standard` class.
1098
+
1099
+
1100
+
1101
+ ==== Changing number formatting configuration
1102
+
1103
+ The typical way to change the number formatting configuration is to create a
1104
+ `Plurimath::Formatter::Standard` object with the desired configuration options.
1105
+
1106
+ There are two types of number formatting configuration to change:
1107
+
1108
+ . Arguments passed to the `Plurimath::Formatter::Standard` class initializer.
1109
+
1110
+ . Overriding options through the `options` argument.
1111
+
1112
+ The arguments are:
1113
+
1114
+ `locale`:: (default: `:en` for English) a symbol or string value. The supported
1115
+ locales are listed in the link:/blog/2024-07-09-number-formatter[number formatter blog post].
1116
+
1117
+ `options`:: (default: empty) a hash of options (`localizer_symbols`). The options
1118
+ are listed in the link:/blog/2024-07-09-number-formatter[number formatter blog post].
1119
+
1120
+ `format_string`:: (default: `nil`, disabled) a string value (localize_number)
1121
+
1122
+ `precision`:: (default: `nil`, disabled) an integer value.
1123
+
1124
+
1125
+ [example]
1126
+ .Passing arguments to the `Plurimath::Formatter::Standard` class initializer
1127
+ ====
1128
+ [source,ruby]
1129
+ ----
1130
+ > options = {
1131
+ fraction_group_digits: 2,
1132
+ fraction_group: ".",
1133
+ group_digits: 2,
1134
+ decimal: ";",
1135
+ group: ",",
1136
+ }
1137
+
1138
+ > formatter = Plurimath::Formatter::Standard.new(locale: :hy, options: options, precision: 2)
1139
+ # format_string: <string value> if provided
1140
+
1141
+ > Plurimath::Math.parse('2121221.3434', :latex).to_latex(formatter: formatter)
1142
+ # => '2,12,12,21;34'
1143
+ ----
1144
+
1145
+ The `precision = 2` option in the initializer causes the formatted value to have
1146
+ decimal places truncated from 4 to 2.
1147
+ ====
1148
+
1149
+
1150
+
1151
+ ==== Creating a custom formatter
1152
+
1153
+ In cases where the standard formatter's available options do not meet the needs
1154
+ for number presentation, a custom formatter can be created to apply new
1155
+ mechanisms of formatting numbers.
1156
+
1157
+ The custom formatter is to be subclassed from `Plurimath::Formatter::Standard`.
1158
+
1159
+ .Creating a custom formatter
1160
+ [source,ruby]
1161
+ ----
1162
+ class MyCustomFormatter < Plurimath::Formatter::Standard <1>
1163
+ def initialize(locale:, precision:, options:, format_string:) <2>
1164
+ super
1165
+ end
1166
+ end
1167
+ ----
1168
+ <1> The custom formatter class inherits from `Plurimath::Formatter::Standard`.
1169
+ <2> The arguments can be overridden in the `initialize` method.
1170
+
1171
+ The default options of the custom formatter are set using the
1172
+ `set_default_options` method.
1173
+
1174
+ .Syntax to override the `set_default_options` method
1175
+ [source,ruby]
1176
+ ----
1177
+ class MyCustomFormatter < Plurimath::Formatter::Standard
1178
+ def initialize(locale:, precision:, options:, format_string:)
1179
+ super
1180
+ end
1181
+
1182
+ def set_default_options(options = {}) <1>
1183
+ options = {
1184
+ fraction_group_digits: 2,
1185
+ fraction_group: ".",
1186
+ ...
1187
+ }
1188
+ end
1189
+ end
1190
+ ----
1191
+ <1> The `set_default_options` method is overridden to set the default options.
1192
+ The shown options are ones inherited from the `Plurimath::Formatter::Standard`
1193
+ class, but additional ones understood by the class can be set.
1194
+
1195
+ It is used in the following manner.
1196
+
1197
+ .Creating a `CustomFormatter` object and using it to format numbers in a formula
1198
+ [example]
1199
+ ====
1200
+ [source,ruby]
1201
+ ----
1202
+ class MyCustomFormatter < Plurimath::Formatter::Standard
1203
+ def initialize(locale: :fr)
1204
+ super
1205
+ end
1206
+
1207
+ def set_default_options(options = {})
1208
+ {
1209
+ fraction_group_digits: 2,
1210
+ fraction_group: ".",
1211
+ group_digits: 2,
1212
+ decimal: ";",
1213
+ group: ",",
1214
+ ...
1215
+ }
1216
+ end
1217
+ end
1218
+
1219
+ > formula = Plurimath::Math.parse('\sum_{i=1}^{1000.001} i^2121221.3434', :latex)
1220
+ # => Plurimath::Math::Formula...
1221
+ > formula.to_latex(formatter: formatter)
1222
+ # => '\sum_{i = 1}^{10,00;00.1} i^{2,12,12,21;34.34}'
1223
+ > formula.to_asciimath(formatter: formatter)
1224
+ # => 'sum_(i = 1)^(10,00;00.1) i^(2,12,12,21;34.34)'
1225
+ ----
1226
+ ====
1227
+
1228
+
1229
+ [[standard_configuration]]
1230
+ === Default number formatting configuration
1231
+
1232
+ The default configuration for formatting numbers is as follows, set in the
1233
+ `Plurimath::Formatter::Standard` class.
1234
+
1235
+ |===
1236
+ |Option key |Description |Value
1237
+
1238
+ |`locale`
1239
+ |The locale used for number formatting
1240
+ |`:en`
1241
+
1242
+ |`fraction_group_digits`
1243
+ |The number of digits in each group of the fraction part
1244
+ |`3`
1245
+
1246
+ |`exponent_sign`
1247
+ |The sign used for the exponent part of the number
1248
+ |`"plus"`
1249
+
1250
+ |`fraction_group`
1251
+ |The character used to separate groups of digits in the fraction part
1252
+ |`"'"`
1253
+
1254
+ |`notation`
1255
+ |The notation used for the number formatting
1256
+ |`:basic`
1257
+
1258
+ |`group_digits`
1259
+ |The number of digits in each group of the integer part
1260
+ |`3`
1261
+
1262
+ |`significant`
1263
+ |The number of significant digits to display
1264
+ |`0`
1265
+
1266
+ |`digit_count`
1267
+ |The number of digits to display
1268
+ |`0`
1269
+
1270
+ |`precision`
1271
+ |The number of decimal places to display
1272
+ |`0`
1273
+
1274
+ |`decimal`
1275
+ |The character used as the decimal separator
1276
+ |`"."`
1277
+
1278
+ |`group`
1279
+ |The character used to separate groups of digits in the integer part
1280
+ |`","`
1281
+
1282
+ |`times`
1283
+ |The character used for multiplication
1284
+ |`"x"`
1285
+
1286
+ |`e`
1287
+ |The character used for exponentiation
1288
+ |`"e"`
1289
+
1290
+ |===
1291
+
1292
+
1293
+
309
1294
 
310
1295
  == Math parse trees
311
1296