From 02575363a64600e6519b13fe5b642102502a18b0 Mon Sep 17 00:00:00 2001 From: mavantgarderc Date: Sun, 7 Dec 2025 03:53:47 +0330 Subject: [PATCH 1/5] Algorithms.Tests(GraphTraversals.Dfs) --- .../Bfs.AllPathBfs.Tests.cs | 0 .../Bfs.BfsWithPredicate.Tests.cs | 0 .../Bfs.ConnectedComponents.Tests.cs | 0 .../Bfs.DetectCycleUnidirected.Tests.cs | 0 .../Bfs.DistanceFromSource.Tests.cs | 0 .../Bfs.GraphExtensions.Tests.cs | 0 .../Bfs.KnightPath.Tests.cs | 0 .../Bfs.MultiSourceBfs.Tests.cs | 0 .../Bfs.NumberOfIslands.Tests.cs | 0 .../Bfs.ShortestPathInGrid.Tests.cs | 0 .../Bfs.TopologicalSortKahn.Tests.cs | 0 .../Bfs.WordLadder.Tests.cs | 0 .../GraphTraversals/Dfs.AllPathsDfs.Tests.cs | 43 +++++++++++++ .../Dfs.ConnectedComponents.Tests.cs | 26 ++++++++ .../GraphTraversals/Dfs.DfsOrder.Tests.cs | 62 +++++++++++++++++++ .../Dfs.DfsWithPredicate.Tests.cs | 30 +++++++++ .../Dfs.HasCycleDirected.Tests.cs | 32 ++++++++++ .../Dfs.HasCycleUnidirected.Tests.cs | 31 ++++++++++ .../GraphTraversals/Dfs.HasPath.Tests.cs | 29 +++++++++ .../Dfs.TopologicalSortDfs.Tests.cs | 25 ++++++++ 20 files changed, 278 insertions(+) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.AllPathBfs.Tests.cs (100%) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.BfsWithPredicate.Tests.cs (100%) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.ConnectedComponents.Tests.cs (100%) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.DetectCycleUnidirected.Tests.cs (100%) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.DistanceFromSource.Tests.cs (100%) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.GraphExtensions.Tests.cs (100%) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.KnightPath.Tests.cs (100%) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.MultiSourceBfs.Tests.cs (100%) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.NumberOfIslands.Tests.cs (100%) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.ShortestPathInGrid.Tests.cs (100%) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.TopologicalSortKahn.Tests.cs (100%) rename tests/Csdsa.Tests/Algorithms/{Bfs => GraphTraversals}/Bfs.WordLadder.Tests.cs (100%) create mode 100644 tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.AllPathsDfs.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.ConnectedComponents.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.DfsOrder.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.DfsWithPredicate.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasCycleDirected.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasCycleUnidirected.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasPath.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.TopologicalSortDfs.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.AllPathBfs.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.AllPathBfs.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.AllPathBfs.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.AllPathBfs.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.BfsWithPredicate.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.BfsWithPredicate.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.BfsWithPredicate.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.BfsWithPredicate.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.ConnectedComponents.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.ConnectedComponents.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.ConnectedComponents.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.ConnectedComponents.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.DetectCycleUnidirected.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.DetectCycleUnidirected.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.DetectCycleUnidirected.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.DetectCycleUnidirected.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.DistanceFromSource.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.DistanceFromSource.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.DistanceFromSource.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.DistanceFromSource.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.GraphExtensions.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.GraphExtensions.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.GraphExtensions.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.GraphExtensions.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.KnightPath.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.KnightPath.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.KnightPath.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.KnightPath.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.MultiSourceBfs.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.MultiSourceBfs.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.MultiSourceBfs.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.MultiSourceBfs.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.NumberOfIslands.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.NumberOfIslands.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.NumberOfIslands.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.NumberOfIslands.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.ShortestPathInGrid.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.ShortestPathInGrid.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.ShortestPathInGrid.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.ShortestPathInGrid.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.TopologicalSortKahn.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.TopologicalSortKahn.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.TopologicalSortKahn.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.TopologicalSortKahn.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Bfs/Bfs.WordLadder.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.WordLadder.Tests.cs similarity index 100% rename from tests/Csdsa.Tests/Algorithms/Bfs/Bfs.WordLadder.Tests.cs rename to tests/Csdsa.Tests/Algorithms/GraphTraversals/Bfs.WordLadder.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.AllPathsDfs.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.AllPathsDfs.Tests.cs new file mode 100644 index 0000000..b53e035 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.AllPathsDfs.Tests.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; + +using Csdsa.Algorithms.GraphTraversal; +using Csdsa.DataStructures.Graphs; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.GraphTraversal; + +public sealed partial class DfsTests +{ + [Fact] + public void AllPathsDfs_FindsAllSimplePaths_InDag() + { + Graph graph = CreateDag(); + + IReadOnlyList> paths = Dfs.AllPathsDFS(graph, 0, 3, maxDepth: 4); + + Assert.Equal(2, paths.Count); + + List[] expectedPaths = + { + new List { 0, 1, 3 }, + new List { 0, 2, 3 }, + }; + + Assert.All( + expectedPaths, + expected => Assert.Contains(paths, p => p.SequenceEqual(expected))); + } + + [Fact] + public void AllPathsDfs_RespectsMaxDepth() + { + Graph graph = CreateDag(); + + // With maxDepth = 2, no path from 0 to 3 fits (paths are length 3). + IReadOnlyList> paths = Dfs.AllPathsDFS(graph, 0, 3, maxDepth: 2); + + Assert.Empty(paths); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.ConnectedComponents.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.ConnectedComponents.Tests.cs new file mode 100644 index 0000000..c1e3199 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.ConnectedComponents.Tests.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Linq; + +using Csdsa.Algorithms.GraphTraversal; +using Csdsa.DataStructures.Graphs; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.GraphTraversal; + +public sealed partial class DfsTests +{ + [Fact] + public void ConnectedComponents_FindsAllComponents() + { + Graph graph = new Graph(isDirected: false); + graph.AddEdge(0, 1); + graph.AddEdge(2, 3); + + IReadOnlyList> components = Dfs.ConnectedComponents(graph); + + Assert.Equal(2, components.Count); + Assert.Contains(components, c => c.Contains(0) && c.Contains(1)); + Assert.Contains(components, c => c.Contains(2) && c.Contains(3)); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.DfsOrder.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.DfsOrder.Tests.cs new file mode 100644 index 0000000..b8deeab --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.DfsOrder.Tests.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; + +using Csdsa.Algorithms.GraphTraversal; +using Csdsa.DataStructures.Graphs; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.GraphTraversal; + +public sealed partial class DfsTests +{ + private static Graph CreateSimpleGraph() + { + // 0 -- 1 -- 2 + // | | + // +----3----+ + Graph graph = new Graph(isDirected: false); + graph.AddEdge(0, 1); + graph.AddEdge(1, 2); + graph.AddEdge(0, 3); + graph.AddEdge(3, 2); + return graph; + } + + private static Graph CreateDag() + { + // 0 -> 1 -> 3 + // \> 2 -/ + Graph graph = new Graph(isDirected: true); + graph.AddEdge(0, 1); + graph.AddEdge(1, 3); + graph.AddEdge(0, 2); + graph.AddEdge(2, 3); + return graph; + } + + [Fact] + public void DfsOrder_Works_OnSimpleGraph() + { + Graph graph = CreateSimpleGraph(); + + IReadOnlyList order = Dfs.DfsOrder(graph, 0); + + Assert.Equal(4, order.Count); + Assert.Equal(0, order[0]); + Assert.Contains(1, order); + Assert.Contains(2, order); + Assert.Contains(3, order); + } + + [Fact] + public void DfsOrder_Works_OnSingleVertex() + { + Graph graph = new Graph(isDirected: false); + graph.AddVertex(0); + + IReadOnlyList order = Dfs.DfsOrder(graph, 0); + + Assert.Single(order); + Assert.Equal(0, order[0]); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.DfsWithPredicate.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.DfsWithPredicate.Tests.cs new file mode 100644 index 0000000..dcf062b --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.DfsWithPredicate.Tests.cs @@ -0,0 +1,30 @@ +using Csdsa.Algorithms.GraphTraversal; +using Csdsa.DataStructures.Graphs; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.GraphTraversal; + +public sealed partial class DfsTests +{ + [Fact] + public void DfsWithPredicate_ReturnsDepth_WhenMatchExists() + { + Graph graph = CreateSimpleGraph(); + + int depth = Dfs.DFSWithPredicate(graph, 0, u => u == 2); + + // In the simple graph, a DFS from 0 will reach 2 with depth 2. + Assert.Equal(2, depth); + } + + [Fact] + public void DfsWithPredicate_ReturnsMinusOne_WhenNoMatchExists() + { + Graph graph = CreateSimpleGraph(); + + int depth = Dfs.DFSWithPredicate(graph, 0, u => u == 99); + + Assert.Equal(-1, depth); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasCycleDirected.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasCycleDirected.Tests.cs new file mode 100644 index 0000000..612ba91 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasCycleDirected.Tests.cs @@ -0,0 +1,32 @@ +using Csdsa.Algorithms.GraphTraversal; +using Csdsa.DataStructures.Graphs; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.GraphTraversal; + +public sealed partial class DfsTests +{ + [Fact] + public void HasCycleDirected_ReturnsFalse_ForAcyclicDirectedGraph() + { + Graph graph = CreateDag(); + + bool hasCycle = Dfs.HasCycleDirected(graph); + + Assert.False(hasCycle); + } + + [Fact] + public void HasCycleDirected_ReturnsTrue_ForDirectedCycle() + { + Graph graph = new Graph(isDirected: true); + graph.AddEdge(0, 1); + graph.AddEdge(1, 2); + graph.AddEdge(2, 0); + + bool hasCycle = Dfs.HasCycleDirected(graph); + + Assert.True(hasCycle); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasCycleUnidirected.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasCycleUnidirected.Tests.cs new file mode 100644 index 0000000..6c15ddd --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasCycleUnidirected.Tests.cs @@ -0,0 +1,31 @@ +using Csdsa.Algorithms.GraphTraversal; +using Csdsa.DataStructures.Graphs; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.GraphTraversal; + +public sealed partial class DfsTests +{ + [Fact] + public void HasCycleUnidirected_ReturnsFalse_ForAcyclicUndirectedGraph() + { + Graph graph = new Graph(isDirected: false); + graph.AddEdge(0, 1); + graph.AddEdge(1, 2); + + bool hasCycle = Dfs.HasCycleUndirected(graph); + + Assert.False(hasCycle); + } + + [Fact] + public void HasCycleUnidirected_ReturnsTrue_ForUndirectedCycle() + { + Graph graph = CreateSimpleGraph(); + + bool hasCycle = Dfs.HasCycleUndirected(graph); + + Assert.True(hasCycle); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasPath.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasPath.Tests.cs new file mode 100644 index 0000000..eebc3db --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.HasPath.Tests.cs @@ -0,0 +1,29 @@ +using Csdsa.Algorithms.GraphTraversal; +using Csdsa.DataStructures.Graphs; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.GraphTraversal; + +public sealed partial class DfsTests +{ + [Fact] + public void HasPath_ReturnsTrue_WhenPathExists() + { + Graph graph = CreateSimpleGraph(); + + bool hasPath = Dfs.HasPath(graph, 0, 2); + + Assert.True(hasPath); + } + + [Fact] + public void HasPath_ReturnsFalse_WhenPathDoesNotExist() + { + Graph graph = CreateSimpleGraph(); + + bool hasPath = Dfs.HasPath(graph, 0, 99); + + Assert.False(hasPath); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.TopologicalSortDfs.Tests.cs b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.TopologicalSortDfs.Tests.cs new file mode 100644 index 0000000..ab2fb68 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/GraphTraversals/Dfs.TopologicalSortDfs.Tests.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +using Csdsa.Algorithms.GraphTraversal; +using Csdsa.DataStructures.Graphs; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.GraphTraversal; + +public sealed partial class DfsTests +{ + [Fact] + public void TopologicalSortDfs_Works_OnDag() + { + Graph graph = CreateDag(); + + IReadOnlyList topo = Dfs.TopologicalSortDFS(graph); + + Assert.Equal(4, topo.Count); + Assert.True(topo.IndexOf(0) < topo.IndexOf(1)); + Assert.True(topo.IndexOf(0) < topo.IndexOf(2)); + Assert.True(topo.IndexOf(1) < topo.IndexOf(3)); + Assert.True(topo.IndexOf(2) < topo.IndexOf(3)); + } +} From 433cc351d4846f1172c7f69442706e0114e42ac6 Mon Sep 17 00:00:00 2001 From: mavantgarderc Date: Sun, 7 Dec 2025 04:14:11 +0330 Subject: [PATCH 2/5] Algorithms(Search.Linear) --- .../Search/Linear/LinearSSearch.IndexOf.cs | 37 +++++++++++++ .../Search/Linear/LinearSearch.AllMatch.cs | 36 +++++++++++++ .../Search/Linear/LinearSearch.AnyMatch.cs | 33 ++++++++++++ .../Linear/LinearSearch.ContainsMatch.cs | 25 +++++++++ .../Linear/LinearSearch.CountMatches.cs | 32 +++++++++++ .../Linear/LinearSearch.FindAllAlias.cs | 19 +++++++ .../Linear/LinearSearch.FindAllMatches.cs | 34 ++++++++++++ .../LinearSearch.FirstOrDefaultMatch.cs | 36 +++++++++++++ .../Search/Linear/LinearSearch.LastIndexOf.cs | 38 +++++++++++++ .../Linear/LinearSearch.RemoveAllMatches.cs | 34 ++++++++++++ .../LinearSearch.SingleOrDefaultMatch.cs | 45 ++++++++++++++++ .../Algorithms/Search/Linear/LinearSearch.cs | 53 +++++++++++++++++++ 12 files changed, 422 insertions(+) create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSSearch.IndexOf.cs create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSearch.AllMatch.cs create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSearch.AnyMatch.cs create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSearch.ContainsMatch.cs create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSearch.CountMatches.cs create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSearch.FindAllAlias.cs create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSearch.FindAllMatches.cs create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSearch.FirstOrDefaultMatch.cs create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSearch.LastIndexOf.cs create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSearch.RemoveAllMatches.cs create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSearch.SingleOrDefaultMatch.cs create mode 100644 src/Csdsa/Algorithms/Search/Linear/LinearSearch.cs diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSSearch.IndexOf.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSSearch.IndexOf.cs new file mode 100644 index 0000000..2efb088 --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSSearch.IndexOf.cs @@ -0,0 +1,37 @@ +namespace Csdsa.Algorithms.Search.Linear; + +public static partial class LinearSearch +{ + /// + /// Returns the zero-based index of the first element in the sequence + /// that satisfies the specified predicate, or -1 if no such element exists. + /// + /// The element type of the sequence. + /// The sequence to search. + /// The predicate used to test each element. + /// + /// The zero-based index of the first matching element, or -1 if no match is found. + /// + /// + /// Thrown when or is . + /// + public static int IndexOf(this IEnumerable collection, Func match) + { + ArgumentNullException.ThrowIfNull(collection); + ArgumentNullException.ThrowIfNull(match); + + int index = 0; + + foreach (T item in collection) + { + if (match(item)) + { + return index; + } + + index++; + } + + return -1; + } +} diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSearch.AllMatch.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.AllMatch.cs new file mode 100644 index 0000000..c721612 --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.AllMatch.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace Csdsa.Algorithms.Search.Linear; + +public static partial class LinearSearch +{ + /// + /// Determines whether all elements in the sequence satisfy the specified predicate. + /// + /// The element type of the sequence. + /// The sequence to test. + /// The predicate used to test each element. + /// + /// if all elements satisfy ; + /// otherwise, . + /// + /// + /// Thrown when or is . + /// + public static bool AllMatch(this IEnumerable collection, Func match) + { + ArgumentNullException.ThrowIfNull(collection); + ArgumentNullException.ThrowIfNull(match); + + foreach (T item in collection) + { + if (!match(item)) + { + return false; + } + } + + return true; + } +} diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSearch.AnyMatch.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.AnyMatch.cs new file mode 100644 index 0000000..cd0ae66 --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.AnyMatch.cs @@ -0,0 +1,33 @@ +namespace Csdsa.Algorithms.Search.Linear; + +public static partial class LinearSearch +{ + /// + /// Determines whether any element in the sequence satisfies the specified predicate. + /// + /// The element type of the sequence. + /// The sequence to test. + /// The predicate used to test each element. + /// + /// if any element satisfies ; + /// otherwise, . + /// + /// + /// Thrown when or is . + /// + public static bool AnyMatch(this IEnumerable collection, Func match) + { + ArgumentNullException.ThrowIfNull(collection); + ArgumentNullException.ThrowIfNull(match); + + foreach (T item in collection) + { + if (match(item)) + { + return true; + } + } + + return false; + } +} diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSearch.ContainsMatch.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.ContainsMatch.cs new file mode 100644 index 0000000..22c8f04 --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.ContainsMatch.cs @@ -0,0 +1,25 @@ +namespace Csdsa.Algorithms.Search.Linear; + +public static partial class LinearSearch +{ + /// + /// Determines whether any element in the sequence satisfies the specified predicate. + /// + /// The element type of the sequence. + /// The sequence to search. + /// The predicate used to test each element. + /// + /// if any element satisfies ; + /// otherwise, . + /// + /// + /// Thrown when or is . + /// + public static bool ContainsMatch(this IEnumerable collection, Func match) + { + ArgumentNullException.ThrowIfNull(collection); + ArgumentNullException.ThrowIfNull(match); + + return collection.IndexOf(match) != -1; + } +} diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSearch.CountMatches.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.CountMatches.cs new file mode 100644 index 0000000..1bd9b04 --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.CountMatches.cs @@ -0,0 +1,32 @@ +namespace Csdsa.Algorithms.Search.Linear; + +public static partial class LinearSearch +{ + /// + /// Counts the number of elements in the sequence that satisfy the specified predicate. + /// + /// The element type of the sequence. + /// The sequence to search. + /// The predicate used to test each element. + /// The number of elements satisfying . + /// + /// Thrown when or is . + /// + public static int CountMatches(this IEnumerable collection, Func match) + { + ArgumentNullException.ThrowIfNull(collection); + ArgumentNullException.ThrowIfNull(match); + + int count = 0; + + foreach (T item in collection) + { + if (match(item)) + { + count++; + } + } + + return count; + } +} diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSearch.FindAllAlias.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.FindAllAlias.cs new file mode 100644 index 0000000..aae0783 --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.FindAllAlias.cs @@ -0,0 +1,19 @@ +namespace Csdsa.Algorithms.Search.Linear; + +public static partial class LinearSearch +{ + /// + /// Returns a list of all elements in the sequence that satisfy the specified predicate. + /// This is an alias for . + /// + /// The element type of the sequence. + /// The sequence to search. + /// The predicate used to test each element. + /// + /// A read-only list containing all elements that satisfy . + /// + public static IReadOnlyList FindAll(this IEnumerable collection, Func match) + { + return FindAllMatches(collection, match); + } +} diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSearch.FindAllMatches.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.FindAllMatches.cs new file mode 100644 index 0000000..e605096 --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.FindAllMatches.cs @@ -0,0 +1,34 @@ +namespace Csdsa.Algorithms.Search.Linear; + +public static partial class LinearSearch +{ + /// + /// Returns a list of all elements in the sequence that satisfy the specified predicate. + /// + /// The element type of the sequence. + /// The sequence to search. + /// The predicate used to test each element. + /// + /// A read-only list containing all elements that satisfy . + /// + /// + /// Thrown when or is . + /// + public static IReadOnlyList FindAllMatches(this IEnumerable collection, Func match) + { + ArgumentNullException.ThrowIfNull(collection); + ArgumentNullException.ThrowIfNull(match); + + List result = new List(); + + foreach (T item in collection) + { + if (match(item)) + { + result.Add(item); + } + } + + return result; + } +} diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSearch.FirstOrDefaultMatch.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.FirstOrDefaultMatch.cs new file mode 100644 index 0000000..f943bbe --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.FirstOrDefaultMatch.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace Csdsa.Algorithms.Search.Linear; + +public static partial class LinearSearch +{ + /// + /// Returns the first element in the sequence that satisfies the specified predicate, + /// or the default value of if no such element is found. + /// + /// The element type of the sequence. + /// The sequence to search. + /// The predicate used to test each element. + /// + /// The first matching element, or default(T) if no match is found. + /// + /// + /// Thrown when or is . + /// + public static T FirstOrDefaultMatch(this IEnumerable collection, Func match) + { + ArgumentNullException.ThrowIfNull(collection); + ArgumentNullException.ThrowIfNull(match); + + foreach (T item in collection) + { + if (match(item)) + { + return item; + } + } + + return default; + } +} diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSearch.LastIndexOf.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.LastIndexOf.cs new file mode 100644 index 0000000..ef51259 --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.LastIndexOf.cs @@ -0,0 +1,38 @@ +namespace Csdsa.Algorithms.Search.Linear; + +public static partial class LinearSearch +{ + /// + /// Returns the zero-based index of the last element in the sequence that + /// satisfies the specified predicate, or -1 if no such element exists. + /// + /// The element type of the sequence. + /// The sequence to search. + /// The predicate used to test each element. + /// + /// The zero-based index of the last matching element, or -1 if no match is found. + /// + /// + /// Thrown when or is . + /// + public static int LastIndexOf(this IEnumerable collection, Func match) + { + ArgumentNullException.ThrowIfNull(collection); + ArgumentNullException.ThrowIfNull(match); + + int lastIndex = -1; + int index = 0; + + foreach (T item in collection) + { + if (match(item)) + { + lastIndex = index; + } + + index++; + } + + return lastIndex; + } +} diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSearch.RemoveAllMatches.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.RemoveAllMatches.cs new file mode 100644 index 0000000..8dbdb3b --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.RemoveAllMatches.cs @@ -0,0 +1,34 @@ +namespace Csdsa.Algorithms.Search.Linear; + +public static partial class LinearSearch +{ + /// + /// Removes all elements from the list that satisfy the specified predicate + /// and returns the number of removed elements. + /// + /// The element type of the list. + /// The list to modify. + /// The predicate used to test each element. + /// The number of elements removed from . + /// + /// Thrown when or is . + /// + public static int RemoveAllMatches(this List collection, Func match) + { + ArgumentNullException.ThrowIfNull(collection); + ArgumentNullException.ThrowIfNull(match); + + int removed = 0; + + for (int i = collection.Count - 1; i >= 0; i--) + { + if (match(collection[i])) + { + collection.RemoveAt(i); + removed++; + } + } + + return removed; + } +} diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSearch.SingleOrDefaultMatch.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.SingleOrDefaultMatch.cs new file mode 100644 index 0000000..cb84f8f --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.SingleOrDefaultMatch.cs @@ -0,0 +1,45 @@ +namespace Csdsa.Algorithms.Search.Linear; + +public static partial class LinearSearch +{ + /// + /// Returns the single element in the sequence that satisfies the specified + /// predicate, or the default value of if no such + /// element is found or if more than one matching element exists. + /// + /// The element type of the sequence. + /// The sequence to search. + /// The predicate used to test each element. + /// + /// The single matching element, or default(T) if zero or multiple matches are found. + /// + /// + /// Thrown when or is . + /// + public static T SingleOrDefaultMatch(this IEnumerable collection, Func match) + { + ArgumentNullException.ThrowIfNull(collection); + ArgumentNullException.ThrowIfNull(match); + + T matchItem = default; + int count = 0; + + foreach (T item in collection) + { + if (!match(item)) + { + continue; + } + + matchItem = item; + count++; + + if (count > 1) + { + return default; + } + } + + return matchItem; + } +} diff --git a/src/Csdsa/Algorithms/Search/Linear/LinearSearch.cs b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.cs new file mode 100644 index 0000000..bc09f90 --- /dev/null +++ b/src/Csdsa/Algorithms/Search/Linear/LinearSearch.cs @@ -0,0 +1,53 @@ +namespace Csdsa.Algorithms.Search.Linear; + +/// +/// Provides linear search (O(n)) extension methods over . +/// +/// Concepts: +/// +/// +/// +/// +/// Generics (T) to enable reusable algorithms across different collection types. +/// +/// +/// +/// +/// Extension methods for enhancing existing types without modifying their original definitions. +/// +/// +/// +/// +/// Higher-order functions () for flexible match criteria. +/// +/// +/// +/// +/// Algorithm complexity of linear search and its O(n) performance characteristics. +/// +/// +/// +/// +/// Key practices: +/// +/// +/// +/// Implementing extension methods to enhance functionality. +/// +/// +/// Designing generic algorithms for maximum code reuse. +/// +/// +/// +/// Staying type-safe by employing predicates +/// instead of ad-hoc casting. +/// +/// +/// +/// Maintaining clear semantics for "first", "last", "single", and "all/any" matches. +/// +/// +/// +public static partial class LinearSearch +{ +} From 0e40157aa6a9bb5f1a66ee0a64103e4cbb2f9830 Mon Sep 17 00:00:00 2001 From: mavantgarderc Date: Sun, 7 Dec 2025 04:14:28 +0330 Subject: [PATCH 3/5] Algorithms.Tests(Search.Linear) --- .../LinearSSearch.RemoveAllMatches.Tests.cs | 32 ++++++++++++++++ .../Linear/LinearSearch.AllMatch.Tests.cs | 28 ++++++++++++++ .../Linear/LinearSearch.AnyMatch.Tests.cs | 28 ++++++++++++++ .../LinearSearch.ContainsMatch.Tests.cs | 28 ++++++++++++++ .../Linear/LinearSearch.CountMatches.Tests.cs | 28 ++++++++++++++ .../Linear/LinearSearch.FindAllAlias.Tests.cs | 31 +++++++++++++++ .../LinearSearch.FindAllMatches.Tests.cs | 32 ++++++++++++++++ .../LinearSearch.FirstOrDefaultMatch.Tests.cs | 28 ++++++++++++++ .../Linear/LinearSearch.IndexOf.Tests.cs | 28 ++++++++++++++ .../Linear/LinearSearch.LastIndexOf.Tests.cs | 28 ++++++++++++++ ...LinearSearch.SingleOrDefaultMatch.Tests.cs | 38 +++++++++++++++++++ .../Search/Linear/LinearSearch.Tests.cs | 28 ++++++++++++++ 12 files changed, 357 insertions(+) create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSSearch.RemoveAllMatches.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.AllMatch.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.AnyMatch.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.ContainsMatch.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.CountMatches.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FindAllAlias.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FindAllMatches.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FirstOrDefaultMatch.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.IndexOf.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.LastIndexOf.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.SingleOrDefaultMatch.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSSearch.RemoveAllMatches.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSSearch.RemoveAllMatches.Tests.cs new file mode 100644 index 0000000..9c39ad6 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSSearch.RemoveAllMatches.Tests.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void RemoveAllMatches_RemovesCorrectNumber() + { + List collection = new List { 1, 2, 3, 2 }; + + int removed = collection.RemoveAllMatches(x => x == 2); + + Assert.Equal(2, removed); + Assert.DoesNotContain(2, collection); + } + + [Fact] + public void RemoveAllMatches_RemovesNone_WhenNoMatch() + { + List collection = new List { 1, 2, 3 }; + + int removed = collection.RemoveAllMatches(x => x == 99); + + Assert.Equal(0, removed); + Assert.Equal(new[] { 1, 2, 3 }, collection); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.AllMatch.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.AllMatch.Tests.cs new file mode 100644 index 0000000..dd3884f --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.AllMatch.Tests.cs @@ -0,0 +1,28 @@ +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void AllMatch_ReturnsTrue_WhenAllElementsMatch() + { + int[] collection = { 2, 4, 6 }; + + bool result = collection.AllMatch(x => x % 2 == 0); + + Assert.True(result); + } + + [Fact] + public void AllMatch_ReturnsFalse_WhenAnyElementDoesNotMatch() + { + int[] collection = { 2, 3, 4 }; + + bool result = collection.AllMatch(x => x % 2 == 0); + + Assert.False(result); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.AnyMatch.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.AnyMatch.Tests.cs new file mode 100644 index 0000000..bb757ab --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.AnyMatch.Tests.cs @@ -0,0 +1,28 @@ +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void AnyMatch_ReturnsTrue_WhenAnyElementMatches() + { + int[] collection = { 1, 2, 3 }; + + bool result = collection.AnyMatch(x => x == 2); + + Assert.True(result); + } + + [Fact] + public void AnyMatch_ReturnsFalse_WhenNoElementMatches() + { + int[] collection = { 1, 2, 3 }; + + bool result = collection.AnyMatch(x => x == 99); + + Assert.False(result); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.ContainsMatch.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.ContainsMatch.Tests.cs new file mode 100644 index 0000000..2d4d8a6 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.ContainsMatch.Tests.cs @@ -0,0 +1,28 @@ +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void ContainsMatch_ReturnsTrue_IfMatchExists() + { + string[] collection = { "foo", "bar" }; + + bool result = collection.ContainsMatch(s => s == "foo"); + + Assert.True(result); + } + + [Fact] + public void ContainsMatch_ReturnsFalse_IfNoMatchExists() + { + string[] collection = { "foo", "bar" }; + + bool result = collection.ContainsMatch(s => s == "baz"); + + Assert.False(result); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.CountMatches.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.CountMatches.Tests.cs new file mode 100644 index 0000000..22cf707 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.CountMatches.Tests.cs @@ -0,0 +1,28 @@ +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void CountMatches_CountsCorrectly() + { + int[] collection = { 1, 2, 2, 3 }; + + int count = collection.CountMatches(x => x == 2); + + Assert.Equal(2, count); + } + + [Fact] + public void CountMatches_ReturnsZero_WhenNoMatch() + { + int[] collection = { 1, 2, 3 }; + + int count = collection.CountMatches(x => x == 99); + + Assert.Equal(0, count); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FindAllAlias.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FindAllAlias.Tests.cs new file mode 100644 index 0000000..8c048d6 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FindAllAlias.Tests.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void FindAll_Alias_ReturnsAllMatches() + { + int[] collection = { 1, 2, 2, 3 }; + + IReadOnlyList all = collection.FindAll(x => x == 2); + + Assert.Equal(2, all.Count); + Assert.All(all, item => Assert.Equal(2, item)); + } + + [Fact] + public void FindAll_Alias_ReturnsEmpty_WhenNoMatches() + { + int[] collection = { 1, 2, 3 }; + + IReadOnlyList all = collection.FindAll(x => x == 99); + + Assert.Empty(all); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FindAllMatches.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FindAllMatches.Tests.cs new file mode 100644 index 0000000..9477a84 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FindAllMatches.Tests.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void FindAllMatches_FindsAllMatchingElements() + { + int[] collection = { 1, 2, 3, 4, 5 }; + + IReadOnlyList matches = LinearSearch.FindAllMatches(collection, x => x % 2 == 0); + + Assert.Equal(2, matches.Count); + Assert.Contains(2, matches); + Assert.Contains(4, matches); + } + + [Fact] + public void FindAllMatches_ReturnsEmpty_WhenNoMatches() + { + int[] collection = { 1, 3, 5 }; + + IReadOnlyList matches = LinearSearch.FindAllMatches(collection, x => x % 2 == 0); + + Assert.Empty(matches); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FirstOrDefaultMatch.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FirstOrDefaultMatch.Tests.cs new file mode 100644 index 0000000..11e607b --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.FirstOrDefaultMatch.Tests.cs @@ -0,0 +1,28 @@ +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void FirstOrDefaultMatch_FindsFirstMatch() + { + int[] collection = { 1, 2, 3 }; + + int first = collection.FirstOrDefaultMatch(x => x > 1); + + Assert.Equal(2, first); + } + + [Fact] + public void FirstOrDefaultMatch_ReturnsDefault_WhenNoMatch() + { + int[] collection = { 1, 2, 3 }; + + int first = collection.FirstOrDefaultMatch(x => x == 99); + + Assert.Equal(0, first); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.IndexOf.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.IndexOf.Tests.cs new file mode 100644 index 0000000..dc7605c --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.IndexOf.Tests.cs @@ -0,0 +1,28 @@ +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void IndexOf_FindsFirstMatch() + { + int[] collection = { 1, 2, 3, 4, 5 }; + + int index = collection.IndexOf(x => x == 4); + + Assert.Equal(3, index); + } + + [Fact] + public void IndexOf_ReturnsMinusOne_IfNotFound() + { + int[] collection = { 1, 2, 3 }; + + int index = collection.IndexOf(x => x == 10); + + Assert.Equal(-1, index); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.LastIndexOf.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.LastIndexOf.Tests.cs new file mode 100644 index 0000000..084998d --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.LastIndexOf.Tests.cs @@ -0,0 +1,28 @@ +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void LastIndexOf_FindsLastMatch() + { + int[] collection = { 1, 2, 2, 4 }; + + int lastIndex = collection.LastIndexOf(x => x == 2); + + Assert.Equal(2, lastIndex); + } + + [Fact] + public void LastIndexOf_ReturnsMinusOne_WhenNoMatch() + { + int[] collection = { 1, 2, 3 }; + + int lastIndex = collection.LastIndexOf(x => x == 99); + + Assert.Equal(-1, lastIndex); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.SingleOrDefaultMatch.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.SingleOrDefaultMatch.Tests.cs new file mode 100644 index 0000000..434dd2c --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.SingleOrDefaultMatch.Tests.cs @@ -0,0 +1,38 @@ +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void SingleOrDefaultMatch_ReturnsSingleMatch() + { + int[] collection = { 1, 2, 3 }; + + int match = collection.SingleOrDefaultMatch(x => x == 2); + + Assert.Equal(2, match); + } + + [Fact] + public void SingleOrDefaultMatch_ReturnsDefault_WhenMultipleMatches() + { + int[] collection = { 1, 2, 2 }; + + int match = collection.SingleOrDefaultMatch(x => x == 2); + + Assert.Equal(0, match); + } + + [Fact] + public void SingleOrDefaultMatch_ReturnsDefault_WhenNoMatch() + { + int[] collection = { 1, 2, 3 }; + + int match = collection.SingleOrDefaultMatch(x => x == 99); + + Assert.Equal(0, match); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.Tests.cs b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.Tests.cs new file mode 100644 index 0000000..2284f34 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Search/Linear/LinearSearch.Tests.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +using Csdsa.Algorithms.Search.Linear; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Search.Linear; + +public sealed partial class LinearSearchTests +{ + [Fact] + public void LinearSearch_Integration_WorksAcrossCommonOperations() + { + int[] data = { 1, 2, 2, 3, 4 }; + + int firstIndex = data.IndexOf(x => x == 2); + int lastIndex = data.LastIndexOf(x => x == 2); + int count = data.CountMatches(x => x == 2); + bool any = data.AnyMatch(x => x == 2); + bool all = data.AllMatch(x => x > 0); + + Assert.Equal(1, firstIndex); + Assert.Equal(2, lastIndex); + Assert.Equal(2, count); + Assert.True(any); + Assert.True(all); + } +} From 8e2c1d0d2307b1c86361fe78b0baa40b5966667a Mon Sep 17 00:00:00 2001 From: mavantgarderc Date: Sun, 7 Dec 2025 05:25:05 +0330 Subject: [PATCH 4/5] Algorithms(Serialization.Json) --- Directory.Packages.props | 1 + .../Base64/SerializationUtils.Base64.cs | 36 +++++++++++++ ...ationUtils.SerializeIgnoreNullsToBase64.cs | 23 +++++++++ ...SerializationUtils.DeserializeFromBytes.cs | 24 +++++++++ .../SerializationUtils.SerializeToBytes.cs | 19 +++++++ ...lizationUtils.SerializeWithNamingPolicy.cs | 34 +++++++++++++ ...ializationUtils.TryDeserializeOrDefault.cs | 33 ++++++++++++ .../Convert/SerializationUtils.Xml.cs | 50 +++++++++++++++++++ .../SerializationUtils.DeserializeFromFile.cs | 24 +++++++++ .../SerializationUtils.SerializeToFile.cs | 24 +++++++++ .../SerializationUtils.DeserializeFromJson.cs | 32 ++++++++++++ ...lizationUtils.DeserializeWithConverters.cs | 47 +++++++++++++++++ .../Json/SerializationUtils.IsValidJson.cs | 33 ++++++++++++ ...SerializationUtils.SerializeIgnoreNulls.cs | 17 +++++++ .../SerializationUtils.SerializeToJson.cs | 26 ++++++++++ ...ializationUtils.SerializeWithConverters.cs | 38 ++++++++++++++ .../SerializationUtils.DeepClone.cs | 19 +++++++ .../SerializationUtils.Polymorphic.cs | 27 ++++++++++ ...lizationUtils.SerializeIncludingPrivate.cs | 21 ++++++++ .../SerializationUtils.Options.cs | 50 +++++++++++++++++++ .../Serialization/SerializationUtils.cs | 42 ++++++++++++++++ ...erializationUtils.DeserializeFromStream.cs | 31 ++++++++++++ .../Stream/SerializationUtils.Network.cs | 37 ++++++++++++++ .../SerializationUtils.SerializeToStream.cs | 22 ++++++++ src/Csdsa/Csdsa.csproj | 1 + 25 files changed, 711 insertions(+) create mode 100644 src/Csdsa/Algorithms/Serialization/Base64/SerializationUtils.Base64.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Base64/SerializationUtils.SerializeIgnoreNullsToBase64.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.DeserializeFromBytes.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.SerializeToBytes.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.SerializeWithNamingPolicy.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.TryDeserializeOrDefault.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.Xml.cs create mode 100644 src/Csdsa/Algorithms/Serialization/File/SerializationUtils.DeserializeFromFile.cs create mode 100644 src/Csdsa/Algorithms/Serialization/File/SerializationUtils.SerializeToFile.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.DeserializeFromJson.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.DeserializeWithConverters.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.IsValidJson.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeIgnoreNulls.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeToJson.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeWithConverters.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.DeepClone.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.Polymorphic.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.SerializeIncludingPrivate.cs create mode 100644 src/Csdsa/Algorithms/Serialization/SerializationUtils.Options.cs create mode 100644 src/Csdsa/Algorithms/Serialization/SerializationUtils.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.DeserializeFromStream.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.Network.cs create mode 100644 src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.SerializeToStream.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 0ccaeaf..a187cc3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -8,5 +8,6 @@ + diff --git a/src/Csdsa/Algorithms/Serialization/Base64/SerializationUtils.Base64.cs b/src/Csdsa/Algorithms/Serialization/Base64/SerializationUtils.Base64.cs new file mode 100644 index 0000000..9c77b12 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Base64/SerializationUtils.Base64.cs @@ -0,0 +1,36 @@ +namespace Csdsa.Algorithms.Serialization.Json; + +/// +/// Provides helpers for Base64-encoding and decoding JSON-based payloads. +/// +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value to a Base64-encoded UTF-8 JSON string. + /// + /// The type of the value. + /// The value to serialize. + /// A Base64-encoded JSON payload. + public static string SerializeToBase64(T data) + { + byte[] bytes = SerializeToBytes(data); + return Convert.ToBase64String(bytes); + } + + /// + /// Deserializes a Base64-encoded UTF-8 JSON string into an instance of . + /// + /// The target type. + /// The Base64-encoded JSON payload. + /// An instance of . + /// + /// Thrown when is . + /// + public static T DeserializeFromBase64(string base64) + { + ArgumentNullException.ThrowIfNull(base64); + + byte[] bytes = Convert.FromBase64String(base64); + return DeserializeFromBytes(bytes); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Base64/SerializationUtils.SerializeIgnoreNullsToBase64.cs b/src/Csdsa/Algorithms/Serialization/Base64/SerializationUtils.SerializeIgnoreNullsToBase64.cs new file mode 100644 index 0000000..fc0373d --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Base64/SerializationUtils.SerializeIgnoreNullsToBase64.cs @@ -0,0 +1,23 @@ +using System; +using System.Text; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value to JSON while ignoring null properties and + /// then encodes the JSON as a Base64 UTF-8 string. + /// + /// The type of the value. + /// The value to serialize. + /// + /// A Base64-encoded JSON string with null properties omitted. + /// + public static string SerializeIgnoreNullsToBase64(T data) + { + string json = SerializeIgnoreNulls(data); + byte[] bytes = Encoding.UTF8.GetBytes(json); + return Convert.ToBase64String(bytes); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.DeserializeFromBytes.cs b/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.DeserializeFromBytes.cs new file mode 100644 index 0000000..a71d3d3 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.DeserializeFromBytes.cs @@ -0,0 +1,24 @@ +using System; +using System.Text; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Deserializes a UTF-8 encoded JSON byte array into an instance of . + /// + /// The target type. + /// The UTF-8 encoded JSON bytes. + /// An instance of . + /// + /// Thrown when is . + /// + public static T DeserializeFromBytes(byte[] bytes) + { + ArgumentNullException.ThrowIfNull(bytes); + + string json = Encoding.UTF8.GetString(bytes); + return DeserializeFromJson(json); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.SerializeToBytes.cs b/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.SerializeToBytes.cs new file mode 100644 index 0000000..c4ae32d --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.SerializeToBytes.cs @@ -0,0 +1,19 @@ +using System; +using System.Text; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value to a UTF-8 encoded JSON byte array. + /// + /// The type of the value. + /// The value to serialize. + /// A UTF-8 encoded JSON byte array. + public static byte[] SerializeToBytes(T data) + { + string json = SerializeToJson(data); + return Encoding.UTF8.GetBytes(json); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.SerializeWithNamingPolicy.cs b/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.SerializeWithNamingPolicy.cs new file mode 100644 index 0000000..70d42e4 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.SerializeWithNamingPolicy.cs @@ -0,0 +1,34 @@ +using System.Text.Json; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value to JSON using a custom naming policy. + /// Currently supports ; other policies + /// will result in a . + /// + /// The type of the value. + /// The value to serialize. + /// The property naming policy to apply. + /// The JSON representation of . + /// + /// Thrown when is . + /// + /// + /// Thrown when is not supported. + /// + public static string SerializeWithNamingPolicy(T data, JsonNamingPolicy namingPolicy) + { + ArgumentNullException.ThrowIfNull(namingPolicy); + + if (ReferenceEquals(namingPolicy, JsonNamingPolicy.CamelCase)) + { + return System.Text.Json.JsonSerializer.Serialize(data, CamelCaseJsonOptions); + } + + throw new NotSupportedException( + "Only JsonNamingPolicy.CamelCase is supported by SerializeWithNamingPolicy."); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.TryDeserializeOrDefault.cs b/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.TryDeserializeOrDefault.cs new file mode 100644 index 0000000..aae4a2a --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.TryDeserializeOrDefault.cs @@ -0,0 +1,33 @@ +using System.Text.Json; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Attempts to deserialize the specified JSON string into an instance of . + /// Returns the provided fallback value if deserialization fails due to JSON parsing errors + /// or unsupported types. + /// + /// The target type. + /// The JSON payload. + /// The value to return if deserialization fails. + /// + /// The deserialized value, or if deserialization fails. + /// + public static T TryDeserializeOrDefault(string json, T fallback = default) + { + try + { + return DeserializeFromJson(json); + } + catch (JsonException) + { + return fallback; + } + catch (NotSupportedException) + { + return fallback; + } + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.Xml.cs b/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.Xml.cs new file mode 100644 index 0000000..e9155f8 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Convert/SerializationUtils.Xml.cs @@ -0,0 +1,50 @@ +using System.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value to an XML string using . + /// + /// The type of the value. + /// The value to serialize. + /// An XML representation of . + public static string SerializeToXml(T data) + { + XmlSerializer serializer = new XmlSerializer(typeof(T)); + using MemoryStream memory = new MemoryStream(); + serializer.Serialize(memory, data); + return Encoding.UTF8.GetString(memory.ToArray()); + } + + /// + /// Deserializes the specified XML string into an instance of , + /// using an with DTD processing disabled and no resolver. + /// + /// The target type. + /// The XML payload. + /// An instance of . + /// + /// Thrown when is . + /// + public static T DeserializeFromXml(string xml) + { + ArgumentNullException.ThrowIfNull(xml); + + XmlSerializer serializer = new XmlSerializer(typeof(T)); + + using StringReader stringReader = new StringReader(xml); + XmlReaderSettings settings = new XmlReaderSettings + { + DtdProcessing = DtdProcessing.Prohibit, + XmlResolver = null, + }; + + using XmlReader reader = XmlReader.Create(stringReader, settings); + object result = serializer.Deserialize(reader); + return (T)result!; + } +} diff --git a/src/Csdsa/Algorithms/Serialization/File/SerializationUtils.DeserializeFromFile.cs b/src/Csdsa/Algorithms/Serialization/File/SerializationUtils.DeserializeFromFile.cs new file mode 100644 index 0000000..c1f6ee6 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/File/SerializationUtils.DeserializeFromFile.cs @@ -0,0 +1,24 @@ +using System; +using System.IO; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Deserializes JSON from the specified file into an instance of . + /// + /// The target type. + /// The path of the file to read. + /// An instance of . + /// + /// Thrown when is . + /// + public static T DeserializeFromFile(string filePath) + { + ArgumentNullException.ThrowIfNull(filePath); + + string json = File.ReadAllText(filePath); + return DeserializeFromJson(json); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/File/SerializationUtils.SerializeToFile.cs b/src/Csdsa/Algorithms/Serialization/File/SerializationUtils.SerializeToFile.cs new file mode 100644 index 0000000..b27d070 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/File/SerializationUtils.SerializeToFile.cs @@ -0,0 +1,24 @@ +using System; +using System.IO; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value to a JSON file. + /// + /// The type of the value. + /// The value to serialize. + /// The file path to write to. + /// + /// Thrown when is . + /// + public static void SerializeToFile(T data, string filePath) + { + ArgumentNullException.ThrowIfNull(filePath); + + string json = SerializeToJson(data); + File.WriteAllText(filePath, json); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.DeserializeFromJson.cs b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.DeserializeFromJson.cs new file mode 100644 index 0000000..f77ed4d --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.DeserializeFromJson.cs @@ -0,0 +1,32 @@ +using System.Text.Json; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Deserializes the specified JSON string to an instance of + /// using . + /// + /// The target type. + /// The JSON string to deserialize. + /// An instance of . + /// + /// Thrown when is . + /// + /// + /// Thrown when deserialization returns . + /// + public static T DeserializeFromJson(string json) + { + ArgumentNullException.ThrowIfNull(json); + + T result = JsonSerializer.Deserialize(json, DefaultJsonOptions); + if (result == null) + { + throw new InvalidOperationException("Deserialization produced a null result."); + } + + return result; + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.DeserializeWithConverters.cs b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.DeserializeWithConverters.cs new file mode 100644 index 0000000..24192a6 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.DeserializeWithConverters.cs @@ -0,0 +1,47 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Deserializes the specified JSON string to an instance of + /// using the provided JSON converters. + /// + /// The target type. + /// The JSON payload. + /// The converters to register in the options. + /// An instance of . + /// + /// Thrown when or is . + /// + /// + /// Thrown when deserialization returns . + /// + public static T DeserializeWithConverters( + string json, + IEnumerable converters) + { + ArgumentNullException.ThrowIfNull(json); + ArgumentNullException.ThrowIfNull(converters); + + JsonSerializerOptions options = new JsonSerializerOptions + { + WriteIndented = true, + }; + + foreach (JsonConverter converter in converters) + { + options.Converters.Add(converter); + } + + T result = JsonSerializer.Deserialize(json, options); + if (result == null) + { + throw new InvalidOperationException("Deserialization produced a null result."); + } + + return result; + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.IsValidJson.cs b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.IsValidJson.cs new file mode 100644 index 0000000..fa25386 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.IsValidJson.cs @@ -0,0 +1,33 @@ +using System.Text.Json; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Determines whether the specified string is valid JSON. + /// + /// The JSON string to validate. + /// + /// if is non-empty and valid JSON; + /// otherwise, . + /// + public static bool IsValidJson(string json) + { + if (string.IsNullOrWhiteSpace(json)) + { + return false; + } + + try + { + using JsonDocument document = JsonDocument.Parse(json); + _ = document.RootElement; + return true; + } + catch (JsonException) + { + return false; + } + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeIgnoreNulls.cs b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeIgnoreNulls.cs new file mode 100644 index 0000000..9d6cfa7 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeIgnoreNulls.cs @@ -0,0 +1,17 @@ +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value to a JSON string, ignoring properties with null values. + /// + /// The type of the value. + /// The value to serialize. + /// + /// A JSON representation of where null-valued properties are omitted. + /// + public static string SerializeIgnoreNulls(T data) + { + return System.Text.Json.JsonSerializer.Serialize(data, IgnoreNullsJsonOptions); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeToJson.cs b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeToJson.cs new file mode 100644 index 0000000..3373dfc --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeToJson.cs @@ -0,0 +1,26 @@ +using System.Text.Json; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value to a JSON string using . + /// + /// The type of the value to serialize. + /// The value to serialize. + /// The JSON representation of . + /// + /// Thrown when the serialization unexpectedly returns . + /// + public static string SerializeToJson(T data) + { + string json = JsonSerializer.Serialize(data, DefaultJsonOptions); + if (json == null) + { + throw new InvalidOperationException("Serialization produced a null result."); + } + + return json; + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeWithConverters.cs b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeWithConverters.cs new file mode 100644 index 0000000..40c8c35 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Json/SerializationUtils.SerializeWithConverters.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value using the provided JSON converters. + /// + /// The type of the value. + /// The value to serialize. + /// The converters to register in the options. + /// The JSON representation of . + /// + /// Thrown when is . + /// + public static string SerializeWithConverters( + T data, + IEnumerable converters) + { + ArgumentNullException.ThrowIfNull(converters); + + JsonSerializerOptions options = new JsonSerializerOptions + { + WriteIndented = true, + }; + + foreach (JsonConverter converter in converters) + { + options.Converters.Add(converter); + } + + return JsonSerializer.Serialize(data, options); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.DeepClone.cs b/src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.DeepClone.cs new file mode 100644 index 0000000..e3d3807 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.DeepClone.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Creates a deep clone of the specified object by serializing and deserializing it + /// using Newtonsoft.Json with default settings. + /// + /// The type of the object to clone. + /// The object to clone. + /// A deep clone of . + public static T DeepClone(T data) + { + string json = JsonConvert.SerializeObject(data); + return JsonConvert.DeserializeObject(json)!; + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.Polymorphic.cs b/src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.Polymorphic.cs new file mode 100644 index 0000000..3c75d63 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.Polymorphic.cs @@ -0,0 +1,27 @@ +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified polymorphic value to JSON using . + /// + /// The static type of the value to serialize. + /// The value to serialize. + /// The JSON representation of . + public static string SerializePolymorphic(T baseObject) + { + return SerializeToJson(baseObject); + } + + /// + /// Deserializes the specified JSON string into a polymorphic instance of + /// using . + /// + /// The target base type. + /// The JSON payload. + /// An instance of . + public static T DeserializePolymorphic(string json) + { + return DeserializeFromJson(json); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.SerializeIncludingPrivate.cs b/src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.SerializeIncludingPrivate.cs new file mode 100644 index 0000000..68b1f9c --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Polymorphic/SerializationUtils.SerializeIncludingPrivate.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value to JSON using Newtonsoft.Json, + /// including private and compiler-generated members for diagnostics. + /// + /// The type of the value. + /// The value to serialize. + /// + /// A JSON representation of that includes + /// private and compiler-generated members. + /// + public static string SerializeIncludingPrivate(T data) + { + return JsonConvert.SerializeObject(data, NewtonsoftPrivateSerializerSettings); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/SerializationUtils.Options.cs b/src/Csdsa/Algorithms/Serialization/SerializationUtils.Options.cs new file mode 100644 index 0000000..da7fb68 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/SerializationUtils.Options.cs @@ -0,0 +1,50 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Gets the default JSON serialization options used by this module. + /// Includes fields and writes indented JSON. + /// + public static JsonSerializerOptions DefaultJsonOptions { get; } = + new JsonSerializerOptions { WriteIndented = true, IncludeFields = true }; + + /// + /// Gets the JSON serialization options that ignore null values when writing. + /// + public static JsonSerializerOptions IgnoreNullsJsonOptions { get; } = + new JsonSerializerOptions + { + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }; + + /// + /// Gets JSON serialization options that use camelCase property naming. + /// + public static JsonSerializerOptions CamelCaseJsonOptions { get; } = + new JsonSerializerOptions + { + WriteIndented = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + + /// + /// Gets Newtonsoft.Json serializer settings configured to include + /// private and compiler-generated members for diagnostics and deep cloning. + /// + public static JsonSerializerSettings NewtonsoftPrivateSerializerSettings { get; } = + new JsonSerializerSettings + { + Formatting = Formatting.Indented, + ContractResolver = new DefaultContractResolver + { + SerializeCompilerGeneratedMembers = true, + }, + }; +} diff --git a/src/Csdsa/Algorithms/Serialization/SerializationUtils.cs b/src/Csdsa/Algorithms/Serialization/SerializationUtils.cs new file mode 100644 index 0000000..ee8b95d --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/SerializationUtils.cs @@ -0,0 +1,42 @@ +namespace Csdsa.Algorithms.Serialization; + +/// +/// Provides JSON, Base64, XML, and network-oriented serialization helpers. +/// +/// Concepts: +/// +/// +/// +/// JSON, Base64, and XML serialization for arbitrary object graphs. +/// +/// +/// Deep cloning via serialization-based round-tripping. +/// +/// +/// Custom converters and naming policies for JSON payloads. +/// +/// +/// Ignoring null values and providing robust fallback strategies. +/// +/// +/// +/// Key practices: +/// +/// +/// +/// Centralized (de)serialization to keep calling code clean and consistent. +/// +/// +/// Forward-thinking design for network, file, stream, and Base64 scenarios. +/// +/// +/// Robust fallbacks via TryDeserializeOrDefault-style helpers. +/// +/// +/// Diagnostics support via serialization that includes private and compiler-generated members. +/// +/// +/// +public static partial class SerializationUtils +{ +} diff --git a/src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.DeserializeFromStream.cs b/src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.DeserializeFromStream.cs new file mode 100644 index 0000000..5d7d4ff --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.DeserializeFromStream.cs @@ -0,0 +1,31 @@ +using System.Text.Json; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Deserializes JSON from the specified stream to an instance of . + /// + /// The target type. + /// The source stream. + /// An instance of . + /// + /// Thrown when is . + /// + /// + /// Thrown when deserialization returns . + /// + public static T DeserializeFromStream(Stream stream) + { + ArgumentNullException.ThrowIfNull(stream); + + T result = JsonSerializer.Deserialize(stream, DefaultJsonOptions); + if (result == null) + { + throw new InvalidOperationException("Deserialization produced a null result."); + } + + return result; + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.Network.cs b/src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.Network.cs new file mode 100644 index 0000000..9fe28e5 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.Network.cs @@ -0,0 +1,37 @@ +using System; +using System.IO; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value to a network stream as JSON. + /// + /// The type of the value. + /// The value to serialize. + /// The network stream to write to. + /// + /// Thrown when is . + /// + public static void SerializeToNetwork(T data, Stream networkStream) + { + ArgumentNullException.ThrowIfNull(networkStream); + SerializeToStream(data, networkStream); + } + + /// + /// Deserializes JSON from the specified network stream into an instance of . + /// + /// The target type. + /// The network stream to read from. + /// An instance of . + /// + /// Thrown when is . + /// + public static T DeserializeFromNetwork(Stream networkStream) + { + ArgumentNullException.ThrowIfNull(networkStream); + return DeserializeFromStream(networkStream); + } +} diff --git a/src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.SerializeToStream.cs b/src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.SerializeToStream.cs new file mode 100644 index 0000000..c2bffe8 --- /dev/null +++ b/src/Csdsa/Algorithms/Serialization/Stream/SerializationUtils.SerializeToStream.cs @@ -0,0 +1,22 @@ +using System.Text.Json; + +namespace Csdsa.Algorithms.Serialization.Json; + +public static partial class SerializationUtils +{ + /// + /// Serializes the specified value to the provided stream as JSON. + /// + /// The type of the value. + /// The value to serialize. + /// The target stream. + /// + /// Thrown when is . + /// + public static void SerializeToStream(T data, Stream stream) + { + ArgumentNullException.ThrowIfNull(stream); + + JsonSerializer.Serialize(stream, data, DefaultJsonOptions); + } +} diff --git a/src/Csdsa/Csdsa.csproj b/src/Csdsa/Csdsa.csproj index 46165a4..00e1c5f 100644 --- a/src/Csdsa/Csdsa.csproj +++ b/src/Csdsa/Csdsa.csproj @@ -14,6 +14,7 @@ + From efde50031c6655712c968b9dd99ba4f3cae780f4 Mon Sep 17 00:00:00 2001 From: mavantgarderc Date: Sun, 7 Dec 2025 05:25:16 +0330 Subject: [PATCH 5/5] Algorithms.Tests(Serialization.Json) --- .../SerializationUtils.Base64.Tests.cs | 20 +++++++ .../SerializationUtils.DeepClone.Tests.cs | 20 +++++++ ...onUtils.DeserializeWithConverters.Tests.cs | 30 +++++++++++ .../SerializationUtils.Helpers.Tests.cs | 13 +++++ .../SerializationUtils.IsValidJson.Tests.cs | 19 +++++++ .../SerializationUtils.Network.Tests.cs | 31 +++++++++++ .../SerializationUtils.Polymorphic.Tests.cs | 34 ++++++++++++ ...izationUtils.SerializeIgnoreNulls.Tests.cs | 18 +++++++ ...tils.SerializeIgnoreNullsToBase64.Tests.cs | 29 ++++++++++ ...onUtils.SerializeIncludingPrivate.Tests.cs | 29 ++++++++++ ...rializationUtils.SerializeToBytes.Tests.cs | 20 +++++++ ...erializationUtils.SerializeToFile.Tests.cs | 25 +++++++++ ...erializationUtils.SerializeToJson.Tests.cs | 20 +++++++ ...ializationUtils.SerializeToStream.Tests.cs | 26 +++++++++ ...tionUtils.SerializeWithConverters.Tests.cs | 54 +++++++++++++++++++ ...onUtils.SerializeWithNamingPolicy.Tests.cs | 21 ++++++++ .../SerializationUtils.Xml.Tests.cs | 20 +++++++ ...tionutils.TryDeserializeOrDefault.Tests.cs | 21 ++++++++ 18 files changed, 450 insertions(+) create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Base64.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.DeepClone.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.DeserializeWithConverters.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Helpers.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.IsValidJson.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Network.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Polymorphic.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIgnoreNulls.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIgnoreNullsToBase64.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIncludingPrivate.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToBytes.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToFile.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToJson.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToStream.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeWithConverters.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeWithNamingPolicy.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Xml.Tests.cs create mode 100644 tests/Csdsa.Tests/Algorithms/Serialization/Serializationutils.TryDeserializeOrDefault.Tests.cs diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Base64.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Base64.Tests.cs new file mode 100644 index 0000000..2ea5aa3 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Base64.Tests.cs @@ -0,0 +1,20 @@ +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void SerializeToBase64_And_DeserializeFromBase64_Works() + { + Person person = new Person { Name = "Kendrick", Age = 37 }; + + string base64 = SerializationUtils.SerializeToBase64(person); + Person deserialized = SerializationUtils.DeserializeFromBase64(base64); + + Assert.Equal("Kendrick", deserialized.Name); + Assert.Equal(37, deserialized.Age); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.DeepClone.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.DeepClone.Tests.cs new file mode 100644 index 0000000..dd6aaca --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.DeepClone.Tests.cs @@ -0,0 +1,20 @@ +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void DeepClone_CreatesNewObject_WithSameValues() + { + Person person = new Person { Name = "Lelland", Age = 31 }; + + Person clone = SerializationUtils.DeepClone(person); + + Assert.NotSame(person, clone); + Assert.Equal("Lelland", clone.Name); + Assert.Equal(31, clone.Age); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.DeserializeWithConverters.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.DeserializeWithConverters.Tests.cs new file mode 100644 index 0000000..b2727d4 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.DeserializeWithConverters.Tests.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void DeserializeWithConverters_UsesCustomConverter() + { + const string json = """ + { + "Name": "lowercase", + "Age": 20 + } + """; + + Person result = SerializationUtils.DeserializeWithConverters( + json, + new JsonConverter[] { new UppercaseNameConverter() }); + + Assert.Equal("LOWERCASE", result.Name); + Assert.Equal(20, result.Age); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Helpers.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Helpers.Tests.cs new file mode 100644 index 0000000..59b24d2 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Helpers.Tests.cs @@ -0,0 +1,13 @@ +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + public sealed class Person + { + public string Name { get; set; } = "mavantgarderc"; + + public int Age { get; set; } = 22; + + public string? Nickname { get; set; } + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.IsValidJson.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.IsValidJson.Tests.cs new file mode 100644 index 0000000..20d39b9 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.IsValidJson.Tests.cs @@ -0,0 +1,19 @@ +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void IsValidJson_ReturnsTrueForValidJson_AndFalseForInvalid() + { + Person person = new Person { Name = "Nayvadius", Age = 41 }; + + string json = SerializationUtils.SerializeToJson(person); + + Assert.True(SerializationUtils.IsValidJson(json)); + Assert.False(SerializationUtils.IsValidJson("Invalid JSON")); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Network.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Network.Tests.cs new file mode 100644 index 0000000..2487612 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Network.Tests.cs @@ -0,0 +1,31 @@ +using System.IO; + +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void SerializeToAndFromNetwork_Works() + { + Person person = new Person + { + Name = "NetworkTest", + Age = 99, + }; + + using MemoryStream networkStream = new MemoryStream(); + + SerializationUtils.SerializeToNetwork(person, networkStream); + + networkStream.Position = 0; + + Person deserialized = SerializationUtils.DeserializeFromNetwork(networkStream); + + Assert.Equal("NetworkTest", deserialized.Name); + Assert.Equal(99, deserialized.Age); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Polymorphic.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Polymorphic.Tests.cs new file mode 100644 index 0000000..74c9e21 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Polymorphic.Tests.cs @@ -0,0 +1,34 @@ +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + private abstract class Animal + { + public string Name { get; set; } = string.Empty; + } + + private sealed class Dog : Animal + { + public int Age { get; set; } + } + + [Fact] + public void SerializePolymorphic_And_DeserializePolymorphic_Works() + { + Dog dog = new Dog + { + Name = "Snoopy", + Age = 5, + }; + + string json = SerializationUtils.SerializePolymorphic(dog); + Dog deserialized = SerializationUtils.DeserializePolymorphic(json); + + Assert.Equal("Snoopy", deserialized.Name); + Assert.Equal(5, deserialized.Age); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIgnoreNulls.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIgnoreNulls.Tests.cs new file mode 100644 index 0000000..7dc815b --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIgnoreNulls.Tests.cs @@ -0,0 +1,18 @@ +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void SerializeIgnoreNulls_OmitsNullProperties() + { + Person person = new Person { Name = "Aubrey", Age = 38 }; + + string json = SerializationUtils.SerializeIgnoreNulls(person); + + Assert.DoesNotContain("Nickname", json); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIgnoreNullsToBase64.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIgnoreNullsToBase64.Tests.cs new file mode 100644 index 0000000..674a8d8 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIgnoreNullsToBase64.Tests.cs @@ -0,0 +1,29 @@ +using System.Text; + +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void SerializeIgnoreNullsToBase64_OmitsNullPropertiesInDecodedJson() + { + Person person = new Person + { + Name = "Base64Test", + Age = 10, + Nickname = null, + }; + + string base64 = SerializationUtils.SerializeIgnoreNullsToBase64(person); + + byte[] bytes = Convert.FromBase64String(base64); + string json = Encoding.UTF8.GetString(bytes); + + Assert.Contains("Base64Test", json); + Assert.DoesNotContain("Nickname", json); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIncludingPrivate.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIncludingPrivate.Tests.cs new file mode 100644 index 0000000..485e426 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeIncludingPrivate.Tests.cs @@ -0,0 +1,29 @@ +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + private sealed class PrivatePerson + { + public string Name { get; set; } = string.Empty; + + private string Secret { get; set; } = "top-secret"; + } + + [Fact] + public void SerializeIncludingPrivate_ProducesJson() + { + PrivatePerson person = new PrivatePerson + { + Name = "Test", + }; + + string json = SerializationUtils.SerializeIncludingPrivate(person); + + Assert.False(string.IsNullOrWhiteSpace(json)); + Assert.Contains("Test", json); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToBytes.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToBytes.Tests.cs new file mode 100644 index 0000000..963550f --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToBytes.Tests.cs @@ -0,0 +1,20 @@ +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void SerializeToAndFromBytes_Works() + { + Person person = new Person { Name = "Ye", Age = 48 }; + + byte[] bytes = SerializationUtils.SerializeToBytes(person); + Person deserialized = SerializationUtils.DeserializeFromBytes(bytes); + + Assert.Equal("Ye", deserialized.Name); + Assert.Equal(48, deserialized.Age); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToFile.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToFile.Tests.cs new file mode 100644 index 0000000..cc84aba --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToFile.Tests.cs @@ -0,0 +1,25 @@ +using System.IO; + +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void SerializeToAndFromFile_Works() + { + Person person = new Person { Name = "Shawn", Age = 55 }; + const string path = "temp-test.json"; + + SerializationUtils.SerializeToFile(person, path); + Person deserialized = SerializationUtils.DeserializeFromFile(path); + + File.Delete(path); + + Assert.Equal("Shawn", deserialized.Name); + Assert.Equal(55, deserialized.Age); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToJson.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToJson.Tests.cs new file mode 100644 index 0000000..2c9d8ec --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToJson.Tests.cs @@ -0,0 +1,20 @@ +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void SerializeAndDeserialize_ReturnsEqualObject() + { + Person person = new Person { Name = "Jermaine", Age = 40 }; + + string json = SerializationUtils.SerializeToJson(person); + Person deserialized = SerializationUtils.DeserializeFromJson(json); + + Assert.Equal(person.Name, deserialized.Name); + Assert.Equal(person.Age, deserialized.Age); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToStream.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToStream.Tests.cs new file mode 100644 index 0000000..a53cb9a --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeToStream.Tests.cs @@ -0,0 +1,26 @@ +using System.IO; + +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void SerializeToAndFromStream_Works() + { + Person person = new Person { Name = "Dwayne", Age = 42 }; + + using MemoryStream stream = new MemoryStream(); + + SerializationUtils.SerializeToStream(person, stream); + stream.Position = 0; + + Person deserialized = SerializationUtils.DeserializeFromStream(stream); + + Assert.Equal("Dwayne", deserialized.Name); + Assert.Equal(42, deserialized.Age); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeWithConverters.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeWithConverters.Tests.cs new file mode 100644 index 0000000..e0336c1 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeWithConverters.Tests.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + private sealed class UppercaseNameConverter : JsonConverter + { + public override Person Read(ref Utf8JsonReader reader, System.Type typeToConvert, JsonSerializerOptions options) + { + // Minimal read implementation: delegate to default, then adjust. + Person? person = JsonSerializer.Deserialize(ref reader, options); + if (person is null) + { + return new Person(); + } + + person.Name = person.Name.ToUpperInvariant(); + return person; + } + + public override void Write(Utf8JsonWriter writer, Person value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteString(nameof(Person.Name), value.Name.ToUpperInvariant()); + writer.WriteNumber(nameof(Person.Age), value.Age); + if (value.Nickname is not null) + { + writer.WriteString(nameof(Person.Nickname), value.Nickname); + } + + writer.WriteEndObject(); + } + } + + [Fact] + public void SerializeWithConverters_UsesCustomConverter() + { + Person person = new Person { Name = "Jayce", Age = 25 }; + + string json = SerializationUtils.SerializeWithConverters( + person, + new JsonConverter[] { new UppercaseNameConverter() }); + + Assert.Contains("JAYCE", json); + Assert.DoesNotContain("Jayce", json); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeWithNamingPolicy.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeWithNamingPolicy.Tests.cs new file mode 100644 index 0000000..d1ba56a --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.SerializeWithNamingPolicy.Tests.cs @@ -0,0 +1,21 @@ +using System.Text.Json; + +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void SerializeWithNamingPolicy_CamelCase_UsesCamelCasePropertyNames() + { + Person person = new Person { Name = "Webster", Age = 34 }; + + string json = SerializationUtils.SerializeWithNamingPolicy(person, JsonNamingPolicy.CamelCase); + + Assert.Contains("name", json); + Assert.Contains("age", json); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Xml.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Xml.Tests.cs new file mode 100644 index 0000000..22cebb0 --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/SerializationUtils.Xml.Tests.cs @@ -0,0 +1,20 @@ +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void SerializeToAndFromXml_Works() + { + Person person = new Person { Name = "Curtis", Age = 49 }; + + string xml = SerializationUtils.SerializeToXml(person); + Person deserialized = SerializationUtils.DeserializeFromXml(xml); + + Assert.Equal("Curtis", deserialized.Name); + Assert.Equal(49, deserialized.Age); + } +} diff --git a/tests/Csdsa.Tests/Algorithms/Serialization/Serializationutils.TryDeserializeOrDefault.Tests.cs b/tests/Csdsa.Tests/Algorithms/Serialization/Serializationutils.TryDeserializeOrDefault.Tests.cs new file mode 100644 index 0000000..60a104a --- /dev/null +++ b/tests/Csdsa.Tests/Algorithms/Serialization/Serializationutils.TryDeserializeOrDefault.Tests.cs @@ -0,0 +1,21 @@ +using Csdsa.Algorithms.Serialization.Json; + +using Xunit; + +namespace Csdsa.Tests.Algorithms.Serialization.Json; + +public sealed partial class SerializationUtilsTests +{ + [Fact] + public void TryDeserializeOrDefault_ReturnsFallback_OnInvalidJson() + { + const string invalidJson = "{ invalid json }"; + + Person fallback = new Person { Name = "Fallback", Age = 0 }; + + Person result = SerializationUtils.TryDeserializeOrDefault(invalidJson, fallback); + + Assert.Equal("Fallback", result.Name); + Assert.Equal(0, result.Age); + } +}