diff --git a/.github/workflows/check_code_format.yml b/.github/workflows/check_code_format.yml new file mode 100644 index 0000000..dc11f66 --- /dev/null +++ b/.github/workflows/check_code_format.yml @@ -0,0 +1,40 @@ +--- +name: check_code_format + +'on': + workflow_dispatch: + push: + branches: + - main + pull_request: + +jobs: + check_format_code: + name: check format code + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install fprettify + run: pip install fprettify + + - name: Display fprettify version + run: fprettify --version + + - name: Format code + run: | + git clean -f -x -d + fprettify --indent 4 --recursive . + + - name: Fail if needs reformatting + run: | + if [[ $(git status --porcelain) ]]; then + echo "please reformat/fprettify these files:" + git status --porcelain=v1 + exit 1 + fi + + - name: Exact diff of needed reformatting + if: failure() + run: git diff +... diff --git a/CMakeLists.txt b/CMakeLists.txt index ef62b3a..55a9b27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,16 @@ cmake_minimum_required(VERSION 3.16) project(FortranProject LANGUAGES Fortran) -add_compile_options(-Wall -Wextra -Wpedantic) +add_compile_options( + -Wall + -Wextra + -Wpedantic + -Waliasing + -Wconversion-extra + -Wimplicit-interface + -Wimplicit-procedure + -Wsurprising + -Werror) function(add_fortran_sources DIR SOURCES) file(GLOB_RECURSE NEW_SOURCES "${DIR}/*.f90") diff --git a/DIRECTORY.md b/DIRECTORY.md index 9db0263..296bcaf 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -12,6 +12,7 @@ ## Searches * [linear_search](/modules/searches/linear_search.f90) * [recursive_linear_search](/modules/searches/recursive_linear_search.f90) +* [ternary_search](/modules/searches/ternary_search_module.f90) ## Sorts * [bubble_sort](/modules/sorts/bubble_sort.f90) * [recursive_bubble_sort](/modules/sorts/recursive_bubble_sort.f90) diff --git a/README.md b/README.md index c9d2e21..85c152c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@
- GitHub Workflow Status + GitHub Workflow Status pre-commit diff --git a/examples/searches/example_ternary_search_array_based.f90 b/examples/searches/example_ternary_search_array_based.f90 new file mode 100644 index 0000000..e53ee3b --- /dev/null +++ b/examples/searches/example_ternary_search_array_based.f90 @@ -0,0 +1,20 @@ +! Example Program: Array-based Ternary Search +! This program demonstrates how to use the array-based ternary search algorithm +! implemented in the `ternary_search` module to find a target element in a sorted array. + +program example_ternary_search_array + use ternary_search + implicit none + integer :: result ! Holds the index of the found target + integer, dimension(10) :: arr = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] ! Sorted Test Array + integer :: target ! Target value to search for + + target = 17 + result = ternary_search_array(arr, target, 1, size(arr)) + + if (result /= -1) then + print *, "Target found at index:", result + else + print *, "Target not found." + end if +end program example_ternary_search_array diff --git a/examples/searches/example_ternary_search_function_based.f90 b/examples/searches/example_ternary_search_function_based.f90 new file mode 100644 index 0000000..bfa60a2 --- /dev/null +++ b/examples/searches/example_ternary_search_function_based.f90 @@ -0,0 +1,74 @@ +!> Example Program: Function-based Ternary Search for Minimum and Maximum +!! +!! Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +!! in Pull Request: #24 +!! https://github.com/TheAlgorithms/Fortran/pull/24 +!! +!! Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +!! addressing bugs/corrections to this file. Thank you! +!! +!! This program demonstrates how to use the function-based ternary search algorithm +!! from the `ternary_search` module to find the minimum and maximum of unimodal functions. + +program ternary_search_function_based + use ternary_search + implicit none + + ! Define the variables + integer, parameter :: dp = kind(0.0d0) ! Define double precision kind + real(8) :: result_min, result_max ! Results for minimum and maximum values + real(8) :: min_point, max_point ! Points where minimum and maximum occur + real(8) :: left, right, tol ! Left and right bounds, and tolerance + + interface + ! Function with a minimum (example function - defined externally) + real(8) function f_min(x) + real(8), intent(in) :: x + end function f_min + + ! Function with a maximum (example function - defined externally) + real(8) function f_max(x) + real(8), intent(in) :: x + end function f_max + end interface + + ! The boundary values can vary depending on the problem context. + ! In this example, they are chosen arbitrarily. + left = 0.0d0 + right = 10.0d0 + + ! The tolerance value defines how close the left and right bounds must be for the search to terminate. + tol = 1.0e-6_dp + + ! Call the ternary search to find the minimum point of f_min + min_point = ternary_search_minimum(f_min, left, right, tol) + result_min = f_min(min_point) + + ! Call the ternary search to find the maximum point of f_max + max_point = ternary_search_maximum(f_max, left, right, tol) + result_max = f_max(max_point) + + print *, "Minimum of the function f_min is at x =", min_point, "with value =", result_min + print *, "Maximum of the function f_max is at x =", max_point, "with value =", result_max + +end program ternary_search_function_based + +! Define the unimodal function f_min with a minimum near x = 5.0 +! The quadratic term (x - 5.0)**2 defines a parabola that is concave upward with a minimum at x = 5.0 +! and values increasing as x moves away from 5. +! The cosine term introduces oscillations, affecting the exact location of the minimum slightly away from 5.0. + +real(8) function f_min(x) + real(8), intent(in) :: x + f_min = (x - 5.0d0)**2 + cos(x) ! Example of a quadratic function with a cosine oscillation +end function f_min + +! Define the unimodal function f_max with a maximum near x = 5.0 +! The quadratic term -(x - 5.0)**2 defines a parabola that is concave downward with a maximum at x = 5.0 +! and values decreasing as x moves away from 5. +! The cosine term introduces oscillations, affecting the exact location of the maximum slightly away from 5.0. + +real(8) function f_max(x) + real(8), intent(in) :: x + f_max = -(x - 5.0d0)**2 + cos(x) ! Example of a quadratic function with a cosine oscillation +end function f_max diff --git a/modules/searches/ternary_search_module.f90 b/modules/searches/ternary_search_module.f90 new file mode 100644 index 0000000..9cd1b67 --- /dev/null +++ b/modules/searches/ternary_search_module.f90 @@ -0,0 +1,160 @@ +!> Ternary Search Algorithm Module +!! +!! This module implements two types of ternary search algorithm: array-based and function-based. +!! +!! Created by: Ramy-Badr-Ahmed (https://github.com/Ramy-Badr-Ahmed) +!! in Pull Request: #24 +!! https://github.com/TheAlgorithms/Fortran/pull/24 +!! +!! Please mention me (@Ramy-Badr-Ahmed) in any issue or pull request +!! addressing bugs/corrections to this file. Thank you! +!! +!! The array-based ternary search is used to find a target element within a sorted array, while the function-based +!! approach is used to find the minimum or maximum of a unimodal function. +!! +!! Array-based ternary search: +!! - Given a sorted array and a target, it splits the array into three parts and recursively searches for the target. +!! +!! Function-based ternary search: +!! - Used for unimodal functions, which have a single peak (maximum) or valley (minimum). +!! This method divides the function’s search space into thirds to converge on the minimum or maximum. +!! + +module ternary_search + implicit none +contains + + !> Array-based ternary search algorithm + !! This recursive function searches for the target value in a sorted array. + !! + !! Input: + !! - arr: The sorted array to search within. + !! - target: The value to search for. + !! - left, right: The range of indices within which to search. + !! + !! Output: + !! - The index of the target element if found, otherwise -1. + recursive integer function ternary_search_array(arr, target, left, right) result(result_index) + implicit none + integer, intent(in) :: arr(:) ! Array to search within + integer, intent(in) :: target ! Target value to search for + integer, intent(in) :: left, right ! Left and right indices + integer :: mid1, mid2 ! Midpoints + integer :: new_left, new_right ! Temporary indices + + ! Base case: if the range is invalid, return -1 (not found) + if (right < left) then + result_index = -1 + return + end if + + ! Divide array into three parts + mid1 = left + (right - left)/3 + mid2 = right - (right - left)/3 + + ! Check if the target is at mid1 or mid2 + if (arr(mid1) == target) then + result_index = mid1 + return + else if (arr(mid2) == target) then + result_index = mid2 + return + end if + + ! Recursive search in the appropriate third of the array + if (target < arr(mid1)) then + new_right = mid1 - 1 + result_index = ternary_search_array(arr, target, left, new_right) + else if (target > arr(mid2)) then + new_left = mid2 + 1 + result_index = ternary_search_array(arr, target, new_left, right) + else + new_left = mid1 + 1 + new_right = mid2 - 1 + result_index = ternary_search_array(arr, target, new_left, new_right) + end if + end function ternary_search_array + + !> Function-based ternary search to find the minimum of a unimodal function + !! This function finds the minimum point of a unimodal function using the ternary search algorithm. + !! + !! Input: + !! - f: The unimodal function to search. + !! - left, right: The range within which to search for the minimum. + !! - tol: The tolerance to determine convergence. + !! + !! Output: + !! - The point at which the function achieves its minimum value. + recursive real(8) function ternary_search_minimum(f, left, right, tol) result(minimum) + implicit none + interface + real(8) function f(x) + real(8), intent(in) :: x + end function f + end interface + real(8), intent(in) :: left, right, tol + real(8) :: mid1, mid2 + real(8) :: l, r + + l = left + r = right + + ! Termination condition based on tolerance + do while (r - l > tol) + mid1 = l + (r - l)/3.0d0 + mid2 = r - (r - l)/3.0d0 + + ! Compare function values at midpoints + if (f(mid1) < f(mid2)) then + r = mid2 + else + l = mid1 + end if + end do + + ! The minimum point is approximately at the midpoint + minimum = (l + r)/2.0d0 + end function ternary_search_minimum + + !> Function-based ternary search to find the maximum of a unimodal function + !! This function finds the maximum point of a unimodal function using the ternary search algorithm. + !! + !! Input: + !! - f: The unimodal function to search. + !! - left, right: The range within which to search for the maximum. + !! - tol: The tolerance to determine convergence. + !! + !! Output: + !! - The point at which the function achieves its maximum value. + recursive real(8) function ternary_search_maximum(f, left, right, tol) result(maximum) + implicit none + interface + real(8) function f(x) + real(8), intent(in) :: x + end function f + end interface + real(8), intent(in) :: left, right, tol + real(8) :: mid1, mid2 + real(8) :: l, r + + l = left + r = right + + ! Termination condition based on tolerance + do while (r - l > tol) + mid1 = l + (r - l)/3.0d0 + mid2 = r - (r - l)/3.0d0 + + ! Compare function values at midpoints + if (f(mid1) > f(mid2)) then + r = mid2 + else + l = mid1 + end if + end do + + ! The maximum point is approximately at the midpoint + maximum = (l + r)/2.0d0 + end function ternary_search_maximum + +end module ternary_search