diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..f5ca14e
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,312 @@
+# This file is for unifying the coding style for different editors and IDEs.
+# More information at http://EditorConfig.org
+
+# No .editorconfig files above the root directory
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = false
+max_line_length = 130
+tab_width = 4
+trim_trailing_whitespace = true
+ij_continuation_indent_size = 8
+ij_formatter_off_tag=@formatter:off
+ij_formatter_on_tag = @formatter:on
+ij_formatter_tags_enabled = true
+ij_smart_tabs = false
+ij_visual_guides = none
+ij_wrap_on_typing = false
+
+[*.java]
+indent_size = 3
+tab_width = 3
+ij_continuation_indent_size = 6
+ij_java_align_consecutive_assignments = false
+ij_java_align_consecutive_variable_declarations = false
+ij_java_align_group_field_declarations = false
+ij_java_align_multiline_annotation_parameters = false
+ij_java_align_multiline_array_initializer_expression = false
+ij_java_align_multiline_assignment = false
+ij_java_align_multiline_binary_operation = false
+ij_java_align_multiline_chained_methods = false
+ij_java_align_multiline_extends_list = false
+ij_java_align_multiline_for = true
+ij_java_align_multiline_method_parentheses = false
+ij_java_align_multiline_parameters = false
+ij_java_align_multiline_parameters_in_calls = false
+ij_java_align_multiline_parenthesized_expression = false
+ij_java_align_multiline_records = true
+ij_java_align_multiline_resources = false
+ij_java_align_multiline_ternary_operation = false
+ij_java_align_multiline_text_blocks = false
+ij_java_align_multiline_throws_list = false
+ij_java_align_subsequent_simple_methods = false
+ij_java_align_throws_keyword = false
+ij_java_annotation_parameter_wrap = off
+ij_java_array_initializer_new_line_after_left_brace = false
+ij_java_array_initializer_right_brace_on_new_line = false
+ij_java_array_initializer_wrap = normal
+ij_java_assert_statement_colon_on_next_line = false
+ij_java_assert_statement_wrap = off
+ij_java_assignment_wrap = off
+ij_java_binary_operation_sign_on_next_line = true
+ij_java_binary_operation_wrap = normal
+ij_java_blank_lines_after_anonymous_class_header = 0
+ij_java_blank_lines_after_class_header = 0
+ij_java_blank_lines_after_imports = 2
+ij_java_blank_lines_after_package = 1
+ij_java_blank_lines_around_class = 1
+ij_java_blank_lines_around_field = 0
+ij_java_blank_lines_around_field_in_interface = 0
+ij_java_blank_lines_around_initializer = 1
+ij_java_blank_lines_around_method = 1
+ij_java_blank_lines_around_method_in_interface = 1
+ij_java_blank_lines_before_class_end = 0
+ij_java_blank_lines_before_imports = 1
+ij_java_blank_lines_before_method_body = 0
+ij_java_blank_lines_before_package = 1
+ij_java_block_brace_style = end_of_line
+ij_java_block_comment_at_first_column = true
+ij_java_call_parameters_new_line_after_left_paren = false
+ij_java_call_parameters_right_paren_on_new_line = false
+ij_java_call_parameters_wrap = normal
+ij_java_case_statement_on_separate_line = true
+ij_java_catch_on_new_line = true
+ij_java_class_annotation_wrap = split_into_lines
+ij_java_class_brace_style = end_of_line
+ij_java_class_count_to_use_import_on_demand = 100
+ij_java_class_names_in_javadoc = 1
+ij_java_do_not_indent_top_level_class_members = false
+ij_java_do_not_wrap_after_single_annotation = false
+ij_java_do_while_brace_force = never
+ij_java_doc_add_blank_line_after_description = true
+ij_java_doc_add_blank_line_after_param_comments = false
+ij_java_doc_add_blank_line_after_return = false
+ij_java_doc_add_p_tag_on_empty_lines = true
+ij_java_doc_align_exception_comments = true
+ij_java_doc_align_param_comments = true
+ij_java_doc_do_not_wrap_if_one_line = false
+ij_java_doc_enable_formatting = true
+ij_java_doc_enable_leading_asterisks = true
+ij_java_doc_indent_on_continuation = false
+ij_java_doc_keep_empty_lines = false
+ij_java_doc_keep_empty_parameter_tag = true
+ij_java_doc_keep_empty_return_tag = true
+ij_java_doc_keep_empty_throws_tag = true
+ij_java_doc_keep_invalid_tags = true
+ij_java_doc_param_description_on_new_line = true
+ij_java_doc_preserve_line_breaks = true
+ij_java_doc_use_throws_not_exception_tag = true
+ij_java_else_on_new_line = true
+ij_java_entity_dd_suffix = EJB
+ij_java_entity_eb_suffix = Bean
+ij_java_entity_hi_suffix = Home
+ij_java_entity_lhi_prefix = Local
+ij_java_entity_lhi_suffix = Home
+ij_java_entity_li_prefix = Local
+ij_java_entity_pk_class = java.lang.String
+ij_java_entity_vo_suffix = VO
+ij_java_enum_constants_wrap = off
+ij_java_extends_keyword_wrap = normal
+ij_java_extends_list_wrap = normal
+ij_java_field_annotation_wrap = split_into_lines
+ij_java_finally_on_new_line = true
+ij_java_for_brace_force = never
+ij_java_for_statement_new_line_after_left_paren = false
+ij_java_for_statement_right_paren_on_new_line = false
+ij_java_for_statement_wrap = off
+ij_java_generate_final_locals = true
+ij_java_generate_final_parameters = true
+ij_java_if_brace_force = always
+ij_java_imports_layout = $*, |, de.hybris.**, |, com.hybris.**, |, java.**, |, javax.**, |, org.**, |, com.**, |, de.**, |, *
+ij_java_indent_case_from_switch = true
+ij_java_insert_inner_class_imports = false
+ij_java_insert_override_annotation = true
+ij_java_keep_blank_lines_before_right_brace = 10
+ij_java_keep_blank_lines_between_package_declaration_and_header = 2
+ij_java_keep_blank_lines_in_code = 10
+ij_java_keep_blank_lines_in_declarations = 10
+ij_java_keep_control_statement_in_one_line = false
+ij_java_keep_first_column_comment = false
+ij_java_keep_indents_on_empty_lines = false
+ij_java_keep_line_breaks = true
+ij_java_keep_multiple_expressions_in_one_line = false
+ij_java_keep_simple_blocks_in_one_line = false
+ij_java_keep_simple_classes_in_one_line = false
+ij_java_keep_simple_lambdas_in_one_line = false
+ij_java_keep_simple_methods_in_one_line = false
+ij_java_label_indent_absolute = false
+ij_java_label_indent_size = 0
+ij_java_lambda_brace_style = end_of_line
+ij_java_layout_static_imports_separately = true
+ij_java_line_comment_add_space = false
+ij_java_line_comment_at_first_column = true
+ij_java_message_dd_suffix = EJB
+ij_java_message_eb_suffix = Bean
+ij_java_method_annotation_wrap = split_into_lines
+ij_java_method_brace_style = end_of_line
+ij_java_method_call_chain_wrap = normal
+ij_java_method_parameters_new_line_after_left_paren = false
+ij_java_method_parameters_right_paren_on_new_line = false
+ij_java_method_parameters_wrap = normal
+ij_java_modifier_list_wrap = false
+ij_java_names_count_to_use_import_on_demand = 100
+ij_java_new_line_after_lparen_in_record_header = false
+ij_java_parameter_annotation_wrap = off
+ij_java_parentheses_expression_new_line_after_left_paren = false
+ij_java_parentheses_expression_right_paren_on_new_line = false
+ij_java_place_assignment_sign_on_next_line = false
+ij_java_prefer_longer_names = true
+ij_java_prefer_parameters_wrap = false
+ij_java_record_components_wrap = normal
+ij_java_repeat_synchronized = true
+ij_java_replace_instanceof_and_cast = false
+ij_java_replace_null_check = true
+ij_java_replace_sum_lambda_with_method_ref = true
+ij_java_resource_list_new_line_after_left_paren = false
+ij_java_resource_list_right_paren_on_new_line = false
+ij_java_resource_list_wrap = on_every_item
+ij_java_rparen_on_new_line_in_record_header = false
+ij_java_session_dd_suffix = EJB
+ij_java_session_eb_suffix = Bean
+ij_java_session_hi_suffix = Home
+ij_java_session_lhi_prefix = Local
+ij_java_session_lhi_suffix = Home
+ij_java_session_li_prefix = Local
+ij_java_session_si_suffix = Service
+ij_java_space_after_closing_angle_bracket_in_type_argument = false
+ij_java_space_after_colon = true
+ij_java_space_after_comma = true
+ij_java_space_after_comma_in_type_arguments = true
+ij_java_space_after_for_semicolon = true
+ij_java_space_after_quest = true
+ij_java_space_after_type_cast = true
+ij_java_space_before_annotation_array_initializer_left_brace = false
+ij_java_space_before_annotation_parameter_list = false
+ij_java_space_before_array_initializer_left_brace = true
+ij_java_space_before_catch_keyword = true
+ij_java_space_before_catch_left_brace = true
+ij_java_space_before_catch_parentheses = true
+ij_java_space_before_class_left_brace = true
+ij_java_space_before_colon = true
+ij_java_space_before_colon_in_foreach = true
+ij_java_space_before_comma = false
+ij_java_space_before_do_left_brace = true
+ij_java_space_before_else_keyword = true
+ij_java_space_before_else_left_brace = true
+ij_java_space_before_finally_keyword = true
+ij_java_space_before_finally_left_brace = true
+ij_java_space_before_for_left_brace = true
+ij_java_space_before_for_parentheses = true
+ij_java_space_before_for_semicolon = false
+ij_java_space_before_if_left_brace = true
+ij_java_space_before_if_parentheses = true
+ij_java_space_before_method_call_parentheses = false
+ij_java_space_before_method_left_brace = true
+ij_java_space_before_method_parentheses = false
+ij_java_space_before_opening_angle_bracket_in_type_parameter = false
+ij_java_space_before_quest = true
+ij_java_space_before_switch_left_brace = true
+ij_java_space_before_switch_parentheses = true
+ij_java_space_before_synchronized_left_brace = true
+ij_java_space_before_synchronized_parentheses = true
+ij_java_space_before_try_left_brace = true
+ij_java_space_before_try_parentheses = true
+ij_java_space_before_type_parameter_list = false
+ij_java_space_before_while_keyword = true
+ij_java_space_before_while_left_brace = true
+ij_java_space_before_while_parentheses = true
+ij_java_space_inside_one_line_enum_braces = false
+ij_java_space_within_empty_array_initializer_braces = false
+ij_java_space_within_empty_method_call_parentheses = false
+ij_java_space_within_empty_method_parentheses = false
+ij_java_spaces_around_additive_operators = true
+ij_java_spaces_around_assignment_operators = true
+ij_java_spaces_around_bitwise_operators = true
+ij_java_spaces_around_equality_operators = true
+ij_java_spaces_around_lambda_arrow = true
+ij_java_spaces_around_logical_operators = true
+ij_java_spaces_around_method_ref_dbl_colon = false
+ij_java_spaces_around_multiplicative_operators = true
+ij_java_spaces_around_relational_operators = true
+ij_java_spaces_around_shift_operators = true
+ij_java_spaces_around_type_bounds_in_type_parameters = true
+ij_java_spaces_around_unary_operator = false
+ij_java_spaces_within_angle_brackets = false
+ij_java_spaces_within_annotation_parentheses = false
+ij_java_spaces_within_array_initializer_braces = true
+ij_java_spaces_within_braces = false
+ij_java_spaces_within_brackets = false
+ij_java_spaces_within_cast_parentheses = false
+ij_java_spaces_within_catch_parentheses = false
+ij_java_spaces_within_for_parentheses = false
+ij_java_spaces_within_if_parentheses = false
+ij_java_spaces_within_method_call_parentheses = false
+ij_java_spaces_within_method_parentheses = false
+ij_java_spaces_within_parentheses = false
+ij_java_spaces_within_record_header = false
+ij_java_spaces_within_switch_parentheses = false
+ij_java_spaces_within_synchronized_parentheses = false
+ij_java_spaces_within_try_parentheses = false
+ij_java_spaces_within_while_parentheses = false
+ij_java_special_else_if_treatment = true
+ij_java_subclass_name_suffix = Impl
+ij_java_ternary_operation_signs_on_next_line = false
+ij_java_ternary_operation_wrap = on_every_item
+ij_java_test_name_suffix = Test
+ij_java_throws_keyword_wrap = normal
+ij_java_throws_list_wrap = normal
+ij_java_use_external_annotations = false
+ij_java_use_fq_class_names = false
+ij_java_use_relative_indents = false
+ij_java_use_single_class_imports = true
+ij_java_variable_annotation_wrap = off
+ij_java_visibility = public
+ij_java_while_brace_force = never
+ij_java_while_on_new_line = true
+ij_java_wrap_comments = true
+ij_java_wrap_first_method_in_call_chain = false
+ij_java_wrap_long_lines = false
+
+[.editorconfig]
+ij_editorconfig_align_group_field_declarations = false
+ij_editorconfig_space_after_colon = false
+ij_editorconfig_space_after_comma = true
+ij_editorconfig_space_before_colon = false
+ij_editorconfig_space_before_comma = false
+ij_editorconfig_spaces_around_assignment_operators = true
+
+[{*.ant,*.edmx,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.qrc,*.rng,*.tld,*.wadl,*.wsdd,*.wsdl,*.xjb,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
+ij_xml_align_attributes = true
+ij_xml_align_text = false
+ij_xml_attribute_wrap = normal
+ij_xml_block_comment_at_first_column = true
+ij_xml_keep_blank_lines = 2
+ij_xml_keep_indents_on_empty_lines = false
+ij_xml_keep_line_breaks = true
+ij_xml_keep_line_breaks_in_text = true
+ij_xml_keep_whitespaces = false
+ij_xml_keep_whitespaces_around_cdata = preserve
+ij_xml_keep_whitespaces_inside_cdata = false
+ij_xml_line_comment_at_first_column = true
+ij_xml_space_after_tag_name = false
+ij_xml_space_around_equals_in_attribute = false
+ij_xml_space_inside_empty_tag = true
+ij_xml_text_wrap = normal
+
+[{*.yaml,*.yml}]
+indent_size = 2
+ij_yaml_align_values_properties = do_not_align
+ij_yaml_autoinsert_sequence_marker = true
+ij_yaml_block_mapping_on_new_line = false
+ij_yaml_indent_sequence_value = true
+ij_yaml_keep_indents_on_empty_lines = false
+ij_yaml_keep_line_breaks = true
+ij_yaml_sequence_on_new_line = false
+ij_yaml_space_before_colon = false
+ij_yaml_spaces_within_braces = true
+ij_yaml_spaces_within_brackets = true
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..1c48460
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @rabestro
diff --git a/.github/ISSUE_TEMPLATE/bug-issue.md b/.github/ISSUE_TEMPLATE/bug-issue.md
new file mode 100644
index 0000000..ab645cc
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-issue.md
@@ -0,0 +1,46 @@
+---
+name: Bug Issue
+about: Use this template for reporting a bug
+labels: 'type:bug'
+
+---
+
+Please make sure that this is a bug. As per our
+[GitHub Policy](https://github.com/tensorflow/tensorflow/blob/master/ISSUES.md),
+we only address code/doc bugs, performance issues, feature requests and
+build/installation issues on GitHub. tag:bug_template
+
+**System information**
+- Have I written custom code (as opposed to using a stock example script provided in TensorFlow):
+- OS Platform and Distribution (e.g., Linux Ubuntu 16.04):
+- Mobile device (e.g. iPhone 8, Pixel 2, Samsung Galaxy) if the issue happens on mobile device:
+- TensorFlow installed from (source or binary):
+- TensorFlow version (use command below):
+- Python version:
+- Bazel version (if compiling from source):
+- GCC/Compiler version (if compiling from source):
+- CUDA/cuDNN version:
+- GPU model and memory:
+
+You can collect some of this information using our environment capture
+[script](https://github.com/tensorflow/tensorflow/tree/master/tools/tf_env_collect.sh)
+You can also obtain the TensorFlow version with:
+1. TF 1.0: `python -c "import tensorflow as tf; print(tf.GIT_VERSION, tf.VERSION)"`
+2. TF 2.0: `python -c "import tensorflow as tf; print(tf.version.GIT_VERSION, tf.version.VERSION)"`
+
+**Describe the current behavior**
+
+**Describe the expected behavior**
+
+**[Contributing](https://www.tensorflow.org/community/contribute)**
+
+- Do you want to contribute a PR? (yes/no):
+- Briefly describe your candidate solution(if contributing):
+
+**Standalone code to reproduce the issue**
+Provide a reproducible test case that is the bare minimum necessary to generate
+the problem. If possible, please share a link to Colab/Jupyter/any notebook.
+
+**Other info / logs** Include any logs or source code that would be helpful to
+diagnose the problem. If including tracebacks, please include the full
+traceback. Large logs and files should be attached.
diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md
new file mode 100644
index 0000000..0bc1613
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature-request.md
@@ -0,0 +1,17 @@
+---
+name: Feature Request
+about: Use this template for raising a feature request
+labels: 'type:feature'
+
+---
+
+Please make sure that this is a feature request. As per our [GitHub Policy](https://github.com/tensorflow/tensorflow/blob/master/ISSUES.md), we only address code/doc bugs, performance issues, feature requests and build/installation issues on GitHub. tag:feature_template
+
+
+**Describe the feature and the current behavior/state.**
+
+**Will this change the current api? How?**
+
+**Who will benefit with this feature?**
+
+**Any Other info.**
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..72b8671
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,36 @@
+name: Build
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ types: [opened, synchronize, reopened]
+jobs:
+ build:
+ name: Build
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
+ - name: Set up JDK 17
+ uses: actions/setup-java@v1
+ with:
+ java-version: 17
+ - name: Cache SonarCloud packages
+ uses: actions/cache@v1
+ with:
+ path: ~/.sonar/cache
+ key: ${{ runner.os }}-sonar
+ restore-keys: ${{ runner.os }}-sonar
+ - name: Cache Gradle packages
+ uses: actions/cache@v1
+ with:
+ path: ~/.gradle/caches
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
+ restore-keys: ${{ runner.os }}-gradle
+ - name: Build and analyze
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ run: ./gradlew build sonarqube --info
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..a73edb8
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,74 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ master ]
+ schedule:
+ - cron: '15 16 * * 1'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'java' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
+ # Learn more about CodeQL language support at https://git.io/codeql-language-support
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ - uses: actions/setup-java@v2
+ with:
+ distribution: 'temurin' # See 'Supported distributions' for available options
+ java-version: '17'
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v1
+
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 https://git.io/JvXDl
+
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ #- run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b1b10da
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,37 @@
+##############################
+## Java
+##############################
+.mtj.tmp/
+*.class
+*.jar
+*.war
+*.ear
+*.nar
+hs_err_pid*
+
+##############################
+## Gradle
+##############################
+bin/
+build/
+.gradle
+.gradletasknamecache
+gradle-app.setting
+!gradle-wrapper.jar
+
+##############################
+## IntelliJ
+##############################
+out/
+.idea/
+.idea_modules/
+*.iml
+*.ipr
+*.iws
+
+##############################
+## OS X
+##############################
+.DS_Store
+/algorithm/gradle.properties
+/sample/gradle.properties
diff --git a/.gitmessage b/.gitmessage
new file mode 100644
index 0000000..055d59b
--- /dev/null
+++ b/.gitmessage
@@ -0,0 +1,44 @@
+# : (Max 50 char, Why is this change necessary?)
+# |<---- Using a Maximum Of 50 Characters ---->|
+
+# |<---- Try To Limit Each Line to a Maximum Of 72 Characters ---->|
+# Explain how the commit addresses the issue
+
+# IMPORTANT!! Describe any side effects of the change.
+
+# Provide links or keys to any relevant tickets, articles or other resources
+# Examples: "Jira issue [ABC-123]" or "Closes Github issue #123"
+
+# --- COMMIT END ---
+# Type can be
+# feat (new feature)
+# fix (bug fix)
+# refactor (refactoring production code)
+# style (formatting, missing semi colons, etc; no code change)
+# docs (changes to documentation)
+# test (adding or refactoring tests; no production code change)
+# chore (updating grunt tasks etc; no production code change)
+# wip (work in progress commit to be squashed -- do not push!)**
+# --------------------
+# Remember to
+# - Capitalize the subject line
+# - Use the imperative mood in the subject line
+# - Do not end the subject line with a period
+# - Separate subject from body with a blank line
+# - Use the body to explain what and why vs. how
+# - Can use multiple lines with "-" for bullet points in body.
+# --------------------
+# ** wip commit type
+# A wip commit should only happen on a local branch. These commits are for
+# unfinished snapshots that should not be checked into a shared branch.
+# These commits should be squashed before changes are merged to
+# a shared branch.
+# --------------------
+# For more information about this template and useful commit messages, check out
+# - https://gist.github.com/median-man/3a7c4324005e96f02691f3a20aeac26b
+# - https://gist.github.com/adeekshith/cd4c95a064977cdc6c50
+# - https://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message
+# - https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
+# - https://8thlight.com/blog/kevin-liddle/2012/09/27/mind-your-git-manners.html
+#
+#
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 36dedf8..3643ec5 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -5,13 +5,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 593cb31..4cbd4e5 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -7,10 +7,12 @@
-
+
+
+
\ No newline at end of file
diff --git a/CNAME b/CNAME
new file mode 100644
index 0000000..e1f8911
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+algorithms.jc.id.lv
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..244293b
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Jegors Čemisovs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 3027aad..8d95bb6 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,139 @@
+[](https://sonarcloud.io/summary/new_code?id=rabestro_algorithms)
+
# Graph search algorithms
-This project was created to test graph search algorithms. There are implementations and tests for two algorithms:
+The project implements an interface for the weighted graph, as well as two algorithms for finding a path in the graph.
+
+There are implementations and tests for two algorithms:
+
+- [Breadth-first search](https://en.wikipedia.org/wiki/Breadth-first_search)
+- [Dijkstra's Algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm)
+
+The implementation is written in Java 17. [API documentation](https://algorithms.jc.id.lv/docs/api/) is available. You
+can also see the [specifications](https://algorithms.jc.id.lv/docs/spock-reports/) generated with the spock-reports.
+
+## Demo. Graph Shell
+
+To demonstrate the work of search algorithms, I made a small console program '[Graph Shell](graph-shell/README.md)'. The program
+allows you to select [one of three build-in graph samples](#Graph-Samples) and search for a path using two algorithms. The source
+code of the program is located in `graph-shell` module.
+
+[](https://asciinema.org/a/468058)
+
+### Usage in other projects
+
+These algorithms used in the [Hypermetro](https://rabestro.github.io/hypermetro/) project, where they are
+utilized to find the optimal route in the metro schema.
+
+
+## How to use the algorithms in your program
+
+The first step is to create a graph structure. The Graph interface is generic, so you can use any Java type for vertex
+and any [Number](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Number.html) type for distance.
+
+### Example
+
+In the following Java code we create a graph structure with eight nodes. We
+use [Character](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Character.html) class for vertex
+identification and [Integer](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Integer.html) for the
+distance. You can see the graphic representation of the scheme [here](docs/assets/complex.gif).
-- [Breadth-first search](src/main/java/graph/BreadthFirstSearch.java)
-- [Dijkstra's Algorithm](src/main/java/graph/DijkstrasAlgorithm.java)
+```java
+var graph = Graph.of(Map.of(
+ 'A', Map.of('B', 5, 'H', 2),
+ 'B', Map.of('A', 5, 'C', 7),
+ 'C', Map.of('B', 7, 'D', 3, 'G', 4),
+ 'D', Map.of('C', 20, 'E', 4),
+ 'E', Map.of('F', 5),
+ 'F', Map.of('G', 6),
+ 'G', Map.of('C', 4),
+ 'H', Map.of('G', 3)
+ ));
+```
-## Technical specifications
+The second step is creating a search algorithm class. You can choose one of the two algorithms.
-Algorithm code in Java 17. Tests written in Groovy 3 using Spock Framework 2.
+### Example
+
+```java
+var fastest = new DijkstrasAlgorithm();
+var shortest = new BreadthFirstSearch();
+```
+
+Now we can search for the route.
+
+### Example
+
+```java
+var source = 'D';
+var target = 'C';
+
+var routeOne = shortest.findPath(graph, source, target);
+var routeTwo = fastest.findPath(graph, source, target);
+```
+
+As result, you get a list with the path.
+
+```java
+routeOne==['D','C']
+ routeTwo==['D','E','F','G','C']
+```
+
+## Unit Tests
+
+Tests are written in Groove language. For unit testing, the [Spock Framework](https://spockframework.org/) was used. To test the operation of the algorithms, the following sample graphs were created.
## Graph Samples
-To test the operation of the algorithms, the following sample graphs were created.
+### Small Graph Sample
+
+```mermaid
+flowchart LR
+ A((A))
+ B((B))
+ C((C))
+ A -->|7| B
+ A -->|2| C
+ B -->|3| A
+ B -->|5| C
+ C -->|1| A
+ C -->|3| B
+```
+
+
+
+
+### Medium Graph Sample
-
+```mermaid
+flowchart LR
+ A --> |5 | B
+ B --> |5 | A
+ B --> |10| C
+ C --> |5 | D
+ C --> |20| B
+ D --> |5 | E
+ E --> |5 | B
+```
-### Medium Graph
+
-
+### Complex Graph Sample
-### Complex Graph
+```mermaid
+flowchart LR
+ A --> |5 | B
+ A --> |2 | H
+ B --> |5 | A
+ B --> |7 | C
+ C --> |7 | B
+ C --> |3 | D
+ C --> |4 | G
+ D --> |20| C
+ D --> |4 | E
+ E --> |5 | F
+ G --> |4 | C
+ H --> |3 | G
+```
-
+
\ No newline at end of file
diff --git a/_config.yml b/_config.yml
new file mode 100644
index 0000000..e8eb639
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1,12 @@
+theme: jekyll-theme-minimal
+title: Algorithms
+author: Jegors Čemisovs
+email: jegors.cemisovs@outlook.lv
+description: >
+ Graph pathfinding algorithms.
+
+# social links
+twitter_username: rabestro # DO NOT include the @ character, or else the build will fail!
+github_username: rabestro # DO NOT include the @ character, or else the build will fail!
+
+show_excerpts: true # set to false to remove excerpts on the homepage
diff --git a/algorithm/build.gradle b/algorithm/build.gradle
new file mode 100644
index 0000000..655e701
--- /dev/null
+++ b/algorithm/build.gradle
@@ -0,0 +1,86 @@
+plugins {
+ id 'groovy'
+ id 'java-library'
+ id 'maven-publish'
+ id 'jacoco'
+ id 'org.sonarqube' version '3.4.0.2513'
+}
+
+sonarqube {
+ properties {
+ property "sonar.projectKey", "rabestro_algorithms"
+ property "sonar.organization", "rabestro"
+ property "sonar.host.url", "https://sonarcloud.io"
+ }
+}
+
+group 'lv.id.jc'
+version '1.1-SNAPSHOT'
+
+repositories {
+ mavenCentral()
+}
+
+// Configures the publishing
+publishing {
+ repositories {
+ // The target repository
+ maven {
+ // Choose whatever name you want
+ name = "Algorithms"
+ // The url of the repository, where the artifacts will be published
+ url = "https://maven.pkg.github.com/rabestro/algorithms"
+ credentials {
+ // The credentials (described in the next section)
+ username = project.findProperty("gpr.user")
+ password = project.findProperty("gpr.key")
+ }
+ }
+ }
+ publications {
+ gpr(MavenPublication) {
+ from(components.java)
+ // Fixes the error with dynamic versions when using Spring Boot
+ versionMapping {
+ usage('java-api') {
+ fromResolutionOf('runtimeClasspath')
+ }
+ usage('java-runtime') {
+ fromResolutionResult(null)
+ }
+ }
+ }
+ }
+}
+
+dependencies {
+ // Spock Framework
+ testImplementation 'org.spockframework:spock-core:2.2-groovy-3.0'
+ testImplementation 'org.codehaus.groovy:groovy-all:3.0.12'
+
+ // Spock Reports
+ testRuntimeClasspath('com.athaydes:spock-reports:2.3.1-groovy-3.0') {
+ transitive = false // this avoids affecting your version of Groovy/Spock
+ }
+ // Required for spock-reports
+ testImplementation 'org.slf4j:slf4j-api:2.0.1'
+ testRuntimeClasspath 'org.slf4j:slf4j-simple:2.0.1'
+
+ // JUnit 5 Parameterized Tests
+ testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.1'
+}
+
+test {
+ useJUnitPlatform()
+ finalizedBy jacocoTestReport
+}
+
+jacocoTestReport {
+ dependsOn test
+
+ reports {
+ xml.required = true
+ csv.required = false
+ html.outputLocation = layout.buildDirectory.dir('jacocoHtml')
+ }
+}
diff --git a/src/main/java/lv/id/jc/algorithm/graph/BreadthFirstSearch.java b/algorithm/src/main/java/lv/id/jc/algorithm/graph/BreadthFirstSearch.java
similarity index 77%
rename from src/main/java/lv/id/jc/algorithm/graph/BreadthFirstSearch.java
rename to algorithm/src/main/java/lv/id/jc/algorithm/graph/BreadthFirstSearch.java
index 6d5e0fd..b7e927d 100644
--- a/src/main/java/lv/id/jc/algorithm/graph/BreadthFirstSearch.java
+++ b/algorithm/src/main/java/lv/id/jc/algorithm/graph/BreadthFirstSearch.java
@@ -1,5 +1,6 @@
package lv.id.jc.algorithm.graph;
+import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -11,24 +12,26 @@
/**
* Algorithm for finding the shortest paths between nodes in a graph.
- *
+ *
* The algorithm doesn't take into account the distance between nodes.
*
- * @param type of vertex id
+ * @param the type of vertex
+ * @author Jegors Čemisovs
+ * @since 1.0
*/
public class BreadthFirstSearch implements SearchAlgorithm {
@Override
public List findPath(Graph graph, T source, T target) {
- final var queue = new LinkedList();
- final var visited = new HashSet();
- final var previous = new HashMap();
+ var queue = new ArrayDeque();
+ var visited = new HashSet();
+ var previous = new HashMap();
queue.add(source);
while (!queue.isEmpty()) {
- final var node = queue.removeFirst();
+ var node = queue.removeFirst();
if (target.equals(node)) {
- final var path = new LinkedList();
+ var path = new LinkedList();
iterate(node, Objects::nonNull, previous::get).forEach(path::addFirst);
return path;
}
diff --git a/src/main/java/lv/id/jc/algorithm/graph/DijkstrasAlgorithm.java b/algorithm/src/main/java/lv/id/jc/algorithm/graph/DijkstrasAlgorithm.java
similarity index 59%
rename from src/main/java/lv/id/jc/algorithm/graph/DijkstrasAlgorithm.java
rename to algorithm/src/main/java/lv/id/jc/algorithm/graph/DijkstrasAlgorithm.java
index c3d4352..41f7817 100644
--- a/src/main/java/lv/id/jc/algorithm/graph/DijkstrasAlgorithm.java
+++ b/algorithm/src/main/java/lv/id/jc/algorithm/graph/DijkstrasAlgorithm.java
@@ -1,5 +1,6 @@
package lv.id.jc.algorithm.graph;
+import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -9,25 +10,27 @@
/**
* Algorithm for finding the fastest paths between nodes in a graph.
- *
+ *
* The algorithm uses information about edge's distance to find the fastest path.
*
- * @param type of vertex id
+ * @param the type of vertex
+ * @author Jegors Čemisovs
+ * @since 1.0
*/
public class DijkstrasAlgorithm implements SearchAlgorithm {
@Override
public List findPath(Graph graph, T source, T target) {
- final var queue = new LinkedList();
- final var distances = new HashMap();
- final var previous = new HashMap();
+ var queue = new ArrayDeque();
+ var distances = new HashMap();
+ var previous = new HashMap();
queue.add(source);
distances.put(source, .0);
while (!queue.isEmpty()) {
- final var prev = queue.removeFirst();
+ var prev = queue.removeFirst();
graph.edges(prev).forEach((node, time) -> {
- final var distance = distances.get(prev) + time.doubleValue();
+ var distance = distances.get(prev) + time.doubleValue();
if (distance < distances.getOrDefault(node, Double.MAX_VALUE)) {
previous.put(node, prev);
distances.put(node, distance);
@@ -35,10 +38,12 @@ public List findPath(Graph graph, T source, T target) {
}
});
}
-
- final var path = new LinkedList();
- iterate(target, Objects::nonNull, previous::get).forEach(path::addFirst);
- return path;
+ if (previous.containsKey(target) || source.equals(target)) {
+ var path = new LinkedList();
+ iterate(target, Objects::nonNull, previous::get).forEach(path::addFirst);
+ return path;
+ }
+ return List.of();
}
}
diff --git a/algorithm/src/main/java/lv/id/jc/algorithm/graph/Graph.java b/algorithm/src/main/java/lv/id/jc/algorithm/graph/Graph.java
new file mode 100644
index 0000000..2b4de63
--- /dev/null
+++ b/algorithm/src/main/java/lv/id/jc/algorithm/graph/Graph.java
@@ -0,0 +1,71 @@
+package lv.id.jc.algorithm.graph;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.IntStream;
+
+/**
+ * An interface for weighted directed graph (network)
+ *
+ * @param the type of vertex in this graph
+ * @author Jegors Čemisovs
+ * @since 1.1
+ */
+@FunctionalInterface
+public interface Graph {
+ /**
+ * Creates a Graph object by given schema.
+ *
+ * In a graph schema, each vertex is assigned an edge map.
+ * If the vertex has no edges, then it should be assigned an empty map.
+ *
+ * @param schema of the graph
+ * @param the type of vertex in this graph
+ * @return graph object with given schema
+ */
+ static Graph of(Map> schema) {
+ return () -> schema;
+ }
+
+ /**
+ * The schema of this graph.
+ *
+ * In a graph schema, each vertex is assigned an edge map.
+ * If the vertex has no edges, then it should be assigned an empty map.
+ *
+ * @return the graph scheme
+ */
+ Map> schema();
+
+ /**
+ * Returns the edges of the given vertex,
+ * or {@code null} if this graph contains no given vertex.
+ *
+ *
A return value of {@code null} does not necessarily
+ * indicate that the specified vertex is not present in the graph;
+ * it's also possible that in the graph schema, {@code null} was specified
+ * for the edges of this vertex instead of an empty map.
+ *
+ * @param vertex vertex
+ * @return all links for the given vertex
+ * or null if no such vertex in the graph
+ */
+ default Map edges(T vertex) {
+ return schema().get(vertex);
+ }
+
+ /**
+ * Calculate the distance for the given path
+ *
+ * @param path the list of vertices representing the path
+ * @return distance for the given path as double
+ * @throws NullPointerException if {@code path} is incorrect and contains more than one vertex
+ */
+ default double getDistance(List path) {
+ return IntStream
+ .range(1, path.size())
+ .mapToObj(i -> edges(path.get(i - 1)).get(path.get(i)))
+ .mapToDouble(Number::doubleValue)
+ .sum();
+ }
+}
diff --git a/src/main/java/lv/id/jc/algorithm/graph/SearchAlgorithm.java b/algorithm/src/main/java/lv/id/jc/algorithm/graph/SearchAlgorithm.java
similarity index 78%
rename from src/main/java/lv/id/jc/algorithm/graph/SearchAlgorithm.java
rename to algorithm/src/main/java/lv/id/jc/algorithm/graph/SearchAlgorithm.java
index 166a67e..cf8f54f 100644
--- a/src/main/java/lv/id/jc/algorithm/graph/SearchAlgorithm.java
+++ b/algorithm/src/main/java/lv/id/jc/algorithm/graph/SearchAlgorithm.java
@@ -5,14 +5,16 @@
/**
* A functional interface for graph search algorithm
*
- * @param type of vertex id
+ * @param the type of vertex
+ * @author Jegors Čemisovs
+ * @since 1.0
*/
@FunctionalInterface
public interface SearchAlgorithm {
/**
* Find the path from the source node to the target
*
- * @param graph The graph in which we search for the path
+ * @param graph The graph in which we search for the path
* @param source Search starting point identifier
* @param target Search finish point identifier
* @return Path found or empty list if path cannot be found
diff --git a/src/main/java/lv/id/jc/algorithm/graph/package-info.java b/algorithm/src/main/java/lv/id/jc/algorithm/graph/package-info.java
similarity index 100%
rename from src/main/java/lv/id/jc/algorithm/graph/package-info.java
rename to algorithm/src/main/java/lv/id/jc/algorithm/graph/package-info.java
diff --git a/algorithm/src/main/java/module-info.java b/algorithm/src/main/java/module-info.java
new file mode 100644
index 0000000..941d8df
--- /dev/null
+++ b/algorithm/src/main/java/module-info.java
@@ -0,0 +1,8 @@
+/**
+ * The module contains an interface for a graph and an interface for a graph search algorithm.
+ * There is an implementation of two search algorithms:
+ * Dijkstra's algorithm and Breadth First Search algorithm.
+ */
+module lv.id.jc.algorithm.graph {
+ exports lv.id.jc.algorithm.graph;
+}
\ No newline at end of file
diff --git a/src/test/groovy/graph/BreadthFirstSearchSpec.groovy b/algorithm/src/test/groovy/lv/id/jc/algorithm/graph/BreadthFirstSearchSpec.groovy
similarity index 68%
rename from src/test/groovy/graph/BreadthFirstSearchSpec.groovy
rename to algorithm/src/test/groovy/lv/id/jc/algorithm/graph/BreadthFirstSearchSpec.groovy
index a739879..fba94a7 100644
--- a/src/test/groovy/graph/BreadthFirstSearchSpec.groovy
+++ b/algorithm/src/test/groovy/lv/id/jc/algorithm/graph/BreadthFirstSearchSpec.groovy
@@ -1,12 +1,6 @@
-package graph
+package lv.id.jc.algorithm.graph
-import lv.id.jc.algorithm.graph.BreadthFirstSearch
-import lv.id.jc.algorithm.graph.Graph
-import spock.lang.Narrative
-import spock.lang.See
-import spock.lang.Specification
-import spock.lang.Subject
-import spock.lang.Title
+import spock.lang.*
@Title("Breadth First Search Algorithm")
@See("https://en.wikipedia.org/wiki/Breadth-first_search")
@@ -18,20 +12,20 @@ class BreadthFirstSearchSpec extends Specification {
def algorithm = new BreadthFirstSearch()
def 'should find a route for simple graph'() {
- given: 'A simple graph'
- def graph = new Graph([
+ given:
+ def graph = Graph.of([
A: [B: 7, C: 2],
B: [A: 3, C: 5],
C: [A: 1, B: 3]
])
- when: 'we use Breadth First Search algorithm to find a path'
+ when:
def path = algorithm.findPath(graph, source, target)
- then: 'we get the shortest path'
+ then:
path == shortest
- and: 'the distance calculated correctly'
+ and:
graph.getDistance(path) == time as double
where:
@@ -43,8 +37,8 @@ class BreadthFirstSearchSpec extends Specification {
}
def 'should find a route for complex graph'() {
- given: 'A complex graph'
- def graph = new Graph([
+ given:
+ def graph = Graph.of([
A: [B: 1],
B: [A: 1, D: 1],
C: [A: 1],
@@ -52,13 +46,13 @@ class BreadthFirstSearchSpec extends Specification {
E: [F: 1],
F: [D: 1, E: 1]])
- when: 'we use Breadth First Search algorithm to find a path'
+ when:
def path = algorithm.findPath(graph, source, target)
- then: 'we get the shortest path'
+ then:
path == shortest
- and: 'the distance calculated correctly'
+ and:
graph.getDistance(path) == time as double
where:
@@ -75,4 +69,25 @@ class BreadthFirstSearchSpec extends Specification {
time = shortest.size() - 1
}
+ def 'should thrown an exception for an empty graph'() {
+ given:
+ def graph = Graph.of([:])
+
+ when:
+ algorithm.findPath(graph, 'A', 'B')
+
+ then:
+ thrown NullPointerException
+ }
+
+ def "should return an empty path if can't find a route"() {
+ given: 'a simple graph with no edge between nodes'
+ def graph = Graph.of([A: [:], B: [:]])
+
+ when:
+ def path = algorithm.findPath(graph, 'A', 'B')
+
+ then:
+ path == []
+ }
}
diff --git a/src/test/groovy/graph/DijkstrasAlgorithmSpec.groovy b/algorithm/src/test/groovy/lv/id/jc/algorithm/graph/DijkstrasAlgorithmSpec.groovy
similarity index 63%
rename from src/test/groovy/graph/DijkstrasAlgorithmSpec.groovy
rename to algorithm/src/test/groovy/lv/id/jc/algorithm/graph/DijkstrasAlgorithmSpec.groovy
index 8780ea1..ca30c65 100644
--- a/src/test/groovy/graph/DijkstrasAlgorithmSpec.groovy
+++ b/algorithm/src/test/groovy/lv/id/jc/algorithm/graph/DijkstrasAlgorithmSpec.groovy
@@ -1,48 +1,44 @@
-package graph
+package lv.id.jc.algorithm.graph
-import lv.id.jc.algorithm.graph.DijkstrasAlgorithm
-import lv.id.jc.algorithm.graph.Graph
-import spock.lang.Narrative
-import spock.lang.See
-import spock.lang.Specification
-import spock.lang.Subject
-import spock.lang.Title
+import spock.lang.*
@Title("Dijkstra's Algorithm")
@See("https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm")
-@Narrative("Dijkstra's algorithm is an algorithm for finding the fastest paths between nodes in a graph")
+@Narrative("""
+Dijkstra's algorithm is an algorithm for finding
+the fastest paths between nodes in a graph
+""")
class DijkstrasAlgorithmSpec extends Specification {
+
@Subject
- def algorithm = new DijkstrasAlgorithm()
+ def algorithm = new DijkstrasAlgorithm()
def 'should find a route for a simple graph'() {
- given: 'A simple graph'
- def graph = new Graph([
+ given:
+ def graph = Graph.of([
A: [B: 7, C: 2],
B: [A: 3, C: 5],
C: [A: 1, B: 3]
])
- when: "we use Dijkstra's algorithm to find a path"
+ when:
def path = algorithm.findPath(graph, source, target)
+ def time = graph.getDistance(path)
- then: 'we get the fastest way'
- path == fastest
-
- and: 'the distance calculated correctly'
- graph.getDistance(path) == time as double
+ then:
+ path == fastestPath
+ time == fastestTime
where:
- source | target || time | fastest
- 'A' | 'A' || 0 | ['A']
- 'B' | 'B' || 0 | ['B']
- 'C' | 'C' || 0 | ['C']
- 'A' | 'B' || 5 | ['A', 'C', 'B']
+ source | target || fastestPath | fastestTime
+ 'A' | 'A' || ['A'] | 0
+ 'B' | 'A' || ['B', 'A'] | 3
+ 'A' | 'B' || ['A', 'C', 'B'] | 5
}
def 'should find a route for a medium graph'() {
- given: 'A medium graph'
- def graph = new Graph([
+ given:
+ def graph = Graph.of([
A: [B: 5],
B: [A: 5, C: 10],
C: [B: 20, D: 5],
@@ -50,13 +46,13 @@ class DijkstrasAlgorithmSpec extends Specification {
E: [B: 5]
])
- when: "we use Dijkstra's algorithm to find a path"
+ when:
def path = algorithm.findPath(graph, source, target)
- then: 'we get the fastest way'
+ then:
path == fastest
- and: 'the distance calculated correctly'
+ and:
graph.getDistance(path) == time as double
where:
@@ -70,8 +66,8 @@ class DijkstrasAlgorithmSpec extends Specification {
}
def 'should find a route for a complex graph'() {
- given: 'A complex graph'
- def graph = new Graph([
+ given:
+ def graph = Graph.of([
A: [B: 5, H: 2],
B: [A: 5, C: 7],
C: [B: 7, D: 3, G: 4],
@@ -82,13 +78,13 @@ class DijkstrasAlgorithmSpec extends Specification {
H: [G: 3]
])
- when: "we use Dijkstra's algorithm to find a path"
+ when:
def path = algorithm.findPath(graph, source, target)
- then: 'we get the fastest way'
+ then:
path == fastest
- and: 'the distance calculated correctly'
+ and:
graph.getDistance(path) == time as double
where:
@@ -107,4 +103,25 @@ class DijkstrasAlgorithmSpec extends Specification {
'D' | 'H' || 33 | ['D', 'E', 'F', 'G', 'C', 'B', 'A', 'H']
}
+ def 'should thrown NPE for an empty graph'() {
+ given:
+ def graph = Graph.of([:])
+
+ when:
+ algorithm.findPath(graph, 'A', 'B')
+
+ then:
+ thrown NullPointerException
+ }
+
+ def "should return an empty path if can't find a route"() {
+ given: 'a simple graph with no edge between nodes'
+ def graph = Graph.of([A: [:], B: [:]])
+
+ when:
+ def path = algorithm.findPath(graph, 'A', 'B')
+
+ then:
+ path == []
+ }
}
diff --git a/src/test/groovy/graph/GraphSpec.groovy b/algorithm/src/test/groovy/lv/id/jc/algorithm/graph/GraphSpec.groovy
similarity index 92%
rename from src/test/groovy/graph/GraphSpec.groovy
rename to algorithm/src/test/groovy/lv/id/jc/algorithm/graph/GraphSpec.groovy
index adb3358..c2b3a50 100644
--- a/src/test/groovy/graph/GraphSpec.groovy
+++ b/algorithm/src/test/groovy/lv/id/jc/algorithm/graph/GraphSpec.groovy
@@ -1,6 +1,5 @@
-package graph
+package lv.id.jc.algorithm.graph
-import lv.id.jc.algorithm.graph.Graph
import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Title
@@ -10,9 +9,8 @@ import spock.lang.Title
class GraphSpec extends Specification {
def "should return edges for a given node"() {
-
given: 'a simple graph with three nodes'
- def graph = new Graph([
+ def graph = Graph.of([
A: [B: 7, C: 2],
B: [A: 3, C: 5],
C: [A: 1, B: 3]
@@ -30,7 +28,7 @@ class GraphSpec extends Specification {
def 'should calculate distance for a path'() {
given: "a complex graph with eight nodes"
- def graph = new Graph([
+ def graph = Graph.of([
A: [B: 5, H: 2],
B: [A: 5, C: 7],
C: [B: 7, D: 3, G: 4],
@@ -58,7 +56,7 @@ class GraphSpec extends Specification {
def 'should be zero distance for an empty path'() {
given: 'any graph'
- def graph = new Graph(_ as Map)
+ def graph = Graph.of(_ as Map)
expect: 'the distance is zero for an empty path'
graph.getDistance([]) == 0
@@ -66,7 +64,7 @@ class GraphSpec extends Specification {
def 'should be zero distance for any one node path'() {
given: 'any graph'
- def graph = new Graph(_ as Map)
+ def graph = Graph.of(_ as Map)
expect: 'the zero distance for any one-node path'
graph.getDistance(oneNodePath) == 0
@@ -79,7 +77,7 @@ class GraphSpec extends Specification {
def 'should throw NPE for incorrect path'() {
given: "a medium graph with five nodes"
- def graph = new Graph([
+ def graph = Graph.of([
A: [B: 5],
B: [A: 5, C: 10],
C: [B: 20, D: 5],
diff --git a/src/test/groovy/graph/SearchAlgorithmSpec.groovy b/algorithm/src/test/groovy/lv/id/jc/algorithm/graph/SearchAlgorithmsSpec.groovy
similarity index 69%
rename from src/test/groovy/graph/SearchAlgorithmSpec.groovy
rename to algorithm/src/test/groovy/lv/id/jc/algorithm/graph/SearchAlgorithmsSpec.groovy
index 075d625..4182a97 100644
--- a/src/test/groovy/graph/SearchAlgorithmSpec.groovy
+++ b/algorithm/src/test/groovy/lv/id/jc/algorithm/graph/SearchAlgorithmsSpec.groovy
@@ -1,23 +1,21 @@
-package graph
+package lv.id.jc.algorithm.graph
-import lv.id.jc.algorithm.graph.BreadthFirstSearch
-import lv.id.jc.algorithm.graph.DijkstrasAlgorithm
-import lv.id.jc.algorithm.graph.Graph
-import spock.lang.Specification
-import spock.lang.Subject
-import spock.lang.Title
+import spock.lang.*
+@Issue("30")
@Title("Comparison of two algorithms")
-class SearchAlgorithmSpec extends Specification {
+@See("https://en.wikipedia.org/wiki/Breadth-first_search")
+@See("https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm")
+class SearchAlgorithmsSpec extends Specification {
@Subject
- def bfsAlgorithm = new BreadthFirstSearch()
+ def bfsAlgorithm = new BreadthFirstSearch()
@Subject
- def dijkstras = new DijkstrasAlgorithm()
+ def dijkstras = new DijkstrasAlgorithm()
def 'should find a route for a complex graph'() {
- given: 'A complex graph sample'
- def graph = new Graph([
+ given: "a complex graph with eight nodes"
+ def graph = Graph.of([
A: [B: 5, H: 2],
B: [A: 5, C: 7],
C: [B: 7, D: 3, G: 4],
@@ -28,10 +26,10 @@ class SearchAlgorithmSpec extends Specification {
H: [G: 3]
])
- when: 'we use Breadth First Search algorithm for the first route'
+ when: "we use Breadth First Search algorithm for the first route"
def routeOne = bfsAlgorithm.findPath(graph, source, target)
- and: 'we use Dijkstras algorithm for the second route'
+ and: "we use Dijkstra's algorithm for the second route"
def routeTwo = dijkstras.findPath(graph, source, target)
then: "the first route is the shortest"
@@ -41,11 +39,11 @@ class SearchAlgorithmSpec extends Specification {
routeTwo == fastest
and: 'the distance calculated correctly'
- graph.getDistance(routeOne) == d1 as double
- graph.getDistance(routeTwo) == d2 as double
+ graph.getDistance(routeOne) == t1 as double
+ graph.getDistance(routeTwo) == t2 as double
where:
- source | target || d1 | shortest | d2 | fastest
+ source | target || t1 | shortest | t2 | fastest
'A' | 'A' || 0 | ['A'] | 0 | ['A']
'B' | 'B' || 0 | ['B'] | 0 | ['B']
'A' | 'B' || 5 | ['A', 'B'] | 5 | ['A', 'B']
diff --git a/algorithm/src/test/java/lv/id/jc/algorithm/graph/DijkstrasAlgorithmTest.java b/algorithm/src/test/java/lv/id/jc/algorithm/graph/DijkstrasAlgorithmTest.java
new file mode 100644
index 0000000..6bf0c3e
--- /dev/null
+++ b/algorithm/src/test/java/lv/id/jc/algorithm/graph/DijkstrasAlgorithmTest.java
@@ -0,0 +1,56 @@
+package lv.id.jc.algorithm.graph;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+// Java 8 + JUnit 5 + Parameterized Test
+class DijkstrasAlgorithmTest {
+
+ // Subject under test
+ SearchAlgorithm algorithm = new DijkstrasAlgorithm<>();
+
+ // Sample data
+ private static Stream providePathTime() {
+ return Stream.of(
+ Arguments.of("A", "A", Collections.singletonList("A"), 0),
+ Arguments.of("B", "A", Arrays.asList("B", "A"), 3),
+ Arguments.of("A", "B", Arrays.asList("A", "C", "B"), 5)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("providePathTime")
+ @DisplayName("should find a route for a simple graph")
+ void testRouteForSimpleGraph(String source, String target, List fastestPath, double fastestTime) {
+ // given
+ Map fromA = new HashMap<>();
+ fromA.put("B", 7);
+ fromA.put("C", 2);
+ Map fromB = new HashMap<>();
+ fromB.put("A", 3);
+ fromB.put("C", 5);
+ Map fromC = new HashMap<>();
+ fromC.put("A", 1);
+ fromC.put("B", 3);
+ Map> nodes = new HashMap<>();
+ nodes.put("A", fromA);
+ nodes.put("B", fromB);
+ nodes.put("C", fromC);
+ Graph graph = Graph.of(nodes);
+
+ // when
+ List path = algorithm.findPath(graph, source, target);
+ double time = graph.getDistance(path);
+
+ // then
+ assertEquals(fastestPath, path);
+ assertEquals(fastestTime, time);
+ }
+}
\ No newline at end of file
diff --git a/algorithm/src/test/java/lv/id/jc/algorithm/graph/SearchAlgorithmsTest.java b/algorithm/src/test/java/lv/id/jc/algorithm/graph/SearchAlgorithmsTest.java
new file mode 100644
index 0000000..92f6bd0
--- /dev/null
+++ b/algorithm/src/test/java/lv/id/jc/algorithm/graph/SearchAlgorithmsTest.java
@@ -0,0 +1,147 @@
+package lv.id.jc.algorithm.graph;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SearchAlgorithmsTest {
+
+ private SearchAlgorithm bfsAlgorithm;
+ private SearchAlgorithm dijkstras;
+ private Graph graph;
+
+ @BeforeEach
+ void setUp() {
+ LinkedHashMap fromA = new LinkedHashMap<>();
+ fromA.put("B", 5);
+ fromA.put("H", 2);
+ LinkedHashMap fromB = new LinkedHashMap<>();
+ fromB.put("A", 5);
+ fromB.put("C", 7);
+ LinkedHashMap fromC = new LinkedHashMap<>();
+ fromC.put("B", 7);
+ fromC.put("D", 3);
+ fromC.put("G", 4);
+ LinkedHashMap fromD = new LinkedHashMap<>();
+ fromD.put("C", 20);
+ fromD.put("E", 4);
+ LinkedHashMap fromE = new LinkedHashMap<>();
+ fromE.put("F", 5);
+ LinkedHashMap fromF = new LinkedHashMap<>();
+ fromF.put("G", 6);
+ LinkedHashMap fromG = new LinkedHashMap<>();
+ fromG.put("C", 4);
+ LinkedHashMap fromH = new LinkedHashMap<>();
+ fromH.put("G", 3);
+
+ LinkedHashMap> nodes = new LinkedHashMap<>();
+ nodes.put("A", fromA);
+ nodes.put("B", fromB);
+ nodes.put("C", fromC);
+ nodes.put("D", fromD);
+ nodes.put("E", fromE);
+ nodes.put("F", fromF);
+ nodes.put("G", fromG);
+ nodes.put("H", fromH);
+
+ graph = Graph.of(nodes);
+ dijkstras = new DijkstrasAlgorithm<>();
+ bfsAlgorithm = new BreadthFirstSearch<>();
+ }
+
+ @AfterEach
+ void tearDown() {
+ graph = null;
+ dijkstras = null;
+ bfsAlgorithm = null;
+ }
+
+ @Test
+ void testFindPathAA() {
+ // Java 8
+
+ // given
+ String source = "A";
+ String target = "A";
+
+ List shortest = new ArrayList<>();
+ shortest.add("A");
+
+ List fastest = new ArrayList<>();
+ fastest.add("A");
+
+ // when
+ List routeOne = bfsAlgorithm.findPath(graph, source, target);
+ List routeTwo = dijkstras.findPath(graph, source, target);
+
+ // then
+ assertEquals(shortest, routeOne);
+ assertEquals(fastest, routeTwo);
+
+ assertEquals(0, graph.getDistance(shortest));
+ assertEquals(0, graph.getDistance(fastest));
+ }
+
+ @Test
+ void testFindPathBB() {
+ // Java 11
+
+ var source = "B";
+ var target = "B";
+
+ var shortest = List.of("B");
+ var fastest = List.of("B");
+
+ var routeOne = bfsAlgorithm.findPath(graph, source, target);
+ var routeTwo = dijkstras.findPath(graph, source, target);
+
+ assertEquals(shortest, routeOne);
+ assertEquals(fastest, routeTwo);
+
+ assertEquals(0, graph.getDistance(shortest));
+ assertEquals(0, graph.getDistance(fastest));
+ }
+
+ @Test
+ void testFindPathCD() {
+ var source = "C";
+ var target = "D";
+
+ var shortest = List.of("C", "D");
+ var fastest = List.of("C", "D");
+
+ var routeOne = bfsAlgorithm.findPath(graph, source, target);
+ var routeTwo = dijkstras.findPath(graph, source, target);
+
+ assertEquals(shortest, routeOne);
+ assertEquals(fastest, routeTwo);
+
+ assertEquals(3, graph.getDistance(shortest));
+ assertEquals(3, graph.getDistance(fastest));
+ }
+
+ @Test
+ void testFindPathDC() {
+ var source = "D";
+ var target = "C";
+ var shortest = List.of("D", "C");
+ var fastest = List.of("D", "E", "F", "G", "C");
+
+ var routeOne = bfsAlgorithm.findPath(graph, source, target);
+ var routeTwo = dijkstras.findPath(graph, source, target);
+
+ assertEquals(shortest, routeOne);
+ assertEquals(fastest, routeTwo);
+
+ assertEquals(20, graph.getDistance(shortest));
+ assertEquals(19, graph.getDistance(fastest));
+ }
+
+}
\ No newline at end of file
diff --git a/algorithm/src/test/resources/SpockConfig.groovy b/algorithm/src/test/resources/SpockConfig.groovy
new file mode 100644
index 0000000..9d1c0df
--- /dev/null
+++ b/algorithm/src/test/resources/SpockConfig.groovy
@@ -0,0 +1,14 @@
+report {
+ issueNamePrefix 'Issue #'
+ issueUrlPrefix 'https://github.com/rabestro/algorithms/issues/'
+}
+
+spockReports {
+ set(['com.athaydes.spockframework.report.showCodeBlocks' : true,
+ 'com.athaydes.spockframework.report.outputDir' : '../docs/spock-reports',
+ 'com.athaydes.spockframework.report.projectName' : 'Graph search algorithms',
+ 'com.athaydes.spockframework.report.projectVersion' : 1.1,
+ 'com.athaydes.spockframework.report.internal.HtmlReportCreator.enabled': true,
+ 'com.athaydes.spockframework.report.IReportCreator' : 'com.athaydes.spockframework.report.internal.HtmlReportCreator'
+ ])
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index 29cedc6..0000000
--- a/build.gradle
+++ /dev/null
@@ -1,28 +0,0 @@
-plugins {
- id 'groovy'
- id 'java'
-}
-
-group 'lv.id.jc'
-version '1.0'
-
-repositories {
- mavenCentral()
-}
-
-dependencies {
- implementation 'org.codehaus.groovy:groovy-all:3.0.9'
-
- testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0'
- testImplementation 'org.codehaus.groovy:groovy-all:3.0.9'
-
- testRuntimeClasspath( "com.athaydes:spock-reports:2.1.1-groovy-3.0" ) {
-// transitive = false // this avoids affecting your version of Groovy/Spock
- }
- testImplementation 'org.slf4j:slf4j-api:1.7.32'
- testRuntimeClasspath 'org.slf4j:slf4j-simple:1.7.32'
-}
-
-test {
- useJUnitPlatform()
-}
diff --git a/docs/LICENSE b/docs/LICENSE
new file mode 100644
index 0000000..244293b
--- /dev/null
+++ b/docs/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Jegors Čemisovs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/docs/_config.yml b/docs/_config.yml
deleted file mode 100644
index c419263..0000000
--- a/docs/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-theme: jekyll-theme-cayman
\ No newline at end of file
diff --git a/docs/api/allclasses-index.html b/docs/api/allclasses-index.html
index 5445343..55a7bac 100644
--- a/docs/api/allclasses-index.html
+++ b/docs/api/allclasses-index.html
@@ -1,11 +1,11 @@
-
+
All Classes and Interfaces
-
+
@@ -32,7 +32,8 @@
The following sections describe the different kinds of pages in this collection.
+
+
Module
+
Each module has a page that contains a list of its packages, dependencies on other modules, and services, with a summary for each. These pages may contain the following categories:
+
+
Packages
+
Modules
+
Services
+
+
Package
Each package has a page that contains a list of its classes and interfaces, with a summary for each. These pages may contain the following categories: