diff --git a/app/src/main/java/fun/youthlic/Graph.java b/app/src/main/java/fun/youthlic/Graph.java index aa6d303..6695169 100644 --- a/app/src/main/java/fun/youthlic/Graph.java +++ b/app/src/main/java/fun/youthlic/Graph.java @@ -11,6 +11,7 @@ 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 { @@ -54,6 +55,29 @@ 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; @@ -87,13 +111,13 @@ public class Graph { return Optional.of(bridgeWords); } - public Optional> findShortestPath(final String word1, final String word2) { + public List 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 Optional.empty(); + return path; } final Node startNode = nodes.get(startWord); @@ -101,7 +125,7 @@ public class Graph { if (startNode == endNode) { path.add(startNode.id); - return Optional.of(path); + return path; } final var distances = new HashMap(); @@ -131,7 +155,7 @@ public class Graph { } if (distances.get(endNode) == Integer.MAX_VALUE) { - return Optional.of(path); + return path; } final var reversePath = new LinkedList(); @@ -141,16 +165,16 @@ public class Graph { current = previousNodes.get(current); } - return Optional.of(reversePath); + return reversePath; } - public double computePageRank(final String nodeId) { + public double computePageRank(final String nodeId, final double d) { final Node node = nodes.get(nodeId); if (node == null) { - return -1.0; + return 0.0; } - return pageRanks.getOrDefault(node, -1.0); + return pageRanks.getOrDefault(node, 0.0); } public List randomWalk() { @@ -224,7 +248,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); + sb.append(" ").append(edge).append("\n"); } } sb.append("}"); diff --git a/app/src/main/java/fun/youthlic/GraphCLI.java b/app/src/main/java/fun/youthlic/GraphCLI.java index 1c6c245..cc5b3c8 100644 --- a/app/src/main/java/fun/youthlic/GraphCLI.java +++ b/app/src/main/java/fun/youthlic/GraphCLI.java @@ -1,20 +1,6 @@ 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; @@ -23,316 +9,20 @@ 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 { - terminal = TerminalBuilder.builder().system(true).build(); + 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); + } } catch (final IOException e) { - 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; - } + } finally { } } } - -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; - } -} diff --git a/gradle.properties b/gradle.properties index 6b91bb5..377538c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,5 @@ # 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/gradlew.bat b/gradlew.bat index 9d21a21..9b42019 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 deleted file mode 100644 index dc0e3ea..0000000 --- a/output.txt +++ /dev/null @@ -1,2 +0,0 @@ -B1 output.txt -scientist carefully analyzed it again diff --git a/settings.gradle.kts b/settings.gradle.kts index ffdc88e..156979d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,6 +4,7 @@ * 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" diff --git a/test.txt b/test.txt deleted file mode 100644 index 97d4c04..0000000 --- a/test.txt +++ /dev/null @@ -1,2 +0,0 @@ -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.