margin 0.8.0__tar.gz → 0.8.2__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.
Potentially problematic release.
This version of margin might be problematic. Click here for more details.
- {margin-0.8.0/margin.egg-info → margin-0.8.2}/PKG-INFO +1 -1
- {margin-0.8.0 → margin-0.8.2}/margin/__init__.py +6 -3
- {margin-0.8.0 → margin-0.8.2}/margin/causal.py +25 -1
- {margin-0.8.0 → margin-0.8.2}/margin/correlate.py +1 -0
- {margin-0.8.0 → margin-0.8.2}/margin/ledger.py +8 -0
- margin-0.8.2/margin/provenance.py +130 -0
- {margin-0.8.0 → margin-0.8.2/margin.egg-info}/PKG-INFO +1 -1
- {margin-0.8.0 → margin-0.8.2}/pyproject.toml +1 -1
- margin-0.8.0/margin/provenance.py +0 -21
- {margin-0.8.0 → margin-0.8.2}/LICENSE +0 -0
- {margin-0.8.0 → margin-0.8.2}/NOTICE +0 -0
- {margin-0.8.0 → margin-0.8.2}/README.md +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/__main__.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/algebra.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/anomaly.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/bridge.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/calibrate.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/composite.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/confidence.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/config.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/contract.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/diff.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/drift.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/events.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/forecast.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/health.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/intent.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/loop.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/observation.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/persist.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/policy/__init__.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/policy/compose.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/policy/core.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/policy/temporal.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/policy/trace.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/policy/tuning.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/policy/validate.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/predicates.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/streaming.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/transitions.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/uncertain.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin/validity.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin.egg-info/SOURCES.txt +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin.egg-info/dependency_links.txt +0 -0
- {margin-0.8.0 → margin-0.8.2}/margin.egg-info/top_level.txt +0 -0
- {margin-0.8.0 → margin-0.8.2}/setup.cfg +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_algebra.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_anomaly.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_bridge.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_calibrate.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_causal.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_composite.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_confidence.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_config.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_contract.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_correlate.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_diff.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_drift.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_events.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_forecast.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_health.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_healthcare.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_intent.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_ledger.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_loop.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_neuro.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_observation.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_persist.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_policy.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_policy_compose.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_policy_temporal.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_policy_trace.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_policy_tuning.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_policy_validate.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_predicates.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_printer3d.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_provenance.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_reverse_bridge.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_ros2.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_staleness.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_streaming.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_transformer.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_transitions.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_uncertain.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_validity.py +0 -0
- {margin-0.8.0 → margin-0.8.2}/tests/test_windowing.py +0 -0
|
@@ -17,7 +17,10 @@ Structure:
|
|
|
17
17
|
# Foundation
|
|
18
18
|
from .confidence import Confidence
|
|
19
19
|
from .validity import Validity, ValidityMode
|
|
20
|
-
from .provenance import
|
|
20
|
+
from .provenance import (
|
|
21
|
+
new_id, _new_id, are_correlated, merge, merge_provenance,
|
|
22
|
+
ProvenanceNode, ProvenanceGraph, create_root_provenance,
|
|
23
|
+
)
|
|
21
24
|
from .uncertain import UncertainValue, Source, EpistemicSource, UncertaintyMode
|
|
22
25
|
from .algebra import add, subtract, multiply, divide, scale, compare, weighted_average
|
|
23
26
|
from .health import Health, Thresholds, classify, SEVERITY
|
|
@@ -112,7 +115,8 @@ __all__ = [
|
|
|
112
115
|
# Foundation
|
|
113
116
|
"Confidence",
|
|
114
117
|
"Validity", "ValidityMode",
|
|
115
|
-
"new_id", "are_correlated", "merge",
|
|
118
|
+
"new_id", "_new_id", "are_correlated", "merge", "merge_provenance",
|
|
119
|
+
"ProvenanceNode", "ProvenanceGraph", "create_root_provenance",
|
|
116
120
|
"UncertainValue", "Source",
|
|
117
121
|
"add", "subtract", "multiply", "divide", "scale", "compare", "weighted_average",
|
|
118
122
|
"Health", "Thresholds", "classify", "SEVERITY",
|
|
@@ -195,4 +199,3 @@ CorrectionRecord = Record
|
|
|
195
199
|
CorrectionLedger = Ledger
|
|
196
200
|
classify_gap_health = classify
|
|
197
201
|
compare_to_threshold = compare
|
|
198
|
-
merge_provenance = merge
|
|
@@ -45,6 +45,10 @@ class CausalLink:
|
|
|
45
45
|
strength: 0.0-1.0, how strong the relationship is (1.0 = deterministic)
|
|
46
46
|
condition: optional — this link only applies when source is in this health state
|
|
47
47
|
evidence: free text — why we believe this link exists
|
|
48
|
+
origin: "asserted" (manually declared) or "discovered" (auto_causal_graph).
|
|
49
|
+
Asserted links are authoritative. Discovered links are correlational
|
|
50
|
+
heuristics — lag-based causal direction is plausible but not proven.
|
|
51
|
+
Never treat discovered links as ground truth in high-stakes domains.
|
|
48
52
|
"""
|
|
49
53
|
source: str
|
|
50
54
|
target: str
|
|
@@ -52,6 +56,15 @@ class CausalLink:
|
|
|
52
56
|
strength: float = 1.0
|
|
53
57
|
condition: Optional[Health] = None
|
|
54
58
|
evidence: str = ""
|
|
59
|
+
origin: str = "asserted" # "asserted" | "discovered"
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def is_asserted(self) -> bool:
|
|
63
|
+
return self.origin == "asserted"
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def is_discovered(self) -> bool:
|
|
67
|
+
return self.origin == "discovered"
|
|
55
68
|
|
|
56
69
|
def to_dict(self) -> dict:
|
|
57
70
|
d = {
|
|
@@ -59,6 +72,7 @@ class CausalLink:
|
|
|
59
72
|
"cause_type": self.cause_type.value,
|
|
60
73
|
"strength": round(self.strength, 4),
|
|
61
74
|
"evidence": self.evidence,
|
|
75
|
+
"origin": self.origin,
|
|
62
76
|
}
|
|
63
77
|
if self.condition:
|
|
64
78
|
d["condition"] = self.condition.value
|
|
@@ -72,11 +86,13 @@ class CausalLink:
|
|
|
72
86
|
strength=d.get("strength", 1.0),
|
|
73
87
|
condition=Health(d["condition"]) if "condition" in d else None,
|
|
74
88
|
evidence=d.get("evidence", ""),
|
|
89
|
+
origin=d.get("origin", "asserted"),
|
|
75
90
|
)
|
|
76
91
|
|
|
77
92
|
def __repr__(self) -> str:
|
|
78
93
|
cond = f" when {self.condition.value}" if self.condition else ""
|
|
79
|
-
|
|
94
|
+
tag = "[discovered]" if self.is_discovered else ""
|
|
95
|
+
return f"{self.source} --{self.cause_type.value}--> {self.target}{cond}{tag}"
|
|
80
96
|
|
|
81
97
|
|
|
82
98
|
# -----------------------------------------------------------------------
|
|
@@ -124,6 +140,14 @@ class CausalGraph:
|
|
|
124
140
|
"""All links where `component` is the source (what it affects)."""
|
|
125
141
|
return [l for l in self.links if l.source == component]
|
|
126
142
|
|
|
143
|
+
def asserted_links(self) -> list[CausalLink]:
|
|
144
|
+
"""Links that were manually declared (authoritative)."""
|
|
145
|
+
return [l for l in self.links if l.is_asserted]
|
|
146
|
+
|
|
147
|
+
def discovered_links(self) -> list[CausalLink]:
|
|
148
|
+
"""Links auto-discovered via correlation (heuristic, not proven)."""
|
|
149
|
+
return [l for l in self.links if l.is_discovered]
|
|
150
|
+
|
|
127
151
|
def upstream(self, component: str, depth: int = 10) -> list[str]:
|
|
128
152
|
"""All components upstream of `component` (transitive causes)."""
|
|
129
153
|
visited = set()
|
|
@@ -85,6 +85,7 @@ class Correlation:
|
|
|
85
85
|
cause_type=cause_type,
|
|
86
86
|
strength=self.strength,
|
|
87
87
|
evidence=f"auto-correlated: r={self.coefficient:.3f}, lag={self.lag}, n={self.n_samples}",
|
|
88
|
+
origin="discovered", # lag-based causal direction is a heuristic, not proven
|
|
88
89
|
)
|
|
89
90
|
|
|
90
91
|
def to_dict(self) -> dict:
|
|
@@ -109,12 +109,20 @@ class Ledger:
|
|
|
109
109
|
"""
|
|
110
110
|
Accumulates Records across a session.
|
|
111
111
|
Provides aggregate stats and renders the intervention history.
|
|
112
|
+
|
|
113
|
+
max_records: optional bound on records kept. When set, old records
|
|
114
|
+
are dropped (oldest first) on each append. Use this for
|
|
115
|
+
long-running production loops to prevent unbounded growth.
|
|
116
|
+
None (default) = unbounded, suitable for batch/replay use.
|
|
112
117
|
"""
|
|
113
118
|
label: str = ""
|
|
114
119
|
records: list[Record] = field(default_factory=list)
|
|
120
|
+
max_records: Optional[int] = None
|
|
115
121
|
|
|
116
122
|
def append(self, record: Record) -> None:
|
|
117
123
|
self.records.append(record)
|
|
124
|
+
if self.max_records is not None and len(self.records) > self.max_records:
|
|
125
|
+
self.records.pop(0)
|
|
118
126
|
|
|
119
127
|
def __len__(self) -> int:
|
|
120
128
|
return len(self.records)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provenance tracking for uncertain values.
|
|
3
|
+
Detects correlation between derived values via shared ancestry.
|
|
4
|
+
Optionally tracks full lineage via ProvenanceGraph.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import uuid
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def new_id() -> str:
|
|
15
|
+
"""Short unique ID for provenance tracking."""
|
|
16
|
+
return uuid.uuid4().hex[:8]
|
|
17
|
+
|
|
18
|
+
# margin-poc compat alias
|
|
19
|
+
_new_id = new_id
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def are_correlated(ids_a: list[str], ids_b: list[str]) -> bool:
|
|
23
|
+
"""True if two provenance chains share any common ancestor."""
|
|
24
|
+
return bool(set(ids_a) & set(ids_b))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def merge(ids_a: list[str], ids_b: list[str]) -> list[str]:
|
|
28
|
+
"""Merge two provenance chains, adding a new derived ID."""
|
|
29
|
+
return list(set(ids_a) | set(ids_b) | {new_id()})
|
|
30
|
+
|
|
31
|
+
# margin-poc compat alias
|
|
32
|
+
merge_provenance = merge
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# -----------------------------------------------------------------------
|
|
36
|
+
# Provenance graph — full lineage tracking
|
|
37
|
+
# -----------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class ProvenanceNode:
|
|
41
|
+
"""
|
|
42
|
+
A node in the provenance graph representing a single derived value.
|
|
43
|
+
|
|
44
|
+
id: unique identifier
|
|
45
|
+
operation: what created this value (e.g. "add", "measure_gap_IOI")
|
|
46
|
+
source_ids: IDs of input values this was derived from
|
|
47
|
+
"""
|
|
48
|
+
id: str
|
|
49
|
+
operation: str
|
|
50
|
+
source_ids: list[str] = field(default_factory=list)
|
|
51
|
+
|
|
52
|
+
def to_dict(self) -> dict:
|
|
53
|
+
return {"id": self.id, "operation": self.operation, "source_ids": self.source_ids}
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def from_dict(cls, d: dict) -> ProvenanceNode:
|
|
57
|
+
return cls(id=d["id"], operation=d["operation"], source_ids=d.get("source_ids", []))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ProvenanceGraph:
|
|
61
|
+
"""
|
|
62
|
+
Tracks the full lineage of uncertain values through computations.
|
|
63
|
+
|
|
64
|
+
Used to detect correlations (shared provenance affects how
|
|
65
|
+
uncertainties combine) and to explain where a value came from.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(self):
|
|
69
|
+
self.nodes: dict[str, ProvenanceNode] = {}
|
|
70
|
+
|
|
71
|
+
def add_node(self, node: ProvenanceNode) -> None:
|
|
72
|
+
self.nodes[node.id] = node
|
|
73
|
+
|
|
74
|
+
def get_node(self, node_id: str) -> Optional[ProvenanceNode]:
|
|
75
|
+
return self.nodes.get(node_id)
|
|
76
|
+
|
|
77
|
+
def find_common_ancestors(self, ids_a: set[str], ids_b: set[str]) -> set[str]:
|
|
78
|
+
"""Find common ancestor IDs between two provenance sets."""
|
|
79
|
+
return ids_a & ids_b
|
|
80
|
+
|
|
81
|
+
def trace_lineage(self, node_id: str, depth: int = 10) -> list[ProvenanceNode]:
|
|
82
|
+
"""Trace the lineage of a node back through the graph (oldest first)."""
|
|
83
|
+
lineage = []
|
|
84
|
+
to_visit = [node_id]
|
|
85
|
+
visited: set[str] = set()
|
|
86
|
+
|
|
87
|
+
while to_visit and len(lineage) < depth:
|
|
88
|
+
current_id = to_visit.pop(0)
|
|
89
|
+
if current_id in visited:
|
|
90
|
+
continue
|
|
91
|
+
visited.add(current_id)
|
|
92
|
+
|
|
93
|
+
node = self.get_node(current_id)
|
|
94
|
+
if node is None:
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
lineage.append(node)
|
|
98
|
+
to_visit.extend(node.source_ids)
|
|
99
|
+
|
|
100
|
+
return list(reversed(lineage))
|
|
101
|
+
|
|
102
|
+
def create_root(self, operation: str = "root") -> str:
|
|
103
|
+
"""Create a root provenance node and return its ID."""
|
|
104
|
+
node_id = new_id()
|
|
105
|
+
self.add_node(ProvenanceNode(id=node_id, operation=operation))
|
|
106
|
+
return node_id
|
|
107
|
+
|
|
108
|
+
def derive(self, operation: str, source_ids: list[str]) -> str:
|
|
109
|
+
"""Create a derived provenance node and return its ID."""
|
|
110
|
+
node_id = new_id()
|
|
111
|
+
self.add_node(ProvenanceNode(id=node_id, operation=operation, source_ids=source_ids))
|
|
112
|
+
return node_id
|
|
113
|
+
|
|
114
|
+
def to_dict(self) -> dict:
|
|
115
|
+
return {"nodes": {k: v.to_dict() for k, v in self.nodes.items()}}
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def from_dict(cls, d: dict) -> ProvenanceGraph:
|
|
119
|
+
g = cls()
|
|
120
|
+
for k, v in d.get("nodes", {}).items():
|
|
121
|
+
g.add_node(ProvenanceNode.from_dict(v))
|
|
122
|
+
return g
|
|
123
|
+
|
|
124
|
+
def __repr__(self) -> str:
|
|
125
|
+
return f"ProvenanceGraph({len(self.nodes)} nodes)"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def create_root_provenance(operation: str = "root") -> list[str]:
|
|
129
|
+
"""Create a fresh provenance ID for a root (measured) value."""
|
|
130
|
+
return [new_id()]
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "margin"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.2"
|
|
8
8
|
description = "Typed health classification, uncertainty algebra, and correction auditing for any system that measures things and needs to explain what happened."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Provenance tracking for uncertain values.
|
|
3
|
-
Detects correlation between derived values via shared ancestry.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import uuid
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def new_id() -> str:
|
|
10
|
-
"""Short unique ID for provenance tracking."""
|
|
11
|
-
return uuid.uuid4().hex[:8]
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def are_correlated(ids_a: list[str], ids_b: list[str]) -> bool:
|
|
15
|
-
"""True if two provenance chains share any common ancestor."""
|
|
16
|
-
return bool(set(ids_a) & set(ids_b))
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def merge(ids_a: list[str], ids_b: list[str]) -> list[str]:
|
|
20
|
-
"""Merge two provenance chains, adding a new derived ID."""
|
|
21
|
-
return list(set(ids_a) | set(ids_b) | {new_id()})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|