From d930f7a6f5c8465832513e271cb0f9f9129d17a9 Mon Sep 17 00:00:00 2001 From: ulic-youthlic Date: Tue, 29 Apr 2025 19:12:55 +0800 Subject: [PATCH 1/4] finish GraphCLI --- app/src/main/java/fun/youthlic/Graph.java | 42 +-- app/src/main/java/fun/youthlic/GraphCLI.java | 330 ++++++++++++++++++- 2 files changed, 329 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/fun/youthlic/Graph.java b/app/src/main/java/fun/youthlic/Graph.java index 6695169..aa6d303 100644 --- a/app/src/main/java/fun/youthlic/Graph.java +++ b/app/src/main/java/fun/youthlic/Graph.java @@ -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 nodes; private Map pageRanks; @@ -111,13 +87,13 @@ public class Graph { return Optional.of(bridgeWords); } - public List findShortestPath(final String word1, final String word2) { + public Optional> 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(); 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(); @@ -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(); @@ -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 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("}"); diff --git a/app/src/main/java/fun/youthlic/GraphCLI.java b/app/src/main/java/fun/youthlic/GraphCLI.java index cc5b3c8..1c6c245 100644 --- a/app/src/main/java/fun/youthlic/GraphCLI.java +++ b/app/src/main/java/fun/youthlic/GraphCLI.java @@ -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 "); + } 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 "); + System.out.println(" page-rank "); + System.out.println(" bridge-words "); + System.out.println(" new-text "); + System.out.println(" shortest-path "); + break; + case "shortest-path": + if (commands.length != 3) { + System.out.println("Usage: shortest-path "); + } 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 "); + } 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 "); + } 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 "); + } 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; + } +} From 20c3180c6ce0df86af83c1f6e2e80acbece42c8e Mon Sep 17 00:00:00 2001 From: ulic-youthlic Date: Sun, 11 May 2025 20:10:05 +0800 Subject: [PATCH 2/4] modify two files, too --- output.txt | 1 + test.txt | 1 + 2 files changed, 2 insertions(+) create mode 100644 output.txt create mode 100644 test.txt diff --git a/output.txt b/output.txt new file mode 100644 index 0000000..8445d52 --- /dev/null +++ b/output.txt @@ -0,0 +1 @@ +B1 output.txt diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..40a6868 --- /dev/null +++ b/test.txt @@ -0,0 +1 @@ +B1 test.txt From 99e45fcc9ba3a5da056fe2f588dacd398a651d7c Mon Sep 17 00:00:00 2001 From: ulic-youthlic Date: Sun, 11 May 2025 20:03:44 +0800 Subject: [PATCH 3/4] modify two file --- gradlew.bat | 188 ++++++++++++++++++++++++++-------------------------- output.txt | 1 + test.txt | 1 + 3 files changed, 96 insertions(+), 94 deletions(-) diff --git a/gradlew.bat b/gradlew.bat index 9b42019..9d21a21 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -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 diff --git a/output.txt b/output.txt index 8445d52..dc0e3ea 100644 --- a/output.txt +++ b/output.txt @@ -1 +1,2 @@ B1 output.txt +scientist carefully analyzed it again diff --git a/test.txt b/test.txt index 40a6868..97d4c04 100644 --- a/test.txt +++ b/test.txt @@ -1 +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. From d93e6fb8de0c713acf57880391eec53155bdf90b Mon Sep 17 00:00:00 2001 From: ulic-youthlic Date: Sun, 11 May 2025 20:17:47 +0800 Subject: [PATCH 4/4] modify two files --- gradle.properties | 1 - settings.gradle.kts | 1 - 2 files changed, 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 377538c..6b91bb5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 diff --git a/settings.gradle.kts b/settings.gradle.kts index 156979d..ffdc88e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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"