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 })); + } +}