diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs index 177041506..68bf09f94 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs @@ -156,7 +156,7 @@ internal class Vp8Residual return LossyUtils.Vp8BitCost(0, (byte)p0); } - if (Avx2.IsSupported) + if (Sse2.IsSupported) { Span scratch = stackalloc byte[32]; Span ctxs = scratch.Slice(0, 16); @@ -165,19 +165,23 @@ internal class Vp8Residual // Precompute clamped levels and contexts, packed to 8b. ref short outputRef = ref MemoryMarshal.GetReference(this.Coeffs); - Vector256 c0 = Unsafe.As>(ref outputRef).AsInt16(); - Vector256 d0 = Avx2.Subtract(Vector256.Zero, c0); - Vector256 e0 = Avx2.Max(c0, d0); // abs(v), 16b - Vector256 f = Avx2.PackSignedSaturate(e0, e0); - Vector256 g = Avx2.Min(f.AsByte(), Vector256.Create((byte)2)); - Vector256 h = Avx2.Min(f.AsByte(), Vector256.Create((byte)67)); // clampLevel in [0..67] + Vector128 c0 = Unsafe.As>(ref outputRef).AsInt16(); + Vector128 c1 = Unsafe.As>(ref Unsafe.Add(ref outputRef, 8)).AsInt16(); + Vector128 d0 = Sse2.Subtract(Vector128.Zero, c0); + Vector128 d1 = Sse2.Subtract(Vector128.Zero, c1); + Vector128 e0 = Sse2.Max(c0, d0); // abs(v), 16b + Vector128 e1 = Sse2.Max(c1, d1); + Vector128 f = Sse2.PackSignedSaturate(e0, e1); + Vector128 g = Sse2.Min(f.AsByte(), Vector128.Create((byte)2)); // context = 0, 1, 2 + Vector128 h = Sse2.Min(f.AsByte(), Vector128.Create((byte)67)); // clampLevel in [0..67] ref byte ctxsRef = ref MemoryMarshal.GetReference(ctxs); ref byte levelsRef = ref MemoryMarshal.GetReference(levels); ref ushort absLevelsRef = ref MemoryMarshal.GetReference(absLevels); - Unsafe.As>(ref ctxsRef) = g.GetLower(); - Unsafe.As>(ref levelsRef) = h.GetLower(); - Unsafe.As>(ref absLevelsRef) = e0.AsUInt16(); + Unsafe.As>(ref ctxsRef) = g; + Unsafe.As>(ref levelsRef) = h; + Unsafe.As>(ref absLevelsRef) = e0.AsUInt16(); + Unsafe.As>(ref Unsafe.Add(ref absLevelsRef, 8)) = e1.AsUInt16(); int level; int flevel; diff --git a/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs b/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs index 1f9136e9a..4982929c2 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Text; +using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -9,10 +11,234 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp; [Trait("Format", "Webp")] public class Vp8ResidualTests { + private static void WriteVp8Residual(string filename, Vp8Residual residual) + { + using FileStream stream = File.Open(filename, FileMode.Create); + using BinaryWriter writer = new(stream, Encoding.UTF8, false); + + writer.Write(residual.First); + writer.Write(residual.Last); + writer.Write(residual.CoeffType); + + for (int i = 0; i < residual.Coeffs.Length; i++) + { + writer.Write(residual.Coeffs[i]); + } + + for (int i = 0; i < residual.Prob.Length; i++) + { + for (int j = 0; j < residual.Prob[i].Probabilities.Length; j++) + { + writer.Write(residual.Prob[i].Probabilities[j].Probabilities); + } + } + + for (int i = 0; i < residual.Costs.Length; i++) + { + Vp8Costs costs = residual.Costs[i]; + Vp8CostArray[] costsArray = costs.Costs; + for (int j = 0; j < costsArray.Length; j++) + { + for (int k = 0; k < costsArray[j].Costs.Length; k++) + { + writer.Write(costsArray[j].Costs[k]); + } + } + } + + for (int i = 0; i < residual.Stats.Length; i++) + { + for (int j = 0; j < residual.Stats[i].Stats.Length; j++) + { + for (int k = 0; k < residual.Stats[i].Stats[j].Stats.Length; k++) + { + writer.Write(residual.Stats[i].Stats[j].Stats[k]); + } + } + } + + writer.Flush(); + } + + private static Vp8Residual ReadVp8Residual(string fileName) + { + using FileStream stream = File.Open(fileName, FileMode.Open); + using BinaryReader reader = new(stream, Encoding.UTF8, false); + + Vp8Residual residual = new() + { + First = reader.ReadInt32(), + Last = reader.ReadInt32(), + CoeffType = reader.ReadInt32() + }; + + for (int i = 0; i < residual.Coeffs.Length; i++) + { + residual.Coeffs[i] = reader.ReadInt16(); + } + + Vp8BandProbas[] bandProbas = new Vp8BandProbas[8]; + for (int i = 0; i < bandProbas.Length; i++) + { + bandProbas[i] = new Vp8BandProbas(); + for (int j = 0; j < bandProbas[i].Probabilities.Length; j++) + { + for (int k = 0; k < 11; k++) + { + bandProbas[i].Probabilities[j].Probabilities[k] = reader.ReadByte(); + } + } + } + + residual.Prob = bandProbas; + + residual.Costs = new Vp8Costs[16]; + for (int i = 0; i < residual.Costs.Length; i++) + { + residual.Costs[i] = new Vp8Costs(); + Vp8CostArray[] costsArray = residual.Costs[i].Costs; + for (int j = 0; j < costsArray.Length; j++) + { + for (int k = 0; k < costsArray[j].Costs.Length; k++) + { + costsArray[j].Costs[k] = reader.ReadUInt16(); + } + } + } + + residual.Stats = new Vp8Stats[8]; + for (int i = 0; i < residual.Stats.Length; i++) + { + residual.Stats[i] = new Vp8Stats(); + for (int j = 0; j < residual.Stats[i].Stats.Length; j++) + { + for (int k = 0; k < residual.Stats[i].Stats[j].Stats.Length; k++) + { + residual.Stats[i].Stats[j].Stats[k] = reader.ReadUInt32(); + } + } + } + + return residual; + } + + [Fact] + public void Vp8Residual_Serialization_Works() + { + // arrange + Vp8Residual expected = new(); + Vp8EncProba encProb = new(); + Random rand = new(439); + CreateRandomProbas(encProb, rand); + CreateCosts(encProb, rand); + expected.Init(1, 0, encProb); + for (int i = 0; i < expected.Coeffs.Length; i++) + { + expected.Coeffs[i] = (byte)rand.Next(255); + } + + // act + string fileName = "Vp8SerializationTest.bin"; + WriteVp8Residual(fileName, expected); + Vp8Residual actual = ReadVp8Residual(fileName); + File.Delete(fileName); + + // assert + Assert.Equal(expected.CoeffType, actual.CoeffType); + Assert.Equal(expected.Coeffs, actual.Coeffs); + Assert.Equal(expected.Costs.Length, actual.Costs.Length); + Assert.Equal(expected.First, actual.First); + Assert.Equal(expected.Last, actual.Last); + Assert.Equal(expected.Stats.Length, actual.Stats.Length); + for (int i = 0; i < expected.Stats.Length; i++) + { + Vp8StatsArray[] expectedStats = expected.Stats[i].Stats; + Vp8StatsArray[] actualStats = actual.Stats[i].Stats; + Assert.Equal(expectedStats.Length, actualStats.Length); + for (int j = 0; j < expectedStats.Length; j++) + { + Assert.Equal(expectedStats[j].Stats, actualStats[j].Stats); + } + } + + Assert.Equal(expected.Prob.Length, actual.Prob.Length); + for (int i = 0; i < expected.Prob.Length; i++) + { + Vp8ProbaArray[] expectedProbabilities = expected.Prob[i].Probabilities; + Vp8ProbaArray[] actualProbabilities = actual.Prob[i].Probabilities; + Assert.Equal(expectedProbabilities.Length, actualProbabilities.Length); + for (int j = 0; j < expectedProbabilities.Length; j++) + { + Assert.Equal(expectedProbabilities[j].Probabilities, actualProbabilities[j].Probabilities); + } + } + + for (int i = 0; i < expected.Costs.Length; i++) + { + Vp8CostArray[] expectedCosts = expected.Costs[i].Costs; + Vp8CostArray[] actualCosts = actual.Costs[i].Costs; + Assert.Equal(expectedCosts.Length, actualCosts.Length); + for (int j = 0; j < expectedCosts.Length; j++) + { + Assert.Equal(expectedCosts[j].Costs, actualCosts[j].Costs); + } + } + } + + [Fact] + public void GetResidualCost_Works() + { + // arrange + int ctx0 = 0; + int expected = 20911; + Vp8Residual residual = ReadVp8Residual(Path.Combine("TestDataWebp", "Vp8Residual.bin")); + + // act + int actual = residual.GetResidualCost(ctx0); + + // assert + Assert.Equal(expected, actual); + } + + private static void CreateRandomProbas(Vp8EncProba probas, Random rand) + { + for (int t = 0; t < WebpConstants.NumTypes; ++t) + { + for (int b = 0; b < WebpConstants.NumBands; ++b) + { + for (int c = 0; c < WebpConstants.NumCtx; ++c) + { + for (int p = 0; p < WebpConstants.NumProbas; ++p) + { + probas.Coeffs[t][b].Probabilities[c].Probabilities[p] = (byte)rand.Next(255); + } + } + } + } + } + + private static void CreateCosts(Vp8EncProba probas, Random rand) + { + for (int i = 0; i < probas.RemappedCosts.Length; i++) + { + for (int j = 0; j < probas.RemappedCosts[i].Length; j++) + { + for (int k = 0; k < probas.RemappedCosts[i][j].Costs.Length; k++) + { + ushort[] costs = probas.RemappedCosts[i][j].Costs[k].Costs; + for (int m = 0; m < costs.Length; m++) + { + costs[m] = (byte)rand.Next(255); + } + } + } + } + } + private static void RunSetCoeffsTest() { // arrange - var residual = new Vp8Residual(); + Vp8Residual residual = new(); short[] coeffs = { 110, 0, -2, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0 }; // act @@ -23,11 +249,8 @@ public class Vp8ResidualTests } [Fact] - public void RunSetCoeffsTest_Works() => RunSetCoeffsTest(); - - [Fact] - public void RunSetCoeffsTest_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.AllowAll); + public void SetCoeffsTest_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.AllowAll); [Fact] - public void RunSetCoeffsTest_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.DisableHWIntrinsic); + public void SetCoeffsTest_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.DisableSSE2); } diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index a6197b600..d17cffc4f 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -56,6 +56,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/tests/ImageSharp.Tests/TestDataWebp/Vp8Residual.bin b/tests/ImageSharp.Tests/TestDataWebp/Vp8Residual.bin new file mode 100644 index 000000000..1fbb39277 Binary files /dev/null and b/tests/ImageSharp.Tests/TestDataWebp/Vp8Residual.bin differ diff --git a/tests/ImageSharp.Tests/TestDataWebp/Vp8Residual.json b/tests/ImageSharp.Tests/TestDataWebp/Vp8Residual.json new file mode 100644 index 000000000..41a524b89 --- /dev/null +++ b/tests/ImageSharp.Tests/TestDataWebp/Vp8Residual.json @@ -0,0 +1 @@ +{"First":1,"Last":15,"CoeffType":0,"Coeffs":[0,-3,-5,1,1,0,0,1,2,0,-1,-1,0,-1,-1,2],"Prob":[{"Probabilities":[{"Probabilities":"gICAgICAgICAgIA="},{"Probabilities":"gICAgICAgICAgIA="},{"Probabilities":"gICAgICAgICAgIA="}]},{"Probabilities":[{"Probabilities":"/Yj+/+TbgICAgIA="},{"Probabilities":"vYHy/+PV/9uAgIA="},{"Probabilities":"an7j/NbR//+AgIA="}]},{"Probabilities":[{"Probabilities":"AWL4/+zi//+AgIA="},{"Probabilities":"tYXu/t3q/5qAgIA="},{"Probabilities":"TobK98a0/9uAgIA="}]},{"Probabilities":[{"Probabilities":"Abn5//P/gICAgIA="},{"Probabilities":"uJb3/+zggICAgIA="},{"Probabilities":"TW7Y/+zmgICAgIA="}]},{"Probabilities":[{"Probabilities":"AWX7//H/gICAgIA="},{"Probabilities":"qovx/OzR//+AgIA="},{"Probabilities":"JXTE8+T///+AgIA="}]},{"Probabilities":[{"Probabilities":"Acz+//X/gICAgIA="},{"Probabilities":"z6D6/+6AgICAgIA="},{"Probabilities":"Zmfn/9OrgICAgIA="}]},{"Probabilities":[{"Probabilities":"AZj8//D/gICAgIA="},{"Probabilities":"sYfz/+rhgICAgIA="},{"Probabilities":"UIHT/8LggICAgIA="}]},{"Probabilities":[{"Probabilities":"AQH/gICAgICAgIA="},{"Probabilities":"9gH/gICAgICAgIA="},{"Probabilities":"/4CAgICAgICAgIA="}]}],"Stats":[{"Stats":[{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]}]},{"Stats":[{"Stats":[3145728,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]}]},{"Stats":[{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]}]},{"Stats":[{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]}]},{"Stats":[{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]}]},{"Stats":[{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]}]},{"Stats":[{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]}]},{"Stats":[{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]},{"Stats":[0,0,0,0,0,0,0,0,0,0,0]}]}],"Costs":[{"Costs":[{"Costs":[256,512,1024,1280,1280,1280,1280,1280,1280,1280,1280,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536]},{"Costs":[512,768,1280,1536,1536,1536,1536,1536,1536,1536,1536,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792]},{"Costs":[512,768,1280,1536,1536,1536,1536,1536,1536,1536,1536,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792]}]},{"Costs":[{"Costs":[234,287,2122,2957,3618,4379,4379,4379,4379,4379,4379,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635,4635]},{"Costs":[756,784,1887,2721,3318,3692,3692,4353,4353,4353,4353,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934,5934]},{"Costs":[463,503,1342,2023,2578,2810,2810,4599,4599,4599,4599,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108,5108]}]},{"Costs":[{"Costs":[355,194,1496,2462,3209,3259,3259,5048,5048,5048,5048,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557,5557]},{"Costs":[700,761,1783,2503,3379,3707,3707,3861,3861,3861,3861,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820,5820]},{"Costs":[378,504,1102,1692,2013,2333,2333,2994,2994,2994,2994,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575,4575]}]},{"Costs":[{"Costs":[121,489,1867,2959,4748,4147,4147,4147,4147,4147,4147,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403,4403]},{"Costs":[671,818,2118,3088,3805,4387,4387,4387,4387,4387,4387,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643,4643]},{"Costs":[447,410,1071,2031,2844,3340,3340,3340,3340,3340,3340,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596,3596]}]},{"Costs":[{"Costs":[192,343,1900,2902,4691,4176,4176,4176,4176,4176,4176,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432]},{"Costs":[675,739,1866,2791,3528,4132,4132,4132,4132,4132,4132,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388]},{"Costs":[398,477,1157,1633,2350,3355,3355,3355,3355,3355,3355,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611]}]},{"Costs":[{"Costs":[342,197,1751,2791,4580,4028,4028,4028,4028,4028,4028,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284,4284]},{"Costs":[632,723,1799,2794,3349,3302,3302,5091,5091,5091,5091,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600,5600]},{"Costs":[354,387,893,1672,3461,1944,1944,3733,3733,3733,3733,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242,4242]}]},{"Costs":[{"Costs":[86,596,2405,3568,5357,4688,4688,4688,4688,4688,4688,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944,4944]},{"Costs":[790,991,2421,3640,3640,4693,4693,4693,4693,4693,4693,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949,4949]},{"Costs":[527,423,1330,2054,2314,3558,3558,3558,3558,3558,3558,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814,3814]}]},{"Costs":[{"Costs":[192,343,1900,2902,4691,4176,4176,4176,4176,4176,4176,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432]},{"Costs":[675,739,1866,2791,3528,4132,4132,4132,4132,4132,4132,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388]},{"Costs":[398,477,1157,1633,2350,3355,3355,3355,3355,3355,3355,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611]}]},{"Costs":[{"Costs":[192,343,1900,2902,4691,4176,4176,4176,4176,4176,4176,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432]},{"Costs":[675,739,1866,2791,3528,4132,4132,4132,4132,4132,4132,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388]},{"Costs":[398,477,1157,1633,2350,3355,3355,3355,3355,3355,3355,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611]}]},{"Costs":[{"Costs":[192,343,1900,2902,4691,4176,4176,4176,4176,4176,4176,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432]},{"Costs":[675,739,1866,2791,3528,4132,4132,4132,4132,4132,4132,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388]},{"Costs":[398,477,1157,1633,2350,3355,3355,3355,3355,3355,3355,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611]}]},{"Costs":[{"Costs":[192,343,1900,2902,4691,4176,4176,4176,4176,4176,4176,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432]},{"Costs":[675,739,1866,2791,3528,4132,4132,4132,4132,4132,4132,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388]},{"Costs":[398,477,1157,1633,2350,3355,3355,3355,3355,3355,3355,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611]}]},{"Costs":[{"Costs":[192,343,1900,2902,4691,4176,4176,4176,4176,4176,4176,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432]},{"Costs":[675,739,1866,2791,3528,4132,4132,4132,4132,4132,4132,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388]},{"Costs":[398,477,1157,1633,2350,3355,3355,3355,3355,3355,3355,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611]}]},{"Costs":[{"Costs":[192,343,1900,2902,4691,4176,4176,4176,4176,4176,4176,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432]},{"Costs":[675,739,1866,2791,3528,4132,4132,4132,4132,4132,4132,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388]},{"Costs":[398,477,1157,1633,2350,3355,3355,3355,3355,3355,3355,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611]}]},{"Costs":[{"Costs":[192,343,1900,2902,4691,4176,4176,4176,4176,4176,4176,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432]},{"Costs":[675,739,1866,2791,3528,4132,4132,4132,4132,4132,4132,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388]},{"Costs":[398,477,1157,1633,2350,3355,3355,3355,3355,3355,3355,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611]}]},{"Costs":[{"Costs":[192,343,1900,2902,4691,4176,4176,4176,4176,4176,4176,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432,4432]},{"Costs":[675,739,1866,2791,3528,4132,4132,4132,4132,4132,4132,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388,4388]},{"Costs":[398,477,1157,1633,2350,3355,3355,3355,3355,3355,3355,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611,3611]}]},{"Costs":[{"Costs":[1792,7,2308,2564,2564,2564,2564,2564,2564,2564,2564,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820,2820]},{"Costs":[3008,1223,3524,3780,3780,3780,3780,3780,3780,3780,3780,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036,4036]},{"Costs":[2048,2304,2816,3072,3072,3072,3072,3072,3072,3072,3072,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328,3328]}]}]} \ No newline at end of file