plurimath 0.9.11 → 0.10.1

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.
Files changed (194) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +3 -0
  3. data/.rspec-opal +1 -1
  4. data/.rubocop.yml +9 -5
  5. data/.rubocop_todo.yml +0 -0
  6. data/Gemfile +7 -5
  7. data/README.adoc +131 -11
  8. data/Rakefile +11 -0
  9. data/lib/plurimath/asciimath/parse.rb +0 -1
  10. data/lib/plurimath/asciimath/parser.rb +0 -3
  11. data/lib/plurimath/asciimath.rb +5 -1
  12. data/lib/plurimath/cli.rb +1 -1
  13. data/lib/plurimath/errors/formatter/unsupported_base.rb +19 -0
  14. data/lib/plurimath/errors/invalid_type_error.rb +19 -0
  15. data/lib/plurimath/errors/parse_error.rb +39 -0
  16. data/lib/plurimath/errors.rb +1 -0
  17. data/lib/plurimath/formatter/number_formatter.rb +52 -10
  18. data/lib/plurimath/formatter/numbers/base.rb +37 -0
  19. data/lib/plurimath/formatter/numbers/fraction.rb +146 -35
  20. data/lib/plurimath/formatter/numbers/integer.rb +16 -12
  21. data/lib/plurimath/formatter/numbers/significant.rb +84 -52
  22. data/lib/plurimath/formatter/numbers.rb +12 -0
  23. data/lib/plurimath/formatter/standard.rb +0 -1
  24. data/lib/plurimath/formatter.rb +6 -7
  25. data/lib/plurimath/html/parse.rb +0 -1
  26. data/lib/plurimath/html/parser.rb +0 -3
  27. data/lib/plurimath/html.rb +5 -0
  28. data/lib/plurimath/latex/parse.rb +0 -1
  29. data/lib/plurimath/latex/parser.rb +2 -5
  30. data/lib/plurimath/latex.rb +5 -0
  31. data/lib/plurimath/math/core.rb +7 -2
  32. data/lib/plurimath/math/formula/mrow.rb +2 -41
  33. data/lib/plurimath/math/formula/mstyle.rb +6 -0
  34. data/lib/plurimath/math/formula.rb +54 -298
  35. data/lib/plurimath/math/function/abs.rb +0 -1
  36. data/lib/plurimath/math/function/arccos.rb +0 -1
  37. data/lib/plurimath/math/function/arcsin.rb +0 -1
  38. data/lib/plurimath/math/function/arctan.rb +0 -1
  39. data/lib/plurimath/math/function/arg.rb +0 -1
  40. data/lib/plurimath/math/function/bar.rb +0 -1
  41. data/lib/plurimath/math/function/base.rb +0 -3
  42. data/lib/plurimath/math/function/binary_function.rb +5 -3
  43. data/lib/plurimath/math/function/cancel.rb +0 -1
  44. data/lib/plurimath/math/function/ceil.rb +0 -1
  45. data/lib/plurimath/math/function/color.rb +0 -1
  46. data/lib/plurimath/math/function/cos.rb +0 -1
  47. data/lib/plurimath/math/function/cosh.rb +0 -1
  48. data/lib/plurimath/math/function/cot.rb +0 -1
  49. data/lib/plurimath/math/function/coth.rb +0 -1
  50. data/lib/plurimath/math/function/csc.rb +0 -1
  51. data/lib/plurimath/math/function/csch.rb +0 -1
  52. data/lib/plurimath/math/function/ddot.rb +0 -1
  53. data/lib/plurimath/math/function/deg.rb +0 -1
  54. data/lib/plurimath/math/function/det.rb +0 -1
  55. data/lib/plurimath/math/function/dim.rb +0 -1
  56. data/lib/plurimath/math/function/dot.rb +0 -1
  57. data/lib/plurimath/math/function/exp.rb +0 -1
  58. data/lib/plurimath/math/function/fenced.rb +1 -197
  59. data/lib/plurimath/math/function/floor.rb +0 -1
  60. data/lib/plurimath/math/function/font_style/bold-fraktur.rb +0 -1
  61. data/lib/plurimath/math/function/font_style/bold-italic.rb +0 -1
  62. data/lib/plurimath/math/function/font_style/bold-sans-serif.rb +0 -1
  63. data/lib/plurimath/math/function/font_style/bold-script.rb +0 -1
  64. data/lib/plurimath/math/function/font_style/bold.rb +0 -1
  65. data/lib/plurimath/math/function/font_style/double_struck.rb +0 -1
  66. data/lib/plurimath/math/function/font_style/fraktur.rb +0 -1
  67. data/lib/plurimath/math/function/font_style/italic.rb +0 -1
  68. data/lib/plurimath/math/function/font_style/monospace.rb +0 -1
  69. data/lib/plurimath/math/function/font_style/normal.rb +0 -1
  70. data/lib/plurimath/math/function/font_style/sans-serif-bold-italic.rb +0 -1
  71. data/lib/plurimath/math/function/font_style/sans-serif-italic.rb +0 -1
  72. data/lib/plurimath/math/function/font_style/sans-serif.rb +0 -1
  73. data/lib/plurimath/math/function/font_style/script.rb +0 -1
  74. data/lib/plurimath/math/function/font_style.rb +97 -7
  75. data/lib/plurimath/math/function/frac.rb +0 -3
  76. data/lib/plurimath/math/function/gcd.rb +0 -1
  77. data/lib/plurimath/math/function/glb.rb +0 -1
  78. data/lib/plurimath/math/function/hat.rb +0 -1
  79. data/lib/plurimath/math/function/hom.rb +0 -1
  80. data/lib/plurimath/math/function/inf.rb +1 -2
  81. data/lib/plurimath/math/function/int.rb +2 -3
  82. data/lib/plurimath/math/function/intent.rb +0 -1
  83. data/lib/plurimath/math/function/ker.rb +0 -1
  84. data/lib/plurimath/math/function/lcm.rb +0 -1
  85. data/lib/plurimath/math/function/left.rb +0 -1
  86. data/lib/plurimath/math/function/lg.rb +0 -1
  87. data/lib/plurimath/math/function/lim.rb +1 -2
  88. data/lib/plurimath/math/function/liminf.rb +0 -1
  89. data/lib/plurimath/math/function/limits.rb +0 -1
  90. data/lib/plurimath/math/function/limsup.rb +0 -1
  91. data/lib/plurimath/math/function/linebreak.rb +0 -1
  92. data/lib/plurimath/math/function/ln.rb +0 -1
  93. data/lib/plurimath/math/function/log.rb +1 -2
  94. data/lib/plurimath/math/function/longdiv.rb +0 -3
  95. data/lib/plurimath/math/function/lub.rb +0 -1
  96. data/lib/plurimath/math/function/max.rb +0 -1
  97. data/lib/plurimath/math/function/mbox.rb +0 -1
  98. data/lib/plurimath/math/function/menclose.rb +0 -3
  99. data/lib/plurimath/math/function/merror.rb +0 -3
  100. data/lib/plurimath/math/function/mglyph.rb +0 -3
  101. data/lib/plurimath/math/function/min.rb +0 -1
  102. data/lib/plurimath/math/function/mlabeledtr.rb +0 -20
  103. data/lib/plurimath/math/function/mod.rb +0 -1
  104. data/lib/plurimath/math/function/mpadded.rb +0 -3
  105. data/lib/plurimath/math/function/ms.rb +1 -77
  106. data/lib/plurimath/math/function/msgroup.rb +0 -27
  107. data/lib/plurimath/math/function/msline.rb +0 -3
  108. data/lib/plurimath/math/function/multiscript.rb +0 -14
  109. data/lib/plurimath/math/function/nary.rb +4 -0
  110. data/lib/plurimath/math/function/none.rb +1 -4
  111. data/lib/plurimath/math/function/norm.rb +0 -1
  112. data/lib/plurimath/math/function/obrace.rb +0 -1
  113. data/lib/plurimath/math/function/oint.rb +2 -3
  114. data/lib/plurimath/math/function/over.rb +0 -3
  115. data/lib/plurimath/math/function/overset.rb +3 -3
  116. data/lib/plurimath/math/function/phantom.rb +0 -3
  117. data/lib/plurimath/math/function/power.rb +0 -3
  118. data/lib/plurimath/math/function/power_base.rb +0 -3
  119. data/lib/plurimath/math/function/prod.rb +2 -3
  120. data/lib/plurimath/math/function/right.rb +0 -1
  121. data/lib/plurimath/math/function/root.rb +0 -3
  122. data/lib/plurimath/math/function/rule.rb +0 -1
  123. data/lib/plurimath/math/function/scarries.rb +0 -3
  124. data/lib/plurimath/math/function/scarry.rb +22 -0
  125. data/lib/plurimath/math/function/sec.rb +0 -1
  126. data/lib/plurimath/math/function/sech.rb +0 -1
  127. data/lib/plurimath/math/function/semantics.rb +0 -15
  128. data/lib/plurimath/math/function/sin.rb +0 -1
  129. data/lib/plurimath/math/function/sinh.rb +0 -1
  130. data/lib/plurimath/math/function/sqrt.rb +0 -3
  131. data/lib/plurimath/math/function/stackrel.rb +0 -3
  132. data/lib/plurimath/math/function/substack.rb +0 -1
  133. data/lib/plurimath/math/function/sum.rb +2 -3
  134. data/lib/plurimath/math/function/sup.rb +0 -1
  135. data/lib/plurimath/math/function/table/align.rb +0 -1
  136. data/lib/plurimath/math/function/table/array.rb +0 -1
  137. data/lib/plurimath/math/function/table/bmatrix.rb +0 -1
  138. data/lib/plurimath/math/function/table/cases.rb +0 -1
  139. data/lib/plurimath/math/function/table/eqarray.rb +0 -1
  140. data/lib/plurimath/math/function/table/matrix.rb +0 -1
  141. data/lib/plurimath/math/function/table/multline.rb +0 -1
  142. data/lib/plurimath/math/function/table/pmatrix.rb +0 -1
  143. data/lib/plurimath/math/function/table/split.rb +0 -1
  144. data/lib/plurimath/math/function/table/vmatrix.rb +0 -1
  145. data/lib/plurimath/math/function/table.rb +10 -23
  146. data/lib/plurimath/math/function/tan.rb +0 -1
  147. data/lib/plurimath/math/function/tanh.rb +0 -1
  148. data/lib/plurimath/math/function/td.rb +0 -4
  149. data/lib/plurimath/math/function/ternary_function.rb +6 -2
  150. data/lib/plurimath/math/function/text.rb +0 -5
  151. data/lib/plurimath/math/function/tilde.rb +4 -1
  152. data/lib/plurimath/math/function/tr.rb +0 -13
  153. data/lib/plurimath/math/function/ubrace.rb +0 -1
  154. data/lib/plurimath/math/function/ul.rb +4 -1
  155. data/lib/plurimath/math/function/underover.rb +0 -3
  156. data/lib/plurimath/math/function/underset.rb +22 -45
  157. data/lib/plurimath/math/function/unitsml.rb +2 -1
  158. data/lib/plurimath/math/function/vec.rb +4 -1
  159. data/lib/plurimath/math/function.rb +107 -10
  160. data/lib/plurimath/math/number.rb +1 -4
  161. data/lib/plurimath/math/symbols/comma.rb +1 -1
  162. data/lib/plurimath/math/symbols/plus.rb +1 -1
  163. data/lib/plurimath/math/symbols/symbol.rb +17 -4
  164. data/lib/plurimath/math.rb +13 -49
  165. data/lib/plurimath/mathml/formula_transformation.rb +442 -0
  166. data/lib/plurimath/mathml/parser.rb +11 -50
  167. data/lib/plurimath/mathml/translator.rb +584 -0
  168. data/lib/plurimath/mathml/utility/formula_transformation.rb +2 -341
  169. data/lib/plurimath/mathml.rb +5 -2
  170. data/lib/plurimath/number_formatter.rb +2 -1
  171. data/lib/plurimath/omml/parser.rb +0 -1
  172. data/lib/plurimath/omml/transform.rb +2 -1
  173. data/lib/plurimath/omml.rb +3 -0
  174. data/lib/plurimath/setup/opal.rb.erb +3 -4
  175. data/lib/plurimath/unicode_math/parse.rb +0 -5
  176. data/lib/plurimath/unicode_math/parser.rb +1 -6
  177. data/lib/plurimath/unicode_math/parsing_rules/absence_rules.rb +0 -1
  178. data/lib/plurimath/unicode_math/parsing_rules/common_rules.rb +0 -1
  179. data/lib/plurimath/unicode_math/parsing_rules/constants_rules.rb +0 -1
  180. data/lib/plurimath/unicode_math/parsing_rules/masked.rb +0 -1
  181. data/lib/plurimath/unicode_math/parsing_rules/sub_sup.rb +0 -1
  182. data/lib/plurimath/unicode_math/parsing_rules.rb +14 -0
  183. data/lib/plurimath/unicode_math.rb +6 -0
  184. data/lib/plurimath/unitsml.rb +4 -11
  185. data/lib/plurimath/utility.rb +1 -1
  186. data/lib/plurimath/version.rb +1 -1
  187. data/lib/plurimath/xml_engine/oga.rb +6 -6
  188. data/lib/plurimath/xml_engine/ox_engine.rb +2 -2
  189. data/lib/plurimath/xml_engine.rb +2 -0
  190. data/lib/plurimath.rb +43 -18
  191. data/plurimath.gemspec +6 -4
  192. metadata +49 -12
  193. data/lib/plurimath/mathml/utility/empty_defined_methods.rb +0 -483
  194. data/lib/plurimath/mathml/utility.rb +0 -369
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ecaec3071c4b2925d95eceffec830c054c840c7813db4f0b8f4e6e95c366ced
4
- data.tar.gz: 19adb5616581e744f45805329324b950dcad692c670fa07153579ff71f319d05
3
+ metadata.gz: cc005169c3f0268023980ba0d02d40a3e169c73ec1c75541f1ea71d8cadf6de6
4
+ data.tar.gz: 0d9fb467134da2b44ad20d1952d1795f4fa7e233a657b5589ea3b5cf5e99fb26
5
5
  SHA512:
6
- metadata.gz: a4d1ee78250433329ab39e542804c8199afcf9afecb43027c76611c9619fc6f4eb72d2e49ff0b93c801d53f2986ee97b926217c0fe99c57898992aa900f12079
7
- data.tar.gz: 020112a942828e93440ebdc3c95af0a720c8766411f4edf19cca7ec1f39423c67b8808a6cb95ef01e99429dc6f7d06d322f63829b3b2f7a832d2e308fd1aedd9
6
+ metadata.gz: c4d59a8681ab5b3b988d3a46ecfadc50661c43cc2d897816d943b3c621a6f8db202a8fca9b59c72fa732384284071aeec20851051b645cb7c29e4126fa0377d3
7
+ data.tar.gz: 54013f676433c00728a7b022b5c16ec3b77d2afd1949573ee56c0d90c936600289647cef2a886978e23da5be20af8d232dabf4e6ff9d3454dca77667f69d0f50
@@ -15,6 +15,9 @@ on:
15
15
 
16
16
  jobs:
17
17
  release:
18
+ permissions:
19
+ contents: write
20
+ id-token: write
18
21
  uses: metanorma/ci/.github/workflows/rubygems-release.yml@main
19
22
  with:
20
23
  next_version: ${{ github.event.inputs.next_version }}
data/.rspec-opal CHANGED
@@ -3,8 +3,8 @@
3
3
  -q oga/setup_opal
4
4
  -q ll/setup_opal
5
5
  --opal-opt=-g,htmlentities
6
- --opal-opt=-g,equivalent-xml
7
6
  --opal-opt=-g,parslet
7
+ --opal-opt=-g,canon
8
8
  --opal-opt=-g,oga
9
9
  --format documentation
10
10
  --color
data/.rubocop.yml CHANGED
@@ -1,10 +1,14 @@
1
- # Auto-generated by Cimas: Do not edit it manually!
2
- # See https://github.com/metanorma/cimas
3
1
  inherit_from:
4
2
  - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
3
+ - .rubocop_todo.yml
5
4
 
6
- # local repo-specific modifications
7
- # ...
5
+ plugins:
6
+ - rubocop-performance
7
+ - rubocop-rake
8
+ - rubocop-rspec
8
9
 
9
10
  AllCops:
10
- TargetRubyVersion: 2.5
11
+ TargetRubyVersion: 3.0
12
+ NewCops: enable
13
+ Exclude:
14
+ - 'vendor/**/*'
data/.rubocop_todo.yml ADDED
File without changes
data/Gemfile CHANGED
@@ -3,12 +3,14 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in plurimath.gemspec
4
4
  gemspec
5
5
 
6
+ gem 'lutaml-model', github: "lutaml/lutaml-model", branch: "main"
7
+ gem 'canon'
6
8
  gem "rake", "~> 12.0"
7
9
  gem "rspec", "~> 3.0"
8
- gem 'simplecov', require: false, group: :test
9
- gem 'htmlentities'
10
- gem 'equivalent-xml'
11
- gem 'opal-rspec', "~> 1.1.0a"
10
+ gem "rubocop-performance"
11
+ gem "rubocop-rake"
12
+ gem "rubocop-rspec"
12
13
  gem 'oga'
14
+ gem 'opal-rspec', "~> 1.1.0a"
13
15
  gem 'ox'
14
- gem "debug"
16
+ gem 'simplecov', require: false, group: :test
data/README.adoc CHANGED
@@ -129,7 +129,7 @@ plurimath convert -i "equation" -f omml -t mathml -d true
129
129
  ====
130
130
  [source,bash]
131
131
  ----
132
- plurimath convert -e <file_path> -t unicodemath
132
+ plurimath convert -p <file_path> -f asciimath -t unicodemath
133
133
  ----
134
134
  ====
135
135
 
@@ -524,14 +524,25 @@ Symbol to use for the decimal point. Accepts a character.
524
524
  ====
525
525
 
526
526
  `digit_count`:: (`Numeric` value)
527
- Total number of digits to render, with the value truncated.
528
- Accepts an integer value.
527
+ Total number of digits to render (integer + fractional), with the value truncated
528
+ or rounded as necessary. Accepts an integer value.
529
+ +
530
+ When `digit_count` is less than or equal to the integer length, the fractional
531
+ part is omitted entirely, and the integer is rounded based on whether the
532
+ fractional value is >= 0.5 (or >= base/2 for non-decimal bases).
529
533
  +
530
534
  .Specifying a total of 6 digits in rendering the number
531
535
  [example]
532
536
  ====
533
537
  "32232.232" => "32232.2"
534
538
  ====
539
+ +
540
+ .When digit_count is smaller than the integer length
541
+ [example]
542
+ ====
543
+ "99999.999" with `digit_count: 3` => "100,000" (fractional part omitted, integer rounded up)
544
+ "12345.123" with `digit_count: 3` => "12,345" (fractional part omitted, no rounding)
545
+ ====
535
546
 
536
547
 
537
548
  `group`:: (`String` value)
@@ -555,6 +566,116 @@ Accepts an integer value. (default is 3 in most locales.)
555
566
  "32232.232" => "3 22 32.232"
556
567
  ====
557
568
 
569
+ `base`:: (`Numeric` value)
570
+ Sets the numeric base (radix) used to render both the integer and fractional parts of the number.
571
+ Supported values are 2 (binary), 8 (octal), 10 (decimal, default) and 16 (hexadecimal).
572
+ Passing any other value for `base` raises `Plurimath::Formatter::UnsupportedBase`.
573
+ +
574
+ .Rendering numbers in different bases
575
+ [example]
576
+ ====
577
+ "10" with `base: 2, group_digits: 2, group: ","` => "0b10,10"
578
+ "9" with `base: 8` => "0o11"
579
+ "255" with `base: 16` => "0xff"
580
+ "10.75" with `base: 2, group_digits: 2, group: ","` => "0b10,10.11" (integer and fractional parts converted, with grouping)
581
+ ====
582
+
583
+ `base_prefix`:: (`String` or `nil` value)
584
+ Overrides the default base prefix for non‑decimal bases.
585
+ When omitted, standard prefixes are used (`0b` for base 2, `0o` for base 8, `0x` for base 16).
586
+ `nil` or an empty string can be used to omit the prefix.
587
+ +
588
+ .Custom base prefixes
589
+ [example]
590
+ ====
591
+ "255" with `base: 16, base_prefix: "16#"` => "16#ff"
592
+ "255" with `base: 16, base_prefix: nil` => "ff"
593
+ "255" with `base: 16, base_prefix: ""` => "ff"
594
+ ====
595
+
596
+ `base_postfix`:: (`String` value)
597
+ If present, a postfix is appended to the converted number instead of using any base prefix.
598
+ This is applied after digit grouping.
599
+ `nil` or an empty string can be used to omit the postfix.
600
+ +
601
+ .Using postfix notation
602
+ [example]
603
+ ====
604
+ "255" with `base: 16, base_postfix: "_16"` => "ff_16"
605
+ "255" with `base: 16, base_postfix: nil` => "ff"
606
+ "255" with `base: 16, base_postfix: ""` => "ff"
607
+ ====
608
+
609
+ `hex_capital`:: (`Boolean` value)
610
+ When `true` and `base: 16`, all characters in the range `a`-`f` in the converted output
611
+ are rendered using uppercase letters. This includes both hexadecimal digits and any
612
+ separators (such as `decimal`, `group`, `fraction_group`) that happen to be letters
613
+ in the `a`-`f` range. The `base_prefix` and `base_postfix` values are never modified.
614
+ It has no effect for other bases.
615
+ +
616
+ .Uppercase hexadecimal output
617
+ [example]
618
+ ====
619
+ "48879" with `base: 16, hex_capital: true` => "0xBE,EF"
620
+ ====
621
+ +
622
+ 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.
623
+ +
624
+ .Separator uppercasing behavior
625
+ [example]
626
+ ====
627
+ "48879" with `base: 16, hex_capital: true, group: "g"` => "0xBEgEF" (g remains lowercase)
628
+
629
+ "48879" with `base: 16, hex_capital: true, group: "f"` => "0xBEFEF" (f separator becomes F)
630
+ ====
631
+
632
+ NOTE: When `base` is not 10, both the integer and fractional parts are converted to the specified base.
633
+ The decimal separator is rendered as configured by the `decimal` option.
634
+ +
635
+ NOTE: The fractional-part conversion treats the digits after the decimal point as a fractional value (for example, `"10.75"` is interpreted
636
+ as decimal `0.75` for the fractional part) and converts that real value to the requested base, honoring the configured precision.
637
+
638
+ ===== Base conversion with other formatting options
639
+
640
+ Base conversion works seamlessly with other formatting options such as `precision`, `digit_count`,
641
+ `fraction_group`, `fraction_group_digits`, and `significant`.
642
+
643
+ .Base conversion with precision
644
+ [example]
645
+ ====
646
+ Controls the maximum number of fractional digits in the converted base (pads with zeros when needed and truncates repeating expansions to the configured precision):
647
+ +
648
+ "10.75" with `base: 2, precision: 4` => "0b10,10.1100" (fractional part padded to 4 digits in base 2)
649
+ "0.5" with `base: 16, precision: 6` => "0x0.800000" (fractional part converted to base 16 and padded to 6 digits)
650
+ ====
651
+
652
+ .Base conversion with fraction grouping
653
+ [example]
654
+ ====
655
+ Groups fractional digits in the converted base:
656
+ +
657
+ "10.75" with `base: 2, fraction_group_digits: 1, fraction_group: " ", group_digits: 10` => "0b1010.1 1"
658
+ ====
659
+
660
+ .Base conversion with digit_count
661
+ [example]
662
+ ====
663
+ Limits total digits when using base conversion:
664
+ +
665
+ "14236.39239" with `base: 10, digit_count: 6, group_digits: 3` => "14,236.4"
666
+ "10.75" with `base: 2, digit_count: 7, group_digits: 10` => "0b1010.110"
667
+ "255.9" with `base: 16, digit_count: 2, group_digits: 10` => "0x100" (fractional part omitted, integer rounded)
668
+ ====
669
+
670
+ .Base conversion with significant digits
671
+ [example]
672
+ ====
673
+ Applies significant digit rounding with base conversion:
674
+ +
675
+ "1234.56" with `base: 10, significant: 4` => "1,235"
676
+ "1999" with `base: 10, significant: 2` => "2,000"
677
+ ====
678
+
558
679
  `fraction_group`:: (`String` value)
559
680
  Delimiter to use between groups of fractional digits specified in
560
681
  `fraction_group_digits`. Accepts a character.
@@ -607,7 +728,7 @@ selected to be divisible by three to match the common metric prefixes.
607
728
  ====
608
729
 
609
730
  `e`:: (`String` value)
610
- Symbol to use for exponents in E notation (default value `E`). (used in the
731
+ Symbol to use for exponents in E notation (default value `e`). (used in the
611
732
  mode: `e` only).
612
733
  +
613
734
  .Using the lowercase 'e' symbol as the exponent symbol
@@ -743,9 +864,8 @@ formatter.localized_number(
743
864
 
744
865
  Where,
745
866
 
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.
867
+ `<number>`:: (mandatory) The number to be formatted, as a `String` containing the
868
+ decimal representation of the value (for example, `"1234.56789"`).
749
869
 
750
870
  `locale: <locale-symbol>`:: (optional) The locale to be used for number formatting.
751
871
  Value is a symbol.
@@ -1117,7 +1237,7 @@ locales are listed in the link:/blog/2024-07-09-number-formatter[number formatte
1117
1237
  `options`:: (default: empty) a hash of options (`localizer_symbols`). The options
1118
1238
  are listed in the link:/blog/2024-07-09-number-formatter[number formatter blog post].
1119
1239
 
1120
- `format_string`:: (default: `nil`, disabled) a string value (localize_number)
1240
+ `string_format`:: (default: `nil`, disabled) a string value (`localize_number`)
1121
1241
 
1122
1242
  `precision`:: (default: `nil`, disabled) an integer value.
1123
1243
 
@@ -1136,7 +1256,7 @@ are listed in the link:/blog/2024-07-09-number-formatter[number formatter blog p
1136
1256
  }
1137
1257
 
1138
1258
  > formatter = Plurimath::Formatter::Standard.new(locale: :hy, options: options, precision: 2)
1139
- # format_string: <string value> if provided
1259
+ # string_format: <string value> if provided
1140
1260
 
1141
1261
  > Plurimath::Math.parse('2121221.3434', :latex).to_latex(formatter: formatter)
1142
1262
  # => '2,12,12,21;34'
@@ -1160,7 +1280,7 @@ The custom formatter is to be subclassed from `Plurimath::Formatter::Standard`.
1160
1280
  [source,ruby]
1161
1281
  ----
1162
1282
  class MyCustomFormatter < Plurimath::Formatter::Standard <1>
1163
- def initialize(locale:, precision:, options:, format_string:) <2>
1283
+ def initialize(locale:, precision:, options:, string_format:) <2>
1164
1284
  super
1165
1285
  end
1166
1286
  end
@@ -1175,7 +1295,7 @@ The default options of the custom formatter are set using the
1175
1295
  [source,ruby]
1176
1296
  ----
1177
1297
  class MyCustomFormatter < Plurimath::Formatter::Standard
1178
- def initialize(locale:, precision:, options:, format_string:)
1298
+ def initialize(locale:, precision:, options:, string_format:)
1179
1299
  super
1180
1300
  end
1181
1301
 
data/Rakefile CHANGED
@@ -105,7 +105,18 @@ def write_intent_doc_file(file)
105
105
  file.write("\nIntent for unary classes like, sin, cos, tan, etc. will be `Function`.\n")
106
106
  end
107
107
 
108
+ def require_math_function_files
109
+ (
110
+ Dir.glob(File.join(__dir__, "lib/plurimath/math/function", "*.rb")) +
111
+ Dir.glob(File.join(__dir__, "lib/plurimath/math/function", "*", "*.rb"))
112
+ ).each do |file|
113
+ require file
114
+ end
115
+ end
116
+
108
117
  def intent_classes
118
+ require_math_function_files
119
+
109
120
  intent_classes = [
110
121
  Plurimath::Math::Function::TernaryFunction.descendants,
111
122
  Plurimath::Math::Function::BinaryFunction.descendants,
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
3
  module Plurimath
5
4
  class Asciimath
6
5
  class Parse < Parslet::Parser
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "parse"
4
- require_relative "constants"
5
- require_relative "transform"
6
3
  module Plurimath
7
4
  class Asciimath
8
5
  class Parser
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "math"
4
3
  module Plurimath
5
4
  class Asciimath
5
+ autoload :Constants, "#{__dir__}/asciimath/constants"
6
+ autoload :Parse, "#{__dir__}/asciimath/parse"
7
+ autoload :Parser, "#{__dir__}/asciimath/parser"
8
+ autoload :Transform, "#{__dir__}/asciimath/transform"
9
+
6
10
  attr_accessor :text
7
11
 
8
12
  def initialize(text)
data/lib/plurimath/cli.rb CHANGED
@@ -77,7 +77,7 @@ module Plurimath
77
77
  no_commands do
78
78
  def warn_and_exit(message)
79
79
  warn(message)
80
- exit 1
80
+ abort
81
81
  end
82
82
  end
83
83
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plurimath
4
+ module Formatter
5
+ class UnsupportedBase < StandardError
6
+ def initialize(base, supported_bases)
7
+ @base = base
8
+ @supported = supported_bases.keys.map { |key| key.to_s.inspect }.join(", ")
9
+ end
10
+
11
+ def to_s
12
+ <<~MESSAGE
13
+ [plurimath] Unsupported base `#{@base}` for number formatting.
14
+ [plurimath] The formatter `:base` option must be one of: #{@supported}.
15
+ MESSAGE
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plurimath
4
+ module Math
5
+ class InvalidTypeError < TypeError
6
+ def initialize(type = nil)
7
+ super(type ? formula_message(type) : parse_message)
8
+ end
9
+
10
+ def parse_message
11
+ "`type` must be one of: `#{Math::VALID_TYPES.keys.join('`, `')}`"
12
+ end
13
+
14
+ def formula_message(type)
15
+ "Invalid type provided: #{type}. Must be one of #{Formula::MATH_ZONE_TYPES.join(', ')}."
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plurimath
4
+ module Math
5
+ class ParseError < StandardError
6
+ def initialize(text, type)
7
+ @text = text
8
+ @type = type.to_sym
9
+ super(@type == :invalid_unitsml ? unitsml_message : parsing_message)
10
+ end
11
+
12
+ def parsing_message
13
+ <<~MESSAGE
14
+ [plurimath] Error: Failed to parse the following formula with type `#{@type}`.
15
+ [plurimath] Please first manually validate the formula.
16
+ #{generic_part}
17
+ ---- FORMULA BEGIN ----
18
+ #{@text}
19
+ ---- FORMULA END ----
20
+ MESSAGE
21
+ end
22
+
23
+ def unitsml_message
24
+ <<~MESSAGE
25
+ [plurimath] Invalid formula `#{@text}`.
26
+ [plurimath] The use of a variable as an exponent is not valid.
27
+ #{generic_part}
28
+ MESSAGE
29
+ end
30
+
31
+ def generic_part
32
+ <<~MESSAGE.rstrip
33
+ [plurimath] If this is a bug, please report the formula at our issue tracker at:
34
+ [plurimath] https://github.com/plurimath/plurimath/issues
35
+ MESSAGE
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1 @@
1
+ # frozen_string_literal: true
@@ -3,17 +3,33 @@
3
3
  module Plurimath
4
4
  module Formatter
5
5
  class NumberFormatter
6
- attr_reader :number, :data_reader, :prefix
6
+ attr_reader :number, :data_reader
7
7
 
8
+ DEFAULT_BASE = Numbers::Base::DEFAULT_BASE
9
+ HEX_ALPHABETS = "abcdef".freeze
8
10
  STRING_SYMBOLS = {
9
- dot: ".".freeze,
10
- f: "F".freeze,
11
+ dot: ".",
12
+ f: "F",
13
+ }.freeze
14
+ DEFAULT_BASE_PREFIXES = {
15
+ 2 => "0b",
16
+ 8 => "0o",
17
+ 10 => "",
18
+ 16 => "0x",
11
19
  }.freeze
12
20
 
13
21
  def initialize(number, data_reader = {})
14
22
  @number = number
15
23
  @data_reader = data_reader
16
- @prefix = "-" if number.negative?
24
+ @base = data_reader[:base] || DEFAULT_BASE
25
+ raise UnsupportedBase.new(@base, DEFAULT_BASE_PREFIXES) unless DEFAULT_BASE_PREFIXES.key?(@base)
26
+
27
+ # Handle base_prefix: if explicitly provided (even as nil), use it; otherwise use default
28
+ @base_prefix = if data_reader.key?(:base_prefix)
29
+ data_reader[:base_prefix].to_s
30
+ else
31
+ DEFAULT_BASE_PREFIXES[@base]
32
+ end
17
33
  end
18
34
 
19
35
  def format(precision: nil)
@@ -22,16 +38,37 @@ module Plurimath
22
38
  # FIX FOR:
23
39
  # NotImplementedError: String#<< not supported. Mutable String methods are not supported in Opal.
24
40
  result = []
25
- result << integer_format.apply(int, data_reader)
26
- result << fraction_format.apply(frac, data_reader, int) if frac
41
+ result << integer_format.apply(int)
42
+ result << fraction_format.apply(frac, result, integer_format) # use formatted int for correct fraction formatting
27
43
  result = result.join
28
44
  result = signif_format.apply(result, integer_format, fraction_format)
29
- result = "+#{result}" if number.positive? && data_reader[:number_sign].to_s == "plus"
30
- "#{prefix}#{result}"
45
+ result = result.tr(HEX_ALPHABETS, HEX_ALPHABETS.upcase) if upcase_hex?
46
+ result = pre_post_fixed(result) unless base_default?
47
+ "#{prefix_symbol}#{result}"
31
48
  end
32
49
 
33
50
  private
34
51
 
52
+ def upcase_hex?
53
+ @base == 16 && data_reader[:hex_capital]
54
+ end
55
+
56
+ def prefix_symbol
57
+ if number.negative?
58
+ "-"
59
+ elsif data_reader[:number_sign]&.to_sym == :plus
60
+ "+"
61
+ end
62
+ end
63
+
64
+ def pre_post_fixed(result)
65
+ if data_reader.key?(:base_postfix)
66
+ "#{result}#{data_reader[:base_postfix]}"
67
+ else
68
+ "#{@base_prefix}#{result}"
69
+ end
70
+ end
71
+
35
72
  def partition_tokens(number)
36
73
  int, fraction = parse_number(number)
37
74
  [
@@ -53,10 +90,11 @@ module Plurimath
53
90
  def parse_number(number, options = data_reader)
54
91
  precision = options[:precision] || precision_from(number)
55
92
 
93
+ abs = round_to(number, precision).abs
56
94
  num = if precision == 0
57
- round_to(number, precision).abs.fix.to_s(STRING_SYMBOLS[:f])
95
+ abs.fix.to_s(STRING_SYMBOLS[:f])
58
96
  else
59
- round_to(number, precision).abs.round(precision).to_s(STRING_SYMBOLS[:f])
97
+ abs.round(precision).to_s(STRING_SYMBOLS[:f])
60
98
  end
61
99
  num.split(STRING_SYMBOLS[:dot])
62
100
  end
@@ -65,6 +103,10 @@ module Plurimath
65
103
  factor = BigDecimal(10).power(precision)
66
104
  (number * factor).fix / factor
67
105
  end
106
+
107
+ def base_default?
108
+ @base == DEFAULT_BASE
109
+ end
68
110
  end
69
111
  end
70
112
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plurimath
4
+ module Formatter
5
+ module Numbers
6
+ class Base
7
+ HEX_ALPHANUMERIC = %w[0 1 2 3 4 5 6 7 8 9 a b c d e f].freeze
8
+ DEFAULT_BASE = 10
9
+ DIGIT_VALUE = HEX_ALPHANUMERIC.each_with_index.to_h
10
+
11
+ attr_accessor :base, :symbols
12
+
13
+ def initialize(symbols = {})
14
+ @symbols = symbols
15
+ @base = symbols[:base] || DEFAULT_BASE
16
+ end
17
+
18
+ protected
19
+
20
+ def threshold
21
+ @threshold ||= base.div(2)
22
+ end
23
+
24
+ def base_default?
25
+ base == DEFAULT_BASE
26
+ end
27
+
28
+ def next_mapping_char(char)
29
+ current_idx = DIGIT_VALUE[char]
30
+ return nil unless current_idx
31
+
32
+ HEX_ALPHANUMERIC[current_idx + 1]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end