Compare commits

...

4 commits

Author SHA1 Message Date
d93e6fb8de
modify two files 2025-05-11 20:22:31 +08:00
99e45fcc9b
modify two file 2025-05-11 20:16:13 +08:00
20c3180c6c
modify two files, too 2025-05-11 20:10:05 +08:00
d930f7a6f5
finish GraphCLI 2025-05-11 20:02:51 +08:00
7 changed files with 427 additions and 139 deletions

View file

@ -11,7 +11,6 @@ import java.util.Optional;
import java.util.PriorityQueue;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.IntStream;
public class Graph {
@ -55,29 +54,6 @@ public class Graph {
}
}
public static void main(final String[] args) {
final var g = new Graph(
"To explore strange new worlds, To seek out new life and new civilizations");
final var words = g.findBridgeWords("team", "so");
System.out.println(words);
final var path = g.findShortestPath("the", "shared");
System.out.println(path);
System.out.println(
g.toDOT(r -> String.format(
"\"%s\" -> \"%s\" [label=\"%d\"%s]",
r.node1 + (path.contains(r.node1) ? "~" : ""),
r.node2 + (path.contains(r.node2) ? "~" : ""),
r.weight,
(path.contains(r.node1) &&
path.contains(r.node2) &&
IntStream.range(0, path.size() - 1).anyMatch(
i -> path.get(i).equals(r.node1) && path.get(i + 1).equals(r.node2)))
? ",color=\"red\""
: "")));
System.out.println(g.computePageRank("new", 0.85));
System.out.println(g.randomWalk());
}
private final Map<String, Node> nodes;
private Map<Node, Double> pageRanks;
@ -111,13 +87,13 @@ public class Graph {
return Optional.of(bridgeWords);
}
public List<String> findShortestPath(final String word1, final String word2) {
public Optional<List<String>> findShortestPath(final String word1, final String word2) {
final var startWord = word1.toLowerCase(Locale.ENGLISH);
final var endWord = word2.toLowerCase(Locale.ENGLISH);
final var path = new ArrayList<String>();
if (!nodes.containsKey(startWord) || !nodes.containsKey(endWord)) {
return path;
return Optional.empty();
}
final Node startNode = nodes.get(startWord);
@ -125,7 +101,7 @@ public class Graph {
if (startNode == endNode) {
path.add(startNode.id);
return path;
return Optional.of(path);
}
final var distances = new HashMap<Node, Integer>();
@ -155,7 +131,7 @@ public class Graph {
}
if (distances.get(endNode) == Integer.MAX_VALUE) {
return path;
return Optional.of(path);
}
final var reversePath = new LinkedList<String>();
@ -165,16 +141,16 @@ public class Graph {
current = previousNodes.get(current);
}
return reversePath;
return Optional.of(reversePath);
}
public double computePageRank(final String nodeId, final double d) {
public double computePageRank(final String nodeId) {
final Node node = nodes.get(nodeId);
if (node == null) {
return 0.0;
return -1.0;
}
return pageRanks.getOrDefault(node, 0.0);
return pageRanks.getOrDefault(node, -1.0);
}
public List<String> randomWalk() {
@ -248,7 +224,7 @@ public class Graph {
sb.append("digraph G {\n");
for (final Node node : nodes.values()) {
for (final String edge : node.getDOTEdges()) {
sb.append(" ").append(edge).append("\n");
sb.append(" ").append(edge);
}
}
sb.append("}");

View file

@ -1,6 +1,20 @@
package fun.youthlic;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
@ -9,20 +23,316 @@ import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
public class GraphCLI {
static String readFile(final String filename) {
String text = null;
try (BufferedReader inputReader = new BufferedReader(new FileReader(new File(filename)))) {
text = inputReader.lines().collect(Collectors.joining(System.lineSeparator()));
} catch (final IOException e) {
System.out.println("Error reading file: " + e.getMessage());
}
return text;
}
public static void main(final String[] args) {
GraphCLIHelper helper = null;
Terminal terminal = null;
try {
final Terminal terminal = TerminalBuilder.builder().system(true).build();
final LineReader reader = LineReaderBuilder.builder().terminal(terminal).parser(new DefaultParser())
.build();
while (true) {
final String line = reader.readLine("graph> ");
if (line.trim().equalsIgnoreCase("exit"))
break;
System.out.println(line);
}
terminal = TerminalBuilder.builder().system(true).build();
} catch (final IOException e) {
} finally {
e.printStackTrace();
System.exit(-1);
}
final LineReader reader = LineReaderBuilder.builder().terminal(terminal).parser(new DefaultParser())
.build();
while (true) {
final String line = reader.readLine("graph> ");
final var commands = line.split("[ \\t]+");
if (commands.length == 0) {
continue;
}
switch (commands[0]) {
case "load":
if (commands.length != 2) {
System.out.println("Usage: load <filename>");
} else {
final var text = readFile(commands[1]);
if (text == null) {
System.out.println("Load file failed; " + commands[1]);
continue;
}
helper = new GraphCLIHelper(text);
}
break;
case "help":
System.out.println("Available commands:");
System.out.println(" show");
System.out.println(" dot");
System.out.println(" help");
System.out.println(" exit");
System.out.println(" random-walk");
System.out.println(" load <filename>");
System.out.println(" page-rank <word>");
System.out.println(" bridge-words <word1> <word2>");
System.out.println(" new-text <text...>");
System.out.println(" shortest-path <word1> <word2>");
break;
case "shortest-path":
if (commands.length != 3) {
System.out.println("Usage: shortest-path <word1> <word2>");
} else if (helper == null) {
System.out.println("No graph loaded");
break;
} else {
try {
System.out.println(helper.calcShortestPath(commands[1], commands[2]));
} catch (IOException | InterruptedException e) {
System.out.println(e.getMessage());
}
}
break;
case "exit":
return;
case "random-walk":
if (commands.length != 1) {
System.out.println("Usage: random-walk");
} else {
if (helper == null) {
System.out.println("No graph loaded");
break;
}
System.out.println(helper.randomWalk());
}
break;
case "new-text":
if (commands.length < 2) {
System.out.println("Usage: new-text <text...>");
} else if (helper == null) {
System.out.println("No graph loaded");
break;
} else {
System.out.println(helper.generateNewText(
String.join(" ", Arrays.asList(commands).subList(1, commands.length))));
}
break;
case "show":
if (commands.length != 1) {
System.out.println("Usage: show");
} else {
if (helper == null) {
System.out.println("No graph loaded");
break;
}
GraphCLIHelper.showDirectedGraph(helper.graph);
}
break;
case "dot":
if (commands.length != 1) {
System.out.println("Usage: dot");
} else {
if (helper == null) {
System.out.println("No graph loaded");
break;
}
System.out.println(helper.graph.toDOT());
}
case "page-rank":
if (commands.length != 2) {
System.out.println("Usage: page-rank <word>");
} else if (helper == null) {
System.out.println("No graph loaded");
} else {
final var pageRank = helper.calPageRank(commands[1]);
if (pageRank < 0.0) {
System.out.println("Invalid word");
} else {
System.out.println("PageRank of " + commands[1] + ": " + pageRank);
}
}
break;
case "bridge-words":
if (commands.length != 3) {
System.out.println("Usage: bridge-words <word1> <word2>");
} else if (helper == null) {
System.out.println("No graph loaded");
} else {
System.out.println(helper.queryBridgeWords(commands[1], commands[2]));
}
break;
default:
System.out.println("Unknown command: " + commands[0]);
break;
}
}
}
}
class GraphCLIHelper {
Graph graph;
GraphCLIHelper(final String text) {
graph = new Graph(text);
}
static void showDirectedGraph(final Graph G) {
final ProcessBuilder processBuilder = new ProcessBuilder("graph-easy", "--as", "ascii");
processBuilder.redirectErrorStream(true);
Process process = null;
try {
process = processBuilder.start();
} catch (final IOException e) {
System.err.println("Failed to start process");
e.printStackTrace();
}
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8))) {
writer.write(G.toDOT());
writer.flush();
} catch (final IOException e) {
System.err.println("Failed to write to process");
e.printStackTrace();
}
final StringBuilder output = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append(System.lineSeparator());
}
} catch (final IOException e) {
System.err.println("Failed to read from process");
e.printStackTrace();
}
try {
final int exitCode = process.waitFor();
if (exitCode != 0) {
System.err.println("Process exited with code " + exitCode);
} else {
System.out.println(output.toString());
}
} catch (final InterruptedException e) {
System.err.println("Process interrupted");
e.printStackTrace();
}
}
String queryBridgeWords(final String word1, final String word2) {
final var option = graph.findBridgeWords(word1, word2);
if (option.isEmpty()) {
return "No " + word1 + " or " + word2 + " in the graph!";
}
final var bridgeWords = option.get();
if (bridgeWords.isEmpty()) {
return "No bridge words from " + word1 + " to " + word2 + "!";
}
final var output = new StringBuilder();
output.append("The bridge words from ").append(word1).append(" to ").append(word2).append(" are: ");
for (int i = 0; i < bridgeWords.size(); i++) {
output.append(bridgeWords.get(i));
if (i < bridgeWords.size() - 2) {
output.append(", ");
} else if (i == bridgeWords.size() - 2) {
output.append(", and ");
} else {
output.append(".");
}
}
return output.toString();
}
String generateNewText(final String inputText) {
final var output = new StringBuilder();
final var words = inputText.split("\\s+");
for (int i = 0; i < words.length; i++) {
output.append(words[i]);
if (i < words.length - 1) {
output.append(" ");
final var option = graph.findBridgeWords(words[i], words[i + 1]);
if (option.isPresent() && !option.get().isEmpty()) {
final var bridgeWords = option.get();
final Random random = new Random();
final int wordIndex = random.nextInt(bridgeWords.size());
output.append(option.get().get(wordIndex)).append(" ");
}
}
}
return output.toString();
}
String calcShortestPath(final String word1, final String word2)
throws IOException, InterruptedException {
final ProcessBuilder processBuilder = new ProcessBuilder("graph-easy", "--as", "ascii");
processBuilder.redirectErrorStream(true);
Process process = null;
try {
process = processBuilder.start();
} catch (final IOException e) {
throw e;
}
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8))) {
writer.write(calcShortestPathToDOT(word1, word2));
writer.flush();
} catch (final IOException e) {
throw e;
}
final StringBuilder output = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append(System.lineSeparator());
}
} catch (final IOException e) {
throw e;
}
try {
final int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("Process exited with code " + exitCode);
} else {
return output.toString();
}
} catch (final InterruptedException e) {
throw e;
}
}
String calcShortestPathToDOT(final String word1, final String word2) {
var option = graph.findShortestPath(word1, word2);
if (option.isEmpty()) {
return "No " + word1 + " or " + word2 + " in the graph";
} else {
final var path = option.get();
if (path.isEmpty()) {
return "No path from " + word1 + " to " + word2;
}
return graph.toDOT(r -> String.format(
"\"%s\" -> \"%s\" [label=\"%d\"%s]",
r.node1() + (path.contains(r.node1()) ? "~" : ""),
r.node2() + (path.contains(r.node2()) ? "~" : ""),
r.weight(),
(path.contains(r.node1()) && path.contains(r.node2()) &&
IntStream.range(0, path.size() - 1).anyMatch(
i -> path.get(i).equals(r.node1()) && path.get(i + 1).equals(r.node2())))
? ",color=\"red\""
: ""));
}
}
Double calPageRank(final String word) {
final var pageRank = graph.computePageRank(word);
return pageRank;
}
String randomWalk() {
final var text = graph.randomWalk().stream().collect(Collectors.joining(" "));
final Path path = Paths.get("output.txt");
try {
Files.write(path, text.getBytes());
} catch (final IOException e) {
System.err.println("Failed to write to file");
e.printStackTrace();
}
return text;
}
}

View file

@ -1,5 +1,4 @@
# This file was generated by the Gradle 'init' task.
# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties
org.gradle.configuration-cache=true

188
gradlew.bat vendored
View file

@ -1,94 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

2
output.txt Normal file
View file

@ -0,0 +1,2 @@
B1 output.txt
scientist carefully analyzed it again

View file

@ -4,7 +4,6 @@
* The settings file is used to specify which projects to include in your build.
* For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.13/userguide/multi_project_builds.html in the Gradle documentation.
*/
plugins {
// Apply the foojay-resolver plugin to allow automatic download of JDKs
id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"

2
test.txt Normal file
View file

@ -0,0 +1,2 @@
B1 test.txt
The scientist carefully analyzed the data, wrote a detailed report, and shared the report with the team, but the team requested more data, so the scientist analyzed it again.