diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index f66587ad..18525833 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -5,6 +5,7 @@ on:
branches:
- master
pull_request:
+ workflow_dispatch:
jobs:
test:
@@ -25,6 +26,15 @@ jobs:
- name: Install dependencies
run: pip install .[dev]
+ # required only if you want to verify C# with SourceExpander
+ - uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: |
+ 3.1.x
+ 6.0.x
+ - name: Install dependencies (C#)
+ run: dotnet tool install -g SourceExpander.Console
+
- name: Run tests
run: |
unset GITHUB_ACTION
diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml
index 56b6c692..0f455e25 100644
--- a/.github/workflows/verify.yml
+++ b/.github/workflows/verify.yml
@@ -41,6 +41,15 @@ jobs:
- name: Install dependencies (C#)
run: dotnet tool install --global dotnet-script --version 1.4.0
+ # required only if you want to verify C# with SourceExpander
+ - uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: |
+ 3.1.x
+ 6.0.x
+ - name: Install dependencies (C#)
+ run: dotnet tool install -g SourceExpander.Console
+
# required only if you want to verify Go code
- name: Install dependencies (Go)
uses: actions/setup-go@v4
diff --git a/.verify-helper/docs/static/document.ja.md b/.verify-helper/docs/static/document.ja.md
index c2b0892a..8d7550cb 100644
--- a/.verify-helper/docs/static/document.ja.md
+++ b/.verify-helper/docs/static/document.ja.md
@@ -10,6 +10,7 @@
|---|---|---|---|---|---|
| C++ | `.cpp` `.hpp` | `.test.cpp` | `#define [KEY] [VALUE]` | :heavy_check_mark: / :heavy_check_mark: / :heavy_check_mark: | [segment_tree.range_sum_query.test.cpp](https://github.com/online-judge-tools/verification-helper/blob/master/examples/segment_tree.range_sum_query.test.cpp) |
| C# script | `.csx` | `.test.csx` | `// verification-helper: [KEY] [VALUE]` | :heavy_check_mark: / :x: / :heavy_check_mark: | [segment_tree.range_sum_query.test.csx](https://github.com/online-judge-tools/verification-helper/blob/master/examples/csharpscript/segment_tree.range_sum_query.test.csx) |
+| C# | `.cs` | `.test.cs` | `// verification-helper: [KEY] [VALUE]` | :heavy_check_mark: / :heavy_check_mark: / :heavy_check_mark: | [segment_tree.range_sum_query.test.csx](https://github.com/online-judge-tools/verification-helper/blob/master/examples/csharpsx/Verifier/segment_tree.range_sum_query.test.csx) |
| Nim | `.nim` | `_test.nim` | `# verification-helper: [KEY] [VALUE]` | :heavy_check_mark: / :x: / :heavy_check_mark: | [union_find_tree_yosupo_test.nim](https://github.com/online-judge-tools/verification-helper/blob/master/examples/nim/union_find_tree_yosupo_test.nim) |
| Python 3 | `.py` | `.test.py` | `# verification-helper: [KEY] [VALUE]` | :heavy_check_mark: / :x: / :heavy_check_mark: | [union_find_yosupo.test.py](https://github.com/online-judge-tools/verification-helper/blob/master/examples/python/union_find_yosupo.test.py) |
| Haskell | `.hs` | `.test.hs` | `-- verification-helper: [KEY] [VALUE]` | :heavy_check_mark: / :x: / :warning: | [HelloWorld.test.hs](https://github.com/online-judge-tools/verification-helper/blob/master/Examples2/Haskell/HelloWorld.test.hs) |
@@ -40,7 +41,28 @@ CXXFLAGS = ["-std=c++17", "-Wall", "-g", "-fsanitize=undefined", "-D_GLIBCXX_DEB
設定項目はありません。
コンパイラには .NET Core が使われます。
-- いまのところ `.cs` という拡張子が認識されないことに注意してください ([#248](https://github.com/online-judge-tools/verification-helper/issues/248))。
+### C# の設定
+
+[SourceExpander](https://github.com/kzrnm/SourceExpander) を使用して各種機能を実現します。
+
+必須設定
+- ライブラリのターゲットに関わらず、.NET 6 以上の SDK をインストールする。
+- Library と Test は別のプロジェクトとして作成する。
+- Libraryプロジェクトには `SourceExpander.Embedder` を参照に追加する。
+
+具体的な設定は [examples/csharpsx](https://github.com/online-judge-tools/verification-helper/tree/master/examples/csharpsx) を参照。
+
+
+`.verify-helper/config.toml` というファイルを作って以下のように設定を書くと各種設定ができます。
+
+- static_embedding: `dotnet-source-expand` の `--static-embedding` オプション
+- csproj_template: テストファイルのコンパイル時に使われる csproj を指定します。
+
+``` toml
+[[languages.csharp]]
+static_embedding = "// embed"
+csproj_template = ".verify-helper/csproj.template"
+```
### Nim の設定
diff --git a/.verify-helper/docs/static/document.md b/.verify-helper/docs/static/document.md
index ee10850a..9b966564 100644
--- a/.verify-helper/docs/static/document.md
+++ b/.verify-helper/docs/static/document.md
@@ -10,6 +10,7 @@ Summary:
|---|---|---|---|---|---|
| C++ | `.cpp` `.hpp` | `.test.cpp` | `#define [KEY] [VALUE]` | :heavy_check_mark: / :heavy_check_mark: / :heavy_check_mark: | [segment_tree.range_sum_query.test.cpp](https://github.com/online-judge-tools/verification-helper/blob/master/examples/segment_tree.range_sum_query.test.cpp) |
| C# script | `.csx` | `.test.csx` | `// verification-helper: [KEY] [VALUE]` | :heavy_check_mark: / :x: / :heavy_check_mark: | [segment_tree.range_sum_query.test.csx](https://github.com/online-judge-tools/verification-helper/blob/master/examples/csharpscript/segment_tree.range_sum_query.test.csx) |
+| C# | `.cs` | `.test.cs` | `// verification-helper: [KEY] [VALUE]` | :heavy_check_mark: / :heavy_check_mark: / :heavy_check_mark: | [segment_tree.range_sum_query.test.csx](https://github.com/online-judge-tools/verification-helper/blob/master/examples/csharpsx/Verifier/segment_tree.range_sum_query.test.csx) |
| Nim | `.nim` | `_test.nim` | `# verification-helper: [KEY] [VALUE]` | :heavy_check_mark: / :x: / :heavy_check_mark: | [union_find_tree_yosupo_test.nim](https://github.com/online-judge-tools/verification-helper/blob/master/examples/nim/union_find_tree_yosupo_test.nim) |
| Python 3 | `.py` | `.test.py` | `# verification-helper: [KEY] [VALUE]` | :heavy_check_mark: / :x: / :heavy_check_mark: | [union_find_yosupo.test.py](https://github.com/online-judge-tools/verification-helper/blob/master/examples/python/union_find_yosupo.test.py) |
| Haskell | `.hs` | `.test.hs` | `-- verification-helper: [KEY] [VALUE]` | :heavy_check_mark: / :x: / :warning: | [HelloWorld.test.hs](https://github.com/online-judge-tools/verification-helper/blob/master/Examples2/Haskell/HelloWorld.test.hs) |
@@ -35,12 +36,32 @@ CXXFLAGS = ["-std=c++17", "-Wall", "-g", "-fsanitize=undefined", "-D_GLIBCXX_DEB
- If you use environments which [`ulimit`](https://linux.die.net/man/3/ulimit) doesn't work on, and if you want to set `CXXFLAGS` by yourself, please be careful about the stack size.
- The supported extensions are `.cpp`, `.hpp`, `.cc`, and `.h`. Please note that files with other extensions like `.c` `.h++` and files without extensions are not recognized.
-### Settings for C#
+### Settings for C# script
There is no config now.
.NET Core is used as the compiler.
-- Note that currently the `.cs` extension is not recognized ([#248](https://github.com/online-judge-tools/verification-helper/issues/248)).
+### Settings for C#
+
+`oj-verify` and `oj-bundle` work with [SourceExpander](https://github.com/kzrnm/SourceExpander).
+
+Requied settings
+- Install .NET 6 or newer SDK regardless of target framework of library.
+- Create Library project and Test project as different projects.
+- Add `SourceExpander.Embedder` reference to Library project.
+
+For the details refer to [examples/csharpsx](https://github.com/online-judge-tools/verification-helper/tree/master/examples/csharpsx).
+
+You can specify compilers and options with writing `.verify-helper/config.toml` as below.
+
+- static_embedding: `dotnet-source-expand` with `--static-embedding` option.
+- csproj_template: test file will be compiled with this csproj file.
+
+``` toml
+[[languages.csharp]]
+static_embedding = "// embed"
+csproj_template = ".verify-helper/csproj.template"
+```
### Settings for Nim
diff --git a/examples/csharpsx/.editorconfig b/examples/csharpsx/.editorconfig
new file mode 100644
index 00000000..65e257b5
--- /dev/null
+++ b/examples/csharpsx/.editorconfig
@@ -0,0 +1,5 @@
+[*.cs]
+
+# IDE1006: X^C
+dotnet_diagnostic.IDE1006.severity = silent
+dotnet_diagnostic.IDE0044.severity = silent
diff --git a/examples/csharpsx/.gitignore b/examples/csharpsx/.gitignore
new file mode 100644
index 00000000..8a30d258
--- /dev/null
+++ b/examples/csharpsx/.gitignore
@@ -0,0 +1,398 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.tlog
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio 6 auto-generated project file (contains which files were open etc.)
+*.vbp
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# Visual Studio History (VSHistory) files
+.vshistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# JetBrains Rider
+*.sln.iml
diff --git a/examples/csharpsx/Library/HelloWorld.const.cs b/examples/csharpsx/Library/HelloWorld.const.cs
new file mode 100644
index 00000000..aa153574
--- /dev/null
+++ b/examples/csharpsx/Library/HelloWorld.const.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Library
+{
+ public static partial class HelloWorld
+ {
+ private const string Text = "Hello World";
+ }
+}
diff --git a/examples/csharpsx/Library/HelloWorld.cs b/examples/csharpsx/Library/HelloWorld.cs
new file mode 100644
index 00000000..65db9515
--- /dev/null
+++ b/examples/csharpsx/Library/HelloWorld.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Library
+{
+ public static partial class HelloWorld
+ {
+ public static void Hello() => Console.WriteLine(Text);
+ }
+}
diff --git a/examples/csharpsx/Library/Library.csproj b/examples/csharpsx/Library/Library.csproj
new file mode 100644
index 00000000..0387b057
--- /dev/null
+++ b/examples/csharpsx/Library/Library.csproj
@@ -0,0 +1,13 @@
+
+
+
+ netcoreapp3.1
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
diff --git a/examples/csharpsx/Library/SegmentTree.cs b/examples/csharpsx/Library/SegmentTree.cs
new file mode 100644
index 00000000..6a97dd70
--- /dev/null
+++ b/examples/csharpsx/Library/SegmentTree.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace Library
+{
+ public class SegmentTree
+ {
+ public int Count { get; private set; }
+ T Identity;
+ T[] Data;
+ Func Merge;
+ int LeafCount;
+ public SegmentTree(int count, T identity, Func merge)
+ {
+ Count = count;
+ Identity = identity;
+ Merge = merge;
+ LeafCount = 1;
+ while (LeafCount < count) LeafCount <<= 1;
+ Data = new T[LeafCount << 1];
+ for (int i = 1; i < Data.Length; i++) Data[i] = identity;
+ }
+ public T this[int index]
+ {
+ get { return Data[LeafCount + index]; }
+ set { Assign(index, value); }
+ }
+ public void Assign(int i, T x) { Data[i += LeafCount] = x; while (0 < (i >>= 1)) Data[i] = Merge(Data[i << 1], Data[(i << 1) | 1]); }
+ public void Operate(int i, T x) { Data[i += LeafCount] = Merge(Data[i], x); while (0 < (i >>= 1)) Data[i] = Merge(Data[i << 1], Data[(i << 1) | 1]); }
+ public T Slice(int l, int length) => Fold(l, l + length);
+ public T Fold(int l, int r)
+ {
+ T lRes = Identity, rRes = Identity;
+ for (l += LeafCount, r += LeafCount - 1; l <= r; l = (l + 1) >> 1, r = (r - 1) >> 1)
+ {
+ if ((l & 1) == 1) lRes = Merge(lRes, Data[l]);
+ if ((r & 1) == 0) rRes = Merge(Data[r], rRes);
+ }
+ return Merge(lRes, rRes);
+ }
+ }
+}
diff --git a/examples/csharpsx/Tests/Tests.csproj b/examples/csharpsx/Tests/Tests.csproj
new file mode 100644
index 00000000..a2e3112c
--- /dev/null
+++ b/examples/csharpsx/Tests/Tests.csproj
@@ -0,0 +1,13 @@
+
+
+
+ Exe
+ netcoreapp3.1
+ Verifier.LocalRunner
+
+
+
+
+
+
+
diff --git a/examples/csharpsx/Tests/_LocalRunner.test.cs b/examples/csharpsx/Tests/_LocalRunner.test.cs
new file mode 100644
index 00000000..92a72fcb
--- /dev/null
+++ b/examples/csharpsx/Tests/_LocalRunner.test.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Reflection;
+// verification-helper: IGNORE
+namespace Verifier
+{
+ public class LocalRunner
+ {
+ static void Main(string[] args)
+ {
+ string verifier;
+ if (args.Length == 0)
+ {
+ Console.WriteLine("Input verifier name:");
+ verifier = Console.ReadLine();
+ }
+ else
+ {
+ verifier = args[0];
+ }
+ var type = Type.GetType(verifier);
+
+ var method = type.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
+ method.Invoke(null, null);
+ }
+ }
+}
diff --git a/examples/csharpsx/Tests/helloworld.test.cs b/examples/csharpsx/Tests/helloworld.test.cs
new file mode 100644
index 00000000..6580a134
--- /dev/null
+++ b/examples/csharpsx/Tests/helloworld.test.cs
@@ -0,0 +1,9 @@
+// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/1/ITP1_1_A
+
+internal class helloworld
+{
+ public static void Main()
+ {
+ Library.HelloWorld.Hello();
+ }
+}
diff --git a/examples/csharpsx/Tests/segment_tree.point_set_range_composite.test.cs b/examples/csharpsx/Tests/segment_tree.point_set_range_composite.test.cs
new file mode 100644
index 00000000..34771551
--- /dev/null
+++ b/examples/csharpsx/Tests/segment_tree.point_set_range_composite.test.cs
@@ -0,0 +1,44 @@
+// verification-helper: PROBLEM https://judge.yosupo.jp/problem/point_set_range_composite
+using System;
+using System.Linq;
+using Library;
+
+class point_set_range_composite
+{
+ static void Main()
+ {
+ var nq = Console.ReadLine().Split().Select(int.Parse).ToArray();
+ var (n, q) = (nq[0], nq[1]);
+ SegmentTree segTree = new SegmentTree(n, new Linear(1, 0), Linear.Merge);
+
+ for (int i = 0; i < n; i++)
+ {
+ var ab = Console.ReadLine().Split().Select(int.Parse).ToArray();
+ segTree[i] = new Linear(ab[0], ab[1]);
+ }
+
+ for (int i = 0; i < q; i++)
+ {
+ var query = Console.ReadLine().Split().Select(int.Parse).ToArray();
+ if (query[0] == 0)
+ {
+ segTree[query[1]] = new Linear(query[2], query[3]);
+ }
+ else
+ {
+ Console.WriteLine(segTree[query[1]..query[2]].EvalWith(query[3]));
+ }
+ }
+ }
+
+ struct Linear
+ {
+ const int MOD = 998244353;
+ public long A;
+ public long B;
+ public Linear(long a, long b) { A = a; B = b; }
+ public static Linear Merge(Linear a, Linear b) => new Linear(a.A * b.A % MOD, (a.B * b.A + b.B) % MOD);
+ public long EvalWith(int x) => (A * x + B) % MOD;
+ public override string ToString() => $"{A} {B}";
+ }
+}
diff --git a/examples/csharpsx/Tests/segment_tree.range_minimum_query.test.cs b/examples/csharpsx/Tests/segment_tree.range_minimum_query.test.cs
new file mode 100644
index 00000000..89b8e5b5
--- /dev/null
+++ b/examples/csharpsx/Tests/segment_tree.range_minimum_query.test.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Linq;
+using Library;
+
+// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/library/3/DSL/all/DSL_2_A
+class range_minimum_query
+{
+ static void Main()
+ {
+ var nq = Console.ReadLine().Split().Select(int.Parse).ToArray();
+ var (n, q) = (nq[0], nq[1]);
+ SegmentTree segTree = new SegmentTree(n, int.MaxValue, Math.Min);
+
+ for (int i = 0; i < q; i++)
+ {
+ var query = Console.ReadLine().Split().Select(int.Parse).ToArray();
+ if (query[0] == 0)
+ {
+ segTree[query[1]] = query[2];
+ }
+ else
+ {
+ Console.WriteLine(segTree[query[1]..(query[2] + 1)]);
+ }
+ }
+ }
+}
diff --git a/examples/csharpsx/Tests/segment_tree.range_sum_query.test.cs b/examples/csharpsx/Tests/segment_tree.range_sum_query.test.cs
new file mode 100644
index 00000000..a7d34ab1
--- /dev/null
+++ b/examples/csharpsx/Tests/segment_tree.range_sum_query.test.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Linq;
+using Library;
+
+// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/library/3/DSL/all/DSL_2_B
+class range_sum_query
+{
+ static void Main()
+ {
+ var nq = Console.ReadLine().Split().Select(int.Parse).ToArray();
+ var (n, q) = (nq[0], nq[1]);
+ SegmentTree segTree = new SegmentTree(n, 0, (x, y) => x + y);
+
+ for (int i = 0; i < q; i++)
+ {
+ var query = Console.ReadLine().Split().Select(int.Parse).ToArray();
+ if (query[0] == 0)
+ {
+ segTree[query[1] - 1] += query[2];
+ }
+ else
+ {
+ Console.WriteLine(segTree[(query[1] - 1)..query[2]]);
+ }
+ }
+ }
+}
diff --git a/examples/csharpsx/csharpsx.sln b/examples/csharpsx/csharpsx.sln
new file mode 100644
index 00000000..57d00491
--- /dev/null
+++ b/examples/csharpsx/csharpsx.sln
@@ -0,0 +1,36 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.1.32228.430
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Library", "Library\Library.csproj", "{E7C3477D-EEC5-4824-B9B9-8052120382AD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{A36FDB96-6312-4D74-B35F-68542F9C6F84}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7BB23E5C-7EE5-40CB-8DD2-31929A329847}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E7C3477D-EEC5-4824-B9B9-8052120382AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E7C3477D-EEC5-4824-B9B9-8052120382AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E7C3477D-EEC5-4824-B9B9-8052120382AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E7C3477D-EEC5-4824-B9B9-8052120382AD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A36FDB96-6312-4D74-B35F-68542F9C6F84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A36FDB96-6312-4D74-B35F-68542F9C6F84}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A36FDB96-6312-4D74-B35F-68542F9C6F84}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A36FDB96-6312-4D74-B35F-68542F9C6F84}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {D871A8D2-DED0-4FE8-9886-9CE1DD4EB5A4}
+ EndGlobalSection
+EndGlobal
diff --git a/onlinejudge_verify/documentation/main.py b/onlinejudge_verify/documentation/main.py
index 9af9922b..67e9c240 100644
--- a/onlinejudge_verify/documentation/main.py
+++ b/onlinejudge_verify/documentation/main.py
@@ -1,5 +1,6 @@
import json
import pathlib
+import subprocess
from logging import getLogger
from typing import *
@@ -56,6 +57,10 @@ def load_render_config(*, basedir: pathlib.Path) -> SiteRenderConfig:
)
+def _get_gitignored_paths() -> List[pathlib.Path]:
+ return list(map(pathlib.Path, subprocess.check_output(['git', 'ls-files', '--ignored', '--others', '--exclude-standard', '--directory'], text=True).strip().splitlines()))
+
+
# TODO: この configure.py + build.py というファイル分割そこまでうまくはいってなくないか? もうすこし整理したい
def main(*, jobs: int = 1) -> None:
basedir = pathlib.Path.cwd()
@@ -69,7 +74,7 @@ def main(*, jobs: int = 1) -> None:
logger.info('list markdown files...')
markdown_paths = configure.find_markdown_paths(basedir=basedir)
logger.info('list rendering jobs...')
- excluded_paths = list(map(pathlib.Path, config.config_yml.get('exclude', [])))
+ excluded_paths = list(map(pathlib.Path, config.config_yml.get('exclude', []))) + _get_gitignored_paths()
source_code_stats = configure.apply_exclude_list_to_stats(excluded_paths=excluded_paths, source_code_stats=source_code_stats)
markdown_paths = configure.apply_exclude_list_to_paths(markdown_paths, excluded_paths=excluded_paths)
render_jobs = configure.convert_to_page_render_jobs(source_code_stats=source_code_stats, markdown_paths=markdown_paths, site_render_config=config)
diff --git a/onlinejudge_verify/languages/csharp.py b/onlinejudge_verify/languages/csharp.py
new file mode 100644
index 00000000..ba0dcf8d
--- /dev/null
+++ b/onlinejudge_verify/languages/csharp.py
@@ -0,0 +1,277 @@
+# Python Version: 3.x
+import distutils.version
+import functools
+import json
+import os
+import pathlib
+import shutil
+import subprocess
+import xml.etree.ElementTree as ET
+from logging import getLogger
+from typing import *
+
+import onlinejudge_verify.languages.special_comments as special_comments
+from onlinejudge_verify.config import get_config
+from onlinejudge_verify.languages.models import Language, LanguageEnvironment
+
+logger = getLogger(__name__)
+
+
+@overload
+def check_output(command: List[str], *, text: Literal[True]) -> str:
+ ...
+
+
+@overload
+def check_output(command: List[str], *, text: Literal[False]) -> bytes:
+ ...
+
+
+@overload
+def check_output(command: List[str]) -> bytes:
+ ...
+
+
+def check_output(command: List[str], *, text: bool = False) -> Union[bytes, str]:
+ try:
+ return subprocess.check_output(command, text=text)
+ except (subprocess.CalledProcessError) as e:
+ logger.error('raise subprocess.CalledProcessError')
+ logger.info(' stdout: %s', e.stdout)
+ logger.info(' stderr: %s', e.stderr)
+ raise
+
+
+@functools.lru_cache(maxsize=1)
+def _check_dotnet_version() -> None:
+ if not shutil.which('dotnet'):
+ raise RuntimeError('`dotnet` not in $PATH')
+ command = ['dotnet', '--version']
+ logger.info('$ %s', ' '.join(command))
+ res = check_output(command, text=True).strip()
+ logger.info('dotnet version: %s', res)
+ if distutils.version.LooseVersion(res) <= distutils.version.LooseVersion("6"):
+ raise RuntimeError("oj-verify needs .NET 6 SDK or newer")
+
+
+@functools.lru_cache(maxsize=1)
+def _check_expander_console() -> None:
+ if not shutil.which('dotnet-source-expand'):
+ raise RuntimeError('`dotnet-source-expand` not in $PATH. Run `dotnet tool install -g SourceExpander.Console`')
+ command = ['dotnet-source-expand', 'version']
+ logger.info('$ %s', ' '.join(command))
+ res = check_output(command, text=True).strip()
+ logger.info('dotnet-source-expand version: %s', res)
+ if distutils.version.LooseVersion(res) < distutils.version.LooseVersion("5"):
+ raise RuntimeError('`dotnet-source-expand` version must be 5.0.0 or newer. Update SourceExpander.Console. `dotnet tool update -g SourceExpander.Console`')
+
+
+class EmbeddedLibrary:
+ def __init__(self, name: str, version: str) -> None:
+ self.name = name
+ self.version = version
+
+ def __repr__(self) -> str:
+ return 'EmbeddedLibrary("%s", "%s")' % (self.name, self.version)
+
+
+@functools.lru_cache(maxsize=None)
+def _list_embedded(csproj_path: pathlib.Path) -> List[EmbeddedLibrary]:
+ _check_expander_console()
+ if csproj_path is None or csproj_path.suffix != ".csproj":
+ raise RuntimeError('csproj_path must be .csproj')
+ command = ['dotnet-source-expand', 'library-list', str(csproj_path)]
+ logger.info('$ %s', ' '.join(command))
+
+ def enumerate_library(lines: List[str]):
+ for line in lines:
+ sp = line.split(',')
+ if len(sp) >= 2:
+ yield EmbeddedLibrary(sp[0], sp[1])
+
+ res = list(enumerate_library(check_output(command, text=True).strip().splitlines()))
+ logger.debug('libraries: %s', res)
+ return res
+
+
+@functools.lru_cache(maxsize=None)
+def _check_embedded_existing(csproj_path: pathlib.Path) -> None:
+ command = ['dotnet', 'build', str(csproj_path)]
+ logger.info('$ %s', ' '.join(command))
+ check_output(command)
+ l = _list_embedded(csproj_path)
+ if len(l) == 0:
+ raise RuntimeError('Library needs SourceExpander.Embedder')
+
+
+def _check_env(path: pathlib.Path):
+ _check_dotnet_version()
+ _check_expander_console()
+ _check_embedded_existing(_resolve_csproj(path))
+
+
+@functools.lru_cache(maxsize=None)
+def _resolve_csproj(path: pathlib.Path) -> Optional[pathlib.Path]:
+ path = path.resolve()
+ if path.suffix == ".csproj":
+ return path
+
+ proj = next(path.glob("*.csproj"), None)
+ if proj is not None:
+ return proj
+
+ if path == path.parent:
+ return None
+ return _resolve_csproj(path.parent)
+
+
+class DependencyInfo:
+ def __init__(self, filename: str, dependencies: List[str], typenames: Set[str]) -> None:
+ self.filename = filename
+ self.dependencies = dependencies
+ self.defined_types = typenames
+
+ def __repr__(self) -> str:
+ return 'DependencyInfo("%s", %s, %s)' % (self.filename, self.dependencies, self.defined_types)
+
+
+@functools.lru_cache(maxsize=None)
+def _dependency_info_list(csproj_path: pathlib.Path) -> List[DependencyInfo]:
+ _check_expander_console()
+ _check_embedded_existing(csproj_path)
+ if csproj_path is None or csproj_path.suffix != ".csproj":
+ raise RuntimeError('csproj_path must be .csproj')
+
+ command = ['dotnet-source-expand', 'dependency', '-p', str(csproj_path)]
+ logger.info('$ %s', ' '.join(command))
+ res = check_output(command)
+ return json.loads(res, object_hook=lambda d: DependencyInfo(d['FileName'], d['Dependencies'], set(d['TypeNames'])))
+
+
+@functools.lru_cache(maxsize=None)
+def _dependency_info_dict(csproj_path: pathlib.Path) -> Dict[pathlib.Path, DependencyInfo]:
+ deps: Dict[pathlib.Path, DependencyInfo] = dict()
+ for d in _dependency_info_list(csproj_path):
+ p = pathlib.Path(d.filename)
+ if p.exists():
+ deps[p] = d
+ return deps
+
+
+@functools.lru_cache(maxsize=None)
+def _list_dependencies(path: pathlib.Path) -> List[pathlib.Path]:
+ path = path.resolve()
+ depinfo = _dependency_info_dict(_resolve_csproj(path))
+ return [p for p in (pathlib.Path(dep) for dep in depinfo[path].dependencies) if p.exists()]
+
+
+@functools.lru_cache(maxsize=None)
+def _get_target_framework(csproj_path: pathlib.Path) -> str:
+ root = ET.parse(csproj_path).getroot()
+ target = root.findtext('.//TargetFramework')
+ if target is None:
+ raise RuntimeError(' is not found')
+ return target
+
+
+class CSharpConfig:
+ def __init__(self, config: Dict[str, Any]) -> None:
+ root = config.get('languages', {}).get('csharp', {})
+ self.static_embedding: Optional[str] = root.get('static_embedding', None)
+ self.csproj_template: Optional[str] = root.get('csproj_template', None)
+
+
+class CSharpLanguageEnvironment(LanguageEnvironment):
+ def __init__(self, config: CSharpConfig) -> None:
+ super().__init__()
+ self.config = config
+
+ @staticmethod
+ def _write_default_project(output_file: pathlib.Path, target_framework: str) -> None:
+ with open(output_file, 'w') as f:
+ f.write('''
+
+ Exe
+ {}
+
+'''.format(target_framework))
+
+ def _write_csproj(self, output_file: pathlib.Path, csproj_template_path: Optional[pathlib.Path], target_framework: str) -> None:
+ if csproj_template_path is None:
+ self._write_default_project(output_file, target_framework)
+ elif not csproj_template_path.exists():
+ logger.warning('%s is not found.', str(csproj_template_path))
+ self._write_default_project(output_file, target_framework)
+ else:
+ shutil.copy(str(csproj_template_path), str(output_file))
+
+ def _create_runner_project(self, code: bytes, target_framework: str, csproj_template_path: Optional[pathlib.Path], output_dir: pathlib.Path):
+ os.makedirs(str(output_dir), exist_ok=True)
+ self._write_csproj(output_dir / 'runner.csproj', csproj_template_path, target_framework)
+ with open(output_dir / 'main.cs', 'wb') as f:
+ f.write(code)
+
+ def compile(self, path: pathlib.Path, *, basedir: pathlib.Path, tempdir: pathlib.Path) -> None:
+ path = path.resolve()
+ output_dir = tempdir / 'dotnet'
+ _check_env(path)
+ target_framework = _get_target_framework(_resolve_csproj(path))
+ logger.info('build: TargetFramework = %s', target_framework)
+ csproj_template_path = (basedir / pathlib.Path(self.config.csproj_template)) if self.config.csproj_template else None
+ self._create_runner_project(self._expand_code(path), target_framework, csproj_template_path, output_dir)
+
+ command = ['dotnet', 'build', str(output_dir / 'runner.csproj'), '-c', 'Release', '-o', str(output_dir / 'bin')]
+ logger.info('$ %s', ' '.join(command))
+ check_output(command)
+
+ def get_execute_command(self, path: pathlib.Path, *, basedir: pathlib.Path, tempdir: pathlib.Path) -> List[str]:
+ path = path.resolve()
+ output_dir = tempdir / 'dotnet'
+ path = path.resolve()
+ _check_env(path)
+ return [str(output_dir / 'bin' / 'runner')]
+
+ @functools.lru_cache(maxsize=None)
+ def _expand_code_dict(self, csproj_path: pathlib.Path) -> Dict[pathlib.Path, str]:
+ _check_expander_console()
+ command = ['dotnet-source-expand', 'expand-all', str(csproj_path)]
+ if self.config.static_embedding:
+ command.extend(['--static-embedding', self.config.static_embedding])
+ logger.info('$ %s', ' '.join(command))
+ json_res = check_output(command)
+ return {pathlib.Path(t['FilePath']): t['ExpandedCode'] for t in json.loads(json_res)}
+
+ @functools.lru_cache(maxsize=None)
+ def _expand_code(self, path: pathlib.Path) -> bytes:
+ _check_expander_console()
+ csproj_path = _resolve_csproj(path)
+ d = self._expand_code_dict(csproj_path)
+ return d[path].encode('utf-8')
+
+
+class CSharpLanguage(Language):
+ def __init__(self) -> None:
+ super().__init__()
+ self.config = CSharpConfig(get_config())
+ self.environment = CSharpLanguageEnvironment(self.config)
+
+ def list_attributes(self, path: pathlib.Path, *, basedir: pathlib.Path) -> Dict[str, Any]:
+ path = path.resolve()
+ attributes: Dict[str, Any] = special_comments.list_special_comments(path)
+ attributes.update(special_comments.list_doxygen_annotations(path))
+ attributes.setdefault('links', [])
+ attributes['links'].extend(special_comments.list_embedded_urls(path))
+ return attributes
+
+ def list_dependencies(self, path: pathlib.Path, *, basedir: pathlib.Path) -> List[pathlib.Path]:
+ path = path.resolve()
+ _check_env(path)
+ return _list_dependencies(path)
+
+ def bundle(self, path: pathlib.Path, *, basedir: pathlib.Path, options: Dict[str, Any]) -> bytes:
+ path = path.resolve()
+ _check_env(path)
+ return self.environment._expand_code(path)
+
+ def list_environments(self, path: pathlib.Path, *, basedir: pathlib.Path) -> Sequence[CSharpLanguageEnvironment]:
+ return [self.environment]
diff --git a/onlinejudge_verify/languages/list.py b/onlinejudge_verify/languages/list.py
index 4097d6eb..5e47142c 100644
--- a/onlinejudge_verify/languages/list.py
+++ b/onlinejudge_verify/languages/list.py
@@ -4,6 +4,7 @@
from onlinejudge_verify.config import get_config
from onlinejudge_verify.languages.cplusplus import CPlusPlusLanguage
+from onlinejudge_verify.languages.csharp import CSharpLanguage
from onlinejudge_verify.languages.csharpscript import CSharpScriptLanguage
from onlinejudge_verify.languages.go import GoLanguage
from onlinejudge_verify.languages.haskell import HaskellLanguage
@@ -29,6 +30,7 @@ def _get_dict() -> Dict[str, Language]:
_dict['.cc'] = _dict['.cpp']
_dict['.h'] = _dict['.cpp']
_dict['.csx'] = CSharpScriptLanguage()
+ _dict['.cs'] = CSharpLanguage()
_dict['.nim'] = NimLanguage()
_dict['.py'] = PythonLanguage()
_dict['.hs'] = HaskellLanguage()
diff --git a/onlinejudge_verify/verify.py b/onlinejudge_verify/verify.py
index d17a504c..83a0b096 100644
--- a/onlinejudge_verify/verify.py
+++ b/onlinejudge_verify/verify.py
@@ -76,7 +76,7 @@ def verify_file(path: pathlib.Path, *, compilers: List[str], tle: float, jobs: i
directory = pathlib.Path('.verify-helper/cache') / hashlib.md5(url.encode()).hexdigest()
if not (directory / 'test').exists() or list((directory / 'test').iterdir()) == []:
directory.mkdir(parents=True, exist_ok=True)
- exec_command(['sleep', '2'])
+ time.sleep(2)
command = ['oj', 'download', '--system', '-d', str(directory / 'test'), '--silent', url]
if os.environ.get('DROPBOX_TOKEN'):