diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index acddcbd..0d631da 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -7,6 +7,17 @@ on:
branches: [master]
jobs:
+ cpp-static-analysis:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install cppcheck
+ run: sudo apt-get install -y cppcheck
+
+ - name: Run C++ static analysis
+ run: src/cpp/test/static_analysis.sh
+
cpp:
runs-on: ubuntu-latest
steps:
@@ -21,6 +32,52 @@ jobs:
- name: Run C++ BinarySearch test
run: src/cpp/test/Search/test_binary_search.sh
+ - name: Run C++ Knapsack test
+ run: src/cpp/test/DP/test_knapsack.sh
+
+ - name: Run C++ Dijkstra test
+ run: src/cpp/test/Graph/test_dijkstra.sh
+
+ - name: Run C++ Floyd test
+ run: src/cpp/test/Graph/Floyd/test_floyd.sh
+
+ - name: Run C++ Prim test
+ run: src/cpp/test/Graph/test_prim.sh
+
+ - name: Run C++ GCD test
+ run: src/cpp/test/Math/Calc/test_gcd.sh
+
+ - name: Run C++ LCM test
+ run: src/cpp/test/Math/Calc/test_lcm.sh
+
+ - name: Run C++ Primes test
+ run: src/cpp/test/Math/Calc/test_primes.sh
+
+ - name: Run C++ Comb test
+ run: src/cpp/test/Math/Combination/test_comb.sh
+
+ - name: Run C++ ModPow test
+ run: src/cpp/test/Math/ModPow/test_modpow.sh
+
+ - name: Run C++ SegmentTree test
+ run: src/cpp/test/Tree/SegmentTree/test_segmenttree.sh
+
+ - name: Run C++ UnionFind test
+ run: src/cpp/test/Tree/UnionFind/test_unionfind.sh
+
+ java-static-analysis:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: '21'
+
+ - name: Run Java static analysis
+ run: src/java/test/static_analysis.sh
+
java:
runs-on: ubuntu-latest
steps:
@@ -39,3 +96,36 @@ jobs:
- name: Run Java BinarySearch test
run: src/java/test/Search/test_binary_search.sh
+
+ - name: Run Java Knapsack test
+ run: src/java/test/DP/test_knapsack.sh
+
+ - name: Run Java Dijkstra test
+ run: src/java/test/Graph/test_dijkstra.sh
+
+ - name: Run Java Floyd test
+ run: src/java/test/Graph/Floyd/test_floyd.sh
+
+ - name: Run Java Prim test
+ run: src/java/test/Graph/test_prim.sh
+
+ - name: Run Java GCD test
+ run: src/java/test/Math/Calc/test_gcd.sh
+
+ - name: Run Java LCM test
+ run: src/java/test/Math/Calc/test_lcm.sh
+
+ - name: Run Java Primes test
+ run: src/java/test/Math/Calc/test_primes.sh
+
+ - name: Run Java Comb test
+ run: src/java/test/Math/Combination/test_comb.sh
+
+ - name: Run Java ModPow test
+ run: src/java/test/Math/ModPow/test_modpow.sh
+
+ - name: Run Java SegTree test
+ run: src/java/test/Tree/SegmentTree/test_segtree.sh
+
+ - name: Run Java UnionFind test
+ run: src/java/test/Tree/UnionFind/test_unionfind.sh
diff --git a/README.md b/README.md
index 6dd944e..2afd6b4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,32 @@
-
Implementation of algorithms
\ No newline at end of file
+ Implementation of algorithms
+
+C++ and Java implementations.
+
+#### Algorithms
+
+| Category | Algorithm | Description |
+|----------|-----------|-------------|
+| DP | Knapsack | 0-1 knapsack problem |
+| Graph | Dijkstra | Shortest path (non-negative edges) |
+| Graph | Floyd | All-pairs shortest path, negative cycle detection |
+| Graph | Prim | Minimum spanning tree |
+| Math | GCD | Greatest common divisor |
+| Math | LCM | Least common multiple |
+| Math | Primes | Prime factorization, Sieve of Eratosthenes, Sieve of Atkin |
+| Math | Combination | nCk with modular inverse |
+| Math | ModPow | Modular exponentiation by repeated squaring |
+| Search | BFS | Breadth-first search |
+| Search | DFS | Depth-first search |
+| Search | BinarySearch | Lower bound / upper bound |
+| Tree | SegmentTree | Range Minimum Query |
+| Tree | UnionFind | Union-Find with path compression |
+
+#### Test
+
+```
+# C++
+src/cpp/test//test_.sh
+
+# Java
+src/java/test//test_.sh
+```
diff --git a/src/cpp/lib/DP/Knapsack.cpp b/src/cpp/lib/DP/Knapsack.cpp
index 4d8ca9b..f7d0a8c 100644
--- a/src/cpp/lib/DP/Knapsack.cpp
+++ b/src/cpp/lib/DP/Knapsack.cpp
@@ -1,15 +1,11 @@
#include
using namespace std;
-int main() {
- int n, w;
- cin >> n >> w;
-
- vector> arr(n);
- for (int i = 0; i < n; i++) {
- cin >> arr[i][0] >> arr[i][1];
- }
-
+// 0-1 Knapsack problem
+// arr[i] = {value, weight}
+// dp[i][j] = max value using first i items with capacity j
+vector> knapsack(const vector>& arr, int w) {
+ int n = arr.size();
vector> dp(n + 1, vector(w + 1, 0));
for (int i = 1; i <= n; i++) {
@@ -21,15 +17,5 @@ int main() {
}
}
- cout << "DP table:" << endl;
- for (int i = 0; i <= n; i++) {
- for (int j = 0; j < w; j++) {
- cout << dp[i][j] << " ";
- }
- cout << dp[i][w] << endl;
- }
- cout << "ans:" << endl;
- cout << dp[n][w];
-
- return 0;
+ return dp;
}
diff --git a/src/cpp/lib/Graph/Dijkstra.cpp b/src/cpp/lib/Graph/Dijkstra.cpp
index 442b4f2..4306be3 100644
--- a/src/cpp/lib/Graph/Dijkstra.cpp
+++ b/src/cpp/lib/Graph/Dijkstra.cpp
@@ -1,4 +1,4 @@
-//コストが負の辺がない場合(最短から求めるため負の辺を後から取り込むことができない)
+// For graphs with non-negative edge costs
#include
using namespace std;
@@ -9,28 +9,14 @@ struct Node {
}
};
-int main() {
- int n;
- cin >> n;
-
- vector> adj(n);
+vector dijkstra(int start, const vector>& adj) {
+ int n = adj.size();
vector d(n, INT_MAX);
vector visit(n, false);
- for (int i = 0; i < n; i++) {
- int v, k;
- cin >> v >> k;
- for (int j = 0; j < k; j++) {
- int u, c;
- cin >> u >> c;
- adj[v].push_back({u, c});
- }
- }
-
- // dijkstra
- d[0] = 0;
+ d[start] = 0;
priority_queue, greater> pq;
- pq.push({0, 0});
+ pq.push({start, 0});
while (!pq.empty()) {
Node nd = pq.top();
@@ -39,7 +25,7 @@ int main() {
visit[v] = true;
if (d[v] < nd.cost) continue;
- for (auto& node : adj[v]) {
+ for (const auto& node : adj[v]) {
int t = node.node;
if (visit[t]) continue;
if (d[t] > d[v] + node.cost) {
@@ -49,9 +35,5 @@ int main() {
}
}
- for (int i = 0; i < n; i++) {
- cout << d[i] << " ";
- }
-
- return 0;
+ return d;
}
diff --git a/src/cpp/lib/Graph/Floyd/Floyd.cpp b/src/cpp/lib/Graph/Floyd/Floyd.cpp
index 5d54b6a..a49ac1a 100644
--- a/src/cpp/lib/Graph/Floyd/Floyd.cpp
+++ b/src/cpp/lib/Graph/Floyd/Floyd.cpp
@@ -1,21 +1,11 @@
#include
using namespace std;
-int main() {
- int n, m;
- cin >> n >> m;
-
- vector> edge(n, vector(n, INT_MAX));
- for (int i = 0; i < n; i++) edge[i][i] = 0;
-
- for (int i = 0; i < m; i++) {
- int v, u, d;
- cin >> v >> u >> d;
- edge[v][u] = d;
- }
-
- // floyd 計算量はノード数Vとした時、O(|V|^3)。
- for (int k = 0; k < n; k++) { // ノードiからjへ移動する時の経由地をkとする。
+// Floyd-Warshall. O(|V|^3).
+// Returns true if a negative cycle is detected
+bool floyd(vector>& edge) {
+ int n = edge.size();
+ for (int k = 0; k < n; k++) { // k is the intermediate node from i to j
for (int i = 0; i < n; i++) {
if (edge[i][k] == INT_MAX) continue;
for (int j = 0; j < n; j++) {
@@ -25,21 +15,8 @@ int main() {
}
}
- bool negative = false;
for (int i = 0; i < n; i++) {
- if (edge[i][i] < 0) negative = true; // 負の閉路があれば対自ノードへのコストは負となる。
- }
-
- if (negative) {
- cout << "negative cycle" << endl;
- } else {
- for (int i = 0; i < n; i++) {
- for (int j = 0; j < n - 1; j++) {
- cout << edge[i][j] << " ";
- }
- cout << edge[i][n - 1] << endl;
- }
+ if (edge[i][i] < 0) return true; // negative cycle: self-loop cost becomes negative
}
-
- return 0;
+ return false;
}
diff --git a/src/cpp/lib/Graph/Prim.cpp b/src/cpp/lib/Graph/Prim.cpp
index 837a3cb..8b6a53e 100644
--- a/src/cpp/lib/Graph/Prim.cpp
+++ b/src/cpp/lib/Graph/Prim.cpp
@@ -1,24 +1,13 @@
#include
using namespace std;
-int main() {
- int n;
- cin >> n;
-
- vector> edge(n, vector(n));
- vector d(n, INT_MAX); // 最小全域木を構成する時の始点からの距離
- vector p(n, -1); // 最小全域木を構成する時の親ノード
+// Minimum spanning tree weight using Prim's algorithm
+int prim(const vector>& edge) {
+ int n = edge.size();
+ vector d(n, INT_MAX); // distance from start when constructing MST
+ vector p(n, -1); // parent node when constructing MST
vector visited(n, false);
- for (int i = 0; i < n; i++) {
- for (int j = 0; j < n; j++) {
- int t;
- cin >> t;
- edge[i][j] = (t == -1) ? INT_MAX : t;
- }
- }
-
- // prim
d[0] = 0;
while (true) {
int minV = INT_MAX;
@@ -47,7 +36,5 @@ int main() {
sum += edge[i][p[i]];
}
}
- cout << sum << endl;
-
- return 0;
+ return sum;
}
diff --git a/src/cpp/lib/Math/Calc/GCD.cpp b/src/cpp/lib/Math/Calc/GCD.cpp
new file mode 100644
index 0000000..ddf1ece
--- /dev/null
+++ b/src/cpp/lib/Math/Calc/GCD.cpp
@@ -0,0 +1,8 @@
+#include
+using namespace std;
+
+long long gcd(long long c, long long d) {
+ if (c < d) return gcd(d, c);
+ if (d == 0) return c;
+ return gcd(d, c % d);
+}
diff --git a/src/cpp/lib/Math/Calc/Primes.cpp b/src/cpp/lib/Math/Calc/Primes.cpp
index 78f1fa7..d302a44 100644
--- a/src/cpp/lib/Math/Calc/Primes.cpp
+++ b/src/cpp/lib/Math/Calc/Primes.cpp
@@ -1,7 +1,7 @@
#include
using namespace std;
-// 素因数分解
+// Prime factorization
// key: prime, value: count
map primes(int n) {
vector list;
@@ -30,7 +30,7 @@ map primes(int n) {
return mp;
}
-// n以下の素数列挙
+// List primes up to n
// O(NloglogN)
vector eratosthenes(int n) {
vector flag(n + 1, false);
@@ -51,7 +51,7 @@ vector eratosthenes(int n) {
return list;
}
-// n以下の素数列挙
+// List primes up to n
vector atkin(int n) {
vector flag(n + 1, false);
int sq = (int)sqrt(n);
diff --git a/src/cpp/lib/Math/Combination/Comb.cpp b/src/cpp/lib/Math/Combination/Comb.cpp
index 4ce9cb6..21e76d2 100644
--- a/src/cpp/lib/Math/Combination/Comb.cpp
+++ b/src/cpp/lib/Math/Combination/Comb.cpp
@@ -21,7 +21,7 @@ struct Comb {
return b % 2 == 0 ? x * x % MOD : x * (x * a % MOD) % MOD;
}
- // 逆元。x^(-1)≡x^(p-2) (MOD p) xとpは互いに素。
+ // Modular inverse. x^(-1) ≡ x^(p-2) (MOD p), x and p are coprime.
long long inv(long long n) {
return modpow(n, MOD - 2);
}
diff --git a/src/cpp/lib/Math/ModPow/ModPow.cpp b/src/cpp/lib/Math/ModPow/ModPow.cpp
index e72b51c..5bda106 100644
--- a/src/cpp/lib/Math/ModPow/ModPow.cpp
+++ b/src/cpp/lib/Math/ModPow/ModPow.cpp
@@ -3,7 +3,7 @@ using namespace std;
long long MOD;
-// 繰り返し二乗法によるべき乗計算。O(logN)。
+// Modular exponentiation by repeated squaring. O(logN).
long long power(long long x, long long n) {
long long sum = 1;
while (n > 0) {
diff --git a/src/cpp/lib/Search/BFS/BFS.cpp b/src/cpp/lib/Search/BFS/BFS.cpp
index a2f2163..3b98204 100644
--- a/src/cpp/lib/Search/BFS/BFS.cpp
+++ b/src/cpp/lib/Search/BFS/BFS.cpp
@@ -1,7 +1,7 @@
#include
using namespace std;
-void bfs(int s, vector>& adj, vector& d) {
+void bfs(int s, const vector>& adj, vector& d) {
fill(d.begin(), d.end(), INT_MAX);
d[s] = 0;
deque q;
diff --git a/src/cpp/lib/Search/BinarySearch.cpp b/src/cpp/lib/Search/BinarySearch.cpp
index ae93e5d..ef523bb 100644
--- a/src/cpp/lib/Search/BinarySearch.cpp
+++ b/src/cpp/lib/Search/BinarySearch.cpp
@@ -1,7 +1,7 @@
#include
using namespace std;
-int lowerBound(vector& arr, int value) {
+int lowerBound(const vector& arr, int value) {
int left = -1;
int right = arr.size();
while (right - left > 1) {
@@ -15,7 +15,7 @@ int lowerBound(vector& arr, int value) {
return right;
}
-int upperBound(vector& arr, int value) {
+int upperBound(const vector& arr, int value) {
int left = -1;
int right = arr.size();
while (right - left > 1) {
diff --git a/src/cpp/lib/Tree/SegmentTree/SegTree.cpp b/src/cpp/lib/Tree/SegmentTree/SegmentTree.cpp
similarity index 93%
rename from src/cpp/lib/Tree/SegmentTree/SegTree.cpp
rename to src/cpp/lib/Tree/SegmentTree/SegmentTree.cpp
index f37f8e4..cab44e3 100644
--- a/src/cpp/lib/Tree/SegmentTree/SegTree.cpp
+++ b/src/cpp/lib/Tree/SegmentTree/SegmentTree.cpp
@@ -2,11 +2,11 @@
#include
using namespace std;
-struct SegTree {
+struct SegmentTree {
int n = 1;
vector val;
- SegTree(int n_) {
+ explicit SegmentTree(int n_) {
while (n < n_) n *= 2;
val.assign(2 * n - 1, INT_MAX);
}
diff --git a/src/cpp/lib/Tree/UnionFind/UnionFind.cpp b/src/cpp/lib/Tree/UnionFind/UnionFind.cpp
index 06aa7e3..83a98d6 100644
--- a/src/cpp/lib/Tree/UnionFind/UnionFind.cpp
+++ b/src/cpp/lib/Tree/UnionFind/UnionFind.cpp
@@ -4,7 +4,7 @@ using namespace std;
struct UnionFind {
vector parent, rank_, size_;
- UnionFind(int n) : parent(n), rank_(n, 0), size_(n, 1) {
+ explicit UnionFind(int n) : parent(n), rank_(n, 0), size_(n, 1) {
for (int i = 0; i < n; i++) parent[i] = i;
}
@@ -13,7 +13,7 @@ struct UnionFind {
return parent[x] = find(parent[x]);
}
- // 結合して木のサイズを更新
+ // Unite and update tree size
void unite1(int x, int y) {
x = find(x);
y = find(y);
@@ -23,7 +23,7 @@ struct UnionFind {
size_[x] += size_[y];
}
- // 結合して木の深さを更新
+ // Unite and update tree rank
void unite2(int x, int y) {
x = find(x);
y = find(y);
diff --git a/src/cpp/test/DP/test_knapsack.sh b/src/cpp/test/DP/test_knapsack.sh
new file mode 100755
index 0000000..32de524
--- /dev/null
+++ b/src/cpp/test/DP/test_knapsack.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../lib/DP/Knapsack.cpp"
+TMPDIR=$(mktemp -d)
+BIN="$TMPDIR/test_knapsack"
+
+cat "$SRC" > "$TMPDIR/test.cpp"
+cat >> "$TMPDIR/test.cpp" << 'DRIVER'
+
+int main() {
+ int n, w;
+ cin >> n >> w;
+ vector> arr(n);
+ for (int i = 0; i < n; i++) {
+ cin >> arr[i][0] >> arr[i][1];
+ }
+ vector> dp = knapsack(arr, w);
+ cout << "DP table:" << endl;
+ for (int i = 0; i <= n; i++) {
+ for (int j = 0; j < w; j++) {
+ cout << dp[i][j] << " ";
+ }
+ cout << dp[i][w] << endl;
+ }
+ cout << "ans:" << endl;
+ cout << dp[n][w];
+ return 0;
+}
+DRIVER
+
+g++ -o "$BIN" "$TMPDIR/test.cpp" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | "$BIN")
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: 3 items, capacity 4
+run_test "basic" \
+"3 4
+2 1
+3 2
+4 3" \
+"DP table:
+0 0 0 0 0
+0 2 2 2 2
+0 2 3 5 5
+0 2 3 5 6
+ans:
+6"
+
+# Test 2: no items fit
+run_test "no fit" \
+"2 1
+5 2
+3 3" \
+"DP table:
+0 0
+0 0
+0 0
+ans:
+0"
+
+# Test 3: single item
+run_test "single item" \
+"1 3
+5 2" \
+"DP table:
+0 0 0 0
+0 0 5 5
+ans:
+5"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/cpp/test/Graph/Floyd/test_floyd.sh b/src/cpp/test/Graph/Floyd/test_floyd.sh
new file mode 100755
index 0000000..6b5f5e9
--- /dev/null
+++ b/src/cpp/test/Graph/Floyd/test_floyd.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Graph/Floyd/Floyd.cpp"
+TMPDIR=$(mktemp -d)
+BIN="$TMPDIR/test_floyd"
+
+cat "$SRC" > "$TMPDIR/test.cpp"
+cat >> "$TMPDIR/test.cpp" << 'DRIVER'
+
+int main() {
+ int n, m;
+ cin >> n >> m;
+ vector> edge(n, vector(n, INT_MAX));
+ for (int i = 0; i < n; i++) edge[i][i] = 0;
+ for (int i = 0; i < m; i++) {
+ int v, u, d;
+ cin >> v >> u >> d;
+ edge[v][u] = d;
+ }
+ bool negative = floyd(edge);
+ if (negative) {
+ cout << "negative cycle" << endl;
+ } else {
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n - 1; j++) {
+ cout << edge[i][j] << " ";
+ }
+ cout << edge[i][n - 1] << endl;
+ }
+ }
+ return 0;
+}
+DRIVER
+
+g++ -o "$BIN" "$TMPDIR/test.cpp" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | "$BIN")
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: 3 nodes
+run_test "3 nodes" \
+"3 4
+0 1 2
+0 2 5
+1 2 1
+2 0 7" \
+"0 2 3
+8 0 1
+7 9 0"
+
+# Test 2: 2 nodes bidirectional
+run_test "2 nodes bidirectional" \
+"2 2
+0 1 3
+1 0 5" \
+"0 3
+5 0"
+
+# Test 3: negative cycle detection
+run_test "negative cycle" \
+"3 3
+0 1 1
+1 2 -3
+2 0 1" \
+"negative cycle"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/cpp/test/Graph/test_dijkstra.sh b/src/cpp/test/Graph/test_dijkstra.sh
new file mode 100755
index 0000000..54d624e
--- /dev/null
+++ b/src/cpp/test/Graph/test_dijkstra.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../lib/Graph/Dijkstra.cpp"
+TMPDIR=$(mktemp -d)
+BIN="$TMPDIR/test_dijkstra"
+
+cat "$SRC" > "$TMPDIR/test.cpp"
+cat >> "$TMPDIR/test.cpp" << 'DRIVER'
+
+int main() {
+ int n;
+ cin >> n;
+ vector> adj(n);
+ for (int i = 0; i < n; i++) {
+ int v, k;
+ cin >> v >> k;
+ for (int j = 0; j < k; j++) {
+ int u, c;
+ cin >> u >> c;
+ adj[v].push_back({u, c});
+ }
+ }
+ vector d = dijkstra(0, adj);
+ for (int i = 0; i < n; i++) {
+ cout << d[i] << " ";
+ }
+ return 0;
+}
+DRIVER
+
+g++ -o "$BIN" "$TMPDIR/test.cpp" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | "$BIN")
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: 4 nodes 0->1(1), 0->2(4), 1->2(2), 2->3(1)
+run_test "basic graph" \
+"4
+0 2 1 1 2 4
+1 1 2 2
+2 1 3 1
+3 0" \
+"0 1 3 4 "
+
+# Test 2: 3 nodes linear 0->1(5), 1->2(3)
+run_test "linear path" \
+"3
+0 1 1 5
+1 1 2 3
+2 0" \
+"0 5 8 "
+
+# Test 3: single node
+run_test "single node" \
+"1
+0 0" \
+"0 "
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/cpp/test/Graph/test_prim.sh b/src/cpp/test/Graph/test_prim.sh
new file mode 100755
index 0000000..107afe3
--- /dev/null
+++ b/src/cpp/test/Graph/test_prim.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../lib/Graph/Prim.cpp"
+TMPDIR=$(mktemp -d)
+BIN="$TMPDIR/test_prim"
+
+cat "$SRC" > "$TMPDIR/test.cpp"
+cat >> "$TMPDIR/test.cpp" << 'DRIVER'
+
+int main() {
+ int n;
+ cin >> n;
+ vector> edge(n, vector(n));
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ int t;
+ cin >> t;
+ edge[i][j] = (t == -1) ? INT_MAX : t;
+ }
+ }
+ cout << prim(edge) << endl;
+ return 0;
+}
+DRIVER
+
+g++ -o "$BIN" "$TMPDIR/test.cpp" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | "$BIN")
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: 3 nodes triangle
+run_test "triangle" \
+"3
+-1 2 3
+2 -1 1
+3 1 -1" \
+"3"
+
+# Test 2: 4 nodes
+run_test "4 nodes" \
+"4
+-1 2 -1 6
+2 -1 3 -1
+-1 3 -1 1
+6 -1 1 -1" \
+"6"
+
+# Test 3: 2 nodes
+run_test "2 nodes" \
+"2
+-1 5
+5 -1" \
+"5"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/cpp/test/Math/Calc/test_gcd.sh b/src/cpp/test/Math/Calc/test_gcd.sh
new file mode 100755
index 0000000..fe94740
--- /dev/null
+++ b/src/cpp/test/Math/Calc/test_gcd.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Math/Calc/GCD.cpp"
+TMPDIR=$(mktemp -d)
+BIN="$TMPDIR/test_gcd"
+
+cat "$SRC" > "$TMPDIR/test.cpp"
+cat >> "$TMPDIR/test.cpp" << 'DRIVER'
+
+int main() {
+ int q;
+ cin >> q;
+ for (int i = 0; i < q; i++) {
+ long long c, d;
+ cin >> c >> d;
+ cout << gcd(c, d) << endl;
+ }
+ return 0;
+}
+DRIVER
+
+g++ -o "$BIN" "$TMPDIR/test.cpp" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | "$BIN")
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: gcd(12,8)=4, gcd(7,13)=1, gcd(6,6)=6
+run_test "basic" \
+"3
+12 8
+7 13
+6 6" \
+"4
+1
+6"
+
+# Test 2: gcd(1,100)=1
+run_test "one is 1" \
+"1
+1 100" \
+"1"
+
+# Test 3: gcd(1000000,500000)=500000
+run_test "large values" \
+"1
+1000000 500000" \
+"500000"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/cpp/test/Math/Calc/test_lcm.sh b/src/cpp/test/Math/Calc/test_lcm.sh
new file mode 100755
index 0000000..89b9c0f
--- /dev/null
+++ b/src/cpp/test/Math/Calc/test_lcm.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Math/Calc/LCM.cpp"
+TMPDIR=$(mktemp -d)
+BIN="$TMPDIR/test_lcm"
+
+cat "$SRC" > "$TMPDIR/test.cpp"
+cat >> "$TMPDIR/test.cpp" << 'DRIVER'
+
+int main() {
+ int q;
+ cin >> q;
+ for (int i = 0; i < q; i++) {
+ long long c, d;
+ cin >> c >> d;
+ cout << lcm(c, d) << endl;
+ }
+ return 0;
+}
+DRIVER
+
+g++ -o "$BIN" "$TMPDIR/test.cpp" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | "$BIN")
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: lcm(12,8)=24, lcm(7,13)=91, lcm(6,6)=6
+run_test "basic" \
+"3
+12 8
+7 13
+6 6" \
+"24
+91
+6"
+
+# Test 2: lcm(1,100)=100
+run_test "one is 1" \
+"1
+1 100" \
+"100"
+
+# Test 3: lcm(1000000,500000)=1000000
+run_test "large values" \
+"1
+1000000 500000" \
+"1000000"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/cpp/test/Math/Calc/test_primes.sh b/src/cpp/test/Math/Calc/test_primes.sh
new file mode 100755
index 0000000..d2cd5de
--- /dev/null
+++ b/src/cpp/test/Math/Calc/test_primes.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Math/Calc/Primes.cpp"
+TMPDIR=$(mktemp -d)
+BIN="$TMPDIR/test_primes"
+
+cat "$SRC" > "$TMPDIR/test.cpp"
+cat >> "$TMPDIR/test.cpp" << 'DRIVER'
+
+int main() {
+ string cmd;
+ while (cin >> cmd) {
+ int n;
+ cin >> n;
+ if (cmd == "primes") {
+ map mp = primes(n);
+ bool first = true;
+ for (auto& p : mp) {
+ if (!first) cout << " ";
+ cout << p.first << "^" << p.second;
+ first = false;
+ }
+ cout << endl;
+ } else if (cmd == "eratosthenes") {
+ vector v = eratosthenes(n);
+ for (int i = 0; i < (int)v.size(); i++) {
+ if (i > 0) cout << " ";
+ cout << v[i];
+ }
+ cout << endl;
+ } else if (cmd == "atkin") {
+ vector v = atkin(n);
+ for (int i = 0; i < (int)v.size(); i++) {
+ if (i > 0) cout << " ";
+ cout << v[i];
+ }
+ cout << endl;
+ }
+ }
+ return 0;
+}
+DRIVER
+
+g++ -o "$BIN" "$TMPDIR/test.cpp" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | "$BIN")
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: prime factorization 12=2^2*3
+run_test "prime factorization 12" \
+"primes 12" \
+"2^2 3^1"
+
+# Test 2: prime factorization 30=2*3*5
+run_test "prime factorization 30" \
+"primes 30" \
+"2^1 3^1 5^1"
+
+# Test 3: Sieve of Eratosthenes, primes up to 20
+run_test "eratosthenes 20" \
+"eratosthenes 20" \
+"2 3 5 7 11 13 17 19"
+
+# Test 4: Sieve of Atkin, primes up to 20
+run_test "atkin 20" \
+"atkin 20" \
+"2 3 5 7 11 13 17 19"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/cpp/test/Math/Combination/test_comb.sh b/src/cpp/test/Math/Combination/test_comb.sh
new file mode 100755
index 0000000..30238e9
--- /dev/null
+++ b/src/cpp/test/Math/Combination/test_comb.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Math/Combination/Comb.cpp"
+TMPDIR=$(mktemp -d)
+BIN="$TMPDIR/test_comb"
+
+cat "$SRC" > "$TMPDIR/test.cpp"
+cat >> "$TMPDIR/test.cpp" << 'DRIVER'
+
+int main() {
+ int n;
+ long long MOD;
+ cin >> n >> MOD;
+ Comb comb(n, MOD);
+ int q;
+ cin >> q;
+ for (int i = 0; i < q; i++) {
+ string cmd;
+ int a, b;
+ cin >> cmd >> a >> b;
+ if (cmd == "nCk1") {
+ cout << comb.nCk1(a, b) << endl;
+ } else if (cmd == "nCk2") {
+ cout << comb.nCk2(a, b) << endl;
+ }
+ }
+ return 0;
+}
+DRIVER
+
+g++ -o "$BIN" "$TMPDIR/test.cpp" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | "$BIN")
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: basic nCk1 and nCk2
+run_test "basic combinations" \
+"10 1000000007
+4
+nCk1 5 2
+nCk1 10 3
+nCk2 5 2
+nCk1 3 5" \
+"10
+120
+10
+0"
+
+# Test 2: nCk(n,0)=1, nCk(n,n)=1
+run_test "edge cases" \
+"5 1000000007
+2
+nCk1 5 0
+nCk1 5 5" \
+"1
+1"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/cpp/test/Math/ModPow/test_modpow.sh b/src/cpp/test/Math/ModPow/test_modpow.sh
new file mode 100755
index 0000000..0f77035
--- /dev/null
+++ b/src/cpp/test/Math/ModPow/test_modpow.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Math/ModPow/ModPow.cpp"
+TMPDIR=$(mktemp -d)
+BIN="$TMPDIR/test_modpow"
+
+cat "$SRC" > "$TMPDIR/test.cpp"
+cat >> "$TMPDIR/test.cpp" << 'DRIVER'
+
+int main() {
+ int q;
+ cin >> q;
+ for (int i = 0; i < q; i++) {
+ long long x, n, m;
+ cin >> x >> n >> m;
+ MOD = m;
+ cout << power(x, n) << endl;
+ }
+ return 0;
+}
+DRIVER
+
+g++ -o "$BIN" "$TMPDIR/test.cpp" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | "$BIN")
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: basic
+run_test "basic powers" \
+"4
+2 10 1000000007
+3 5 1000000007
+2 29 1000000007
+10 9 7" \
+"1024
+243
+536870912
+6"
+
+# Test 2: exponent 0 and 1
+run_test "edge cases" \
+"2
+5 0 1000000007
+5 1 1000000007" \
+"1
+5"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/cpp/test/Search/BFS/test_bfs.sh b/src/cpp/test/Search/BFS/test_bfs.sh
index 10db456..6c37aa9 100755
--- a/src/cpp/test/Search/BFS/test_bfs.sh
+++ b/src/cpp/test/Search/BFS/test_bfs.sh
@@ -50,7 +50,7 @@ run_test() {
fi
}
-# Test 1: 直線グラフ 1->2->3->4
+# Test 1: linear graph 1->2->3->4
run_test "linear graph" \
"4
1 1 2
@@ -62,7 +62,7 @@ run_test "linear graph" \
3 dist: 2
4 dist: 3"
-# Test 2: 星型グラフ 1->2, 1->3, 1->4
+# Test 2: star graph 1->2, 1->3, 1->4
run_test "star graph" \
"4
1 3 2 3 4
@@ -74,7 +74,7 @@ run_test "star graph" \
3 dist: 1
4 dist: 1"
-# Test 3: ダイヤモンド型 1->2, 1->3, 2->4, 3->4
+# Test 3: diamond graph 1->2, 1->3, 2->4, 3->4
run_test "diamond graph" \
"4
1 2 2 3
@@ -86,7 +86,7 @@ run_test "diamond graph" \
3 dist: 1
4 dist: 2"
-# Test 4: 単一ノード
+# Test 4: single node
run_test "single node" \
"1
1 0" \
diff --git a/src/cpp/test/Search/DFS/test_dfs.sh b/src/cpp/test/Search/DFS/test_dfs.sh
index ec6311e..cc96e49 100755
--- a/src/cpp/test/Search/DFS/test_dfs.sh
+++ b/src/cpp/test/Search/DFS/test_dfs.sh
@@ -52,7 +52,7 @@ run_test() {
fi
}
-# Test 1: 直線グラフ 1->2->3->4
+# Test 1: linear graph 1->2->3->4
run_test "linear graph" \
"4
1 1 2
@@ -64,7 +64,7 @@ run_test "linear graph" \
3 3sec 6sec
4 4sec 5sec"
-# Test 2: 星型グラフ 1->2, 1->3, 1->4
+# Test 2: star graph 1->2, 1->3, 1->4
run_test "star graph" \
"4
1 3 2 3 4
@@ -76,7 +76,7 @@ run_test "star graph" \
3 4sec 5sec
4 6sec 7sec"
-# Test 3: ダイヤモンド型 1->2, 1->3, 2->4, 3->4
+# Test 3: diamond graph 1->2, 1->3, 2->4, 3->4
run_test "diamond graph" \
"4
1 2 2 3
@@ -88,7 +88,7 @@ run_test "diamond graph" \
3 6sec 7sec
4 3sec 4sec"
-# Test 4: 単一ノード
+# Test 4: single node
run_test "single node" \
"1
1 0" \
diff --git a/src/cpp/test/Search/test_binary_search.sh b/src/cpp/test/Search/test_binary_search.sh
index 41dafdf..9ab0fe2 100755
--- a/src/cpp/test/Search/test_binary_search.sh
+++ b/src/cpp/test/Search/test_binary_search.sh
@@ -42,7 +42,7 @@ run_test() {
fi
}
-# Test 1: 基本ケース [1,2,3,4,5] で 3 を検索
+# Test 1: basic [1,2,3,4,5] search for 3
run_test "basic" \
"5
1 2 3 4 5
@@ -50,7 +50,7 @@ run_test "basic" \
3" \
"lower:2 upper:3"
-# Test 2: 重複あり [1,2,2,2,3] で 2 を検索
+# Test 2: duplicates [1,2,2,2,3] search for 2
run_test "duplicates" \
"5
1 2 2 2 3
@@ -58,7 +58,7 @@ run_test "duplicates" \
2" \
"lower:1 upper:4"
-# Test 3: 値が範囲外(大きい) [1,2,3] で 5 を検索
+# Test 3: out of range (too large) [1,2,3] search for 5
run_test "value too large" \
"3
1 2 3
@@ -66,7 +66,7 @@ run_test "value too large" \
5" \
"lower:3 upper:3"
-# Test 4: 値が範囲外(小さい) [2,3,4] で 1 を検索
+# Test 4: out of range (too small) [2,3,4] search for 1
run_test "value too small" \
"3
2 3 4
@@ -74,7 +74,7 @@ run_test "value too small" \
1" \
"lower:0 upper:0"
-# Test 5: 単一要素 [5] で 5 を検索
+# Test 5: single element [5] search for 5
run_test "single element" \
"1
5
diff --git a/src/cpp/test/Tree/SegmentTree/test_segmenttree.sh b/src/cpp/test/Tree/SegmentTree/test_segmenttree.sh
new file mode 100755
index 0000000..54ef42c
--- /dev/null
+++ b/src/cpp/test/Tree/SegmentTree/test_segmenttree.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Tree/SegmentTree/SegmentTree.cpp"
+TMPDIR=$(mktemp -d)
+BIN="$TMPDIR/test_segtree"
+
+cat "$SRC" > "$TMPDIR/test.cpp"
+cat >> "$TMPDIR/test.cpp" << 'DRIVER'
+
+int main() {
+ int n, q;
+ cin >> n >> q;
+ SegmentTree st(n);
+ for (int i = 0; i < q; i++) {
+ string cmd;
+ cin >> cmd;
+ if (cmd == "update") {
+ int k, a;
+ cin >> k >> a;
+ st.update(k, a);
+ } else if (cmd == "query") {
+ int a, b;
+ cin >> a >> b;
+ cout << st.query(a, b) << endl;
+ }
+ }
+ return 0;
+}
+DRIVER
+
+g++ -o "$BIN" "$TMPDIR/test.cpp" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | "$BIN")
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: basic RMQ [5,3,7,1,4]
+run_test "basic RMQ" \
+"5 7
+update 0 5
+update 1 3
+update 2 7
+update 3 1
+update 4 4
+query 0 5
+query 1 3" \
+"1
+3"
+
+# Test 2: update and re-query
+run_test "update and re-query" \
+"3 6
+update 0 10
+update 1 20
+update 2 30
+query 0 3
+update 1 5
+query 0 3" \
+"10
+5"
+
+# Test 3: single element query
+run_test "single element query" \
+"3 4
+update 0 3
+update 1 7
+update 2 5
+query 1 2" \
+"7"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/cpp/test/Tree/UnionFind/test_unionfind.sh b/src/cpp/test/Tree/UnionFind/test_unionfind.sh
new file mode 100755
index 0000000..c1c7a42
--- /dev/null
+++ b/src/cpp/test/Tree/UnionFind/test_unionfind.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Tree/UnionFind/UnionFind.cpp"
+TMPDIR=$(mktemp -d)
+BIN="$TMPDIR/test_unionfind"
+
+cat "$SRC" > "$TMPDIR/test.cpp"
+cat >> "$TMPDIR/test.cpp" << 'DRIVER'
+
+int main() {
+ int n, q;
+ cin >> n >> q;
+ UnionFind uf(n);
+ for (int i = 0; i < q; i++) {
+ string cmd;
+ cin >> cmd;
+ if (cmd == "unite") {
+ int x, y;
+ cin >> x >> y;
+ uf.unite1(x, y);
+ } else if (cmd == "same") {
+ int x, y;
+ cin >> x >> y;
+ cout << (uf.same(x, y) ? "yes" : "no") << endl;
+ } else if (cmd == "size") {
+ int x;
+ cin >> x;
+ cout << uf.size(x) << endl;
+ }
+ }
+ return 0;
+}
+DRIVER
+
+g++ -o "$BIN" "$TMPDIR/test.cpp" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | "$BIN")
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: basic union and find
+run_test "basic union-find" \
+"5 9
+same 0 1
+unite 0 1
+same 0 1
+unite 2 3
+same 0 2
+unite 0 2
+same 0 3
+size 0
+size 4" \
+"no
+yes
+no
+yes
+4
+1"
+
+# Test 2: merge all nodes
+run_test "merge all" \
+"3 5
+unite 0 1
+unite 1 2
+same 0 2
+size 0
+size 2" \
+"yes
+3
+3"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/cpp/test/static_analysis.sh b/src/cpp/test/static_analysis.sh
new file mode 100755
index 0000000..467981a
--- /dev/null
+++ b/src/cpp/test/static_analysis.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+LIB_DIR="$SCRIPT_DIR/../lib"
+
+fail=0
+total=0
+
+while IFS= read -r f; do
+ ((total++))
+ name="${f#$LIB_DIR/}"
+ output=$(cppcheck \
+ --enable=warning,style,performance,portability \
+ --suppress=missingInclude \
+ --suppress=unusedFunction \
+ --std=c++17 \
+ --error-exitcode=1 \
+ --quiet \
+ "$f" 2>&1)
+ if [ $? -ne 0 ]; then
+ echo "FAIL: $name"
+ echo "$output"
+ ((fail++))
+ else
+ echo "PASS: $name"
+ fi
+done < <(find "$LIB_DIR" -name "*.cpp" | sort)
+
+echo ""
+echo "Result: $((total - fail))/$total passed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/lib/DP/Knapsack.java b/src/java/lib/DP/Knapsack.java
index 5da4f72..bd3bc52 100644
--- a/src/java/lib/DP/Knapsack.java
+++ b/src/java/lib/DP/Knapsack.java
@@ -1,23 +1,12 @@
package DP;
-import java.util.*;
-
public class Knapsack {
- public static void main(String[] args) {
-
- Scanner sc = new Scanner(System.in);
-
- int n = sc.nextInt();
- int w = sc.nextInt();
-
- int[][] arr = new int[n][2];
-
- for (int i = 0; i < n; i++) {
- arr[i][0] = sc.nextInt();
- arr[i][1] = sc.nextInt();
- }
-
+ // 0-1 Knapsack problem
+ // arr[i] = {value, weight}
+ // dp[i][j] = max value using first i items with capacity j
+ public int[][] knapsack(int[][] arr, int w) {
+ int n = arr.length;
int[][] dp = new int[n + 1][w + 1];
for (int i = 1; i <= n; i++) {
@@ -29,14 +18,6 @@ public static void main(String[] args) {
}
}
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i <= n; i++) {
- for (int j = 0; j < w; j++) {
- sb.append(dp[i][j] + " ");
- }
- sb.append(dp[i][w] + "\n");
- }
- System.out.print("DP table:\n" + sb);
- System.out.print("ans:\n" + dp[n][w]);
+ return dp;
}
}
diff --git a/src/java/lib/Graph/Dijkstra.java b/src/java/lib/Graph/Dijkstra.java
index 8ddb0e1..4edf231 100644
--- a/src/java/lib/Graph/Dijkstra.java
+++ b/src/java/lib/Graph/Dijkstra.java
@@ -1,4 +1,4 @@
-//コストが負の辺がない場合(最短から求めるため負の辺を後から取り込むことができない)
+// For graphs with non-negative edge costs
package Graph;
@@ -6,78 +6,34 @@
public class Dijkstra {
- Scanner sc = new Scanner(System.in);
- int n = sc.nextInt();
- List[] adj = new ArrayList[n];
- PriorityQueue pq = new PriorityQueue<>();
- boolean[] visit = new boolean[n];
- int[] d = new int[n];
+ public int[] dijkstra(int start, List[] adj) {
+ int n = adj.length;
+ int[] d = new int[n];
+ boolean[] visit = new boolean[n];
+ Arrays.fill(d, Integer.MAX_VALUE);
- public static void main(String[] args){
- new Dijkstra().run();
- }
-
- void run() {
- for (int i=0; i();
- for (int i=0; i pq, List[] adj, boolean[] visit) {
- d[0] = 0;
- pq.add(new Node(0, 0));
+ d[start] = 0;
+ PriorityQueue pq = new PriorityQueue<>((a, b) -> a[1] - b[1]);
+ pq.add(new int[]{start, 0});
while (!pq.isEmpty()) {
- Node nd = pq.poll();
- int v= nd.node;
+ int[] nd = pq.poll();
+ int v = nd[0];
visit[v] = true;
- if (d[v] d[v]+node.cost) {
- d[t] = d[v]+node.cost;
- pq.add(new Node(t, d[t]));
+ if (d[t] > d[v] + node[1]) {
+ d[t] = d[v] + node[1];
+ pq.add(new int[]{t, d[t]});
}
-
}
}
- }
-
- class Node implements Comparable {
- int node;
- int cost;
- public Node(int n, int c) {
- this.node = n;
- this.cost = c;
- }
- public int compareTo(Node nd) {
- if (this.costnd.cost) return 1;
- return 0;
- }
+ return d;
}
}
diff --git a/src/java/lib/Graph/Floyd/Floyd.java b/src/java/lib/Graph/Floyd/Floyd.java
index c4ace42..bdeb98f 100644
--- a/src/java/lib/Graph/Floyd/Floyd.java
+++ b/src/java/lib/Graph/Floyd/Floyd.java
@@ -1,51 +1,23 @@
package Graph.Floyd;
-import java.util.Scanner;
+public class Floyd {
-public class Floyd{
- static Scanner sc = new Scanner(System.in);
- static int n = sc.nextInt();
- static int m = sc.nextInt();
- static int[][] edge = new int[n][n];
-
- public static void main(String[] args){
- for (int i=0; id[i] && !visited[i]){
+ for (int i = 0; i < n; i++) {
+ if (minV > d[i] && !visited[i]) {
u = i;
minV = d[i];
}
}
- if (u==-1) break;
+ if (u == -1) break;
visited[u] = true;
- for (int v=0; vedge[u][v]){
+ for (int v = 0; v < n; v++) {
+ if (!visited[v] && edge[u][v] != Integer.MAX_VALUE) {
+ if (d[v] > edge[u][v]) {
d[v] = edge[u][v];
p[v] = u;
}
@@ -48,11 +35,11 @@ public static void prim(){
}
}
int sum = 0;
- for (int i=0; i primes(int n) {
}
/**
- * n以下の素数列挙
+ * List primes up to n
* O(NloglogN)
* @param n
* @return
@@ -70,7 +70,7 @@ List eratosthenes(int n) {
}
/**
- * n以下の素数列挙
+ * List primes up to n
* @param n
* @return
*/
diff --git a/src/java/lib/Math/Combination/Comb.java b/src/java/lib/Math/Combination/Comb.java
index aa3acc4..1cf7cfb 100644
--- a/src/java/lib/Math/Combination/Comb.java
+++ b/src/java/lib/Math/Combination/Comb.java
@@ -1,7 +1,7 @@
package Math.Combination;
public class Comb {
- int n; // 要素数
+ int n; // number of elements
int MOD = (int)1e9+7;
long[] facts;
@@ -23,7 +23,7 @@ public long modpow(long a, long b){
return b%2 == 0 ? x * x % MOD : x * (x*a%MOD) % MOD;
}
- // 逆元。x^(-1)≡x^(p-2) (MOD p) xとpは互いに素。
+ // Modular inverse. x^(-1) ≡ x^(p-2) (MOD p), x and p are coprime.
public long inv(long n){
return modpow(n, MOD-2);
}
diff --git a/src/java/lib/Math/ModPow/ModPow.java b/src/java/lib/Math/ModPow/ModPow.java
index 0b29b87..f494db2 100644
--- a/src/java/lib/Math/ModPow/ModPow.java
+++ b/src/java/lib/Math/ModPow/ModPow.java
@@ -2,7 +2,7 @@
public class ModPow {
long MOD;
- public long power(long x, long n){ // 繰り返し二乗法によるべき乗計算。O(logN)。
+ public long power(long x, long n){ // Modular exponentiation by repeated squaring. O(logN).
long sum = 1;
while (n > 0){
if ((n & 1) == 1){
diff --git a/src/java/lib/Search/BFS/BFS.java b/src/java/lib/Search/BFS/BFS.java
index ba29c60..f0edefc 100644
--- a/src/java/lib/Search/BFS/BFS.java
+++ b/src/java/lib/Search/BFS/BFS.java
@@ -1,10 +1,10 @@
-package Search.Bfs;
+package Search.BFS;
import java.util.List;
import java.util.ArrayList;
import java.util.ArrayDeque;
-public class Bfs {
+public class BFS {
public int[] bfs(int s, List[] adj, int n) {
int[] d = new int[n];
diff --git a/src/java/lib/Search/DFS/DFS.java b/src/java/lib/Search/DFS/DFS.java
index d29ab6f..d515013 100644
--- a/src/java/lib/Search/DFS/DFS.java
+++ b/src/java/lib/Search/DFS/DFS.java
@@ -1,8 +1,8 @@
-package Search.Dfs;
+package Search.DFS;
import java.util.List;
-public class Dfs {
+public class DFS {
private int sec = 0;
diff --git a/src/java/lib/Tree/UnionFind/UnionFind.java b/src/java/lib/Tree/UnionFind/UnionFind.java
index 54cc2c5..558d1bb 100644
--- a/src/java/lib/Tree/UnionFind/UnionFind.java
+++ b/src/java/lib/Tree/UnionFind/UnionFind.java
@@ -25,7 +25,7 @@ public int find(int x) {
return parent[x] = find(parent[x]);
}
-// 結合して木のサイズを更新
+// Unite and update tree size
public void unite1(int x, int y) {
x = find(x);
y = find(y);
@@ -39,7 +39,7 @@ public void unite1(int x, int y) {
size[x] += size[y];
}
-// 結合して木の深さを更新
+// Unite and update tree rank
public void unite2(int x, int y) {
x = find(x);
y = find(y);
diff --git a/src/java/test/DP/test_knapsack.sh b/src/java/test/DP/test_knapsack.sh
new file mode 100755
index 0000000..9e1c4d7
--- /dev/null
+++ b/src/java/test/DP/test_knapsack.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../lib/DP/Knapsack.java"
+TMPDIR=$(mktemp -d)
+PKG_DIR="$TMPDIR/DP"
+
+mkdir -p "$PKG_DIR"
+cp "$SRC" "$PKG_DIR/Knapsack.java"
+
+cat > "$PKG_DIR/KnapsackTest.java" << 'DRIVER'
+package DP;
+import java.util.Scanner;
+
+public class KnapsackTest {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ int n = sc.nextInt();
+ int w = sc.nextInt();
+ int[][] arr = new int[n][2];
+ for (int i = 0; i < n; i++) {
+ arr[i][0] = sc.nextInt();
+ arr[i][1] = sc.nextInt();
+ }
+ Knapsack ks = new Knapsack();
+ int[][] dp = ks.knapsack(arr, w);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i <= n; i++) {
+ for (int j = 0; j < w; j++) {
+ sb.append(dp[i][j] + " ");
+ }
+ sb.append(dp[i][w] + "\n");
+ }
+ System.out.print("DP table:\n" + sb);
+ System.out.print("ans:\n" + dp[n][w]);
+ }
+}
+DRIVER
+
+javac "$PKG_DIR/Knapsack.java" "$PKG_DIR/KnapsackTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | java -cp "$TMPDIR" DP.KnapsackTest | sed 's/[[:space:]]*$//')
+ expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: 3 items, capacity 4
+run_test "basic" \
+"3 4
+2 1
+3 2
+4 3" \
+"DP table:
+0 0 0 0 0
+0 2 2 2 2
+0 2 3 5 5
+0 2 3 5 6
+ans:
+6"
+
+# Test 2: no items fit
+run_test "no fit" \
+"2 1
+5 2
+3 3" \
+"DP table:
+0 0
+0 0
+0 0
+ans:
+0"
+
+# Test 3: single item
+run_test "single item" \
+"1 3
+5 2" \
+"DP table:
+0 0 0 0
+0 0 5 5
+ans:
+5"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/test/Graph/Floyd/test_floyd.sh b/src/java/test/Graph/Floyd/test_floyd.sh
new file mode 100755
index 0000000..1e53d22
--- /dev/null
+++ b/src/java/test/Graph/Floyd/test_floyd.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Graph/Floyd/Floyd.java"
+TMPDIR=$(mktemp -d)
+PKG_DIR="$TMPDIR/Graph/Floyd"
+
+mkdir -p "$PKG_DIR"
+cp "$SRC" "$PKG_DIR/Floyd.java"
+
+cat > "$PKG_DIR/FloydTest.java" << 'DRIVER'
+package Graph.Floyd;
+import java.util.Scanner;
+
+public class FloydTest {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ int n = sc.nextInt();
+ int m = sc.nextInt();
+ int[][] edge = new int[n][n];
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ edge[i][j] = (i == j) ? 0 : Integer.MAX_VALUE;
+ }
+ }
+ for (int i = 0; i < m; i++) {
+ int v = sc.nextInt();
+ int u = sc.nextInt();
+ int d = sc.nextInt();
+ edge[v][u] = d;
+ }
+ Floyd f = new Floyd();
+ boolean negative = f.floyd(edge);
+ if (negative) {
+ System.out.println("negative cycle");
+ } else {
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n - 1; j++) {
+ System.out.print(edge[i][j] + " ");
+ }
+ System.out.println(edge[i][n - 1]);
+ }
+ }
+ }
+}
+DRIVER
+
+javac "$PKG_DIR/Floyd.java" "$PKG_DIR/FloydTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | java -cp "$TMPDIR" Graph.Floyd.FloydTest | sed 's/[[:space:]]*$//')
+ expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: 3 nodes
+run_test "3 nodes" \
+"3 4
+0 1 2
+0 2 5
+1 2 1
+2 0 7" \
+"0 2 3
+8 0 1
+7 9 0"
+
+# Test 2: 2 nodes bidirectional
+run_test "2 nodes bidirectional" \
+"2 2
+0 1 3
+1 0 5" \
+"0 3
+5 0"
+
+# Test 3: negative cycle detection
+run_test "negative cycle" \
+"3 3
+0 1 1
+1 2 -3
+2 0 1" \
+"negative cycle"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/test/Graph/test_dijkstra.sh b/src/java/test/Graph/test_dijkstra.sh
new file mode 100755
index 0000000..161b58e
--- /dev/null
+++ b/src/java/test/Graph/test_dijkstra.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../lib/Graph/Dijkstra.java"
+TMPDIR=$(mktemp -d)
+PKG_DIR="$TMPDIR/Graph"
+
+mkdir -p "$PKG_DIR"
+cp "$SRC" "$PKG_DIR/Dijkstra.java"
+
+cat > "$PKG_DIR/DijkstraTest.java" << 'DRIVER'
+package Graph;
+import java.util.*;
+
+public class DijkstraTest {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ int n = sc.nextInt();
+ @SuppressWarnings("unchecked")
+ List[] adj = new ArrayList[n];
+ for (int i = 0; i < n; i++) adj[i] = new ArrayList<>();
+ for (int i = 0; i < n; i++) {
+ int v = sc.nextInt();
+ int k = sc.nextInt();
+ for (int j = 0; j < k; j++) {
+ int u = sc.nextInt();
+ int c = sc.nextInt();
+ adj[v].add(new int[]{u, c});
+ }
+ }
+ Dijkstra dij = new Dijkstra();
+ int[] d = dij.dijkstra(0, adj);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < n; i++) {
+ sb.append(d[i] + " ");
+ }
+ System.out.print(sb.toString());
+ }
+}
+DRIVER
+
+javac "$PKG_DIR/Dijkstra.java" "$PKG_DIR/DijkstraTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | java -cp "$TMPDIR" Graph.DijkstraTest | sed 's/[[:space:]]*$//')
+ expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: 4 nodes 0->1(1), 0->2(4), 1->2(2), 2->3(1)
+run_test "basic graph" \
+"4
+0 2 1 1 2 4
+1 1 2 2
+2 1 3 1
+3 0" \
+"0 1 3 4"
+
+# Test 2: 3 nodes linear 0->1(5), 1->2(3)
+run_test "linear path" \
+"3
+0 1 1 5
+1 1 2 3
+2 0" \
+"0 5 8"
+
+# Test 3: single node
+run_test "single node" \
+"1
+0 0" \
+"0"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/test/Graph/test_prim.sh b/src/java/test/Graph/test_prim.sh
new file mode 100755
index 0000000..53dfa99
--- /dev/null
+++ b/src/java/test/Graph/test_prim.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../lib/Graph/Prim.java"
+TMPDIR=$(mktemp -d)
+PKG_DIR="$TMPDIR/Graph"
+
+mkdir -p "$PKG_DIR"
+cp "$SRC" "$PKG_DIR/Prim.java"
+
+cat > "$PKG_DIR/PrimTest.java" << 'DRIVER'
+package Graph;
+import java.util.Scanner;
+
+public class PrimTest {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ int n = sc.nextInt();
+ int[][] edge = new int[n][n];
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ int t = sc.nextInt();
+ edge[i][j] = t == -1 ? Integer.MAX_VALUE : t;
+ }
+ }
+ Prim p = new Prim();
+ System.out.println(p.prim(edge));
+ }
+}
+DRIVER
+
+javac "$PKG_DIR/Prim.java" "$PKG_DIR/PrimTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | java -cp "$TMPDIR" Graph.PrimTest | sed 's/[[:space:]]*$//')
+ expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: 3 nodes triangle
+run_test "triangle" \
+"3
+-1 2 3
+2 -1 1
+3 1 -1" \
+"3"
+
+# Test 2: 4 nodes
+run_test "4 nodes" \
+"4
+-1 2 -1 6
+2 -1 3 -1
+-1 3 -1 1
+6 -1 1 -1" \
+"6"
+
+# Test 3: 2 nodes
+run_test "2 nodes" \
+"2
+-1 5
+5 -1" \
+"5"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/test/Math/Calc/test_gcd.sh b/src/java/test/Math/Calc/test_gcd.sh
new file mode 100755
index 0000000..20164ae
--- /dev/null
+++ b/src/java/test/Math/Calc/test_gcd.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Math/Calc/GCD.java"
+TMPDIR=$(mktemp -d)
+PKG_DIR="$TMPDIR/Math/Calc"
+
+mkdir -p "$PKG_DIR"
+cp "$SRC" "$PKG_DIR/GCD.java"
+
+cat > "$PKG_DIR/GCDTest.java" << 'DRIVER'
+package Math.Calc;
+import java.util.Scanner;
+
+public class GCDTest {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ int q = sc.nextInt();
+ GCD g = new GCD();
+ for (int i = 0; i < q; i++) {
+ long c = sc.nextLong();
+ long d = sc.nextLong();
+ System.out.println(g.gcd(c, d));
+ }
+ }
+}
+DRIVER
+
+javac "$PKG_DIR/GCD.java" "$PKG_DIR/GCDTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | java -cp "$TMPDIR" Math.Calc.GCDTest | sed 's/[[:space:]]*$//')
+ expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: gcd(12,8)=4, gcd(7,13)=1, gcd(6,6)=6
+run_test "basic" \
+"3
+12 8
+7 13
+6 6" \
+"4
+1
+6"
+
+# Test 2: gcd(1,100)=1
+run_test "one is 1" \
+"1
+1 100" \
+"1"
+
+# Test 3: gcd(1000000,500000)=500000
+run_test "large values" \
+"1
+1000000 500000" \
+"500000"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/test/Math/Calc/test_lcm.sh b/src/java/test/Math/Calc/test_lcm.sh
new file mode 100755
index 0000000..51e998c
--- /dev/null
+++ b/src/java/test/Math/Calc/test_lcm.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Math/Calc/LCM.java"
+TMPDIR=$(mktemp -d)
+PKG_DIR="$TMPDIR/Math/Calc"
+
+mkdir -p "$PKG_DIR"
+cp "$SRC" "$PKG_DIR/LCM.java"
+
+cat > "$PKG_DIR/LCMTest.java" << 'DRIVER'
+package Math.Calc;
+import java.util.Scanner;
+
+public class LCMTest {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ int q = sc.nextInt();
+ LCM l = new LCM();
+ for (int i = 0; i < q; i++) {
+ long c = sc.nextLong();
+ long d = sc.nextLong();
+ System.out.println(l.lcm(c, d));
+ }
+ }
+}
+DRIVER
+
+javac "$PKG_DIR/LCM.java" "$PKG_DIR/LCMTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | java -cp "$TMPDIR" Math.Calc.LCMTest | sed 's/[[:space:]]*$//')
+ expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: lcm(12,8)=24, lcm(7,13)=91, lcm(6,6)=6
+run_test "basic" \
+"3
+12 8
+7 13
+6 6" \
+"24
+91
+6"
+
+# Test 2: lcm(1,100)=100
+run_test "one is 1" \
+"1
+1 100" \
+"100"
+
+# Test 3: lcm(1000000,500000)=1000000
+run_test "large values" \
+"1
+1000000 500000" \
+"1000000"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/test/Math/Calc/test_primes.sh b/src/java/test/Math/Calc/test_primes.sh
new file mode 100755
index 0000000..ee7fe87
--- /dev/null
+++ b/src/java/test/Math/Calc/test_primes.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Math/Calc/Primes.java"
+TMPDIR=$(mktemp -d)
+PKG_DIR="$TMPDIR/Math/Calc"
+
+mkdir -p "$PKG_DIR"
+cp "$SRC" "$PKG_DIR/Primes.java"
+
+cat > "$PKG_DIR/PrimesTest.java" << 'DRIVER'
+package Math.Calc;
+import java.util.*;
+
+public class PrimesTest {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ Primes p = new Primes();
+ while (sc.hasNext()) {
+ String cmd = sc.next();
+ int n = sc.nextInt();
+ if (cmd.equals("primes")) {
+ Map mp = p.primes(n);
+ TreeMap sorted = new TreeMap<>(mp);
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (Map.Entry e : sorted.entrySet()) {
+ if (!first) sb.append(" ");
+ sb.append(e.getKey() + "^" + e.getValue());
+ first = false;
+ }
+ System.out.println(sb.toString());
+ } else if (cmd.equals("eratosthenes")) {
+ List list = p.eratosthenes(n);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < list.size(); i++) {
+ if (i > 0) sb.append(" ");
+ sb.append(list.get(i));
+ }
+ System.out.println(sb.toString());
+ } else if (cmd.equals("atkin")) {
+ List list = p.atkin(n);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < list.size(); i++) {
+ if (i > 0) sb.append(" ");
+ sb.append(list.get(i));
+ }
+ System.out.println(sb.toString());
+ }
+ }
+ }
+}
+DRIVER
+
+javac "$PKG_DIR/Primes.java" "$PKG_DIR/PrimesTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | java -cp "$TMPDIR" Math.Calc.PrimesTest | sed 's/[[:space:]]*$//')
+ expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: prime factorization 12=2^2*3
+run_test "prime factorization 12" \
+"primes 12" \
+"2^2 3^1"
+
+# Test 2: prime factorization 30=2*3*5
+run_test "prime factorization 30" \
+"primes 30" \
+"2^1 3^1 5^1"
+
+# Test 3: Sieve of Eratosthenes, primes up to 20
+run_test "eratosthenes 20" \
+"eratosthenes 20" \
+"2 3 5 7 11 13 17 19"
+
+# Test 4: Sieve of Atkin, primes up to 20
+run_test "atkin 20" \
+"atkin 20" \
+"2 3 5 7 11 13 17 19"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/test/Math/Combination/test_comb.sh b/src/java/test/Math/Combination/test_comb.sh
new file mode 100755
index 0000000..37c9134
--- /dev/null
+++ b/src/java/test/Math/Combination/test_comb.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Math/Combination/Comb.java"
+TMPDIR=$(mktemp -d)
+PKG_DIR="$TMPDIR/Math/Combination"
+
+mkdir -p "$PKG_DIR"
+cp "$SRC" "$PKG_DIR/Comb.java"
+
+cat > "$PKG_DIR/CombTest.java" << 'DRIVER'
+package Math.Combination;
+import java.util.Scanner;
+
+public class CombTest {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ int n = sc.nextInt();
+ int MOD = sc.nextInt();
+ Comb comb = new Comb(n, MOD);
+ int q = sc.nextInt();
+ for (int i = 0; i < q; i++) {
+ String cmd = sc.next();
+ int a = sc.nextInt();
+ int b = sc.nextInt();
+ if (cmd.equals("nCk1")) {
+ System.out.println(comb.nCk1(a, b));
+ } else if (cmd.equals("nCk2")) {
+ System.out.println(comb.nCk2(a, b));
+ }
+ }
+ }
+}
+DRIVER
+
+javac "$PKG_DIR/Comb.java" "$PKG_DIR/CombTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | java -cp "$TMPDIR" Math.Combination.CombTest | sed 's/[[:space:]]*$//')
+ expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: basic nCk1 and nCk2
+run_test "basic combinations" \
+"10 1000000007
+4
+nCk1 5 2
+nCk1 10 3
+nCk2 5 2
+nCk1 3 5" \
+"10
+120
+10
+0"
+
+# Test 2: nCk(n,0)=1, nCk(n,n)=1
+run_test "edge cases" \
+"5 1000000007
+2
+nCk1 5 0
+nCk1 5 5" \
+"1
+1"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/test/Math/ModPow/test_modpow.sh b/src/java/test/Math/ModPow/test_modpow.sh
new file mode 100755
index 0000000..6cdf0cd
--- /dev/null
+++ b/src/java/test/Math/ModPow/test_modpow.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Math/ModPow/ModPow.java"
+TMPDIR=$(mktemp -d)
+PKG_DIR="$TMPDIR/Math/ModPow"
+
+mkdir -p "$PKG_DIR"
+cp "$SRC" "$PKG_DIR/ModPow.java"
+
+cat > "$PKG_DIR/ModPowTest.java" << 'DRIVER'
+package Math.ModPow;
+import java.util.Scanner;
+
+public class ModPowTest {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ int q = sc.nextInt();
+ for (int i = 0; i < q; i++) {
+ long x = sc.nextLong();
+ long n = sc.nextLong();
+ long m = sc.nextLong();
+ ModPow mp = new ModPow();
+ mp.MOD = m;
+ System.out.println(mp.power(x, n));
+ }
+ }
+}
+DRIVER
+
+javac "$PKG_DIR/ModPow.java" "$PKG_DIR/ModPowTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | java -cp "$TMPDIR" Math.ModPow.ModPowTest | sed 's/[[:space:]]*$//')
+ expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: basic
+run_test "basic powers" \
+"4
+2 10 1000000007
+3 5 1000000007
+2 29 1000000007
+10 9 7" \
+"1024
+243
+536870912
+6"
+
+# Test 2: exponent 0 and 1
+run_test "edge cases" \
+"2
+5 0 1000000007
+5 1 1000000007" \
+"1
+5"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/test/Search/BFS/test_bfs.sh b/src/java/test/Search/BFS/test_bfs.sh
index 289a2a7..20522b7 100755
--- a/src/java/test/Search/BFS/test_bfs.sh
+++ b/src/java/test/Search/BFS/test_bfs.sh
@@ -2,17 +2,17 @@
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SRC="$SCRIPT_DIR/../../../lib/Search/BFS/BFS.java"
TMPDIR=$(mktemp -d)
-PKG_DIR="$TMPDIR/Search/Bfs"
+PKG_DIR="$TMPDIR/Search/BFS"
mkdir -p "$PKG_DIR"
-cp "$SRC" "$PKG_DIR/Bfs.java"
+cp "$SRC" "$PKG_DIR/BFS.java"
# Create test driver in the same package
-cat > "$PKG_DIR/BfsTest.java" << 'DRIVER'
-package Search.Bfs;
+cat > "$PKG_DIR/BFSTest.java" << 'DRIVER'
+package Search.BFS;
import java.util.*;
-public class BfsTest {
+public class BFSTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
@@ -27,7 +27,7 @@ public class BfsTest {
adj[t].add(u);
}
}
- Bfs bfs = new Bfs();
+ BFS bfs = new BFS();
int[] d = bfs.bfs(0, adj, n);
for (int i = 0; i < n; i++) {
System.out.println((i + 1) + " dist: " + d[i]);
@@ -36,14 +36,14 @@ public class BfsTest {
}
DRIVER
-javac "$PKG_DIR/Bfs.java" "$PKG_DIR/BfsTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+javac "$PKG_DIR/BFS.java" "$PKG_DIR/BFSTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
pass=0
fail=0
run_test() {
local name="$1" input="$2" expected="$3"
- actual=$(echo "$input" | java -cp "$TMPDIR" Search.Bfs.BfsTest | sed 's/[[:space:]]*$//')
+ actual=$(echo "$input" | java -cp "$TMPDIR" Search.BFS.BFSTest | sed 's/[[:space:]]*$//')
expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
if [ "$actual" = "$expected" ]; then
echo "PASS: $name"
@@ -56,7 +56,7 @@ run_test() {
fi
}
-# Test 1: 直線グラフ 1->2->3->4
+# Test 1: linear graph 1->2->3->4
run_test "linear graph" \
"4
1 1 2
@@ -68,7 +68,7 @@ run_test "linear graph" \
3 dist: 2
4 dist: 3"
-# Test 2: 星型グラフ 1->2, 1->3, 1->4
+# Test 2: star graph 1->2, 1->3, 1->4
run_test "star graph" \
"4
1 3 2 3 4
@@ -80,7 +80,7 @@ run_test "star graph" \
3 dist: 1
4 dist: 1"
-# Test 3: ダイヤモンド型 1->2, 1->3, 2->4, 3->4
+# Test 3: diamond graph 1->2, 1->3, 2->4, 3->4
run_test "diamond graph" \
"4
1 2 2 3
@@ -92,7 +92,7 @@ run_test "diamond graph" \
3 dist: 1
4 dist: 2"
-# Test 4: 単一ノード
+# Test 4: single node
run_test "single node" \
"1
1 0" \
diff --git a/src/java/test/Search/DFS/test_dfs.sh b/src/java/test/Search/DFS/test_dfs.sh
index c081aa8..4e84f26 100755
--- a/src/java/test/Search/DFS/test_dfs.sh
+++ b/src/java/test/Search/DFS/test_dfs.sh
@@ -2,17 +2,17 @@
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SRC="$SCRIPT_DIR/../../../lib/Search/DFS/DFS.java"
TMPDIR=$(mktemp -d)
-PKG_DIR="$TMPDIR/Search/Dfs"
+PKG_DIR="$TMPDIR/Search/DFS"
mkdir -p "$PKG_DIR"
-cp "$SRC" "$PKG_DIR/Dfs.java"
+cp "$SRC" "$PKG_DIR/DFS.java"
# Create test driver in the same package
-cat > "$PKG_DIR/DfsTest.java" << 'DRIVER'
-package Search.Dfs;
+cat > "$PKG_DIR/DFSTest.java" << 'DRIVER'
+package Search.DFS;
import java.util.*;
-public class DfsTest {
+public class DFSTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
@@ -30,7 +30,7 @@ public class DfsTest {
boolean[] visit = new boolean[n];
int[] firstV = new int[n];
int[] endV = new int[n];
- Dfs dfs = new Dfs();
+ DFS dfs = new DFS();
dfs.dfs(0, edge, visit, firstV, endV);
for (int i = 0; i < n; i++) {
System.out.println((i + 1) + " " + firstV[i] + "sec " + endV[i] + "sec");
@@ -39,14 +39,14 @@ public class DfsTest {
}
DRIVER
-javac "$PKG_DIR/Dfs.java" "$PKG_DIR/DfsTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+javac "$PKG_DIR/DFS.java" "$PKG_DIR/DFSTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
pass=0
fail=0
run_test() {
local name="$1" input="$2" expected="$3"
- actual=$(echo "$input" | java -cp "$TMPDIR" Search.Dfs.DfsTest | sed 's/[[:space:]]*$//')
+ actual=$(echo "$input" | java -cp "$TMPDIR" Search.DFS.DFSTest | sed 's/[[:space:]]*$//')
expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
if [ "$actual" = "$expected" ]; then
echo "PASS: $name"
@@ -59,7 +59,7 @@ run_test() {
fi
}
-# Test 1: 直線グラフ 1->2->3->4
+# Test 1: linear graph 1->2->3->4
run_test "linear graph" \
"4
1 1 2
@@ -71,7 +71,7 @@ run_test "linear graph" \
3 3sec 6sec
4 4sec 5sec"
-# Test 2: 星型グラフ 1->2, 1->3, 1->4
+# Test 2: star graph 1->2, 1->3, 1->4
run_test "star graph" \
"4
1 3 2 3 4
@@ -83,7 +83,7 @@ run_test "star graph" \
3 4sec 5sec
4 6sec 7sec"
-# Test 3: ダイヤモンド型 1->2, 1->3, 2->4, 3->4
+# Test 3: diamond graph 1->2, 1->3, 2->4, 3->4
run_test "diamond graph" \
"4
1 2 2 3
@@ -95,7 +95,7 @@ run_test "diamond graph" \
3 6sec 7sec
4 3sec 4sec"
-# Test 4: 単一ノード
+# Test 4: single node
run_test "single node" \
"1
1 0" \
diff --git a/src/java/test/Search/test_binary_search.sh b/src/java/test/Search/test_binary_search.sh
index 6609ec0..7e3ecab 100755
--- a/src/java/test/Search/test_binary_search.sh
+++ b/src/java/test/Search/test_binary_search.sh
@@ -48,7 +48,7 @@ run_test() {
fi
}
-# Test 1: 基本ケース [1,2,3,4,5] で 3 を検索
+# Test 1: basic [1,2,3,4,5] search for 3
run_test "basic" \
"5
1 2 3 4 5
@@ -56,7 +56,7 @@ run_test "basic" \
3" \
"lower:2 upper:3"
-# Test 2: 重複あり [1,2,2,2,3] で 2 を検索
+# Test 2: duplicates [1,2,2,2,3] search for 2
run_test "duplicates" \
"5
1 2 2 2 3
@@ -64,7 +64,7 @@ run_test "duplicates" \
2" \
"lower:1 upper:4"
-# Test 3: 値が範囲外(大きい) [1,2,3] で 5 を検索
+# Test 3: out of range (too large) [1,2,3] search for 5
run_test "value too large" \
"3
1 2 3
@@ -72,7 +72,7 @@ run_test "value too large" \
5" \
"lower:3 upper:3"
-# Test 4: 値が範囲外(小さい) [2,3,4] で 1 を検索
+# Test 4: out of range (too small) [2,3,4] search for 1
run_test "value too small" \
"3
2 3 4
@@ -80,7 +80,7 @@ run_test "value too small" \
1" \
"lower:0 upper:0"
-# Test 5: 単一要素 [5] で 5 を検索
+# Test 5: single element [5] search for 5
run_test "single element" \
"1
5
diff --git a/src/java/test/Tree/SegmentTree/test_segtree.sh b/src/java/test/Tree/SegmentTree/test_segtree.sh
new file mode 100755
index 0000000..9ae6790
--- /dev/null
+++ b/src/java/test/Tree/SegmentTree/test_segtree.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Tree/SegmentTree/SegTree.java"
+TMPDIR=$(mktemp -d)
+PKG_DIR="$TMPDIR/Tree/SegmentTree"
+
+mkdir -p "$PKG_DIR"
+cp "$SRC" "$PKG_DIR/SegTree.java"
+
+cat > "$PKG_DIR/SegTreeTest.java" << 'DRIVER'
+package Tree.SegmentTree;
+import java.util.Scanner;
+
+public class SegTreeTest {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ int n = sc.nextInt();
+ int q = sc.nextInt();
+ SegTree st = new SegTree(n);
+ st.init(n);
+ for (int i = 0; i < q; i++) {
+ String cmd = sc.next();
+ if (cmd.equals("update")) {
+ int k = sc.nextInt();
+ int a = sc.nextInt();
+ st.update(k, a);
+ } else if (cmd.equals("query")) {
+ int a = sc.nextInt();
+ int b = sc.nextInt();
+ System.out.println(st.query(a, b));
+ }
+ }
+ }
+}
+DRIVER
+
+javac "$PKG_DIR/SegTree.java" "$PKG_DIR/SegTreeTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | java -cp "$TMPDIR" Tree.SegmentTree.SegTreeTest | sed 's/[[:space:]]*$//')
+ expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: basic RMQ [5,3,7,1,4]
+run_test "basic RMQ" \
+"5 7
+update 0 5
+update 1 3
+update 2 7
+update 3 1
+update 4 4
+query 0 5
+query 1 3" \
+"1
+3"
+
+# Test 2: update and re-query
+run_test "update and re-query" \
+"3 6
+update 0 10
+update 1 20
+update 2 30
+query 0 3
+update 1 5
+query 0 3" \
+"10
+5"
+
+# Test 3: single element query
+run_test "single element query" \
+"3 4
+update 0 3
+update 1 7
+update 2 5
+query 1 2" \
+"7"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/test/Tree/UnionFind/test_unionfind.sh b/src/java/test/Tree/UnionFind/test_unionfind.sh
new file mode 100755
index 0000000..a31ae0c
--- /dev/null
+++ b/src/java/test/Tree/UnionFind/test_unionfind.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+SRC="$SCRIPT_DIR/../../../lib/Tree/UnionFind/UnionFind.java"
+TMPDIR=$(mktemp -d)
+PKG_DIR="$TMPDIR/Tree/UnionFind"
+
+mkdir -p "$PKG_DIR"
+cp "$SRC" "$PKG_DIR/UnionFind.java"
+
+cat > "$PKG_DIR/UnionFindTest.java" << 'DRIVER'
+package Tree.UnionFind;
+import java.util.Scanner;
+
+public class UnionFindTest {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ int n = sc.nextInt();
+ int q = sc.nextInt();
+ UnionFind uf = new UnionFind(n);
+ for (int i = 0; i < q; i++) {
+ String cmd = sc.next();
+ if (cmd.equals("unite")) {
+ int x = sc.nextInt();
+ int y = sc.nextInt();
+ uf.unite1(x, y);
+ } else if (cmd.equals("same")) {
+ int x = sc.nextInt();
+ int y = sc.nextInt();
+ System.out.println(uf.same(x, y) ? "yes" : "no");
+ } else if (cmd.equals("size")) {
+ int x = sc.nextInt();
+ System.out.println(uf.size(x));
+ }
+ }
+ }
+}
+DRIVER
+
+javac "$PKG_DIR/UnionFind.java" "$PKG_DIR/UnionFindTest.java" || { echo "COMPILE FAILED"; rm -rf "$TMPDIR"; exit 1; }
+
+pass=0
+fail=0
+
+run_test() {
+ local name="$1" input="$2" expected="$3"
+ actual=$(echo "$input" | java -cp "$TMPDIR" Tree.UnionFind.UnionFindTest | sed 's/[[:space:]]*$//')
+ expected=$(echo "$expected" | sed 's/[[:space:]]*$//')
+ if [ "$actual" = "$expected" ]; then
+ echo "PASS: $name"
+ ((pass++))
+ else
+ echo "FAIL: $name"
+ echo " expected: $expected"
+ echo " actual: $actual"
+ ((fail++))
+ fi
+}
+
+# Test 1: basic union and find
+run_test "basic union-find" \
+"5 9
+same 0 1
+unite 0 1
+same 0 1
+unite 2 3
+same 0 2
+unite 0 2
+same 0 3
+size 0
+size 4" \
+"no
+yes
+no
+yes
+4
+1"
+
+# Test 2: merge all nodes
+run_test "merge all" \
+"3 5
+unite 0 1
+unite 1 2
+same 0 2
+size 0
+size 2" \
+"yes
+3
+3"
+
+rm -rf "$TMPDIR"
+echo ""
+echo "Result: $pass passed, $fail failed"
+[ "$fail" -eq 0 ] && exit 0 || exit 1
diff --git a/src/java/test/static_analysis.sh b/src/java/test/static_analysis.sh
new file mode 100755
index 0000000..e6fe6cf
--- /dev/null
+++ b/src/java/test/static_analysis.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+LIB_DIR="$SCRIPT_DIR/../lib"
+TMPDIR=$(mktemp -d)
+
+files=$(find "$LIB_DIR" -name "*.java" | sort)
+output=$(javac -Xlint:all -Werror -sourcepath "$LIB_DIR" -d "$TMPDIR" $files 2>&1)
+status=$?
+
+if [ $status -ne 0 ]; then
+ echo "FAIL: static analysis found issues"
+ echo "$output"
+else
+ count=$(echo "$files" | wc -l)
+ echo "PASS: all $count files passed static analysis"
+fi
+
+rm -rf "$TMPDIR"
+exit $status