Dimensions
==========

Project dimensions are created by combining local dimensions (from datasets) with reference dimensions (from MATER).

The combination process follows three steps:

1. **Deduplication** merges duplicate local entries before matching
2. **Matching** identifies how local dimensions relate to references
3. **Assembly** combines matched dimensions with unused references

Deduplication
-------------

Before matching with references, duplicate local entries are merged.

**When duplicates occur**
  Multiple local entries with identical ``(name, value)`` but different parents are consolidated into one entry.

**Parent merging**
  - Named hierarchy keys (e.g., ``continents``, ``custom_hierarchy``) are preserved as-is
  - Duplicate ``default`` parents are renumbered: first stays ``default``, others become ``hierarchy_1``, ``hierarchy_2``, etc.
  - All unique parent values are collected without specific order

**Example**

.. code-block:: python

   # Input: three local entries for Madrid
   [
     {"name": "location", "value": "Madrid", "parents_values": {"default": "Spain"}},
     {"name": "location", "value": "Madrid", "parents_values": {"default": "Castile"}},
     {"name": "location", "value": "Madrid", "parents_values": {"continents": "Europe"}}
   ]
   
   # Output: single merged entry
   {
     "name": "location",
     "value": "Madrid",
     "parents_values": {
       "default": "Spain",
       "hierarchy_1": "Castile",
       "continents": "Europe"
     }
   }

Matching
--------

Each deduplicated local dimension is matched against references through four cases:

**Case 1: Direct value match**
  Local ``value`` exists as reference ``value`` → Keep all reference data, add missing local parents

**Case 2: Reference equivalence match**
  Local ``value`` exists in reference ``equivalence`` → Use reference value, add local value to equivalences if not already present

**Case 3: Local equivalence match**
  Local ``equivalence`` points to reference value or equivalence → Use reference value, add local value to equivalences if not already present

**Case 4: No match**
  Local dimension not found in references → Keep local entry unchanged

Parent Merging (Cases 1-3)
---------------------------

When a local dimension matches a reference, parents are merged by adding only missing values.

**Merge logic**
  1. Keep all reference ``parents_values`` unchanged
  2. Add local parent values that don't already exist
  3. Number new parents as ``hierarchy_N`` using next available number

**Example**

.. code-block:: python

   # Local entry
   {"parents_values": {"default": "Iberia", "hierarchy_1": "Peninsula"}}
   
   # Reference entry  
   {"parents_values": {"default": "Europe", "hierarchy_1": "Peninsula"}}
   
   # Result: Peninsula already exists, only Iberia added
   {"parents_values": {
     "default": "Europe",
     "hierarchy_1": "Peninsula",
     "hierarchy_2": "Iberia"
   }}

**Default key guarantee**
  - If no ``default`` key exists, one is created by copying the first available parent value
  - The original named key is preserved
  - Example: ``{"continents": "Europe"}`` → ``{"default": "Europe", "continents": "Europe"}``

Equivalence Addition (Cases 2-3)
---------------------------------

When a local value becomes an equivalence, it's added only if not already present.

**Numbering logic**
  Find highest existing ``equiv_N`` number, increment by 1.

**Conditional addition**
  Local value is added as ``equiv_N`` **only if** it doesn't already exist in reference equivalences.

**Example**

.. code-block:: python

   # Case: local value "ES" matches reference equivalence
   # Reference already has
   {"value": "Spain", "equivalence": {"iso": "ES"}}
   
   # Local value "ES" is NOT added (already exists)
   # Result unchanged
   {"value": "Spain", "equivalence": {"iso": "ES"}}
   
   # Case: local value "SP" matches reference
   # Reference has
   {"value": "Spain", "equivalence": {"iso": "ES"}}
   
   # Local value "SP" doesn't exist, so it's added
   {"value": "Spain", "equivalence": {"iso": "ES", "equiv_1": "SP"}}

Final Assembly
--------------

After processing all local dimensions, unused reference dimensions are added to complete the dataset.

**Result composition**
  - Matched dimensions (local merged with reference)
  - Unmatched local dimensions (kept as-is)
  - Unused reference dimensions (added directly)

**Example output**

.. code-block:: text

   ✅ Processed 4 local dimensions:
      • 2 matched by value
      • 1 matched by equivalence  
      • 1 kept as-is (not in reference)
      • 7 reference dimensions added
   
   📊 Total project dimensions: 11