Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions AdditionalControllers/Navigation/InterfaceNavigationData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Shared {className, problemName} entry type and Build/Find logic for the per-kind
// navigation catalogs in Nav_Solvers.cs, Nav_Verifiers.cs, and Nav_Visualizations.cs. Each
// catalog scans a ProblemProvider type dictionary (Solvers/Verifiers/Visualizers) for classes
// implementing a single-type-parameter open generic marker interface (ISolver<>, IVerifier<>,
// IVisualization<>) and records which problem type each one was built for.
internal class NavigationEntry {
public string className { get; set; } = "";
public string problemName { get; set; } = "";
}

internal static class InterfaceNavigationData {
// Scans `source` (e.g. ProblemProvider.Solvers) for types implementing the given
// single-type-parameter open generic interface (e.g. typeof(ISolver<>)), recording each
// type's className and the problemName of its generic argument. Types with no matching
// interface are skipped; duplicate class names (case-insensitive) keep the first entry
// encountered.
internal static List<NavigationEntry> Build(Dictionary<string, Type> source, Type openGenericInterface) {
var entries = new List<NavigationEntry>();
foreach (var (_, type) in source) {
var generic = type.GetInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == openGenericInterface);
if (generic == null) continue;

Type problemType = generic.GetGenericArguments()[0];
entries.Add(new NavigationEntry {
className = type.Name,
problemName = problemType.Name,
});
}

return entries
.GroupBy(e => e.className, StringComparer.OrdinalIgnoreCase)
.Select(g => g.First())
.ToList();
}

// problemTypePrefix is accepted for API compatibility but intentionally ignored by every
// caller: problem names are unique across complexity classes, so the name alone
// identifies the entry set. The GUI pins problemType to "NPC", so matching on the prefix
// would drop P / NP-Hard problems.
internal static List<NavigationEntry> Find(List<NavigationEntry> entries, string? problemName, string? problemTypePrefix) {
IEnumerable<NavigationEntry> query = entries;

if (!string.IsNullOrWhiteSpace(problemName)) {
query = query.Where(e => string.Equals(e.problemName, problemName, StringComparison.OrdinalIgnoreCase));
}

return query
.OrderBy(e => e.className, StringComparer.OrdinalIgnoreCase)
.ToList();
}

internal static List<string> FindWithoutExtension(List<NavigationEntry> entries, string? problemName, string? problemTypePrefix) {
return Find(entries, problemName, problemTypePrefix)
.Select(x => x.className)
.ToList();
}
}
51 changes: 4 additions & 47 deletions AdditionalControllers/Navigation/Nav_Solvers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,56 +58,13 @@ private static Dictionary<string, string> BuildComplexity() {
}

internal static class SolverNavigationData {
internal class SolverEntry {
public string className { get; set; } = "";
public string problemName { get; set; } = "";
}

// Build once from reflected solver types so navigation doesn't depend on
// on-disk source layout. Mirrors VerifierNavigationData (see Nav_Verifiers.cs).
internal static readonly List<SolverEntry> Entries = Build();

internal static List<string> FindWithoutExtension(string? problemName, string? problemTypePrefix) {
return Find(problemName, problemTypePrefix)
.Select(x => x.className)
.ToList();
}

private static List<SolverEntry> Build() {
var entries = new List<SolverEntry>();
foreach (var (_, solverType) in ProblemProvider.Solvers) {
var generic = solverType.GetInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ISolver<>));
if (generic == null) continue;

Type problemType = generic.GetGenericArguments()[0];
entries.Add(new SolverEntry {
className = solverType.Name,
problemName = problemType.Name,
});
}
internal static readonly List<NavigationEntry> Entries =
InterfaceNavigationData.Build(ProblemProvider.Solvers, typeof(ISolver<>));

return entries
.GroupBy(e => e.className, StringComparer.OrdinalIgnoreCase)
.Select(g => g.First())
.ToList();
}

// problemTypePrefix is accepted for API compatibility but intentionally ignored:
// problem names are unique across complexity classes, so the name alone identifies
// the solver set. Mirrors the verifier navigation (#317/#318): the GUI pins
// problemType to "NPC", so matching on the prefix would drop P / NP-Hard solvers.
private static List<SolverEntry> Find(string? problemName, string? problemTypePrefix) {
IEnumerable<SolverEntry> query = Entries;

if (!string.IsNullOrWhiteSpace(problemName)) {
query = query.Where(e => string.Equals(e.problemName, problemName, StringComparison.OrdinalIgnoreCase));
}

return query
.OrderBy(e => e.className, StringComparer.OrdinalIgnoreCase)
.ToList();
}
internal static List<string> FindWithoutExtension(string? problemName, string? problemTypePrefix) =>
InterfaceNavigationData.FindWithoutExtension(Entries, problemName, problemTypePrefix);
}

// Get all Solvers for a specific problem (Refactored)
Expand Down
51 changes: 4 additions & 47 deletions AdditionalControllers/Navigation/Nav_Verifiers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,13 @@
using API.Interfaces;

internal static class VerifierNavigationData {
internal class VerifierEntry {
public string className { get; set; } = "";
public string problemName { get; set; } = "";
}

// Build once from reflected verifier types so navigation doesn't depend on
// on-disk source layout.
internal static readonly List<VerifierEntry> Entries = Build();
internal static readonly List<NavigationEntry> Entries =
InterfaceNavigationData.Build(ProblemProvider.Verifiers, typeof(IVerifier<>));

internal static List<string> FindWithoutExtension(string? problemName, string? problemTypePrefix) {
return Find(problemName, problemTypePrefix)
.Select(x => x.className)
.ToList();
}
internal static List<string> FindWithoutExtension(string? problemName, string? problemTypePrefix) =>
InterfaceNavigationData.FindWithoutExtension(Entries, problemName, problemTypePrefix);

internal static bool TryParseProblemKey(string chosenProblem, out string? problemTypePrefix, out string? problemName) {
problemTypePrefix = null;
Expand All @@ -34,42 +27,6 @@ internal static bool TryParseProblemKey(string chosenProblem, out string? proble
problemName = trimmed[(idx + 1)..];
return true;
}

private static List<VerifierEntry> Build() {
var entries = new List<VerifierEntry>();
foreach (var (_, verifierType) in ProblemProvider.Verifiers) {
var generic = verifierType.GetInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IVerifier<>));
if (generic == null) continue;

Type problemType = generic.GetGenericArguments()[0];
string className = verifierType.Name;
entries.Add(new VerifierEntry {
className = className,
problemName = problemType.Name,
});
}

return entries
.GroupBy(e => e.className, StringComparer.OrdinalIgnoreCase)
.Select(g => g.First())
.ToList();
}

// problemTypePrefix is accepted for API compatibility but intentionally ignored:
// problem names are unique across complexity classes, so the name alone identifies
// the verifier set. (Solver/visualization nav still use the prefix; handled separately.)
private static List<VerifierEntry> Find(string? problemName, string? problemTypePrefix) {
IEnumerable<VerifierEntry> query = Entries;

if (!string.IsNullOrWhiteSpace(problemName)) {
query = query.Where(e => string.Equals(e.problemName, problemName, StringComparison.OrdinalIgnoreCase));
}

return query
.OrderBy(e => e.className, StringComparer.OrdinalIgnoreCase)
.ToList();
}
}

// Get all Verifiers for a specific problem\
Expand Down
51 changes: 4 additions & 47 deletions AdditionalControllers/Navigation/Nav_Visualizations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,56 +35,13 @@ private static Dictionary<string, string> Build() {
}

internal static class VisualizationNavigationData {
internal class VisualizationEntry {
public string className { get; set; } = "";
public string problemName { get; set; } = "";
}

// Build once from reflected visualization types so navigation doesn't depend on
// on-disk source layout. Mirrors VerifierNavigationData (see Nav_Verifiers.cs).
internal static readonly List<VisualizationEntry> Entries = Build();

internal static List<string> FindWithoutExtension(string? problemName, string? problemTypePrefix) {
return Find(problemName, problemTypePrefix)
.Select(x => x.className)
.ToList();
}

private static List<VisualizationEntry> Build() {
var entries = new List<VisualizationEntry>();
foreach (var (_, visType) in ProblemProvider.Visualizers) {
var generic = visType.GetInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IVisualization<>));
if (generic == null) continue;

Type problemType = generic.GetGenericArguments()[0];
entries.Add(new VisualizationEntry {
className = visType.Name,
problemName = problemType.Name,
});
}
internal static readonly List<NavigationEntry> Entries =
InterfaceNavigationData.Build(ProblemProvider.Visualizers, typeof(IVisualization<>));

return entries
.GroupBy(e => e.className, StringComparer.OrdinalIgnoreCase)
.Select(g => g.First())
.ToList();
}

// problemTypePrefix is accepted for API compatibility but intentionally ignored:
// problem names are unique across complexity classes, so the name alone identifies
// the visualization set. Mirrors the verifier navigation (#317/#318): the GUI pins
// problemType to "NPC", so matching on the prefix would drop P / NP-Hard problems.
private static List<VisualizationEntry> Find(string? problemName, string? problemTypePrefix) {
IEnumerable<VisualizationEntry> query = Entries;

if (!string.IsNullOrWhiteSpace(problemName)) {
query = query.Where(e => string.Equals(e.problemName, problemName, StringComparison.OrdinalIgnoreCase));
}

return query
.OrderBy(e => e.className, StringComparer.OrdinalIgnoreCase)
.ToList();
}
internal static List<string> FindWithoutExtension(string? problemName, string? problemTypePrefix) =>
InterfaceNavigationData.FindWithoutExtension(Entries, problemName, problemTypePrefix);
}

// Get all Visualizations for a specific problem\
Expand Down
Loading