diff --git a/src/ChebyshevSharp/ChebyshevTT.cs b/src/ChebyshevSharp/ChebyshevTT.cs
index d3a843d..5a7d532 100644
--- a/src/ChebyshevSharp/ChebyshevTT.cs
+++ b/src/ChebyshevSharp/ChebyshevTT.cs
@@ -60,10 +60,10 @@ public class ChebyshevTT
public int NumDimensions => _numDimensions;
/// Bounds [(lo, hi), ...] for each dimension.
- public double[][] Domain => _domain;
+ public double[][] Domain => _domain.Select(d => (double[])d.Clone()).ToArray();
/// Number of Chebyshev nodes per dimension.
- public int[] NNodes => _nNodes;
+ public int[] NNodes => (int[])_nNodes.Clone();
/// Maximum TT rank.
public int MaxRank => _maxRank;
diff --git a/tests/ChebyshevSharp.Tests/TtPublicStateOwnershipTests.cs b/tests/ChebyshevSharp.Tests/TtPublicStateOwnershipTests.cs
new file mode 100644
index 0000000..e4d4ad9
--- /dev/null
+++ b/tests/ChebyshevSharp.Tests/TtPublicStateOwnershipTests.cs
@@ -0,0 +1,65 @@
+using Xunit;
+
+namespace ChebyshevSharp.Tests;
+
+public class TtPublicStateOwnershipTests
+{
+ [Fact]
+ public void Domain_property_returns_deep_snapshot()
+ {
+ var tt = new ChebyshevTT(
+ p => p[0] + p[1],
+ numDimensions: 2,
+ domain: new[] { new[] { -1.0, 1.0 }, new[] { 10.0, 20.0 } },
+ nNodes: new[] { 5, 5 });
+ tt.Build(verbose: false, method: "svd");
+
+ double[][] first = tt.Domain;
+ first[0][0] = double.NaN;
+ first[1] = new[] { -100.0, -50.0 };
+
+ double[][] second = tt.Domain;
+ Assert.Equal(-1.0, second[0][0]);
+ Assert.Equal(10.0, second[1][0]);
+ Assert.NotSame(first, second);
+ Assert.NotSame(first[0], second[0]);
+ }
+
+ [Fact]
+ public void NNodes_property_returns_snapshot()
+ {
+ var tt = new ChebyshevTT(
+ p => p[0] + p[1],
+ numDimensions: 2,
+ domain: new[] { new[] { -1.0, 1.0 }, new[] { -1.0, 1.0 } },
+ nNodes: new[] { 5, 7 });
+
+ int[] first = tt.NNodes;
+ first[0] = 999;
+
+ int[] second = tt.NNodes;
+ Assert.Equal(new[] { 5, 7 }, second);
+ Assert.NotSame(first, second);
+ }
+
+ [Fact]
+ public void Mutating_domain_snapshot_does_not_change_eval_contract()
+ {
+ var tt = new ChebyshevTT(
+ p => p[0] + 2.0 * p[1],
+ numDimensions: 2,
+ domain: new[] { new[] { -1.0, 1.0 }, new[] { -1.0, 1.0 } },
+ nNodes: new[] { 7, 7 },
+ maxRank: 4);
+ tt.Build(verbose: false, method: "svd");
+
+ double before = tt.Eval(new[] { 0.25, -0.5 });
+ double[][] domain = tt.Domain;
+ domain[0][0] = 10.0;
+ domain[0][1] = 20.0;
+
+ double after = tt.Eval(new[] { 0.25, -0.5 });
+ Assert.Equal(before, after, precision: 12);
+ Assert.Throws(() => tt.Eval(new[] { 2.0, 0.0 }));
+ }
+}