python-color-math 0.2.20__tar.gz → 0.2.22__tar.gz

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 (81) hide show
  1. {python_color_math-0.2.20/python_color_math.egg-info → python_color_math-0.2.22}/PKG-INFO +1 -1
  2. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/__init__.py +1 -1
  3. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/colormath.default.json +1 -1
  4. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/config.py +74 -18
  5. {python_color_math-0.2.20 → python_color_math-0.2.22}/pyproject.toml +1 -1
  6. {python_color_math-0.2.20 → python_color_math-0.2.22/python_color_math.egg-info}/PKG-INFO +1 -1
  7. {python_color_math-0.2.20 → python_color_math-0.2.22}/python_color_math.egg-info/SOURCES.txt +1 -0
  8. python_color_math-0.2.22/tests/test_harmonic_tuples.py +84 -0
  9. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_mcp_hardcore.py +1 -0
  10. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_mcp_server.py +1 -0
  11. {python_color_math-0.2.20 → python_color_math-0.2.22}/.pre-commit-hooks.yaml +0 -0
  12. {python_color_math-0.2.20 → python_color_math-0.2.22}/LICENSE +0 -0
  13. {python_color_math-0.2.20 → python_color_math-0.2.22}/MANIFEST.in +0 -0
  14. {python_color_math-0.2.20 → python_color_math-0.2.22}/README.md +0 -0
  15. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/__main__.py +0 -0
  16. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/adapters.py +0 -0
  17. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/completions.py +0 -0
  18. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/converters/__init__.py +0 -0
  19. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/converters/align.py +0 -0
  20. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/converters/block.py +0 -0
  21. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/converters/derivative.py +0 -0
  22. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/converters/equation.py +0 -0
  23. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/converters/generic.py +0 -0
  24. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/converters/integral.py +0 -0
  25. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/converters/limit.py +0 -0
  26. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/converters/matrix.py +0 -0
  27. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/converters/semantic.py +0 -0
  28. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/converters/unicode_converter.py +0 -0
  29. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/custom_definitions.py +0 -0
  30. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/gui.py +0 -0
  31. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/io.py +0 -0
  32. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/ipython.py +0 -0
  33. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/jupyter.py +0 -0
  34. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/main.py +0 -0
  35. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/mcp.py +0 -0
  36. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/mcp_server.py +0 -0
  37. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/__init__.py +0 -0
  38. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/alignment.py +0 -0
  39. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/braket.py +0 -0
  40. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/constants.py +0 -0
  41. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/delimiters.py +0 -0
  42. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/differentials.py +0 -0
  43. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/dimensionless.py +0 -0
  44. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/frontmatter.py +0 -0
  45. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/latex_spans.py +0 -0
  46. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/markdown_scanner.py +0 -0
  47. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/math_parser.py +0 -0
  48. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/physics.py +0 -0
  49. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/scanner.py +0 -0
  50. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/taxonomy.py +0 -0
  51. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/units.py +0 -0
  52. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/parsers/variable_hash.py +0 -0
  53. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/py.typed +0 -0
  54. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/self_test.py +0 -0
  55. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/tutorial.py +0 -0
  56. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/undo.py +0 -0
  57. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/unicode.py +0 -0
  58. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/utils/__init__.py +0 -0
  59. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/utils/coloring.py +0 -0
  60. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/utils/latex_helpers.py +0 -0
  61. {python_color_math-0.2.20 → python_color_math-0.2.22}/color_math/utils/spans.py +0 -0
  62. {python_color_math-0.2.20 → python_color_math-0.2.22}/python_color_math/__init__.py +0 -0
  63. {python_color_math-0.2.20 → python_color_math-0.2.22}/python_color_math/__main__.py +0 -0
  64. {python_color_math-0.2.20 → python_color_math-0.2.22}/python_color_math.egg-info/dependency_links.txt +0 -0
  65. {python_color_math-0.2.20 → python_color_math-0.2.22}/python_color_math.egg-info/entry_points.txt +0 -0
  66. {python_color_math-0.2.20 → python_color_math-0.2.22}/python_color_math.egg-info/requires.txt +0 -0
  67. {python_color_math-0.2.20 → python_color_math-0.2.22}/python_color_math.egg-info/top_level.txt +0 -0
  68. {python_color_math-0.2.20 → python_color_math-0.2.22}/setup.cfg +0 -0
  69. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_adversarial.py +0 -0
  70. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_cli.py +0 -0
  71. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_config_tolerance.py +0 -0
  72. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_custom_definitions.py +0 -0
  73. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_extreme_adversarial.py +0 -0
  74. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_features.py +0 -0
  75. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_frontmatter.py +0 -0
  76. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_fuzz.py +0 -0
  77. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_gui.py +0 -0
  78. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_ipython.py +0 -0
  79. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_physics.py +0 -0
  80. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_unicode.py +0 -0
  81. {python_color_math-0.2.20 → python_color_math-0.2.22}/tests/test_unicode_converter.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-color-math
3
- Version: 0.2.20
3
+ Version: 0.2.22
4
4
  Summary: A fast, local semantic LaTeX colorizer for Markdown, Jupyter, and LaTeX math equations.
5
5
  Author: Aditya
6
6
  License: MIT License
@@ -13,7 +13,7 @@ from .undo import uncolor_text
13
13
 
14
14
  from .ipython import ColorMath, colormath, load_ipython_extension, unload_ipython_extension
15
15
 
16
- __version__ = "0.2.20"
16
+ __version__ = "0.2.22"
17
17
 
18
18
  __all__ = [
19
19
  "__version__",
@@ -18,7 +18,7 @@
18
18
  "options": {
19
19
  "enable_taxonomy": true,
20
20
  "rainbow_delimiters": true,
21
- "variable_data_flow": false,
21
+ "variable_data_flow": true,
22
22
  "color_units": true,
23
23
  "color_differentials": true,
24
24
  "color_braket": true,
@@ -610,26 +610,82 @@ RAINBOW_DELIMITER_COLORS: list[str] = [
610
610
 
611
611
 
612
612
  VARIABLE_HASH_PALETTE: list[str] = [
613
- "#7aa2f7", # Tokyo Blue
614
- "#7dcfff", # Tokyo Cyan
615
- "#bb9af7", # Tokyo Purple
616
- "#f7768e", # Tokyo Pink
617
- "#e0af68", # Tokyo Orange/Gold
618
- "#9ece6a", # Tokyo Green
619
- "#2ac3de", # Light Cyan
620
- "#ff9e64", # Peach
613
+ "#7aa2f7", # 0: Tokyo Blue
614
+ "#7dcfff", # 1: Tokyo Cyan
615
+ "#bb9af7", # 2: Tokyo Purple
616
+ "#f7768e", # 3: Tokyo Pink
617
+ "#e0af68", # 4: Tokyo Orange/Gold
618
+ "#9ece6a", # 5: Tokyo Green
619
+ "#2ac3de", # 6: Light Cyan/Teal
620
+ "#ff9e64", # 7: Peach
621
621
  ]
622
622
 
623
+ CANONICAL_VARIABLE_SLOTS: dict[str, int] = {
624
+ # 1. Spatial 3D Cartesian coordinates: guaranteed maximum pairwise separation
625
+ "x": 0, # Tokyo Blue
626
+ "y": 4, # Tokyo Gold
627
+ "z": 5, # Tokyo Green
628
+
629
+ # 2. Coefficients: high-contrast against (x, y, z) and within the group
630
+ "a": 2, # Tokyo Purple
631
+ "b": 3, # Tokyo Pink
632
+ "c": 6, # Light Cyan/Teal
633
+ "d": 7, # Peach
634
+
635
+ # 3. Parameters / Velocity / Substitutions
636
+ "u": 1, # Tokyo Cyan
637
+ "v": 2, # Tokyo Purple
638
+ "w": 4, # Tokyo Gold
639
+
640
+ # 4. Discrete summation & matrix indices
641
+ "i": 4, # Tokyo Gold
642
+ "j": 0, # Tokyo Blue
643
+ "k": 3, # Tokyo Pink
644
+ "l": 5, # Tokyo Green
645
+ "m": 2, # Tokyo Purple
646
+ "n": 6, # Light Cyan/Teal
647
+
648
+ # 5. Calculus & Analysis duals
649
+ "s": 3, # Tokyo Pink
650
+ "t": 1, # Tokyo Cyan
651
+ "p": 0, # Tokyo Blue
652
+ "q": 7, # Peach
653
+
654
+ # 6. Thermodynamics & State
655
+ "P": 3, # Tokyo Pink
656
+ "V": 0, # Tokyo Blue
657
+ "T": 4, # Tokyo Gold
658
+
659
+ # 7. Greek letters & canonical duals
660
+ "\\theta": 4, "θ": 4, "𝜗": 4, "ϑ": 4,
661
+ "\\phi": 2, "\\varphi": 2, "ϕ": 2, "φ": 2, "𝜙": 2, "𝜑": 2,
662
+ "\\psi": 2, "ψ": 2, "𝜓": 2,
663
+ "\\epsilon": 2, "\\varepsilon": 2, "ε": 2, "𝜀": 2,
664
+ "\\delta": 5, "δ": 5, "𝛿": 5,
665
+ "\\alpha": 2, "α": 2, "𝛼": 2,
666
+ "\\beta": 3, "β": 3, "𝛽": 3,
667
+ "\\gamma": 5, "γ": 5, "𝛾": 5,
668
+ "\\omega": 7, "ω": 7, "𝜔": 7,
669
+ "\\lambda": 6, "λ": 6, "𝜆": 6,
670
+ }
623
671
 
624
- def hash_string_to_color(s: str, palette: list[str] = VARIABLE_HASH_PALETTE) -> str:
625
- """Deterministic string hashing for variable data-flow coloring."""
672
+
673
+ def hash_string_to_slot(s: str, num_slots: int = 8) -> int:
674
+ """Dispersive hash mapping identifiers to palette slots with golden ratio dispersion."""
675
+ if s in CANONICAL_VARIABLE_SLOTS:
676
+ return CANONICAL_VARIABLE_SLOTS[s] % num_slots
677
+
678
+ # Knuth 32-bit multiplicative hash with golden ratio constant 2654435761 (0x9E3779B9)
626
679
  h = 0
627
680
  for char in s:
628
- h = (h * 31 + ord(char)) & 0xFFFFFFFF
629
- if h >= 0x80000000:
630
- h -= 0x100000000
631
- idx = abs(h) % len(palette)
632
- return palette[idx]
681
+ h = ((h << 5) - h + ord(char)) & 0xFFFFFFFF
682
+ return ((h * 2654435761) & 0xFFFFFFFF) % num_slots
683
+
684
+
685
+ def hash_string_to_color(s: str, palette: list[str] = VARIABLE_HASH_PALETTE) -> str:
686
+ """Deterministic string hashing for variable data-flow coloring with harmonic disambiguation."""
687
+ slot = hash_string_to_slot(s, len(palette))
688
+ return palette[slot]
633
689
 
634
690
 
635
691
  @dataclass
@@ -656,7 +712,7 @@ class ColorMathOptions:
656
712
  return cls(
657
713
  enable_taxonomy=True,
658
714
  rainbow_delimiters=True,
659
- variable_data_flow=False,
715
+ variable_data_flow=True,
660
716
  color_units=True,
661
717
  color_differentials=True,
662
718
  color_braket=True,
@@ -871,7 +927,7 @@ def get_bundled_default_config() -> dict[str, object]:
871
927
  "_comment": "Python Color Math factory default configuration",
872
928
  "theme": "default",
873
929
  "colors": dict(DEFAULT_COLORS),
874
- "options": asdict(ColorMathOptions()),
930
+ "options": asdict(ColorMathOptions.extended()),
875
931
  "unicode": dict(DEFAULT_UNICODE_CONFIG),
876
932
  "_role_descriptions": ROLE_DESCRIPTIONS,
877
933
  }
@@ -887,7 +943,7 @@ def load_config(
887
943
  Returns (palette_dict, ColorMathOptions, unicode_config_dict).
888
944
  """
889
945
  palette = dict(DEFAULT_COLORS)
890
- options = ColorMathOptions()
946
+ options = ColorMathOptions.extended()
891
947
  unicode_config = dict(DEFAULT_UNICODE_CONFIG)
892
948
 
893
949
  target = path
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-color-math"
7
- version = "0.2.20"
7
+ version = "0.2.22"
8
8
  description = "A fast, local semantic LaTeX colorizer for Markdown, Jupyter, and LaTeX math equations."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-color-math
3
- Version: 0.2.20
3
+ Version: 0.2.22
4
4
  Summary: A fast, local semantic LaTeX colorizer for Markdown, Jupyter, and LaTeX math equations.
5
5
  Author: Aditya
6
6
  License: MIT License
@@ -70,6 +70,7 @@ tests/test_features.py
70
70
  tests/test_frontmatter.py
71
71
  tests/test_fuzz.py
72
72
  tests/test_gui.py
73
+ tests/test_harmonic_tuples.py
73
74
  tests/test_ipython.py
74
75
  tests/test_mcp_hardcore.py
75
76
  tests/test_mcp_server.py
@@ -0,0 +1,84 @@
1
+ """Unit tests for Harmonic Tuple Disambiguation & Perceptual Contrast."""
2
+
3
+ import unittest
4
+ from color_math.config import (
5
+ VARIABLE_HASH_PALETTE,
6
+ CANONICAL_VARIABLE_SLOTS,
7
+ hash_string_to_color,
8
+ hash_string_to_slot,
9
+ )
10
+ from color_math.converters.block import convert_text
11
+ from color_math.config import ColorMathOptions
12
+
13
+
14
+ class TestHarmonicTupleDisambiguation(unittest.TestCase):
15
+ def test_spatial_coordinates_distinct(self):
16
+ """Verify (x, y, z) are assigned mutually distinct, contrasting colors."""
17
+ col_x = hash_string_to_color("x")
18
+ col_y = hash_string_to_color("y")
19
+ col_z = hash_string_to_color("z")
20
+ self.assertNotEqual(col_x, col_y)
21
+ self.assertNotEqual(col_y, col_z)
22
+ self.assertNotEqual(col_x, col_z)
23
+
24
+ def test_coefficients_distinct(self):
25
+ """Verify (a, b, c, d) are mutually distinct."""
26
+ col_a = hash_string_to_color("a")
27
+ col_b = hash_string_to_color("b")
28
+ col_c = hash_string_to_color("c")
29
+ col_d = hash_string_to_color("d")
30
+ colors = {col_a, col_b, col_c, col_d}
31
+ self.assertEqual(len(colors), 4)
32
+
33
+ def test_parameters_distinct(self):
34
+ """Verify (u, v, w) are mutually distinct."""
35
+ col_u = hash_string_to_color("u")
36
+ col_v = hash_string_to_color("v")
37
+ col_w = hash_string_to_color("w")
38
+ colors = {col_u, col_v, col_w}
39
+ self.assertEqual(len(colors), 3)
40
+
41
+ def test_indices_distinct(self):
42
+ """Verify (i, j, k, l) are mutually distinct."""
43
+ col_i = hash_string_to_color("i")
44
+ col_j = hash_string_to_color("j")
45
+ col_k = hash_string_to_color("k")
46
+ col_l = hash_string_to_color("l")
47
+ colors = {col_i, col_j, col_k, col_l}
48
+ self.assertEqual(len(colors), 4)
49
+
50
+ def test_linear_combination_cross_contrast(self):
51
+ """Verify in ax + by + cz, all six variables receive distinct colors."""
52
+ vars_to_test = ["a", "x", "b", "y", "c", "z"]
53
+ colors = [hash_string_to_color(v) for v in vars_to_test]
54
+ # In term 1: a != x
55
+ self.assertNotEqual(colors[0], colors[1])
56
+ # In term 2: b != y
57
+ self.assertNotEqual(colors[2], colors[3])
58
+ # In term 3: c != z
59
+ self.assertNotEqual(colors[4], colors[5])
60
+ # All 6 must be unique!
61
+ self.assertEqual(len(set(colors)), 6)
62
+
63
+ def test_rendered_linear_combination_equation(self):
64
+ """Verify full math expression rendering of ax + by + cz = d."""
65
+ eq = "$$ax + by + cz = d$$"
66
+ opts = ColorMathOptions.all_enabled()
67
+ res = convert_text(eq, options=opts)
68
+ self.assertIn(r"\textcolor{#bb9af7}{a}", res)
69
+ self.assertIn(r"\textcolor{#7aa2f7}{x}", res)
70
+ self.assertIn(r"\textcolor{#f7768e}{b}", res)
71
+ self.assertIn(r"\textcolor{#e0af68}{y}", res)
72
+ self.assertIn(r"\textcolor{#2ac3de}{c}", res)
73
+ self.assertIn(r"\textcolor{#9ece6a}{z}", res)
74
+
75
+ def test_dispersive_hash_unknown_variables(self):
76
+ """Verify arbitrary variables receive deterministic slots."""
77
+ s1 = hash_string_to_slot("var1")
78
+ s2 = hash_string_to_slot("var1")
79
+ self.assertEqual(s1, s2)
80
+ self.assertTrue(0 <= s1 < 8)
81
+
82
+
83
+ if __name__ == "__main__":
84
+ unittest.main()
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import json
7
+ from pathlib import Path
7
8
  import tempfile
8
9
  import time
9
10
  import unittest
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from pathlib import Path
5
6
  import tempfile
6
7
  import unittest
7
8
  try: