Skip to content
Merged
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
19 changes: 19 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
version: 2

updates:
# Keep the documentation portal dependencies current.
- package-ecosystem: "npm"
directory: "/website"
schedule:
interval: "weekly"
day: "monday"
time: "09:30"
timezone: "America/Sao_Paulo"
open-pull-requests-limit: 10
rebase-strategy: "auto"
commit-message:
prefix: "deps"
include: "scope"
groups:
compatible-minor-and-patch:
update-types:
- "minor"
- "patch"

# Central Package Management + project NuGet dependencies.
- package-ecosystem: "nuget"
directory: "/"
Expand Down
7 changes: 2 additions & 5 deletions .github/workflows/documentation-lighthouse.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,10 @@ jobs:
exit 1
- name: Collect three runs per page
working-directory: website
run: npx --no-install lhci collect --config=lighthouserc.cjs
- name: Publish score summary
working-directory: website
run: node scripts/summarize-lighthouse.mjs
run: npm run lighthouse:collect
- name: Enforce budgets
working-directory: website
run: npx --no-install lhci assert --config=lighthouserc.cjs
run: npm run lighthouse:assert
- name: Upload reports
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-mcp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ on:
version:
description: Semantic version without the mcp-v prefix
required: true
default: '1.0.0'
default: '1.0.1'
type: string

concurrency:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-tool.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ on:
version:
description: Semantic version without the tool-v prefix
required: true
default: '1.0.0'
default: '1.0.1'
type: string

concurrency:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ on:
version:
description: 'Semantic version without the v prefix (for example 1.0.0 or 1.1.0-beta.1)'
required: true
default: '1.6.1'
type: string

concurrency:
Expand Down
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file.

## Unreleased

### ComplexityAnalysis.Analyzers 1.6.1

- Refactored deferred LINQ pipeline analysis into focused resolution and
composition steps without changing diagnostics, complexity estimates,
conservative `Unknown` propagation, evidence, cancellation, or concurrency
behavior.
- Preserved the self-contained `netstandard2.0` analyzer package contract and
existing Roslyn compatibility constraints.

### ComplexityAnalysis.Tool 1.0.1

- Simplified analysis and comparison option parsing while preserving accepted
arguments, validation messages, exit codes, JSON/SARIF formats, quality
gates, and independently versioned .NET Tool packaging.
- Refactored Big-O notation parsing into focused syntax handlers and added
regression coverage for supported and rejected forms.

### ComplexityAnalysis.Mcp 1.0.1

- Simplified startup option parsing and report traversal while preserving tool
schemas, response contracts, path boundaries, cache isolation, cancellation,
timeout behavior, and conservative `Unknown` results.
- Kept the checked-in Official MCP Registry manifest on the currently
published version; the protected release workflow creates and publishes the
synchronized `1.0.1` manifest only after package publication.

### ComplexityAnalysis.Mcp 1.0.0

- Prepared the first independently versioned public MCP .NET Tool package with
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<VersionPrefix>1.6.0</VersionPrefix>
<VersionPrefix>1.6.1</VersionPrefix>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>12.0</LangVersion>
Expand Down
1 change: 1 addition & 0 deletions docs/en/development/security-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This control evaluates the dependency delta of the pull request. It complements,

Dependabot maintains:

- npm dependencies for the documentation portal under `website/`, grouping compatible minor and patch updates while leaving major updates for separate compatibility review;
- NuGet dependencies, including the repository's explicit Roslyn compatibility constraints;
- the .NET SDK declared by `global.json`;
- GitHub Actions references.
Expand Down
12 changes: 7 additions & 5 deletions docs/en/mcp-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,13 @@ under `io.github.rodri-oliveira-dev/complexity-analysis` and resolves its
`nuget` package to NuGet.org; it does not replace installation or stdio startup
in VS Code/Copilot, Codex, or Claude Desktop.

`.mcp/server.json` is the version-controlled baseline. For every manual release
from `main`, `release-mcp.yml` replaces both manifest version fields with the
validated workflow version, proving equality with `mcp-v<version>` and the
NuGet package. After NuGet publication and GitHub Release completion, the
Registry job waits for the NuGet README and its exact `mcp-name` ownership
`.mcp/server.json` is the version-controlled baseline for the version currently
published in the Registry; a prepared package version may therefore be newer.
For every manual release from `main`, `release-mcp.yml` replaces both manifest
version fields with the validated workflow version, proving equality with
`mcp-v<version>` and the NuGet package. After NuGet publication and GitHub
Release completion, the Registry job waits for the NuGet README and its exact
`mcp-name` ownership
marker, verifies the pinned `mcp-publisher` artifact with Sigstore, validates
the manifest, authenticates with GitHub OIDC, publishes, and verifies the API
result. Pull requests execute validation only.
Expand Down
33 changes: 33 additions & 0 deletions docs/en/releases/mcp-1.0.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# ComplexityAnalysis.Mcp 1.0.1

This prepared patch release improves maintainability without changing the MCP
tool surface or its compatibility contract.

## Maintenance fixes

- Split startup option parsing into focused validation helpers while preserving
all accepted options, defaults, limits, and error messages.
- Reused one deterministic member traversal for full evidence lookup and
bounded summaries while preserving member order, counts, truncation, and
exact-snapshot cache behavior.
- Preserved allowed-root enforcement, symbolic-link protection, request
isolation, cancellation, timeouts, and conservative `Unknown` results.

## Compatibility

- The package remains a .NET 10 tool with the `complexity-mcp` command.
- The three read-only MCP tools and their request and response schemas are
unchanged.
- No source, transport, Registry, or package compatibility break is introduced.

## Publication status

Version `1.0.1` is prepared for release after approval and merge. Until the
protected release workflow publishes the package and verifies the Official MCP
Registry entry, installation and discovery documentation continues to identify
the currently published version.

The workflow creates the immutable `mcp-v1.0.1` tag, packages and validates the
tool, produces its SPDX 2.3 SBOM and attestations, publishes the NuGet and GitHub
artifacts, then synchronizes and verifies the versioned Registry manifest. This
preparation does not perform any of those publication steps.
1 change: 1 addition & 0 deletions docs/pt-BR/development/security-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Esse controle avalia o delta de dependências do pull request. Ele complementa,

O Dependabot mantém:

- dependências npm do portal de documentação em `website/`, agrupando atualizações minor e patch compatíveis e mantendo atualizações major separadas para revisão de compatibilidade;
- dependências NuGet, incluindo as restrições explícitas de compatibilidade Roslyn do repositório;
- o .NET SDK declarado em `global.json`;
- referências de GitHub Actions.
Expand Down
12 changes: 7 additions & 5 deletions docs/pt-BR/mcp-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,13 @@ sob `io.github.rodri-oliveira-dev/complexity-analysis` e resolve o pacote
`nuget` pelo NuGet.org; ele não substitui instalação nem inicialização stdio no
VS Code/Copilot, Codex ou Claude Desktop.

`.mcp/server.json` é a base versionada. Em cada release manual da `main`,
`release-mcp.yml` substitui os dois campos de versão do manifesto pela versão
validada no workflow, comprovando igualdade com `mcp-v<versão>` e o pacote
NuGet. Depois da publicação NuGet e conclusão da GitHub Release, o job do
Registry aguarda o README e o marcador exato de propriedade `mcp-name`, verifica
`.mcp/server.json` é a base versionada da versão atualmente publicada no
Registry; por isso, uma versão preparada do pacote pode ser mais nova. Em cada
release manual da `main`, `release-mcp.yml` substitui os dois campos de versão
do manifesto pela versão validada no workflow, comprovando igualdade com
`mcp-v<versão>` e o pacote NuGet. Depois da publicação NuGet e conclusão da
GitHub Release, o job do Registry aguarda o README e o marcador exato de
propriedade `mcp-name`, verifica
o artefato fixado do `mcp-publisher` por Sigstore, valida o manifesto, autentica
por GitHub OIDC, publica e verifica o resultado na API. Pull requests executam
somente validação.
Expand Down
36 changes: 36 additions & 0 deletions docs/pt-BR/releases/mcp-1.0.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# ComplexityAnalysis.Mcp 1.0.1

Esta release patch preparada melhora a manutenibilidade sem alterar a superfície
de ferramentas MCP nem seu contrato de compatibilidade.

## Correções de manutenção

- Dividiu o parsing das opções de inicialização em helpers focados de validação,
preservando todas as opções aceitas, defaults, limites e mensagens de erro.
- Reutilizou uma única travessia determinística de membros para consulta completa
de evidências e resumos limitados, preservando ordem, contagens, truncamento e
comportamento do cache de snapshots exatos.
- Preservou a raiz permitida, a proteção contra links simbólicos, o isolamento
entre requisições, o cancelamento, os timeouts e resultados conservadores
`Unknown`.

## Compatibilidade

- O pacote continua sendo uma ferramenta .NET 10 com o comando
`complexity-mcp`.
- As três ferramentas MCP somente leitura e seus schemas de requisição e
resposta permanecem inalterados.
- Nenhuma quebra de compatibilidade de código-fonte, transporte, Registry ou
pacote foi introduzida.

## Estado da publicação

A versão `1.0.1` está preparada para release após aprovação e merge. Até que o
workflow protegido publique o pacote e verifique a entrada no Official MCP
Registry, a documentação de instalação e descoberta continua identificando a
versão atualmente publicada.

O workflow cria a tag imutável `mcp-v1.0.1`, empacota e valida a ferramenta,
produz a SBOM SPDX 2.3 e as attestations, publica os artefatos NuGet e GitHub e,
em seguida, sincroniza e verifica o manifesto versionado do Registry. Esta
preparação não executa nenhuma dessas etapas de publicação.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@

ComplexityExpression complexity = mapping.ExecutionKind == KnownOperationExecutionKind.Deferred
? ComplexityFactory.Constant()
: IsLinqMapping(mapping)

Check warning on line 58 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / SonarQube Cloud

Extract this nested ternary operation into an independent statement.
? AnalyzeImmediateLinqInvocation(invocation, methodSymbol, mapping)
: AnalyzeImmediateKnownInvocation(invocation, methodSymbol, mapping);

Expand All @@ -76,7 +76,7 @@
return ComplexityFactory.Unknown();
}

if (!mapping.Metadata.EnumeratesReceiver)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Validate analyzer

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Quality

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Validate, build, test and pack tool

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Validate, build, test and pack tool

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Validate, build, test and pack MCP

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Validate, build, test and pack MCP

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Pack analyzer

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Validate, build, test and pack

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Pack analyzer

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Analyze C# with CodeQL

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Performance

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 79 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Package

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)
{
return mapping.Complexity;
}
Expand Down Expand Up @@ -202,7 +202,7 @@
return argumentComplexity;
}

if (!mapping.Metadata.EnumeratesReceiver)

Check warning on line 205 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Validate analyzer

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 205 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Validate, build, test and pack MCP

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 205 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Validate, build, test and pack MCP

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 205 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Pack analyzer

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 205 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Validate, build, test and pack

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 205 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Pack analyzer

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 205 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Analyze C# with CodeQL

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)

Check warning on line 205 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Package

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)
{
return ComplexityComposer.Sequential(argumentComplexity, mapping.Complexity);
}
Expand Down Expand Up @@ -284,7 +284,7 @@
return ComplexityFactory.Constant();
}

if (StringComparer.Ordinal.Equals(operationFamily, "linq-any")

Check warning on line 287 in src/ComplexityAnalysis.Analyzers/Analysis/KnownOperationComplexityAnalyzer.cs

View workflow job for this annotation

GitHub Actions / Validate analyzer

'if' statement can be simplified (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046)
&& !hasLambdaArgument)
{
return ComplexityFactory.Constant();
Expand Down Expand Up @@ -327,32 +327,18 @@
expression = UnwrapParentheses(expression);
plan = SequenceEnumerationPlan.Unknown();

if (TryGetLocalInitializer(
expression,
out ExpressionSyntax? localInitializer,
out _)
&& localInitializer is not null)
if (TryAnalyzeLocalPipeline(expression, out plan, out bool isLocalReference))
{
if (TryResolveStableLocalInitializer(expression, out ExpressionSyntax? stableInitializer)
&& stableInitializer is not null)
{
return TryAnalyzeDeferredPipeline(stableInitializer, out plan);
}

if (IsDeferredPipelineExpression(localInitializer))
{
plan = SequenceEnumerationPlan.Unknown();
return true;
}
return true;
}

if (expression is not InvocationExpressionSyntax invocation
|| !KnownOperationInvocation.TryResolve(invocation, context, Resolver, out IMethodSymbol? methodSymbol, out KnownOperationMapping? mapping)
|| methodSymbol is null
|| mapping is null
|| mapping.ExecutionKind != KnownOperationExecutionKind.Deferred
|| !KnownOperationInvocation.TryGetReceiverExpression(invocation, methodSymbol, out ExpressionSyntax? source)
|| source is null)
if (isLocalReference
|| !TryResolveDeferredInvocation(
expression,
out InvocationExpressionSyntax? invocation,
out IMethodSymbol? methodSymbol,
out KnownOperationMapping? mapping,
out ExpressionSyntax? source))
{
return false;
}
Expand All @@ -374,14 +360,82 @@
return true;
}

plan = CreateDeferredPipelinePlan(invocation, mapping, sourcePlan, delegateComplexity);
return true;
}

private bool TryAnalyzeLocalPipeline(
ExpressionSyntax expression,
out SequenceEnumerationPlan plan,
out bool isLocalReference)
{
plan = SequenceEnumerationPlan.Unknown();
isLocalReference = TryGetLocalInitializer(
expression,
out ExpressionSyntax? localInitializer,
out _)
&& localInitializer is not null;

return isLocalReference
&& (TryResolveStableLocalInitializer(expression, out ExpressionSyntax? stableInitializer)
&& stableInitializer is not null
? TryAnalyzeDeferredPipeline(stableInitializer, out plan)
: IsDeferredPipelineExpression(localInitializer!));
}

private bool TryResolveDeferredInvocation(
ExpressionSyntax expression,
out InvocationExpressionSyntax invocation,
out IMethodSymbol methodSymbol,
out KnownOperationMapping mapping,
out ExpressionSyntax source)
{
invocation = null!;
methodSymbol = null!;
mapping = null!;
source = null!;

if (expression is not InvocationExpressionSyntax candidate)
{
return false;
}

if (!KnownOperationInvocation.TryResolve(
candidate,
context,
Resolver,
out IMethodSymbol? resolvedMethod,
out KnownOperationMapping? resolvedMapping)
|| resolvedMethod is null
|| resolvedMapping is null
|| resolvedMapping.ExecutionKind != KnownOperationExecutionKind.Deferred
|| !KnownOperationInvocation.TryGetReceiverExpression(
candidate,
resolvedMethod,
out ExpressionSyntax? resolvedSource)
|| resolvedSource is null)
{
return false;
}

invocation = candidate;
methodSymbol = resolvedMethod;
mapping = resolvedMapping;
source = resolvedSource;
return true;
}

private SequenceEnumerationPlan CreateDeferredPipelinePlan(
InvocationExpressionSyntax invocation,
KnownOperationMapping mapping,
SequenceEnumerationPlan sourcePlan,
ComplexityExpression delegateComplexity)
{
ComplexityExpression total = sourcePlan.TotalEnumerationComplexity;
ComplexityExpression elementIteration = sourcePlan.ElementIterationComplexity;
string operationFamily = mapping.Metadata.OperationFamily;

if (StringComparer.Ordinal.Equals(operationFamily, "linq-orderby")
|| StringComparer.Ordinal.Equals(operationFamily, "linq-orderby-descending")
|| StringComparer.Ordinal.Equals(operationFamily, "linq-thenby")
|| StringComparer.Ordinal.Equals(operationFamily, "linq-thenby-descending"))
if (IsOrderingOperation(operationFamily))
{
total = ComplexityComposer.Sequential(
total,
Expand All @@ -392,8 +446,7 @@
if (!TryResolveSelectManyInnerDimension(invocation, out ComplexityVariable? innerVariable)
|| innerVariable is null)
{
plan = SequenceEnumerationPlan.Unknown();
return true;
return SequenceEnumerationPlan.Unknown();
}

elementIteration = ComplexityComposer.Nested(
Expand All @@ -415,12 +468,19 @@
ComplexityComposer.Nested(sourcePlan.ElementIterationComplexity, delegateComplexity));
}

plan = new SequenceEnumerationPlan(
return new SequenceEnumerationPlan(
isUnknown: false,
sourcePlan.PrimaryVariable,
elementIteration,
total);
return true;
}

private static bool IsOrderingOperation(string operationFamily)
{
return operationFamily is "linq-orderby"
or "linq-orderby-descending"
or "linq-thenby"
or "linq-thenby-descending";
}

private bool TryResolveSelectManyInnerDimension(
Expand Down
Loading
Loading