Skip to content

Commit

Permalink
addition of specs and of example
Browse files Browse the repository at this point in the history
  • Loading branch information
jvdp1 committed Jul 9, 2024
1 parent 962bcc4 commit 3cf4711
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 6 deletions.
105 changes: 102 additions & 3 deletions doc/specs/stdlib_sorting.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ data:
* `ORD_SORT` is intended to sort simple arrays of intrinsic data
that have significant sections that were partially ordered before
the sort;
* `SORT_ADJ` is based on `ORD_SORT`, but in addition to sorting the
input array, it returns a related array re-ordered in the
same way;
* `SORT_INDEX` is based on `ORD_SORT`, but in addition to sorting the
input array, it returns indices that map the original array to its
sorted version. This enables related arrays to be re-ordered in the
Expand All @@ -60,10 +63,10 @@ data:
The Fortran Standard Library is distributed under the MIT
License. However components of the library may be based on code with
additional licensing restrictions. In particular `ORD_SORT`,
`SORT_INDEX`, and `SORT` are translations of codes with their
`SORT_ADJ`, `SORT_INDEX`, and `SORT` are translations of codes with their
own distribution restrictions.

The `ORD_SORT` and `SORT_INDEX` subroutines are essentially
The `ORD_SORT`, `SORT_ADJ` and `SORT_INDEX` subroutines are essentially
translations to Fortran 2008 of the `"Rust" sort` of the Rust Language
distributed as part of
[`slice.rs`](https://github.com/rust-lang/rust/blob/90eb44a5897c39e3dff9c7e48e3973671dcd9496/src/liballoc/slice.rs).
Expand Down Expand Up @@ -140,6 +143,24 @@ argument or allocated internally on the stack.
Arrays can be also sorted in a decreasing order by providing the argument `reverse
= .true.`.

#### The `SORT_ADJ` subroutine

The `SORT` and `ORD_SORT` subroutines can sort rank 1 isolated
arrays of intrinsic types, but do nothing for the coordinated sorting
of related data, e.g., a related rank 1 array. Therefore the module
provides a subroutine, `SORT_ADJ`, that re-order such a rank 1 array
in the same way as the input array based on the `ORD_SORT` algorithm,
in addition to sorting the input array.

The logic of `SORT_ADJ` parallels that of `ORD_SORT`, with
additional housekeeping to keep the associated array consistent with
the sorted positions of the input array. Because of this additional
housekeeping it has slower runtime performance than `ORD_SORT`.
`SORT_ADJ` requires the use of two "scratch" arrays, that may be
provided as optional `work` and `iwork` arguments or allocated
internally on the stack.


#### The `SORT_INDEX` subroutine

The `SORT` and `ORD_SORT` subroutines can sort rank 1 isolated
Expand Down Expand Up @@ -198,7 +219,7 @@ factor of six. Still, even when it shows enhanced performance, its
performance on partially sorted data is typically an order of
magnitude slower than `ORD_SORT`. Its memory requirements are also
low, being of order O(Ln(N)), while the memory requirements of
`ORD_SORT` and `SORT_INDEX` are of order O(N).
`ORD_SORT`, `SORT_ADJ` and `SORT_INDEX` are of order O(N).

#### The `RADIX_SORT` subroutine

Expand Down Expand Up @@ -385,6 +406,84 @@ element of `array` is a `NaN`.
{!example/sorting/example_radix_sort.f90!}
```

#### `sort_adj` - sorts an associated array in the same way as the input array, while also sorting the array.

##### Status

Experimental

##### Description

Returns the input `array` sorted in the direction requested while
retaining order stability, and an associated array whose elements are
sorted in the same way as the input `array`.

##### Syntax

`call ` [[stdlib_sorting(module):sort_adj(interface)]] `( array, index[, work, iwork, reverse ] )`

##### Class

Generic subroutine.

##### Arguments

`array`: shall be a rank one array of any of the types:
`integer(int8)`, `integer(int16)`, `integer(int32)`, `integer(int64)`,
`real(sp)`, `real(dp)`, `real(qp)`, `character(*)`, `type(string_type)`,
`type(bitset_64)`, or `type(bitset_large)`.
It is an `intent(inout)` argument. On input it
will be an array whose sorting indices are to be determined. On return
it will be the sorted array.

`index`: shall be a rank one `integer` or `real` array of
the size of `array`. It is an `intent(inout)` argument. On return it
shall have values that are the indices needed to sort the original
array in the desired direction.

`work` (optional): shall be a rank one array of any of the same type as
`array`, and shall have at least `size(array)/2` elements. It is an
`intent(out)` argument. It is intended to be used as "scratch"
memory for internal record keeping. If associated with an array in
static storage, its use can significantly reduce the stack memory
requirements for the code. Its contents on return are undefined.

`iwork` (optional): shall be a rank one integer array of the same kind
of the array `index`, and shall have at least `size(array)/2` elements. It
is an `intent(out)` argument. It is intended to be used as "scratch"
memory for internal record keeping. If associated with an array in
static storage, its use can significantly reduce the stack memory
requirements for the code. Its contents on return are undefined.

`reverse` (optional): shall be a scalar of type default logical. It
is an `intent(in)` argument. If present with a value of `.true.` then
`array` will be sorted in order of non-increasing values in stable
order. Otherwise `array` will be sorted in order of non-decreasing
values in stable order.

##### Notes

`SORT_ADJ` implements the hybrid sorting algorithm of `ORD_SORT`,
keeping the values of `index` consistent with the elements of `array`
as it is sorted. As a `merge sort` based algorithm, it is a stable
sorting comparison algorithm. The optional `work` and `iwork` arrays
replace "scratch" memory that would otherwise be allocated on the
stack. If `array` is of any kind of `REAL` the order of the elements in
`index` and `array` on return are undefined if any element of `array`
is a `NaN`. Sorting of `CHARACTER(*)` and `STRING_TYPE` arrays are
based on the operator `>`, and not on the function `LGT`.

It should be emphasized that the order of `array` will typically be
different on return

##### Examples

Sorting a rank one array with `sort_adj`:

```Fortran
{!example/sorting/example_sort_adj.f90!}
```

#### `sort_index` - creates an array of sorting indices for an input array, while also sorting the array.

##### Status
Expand Down
1 change: 1 addition & 0 deletions example/sorting/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
ADD_EXAMPLE(ord_sort)
ADD_EXAMPLE(sort)
ADD_EXAMPLE(sort_adj)
ADD_EXAMPLE(sort_index)
ADD_EXAMPLE(radix_sort)
ADD_EXAMPLE(sort_bitset)
15 changes: 15 additions & 0 deletions example/sorting/example_sort_adj.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
program example_sort_adj
use stdlib_sorting, only: sort_adj
implicit none
integer, allocatable :: array(:)
real, allocatable :: adj(:)

array = [5, 4, 3, 1, 10, 4, 9]
allocate(adj, source=real(array))

call sort_adj(array, adj)

print *, array !print [1, 3, 4, 4, 5, 9, 10]
print *, adj !print [1., 3., 4., 4., 5., 9., 10.]

end program example_sort_adj
70 changes: 70 additions & 0 deletions src/stdlib_sorting.fypp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,76 @@ module stdlib_sorting
!! ! Sort the random data
!! call radix_sort( array )
!! ...
!!```
public sort_adj
!! Version: experimental
!!
!! The generic subroutine implementing the `SORT_ADJ` algorithm to
!! return an index array whose elements are sorted in the same order
!! as the input array in the
!! desired direction. It is primarily intended to be used to sort a
!! rank 1 `integer` or `real` array based on the values of a component of the array.
!! Its use has the syntax:
!!
!! call sort_adj( array, index[, work, iwork, reverse ] )
!!
!! with the arguments:
!!
!! * array: the rank 1 array to be sorted. It is an `intent(inout)`
!! argument of any of the types `integer(int8)`, `integer(int16)`,
!! `integer(int32)`, `integer(int64)`, `real(real32)`, `real(real64)`,
!! `real(real128)`, `character(*)`, `type(string_type)`,
!! `type(bitset_64)`, `type(bitset_large)`. If both the
!! type of `array` is real and at least one of the elements is a `NaN`,
!! then the ordering of the `array` and `index` results is undefined.
!! Otherwise it is defined to be as specified by reverse.
!!
!! * index: a rank 1 `integer` or `real` array. It is an `intent(inout)`
!! argument of the type `integer(int_index)`. Its size shall be the
!! same as `array`. On return, its elements are sorted in the same order
!! as the input `array` in the direction specified by `reverse`.
!!
!! * work (optional): shall be a rank 1 array of the same type as
!! `array`, and shall have at least `size(array)/2` elements. It is an
!! `intent(out)` argument to be used as "scratch" memory
!! for internal record keeping. If associated with an array in static
!! storage, its use can significantly reduce the stack memory requirements
!! for the code. Its value on return is undefined.
!!
!! * iwork (optional): shall be a rank 1 integer array of the same type as `index`,
!! and shall have at least `size(array)/2` elements. It is an
!! `intent(out)` argument to be used as "scratch" memory
!! for internal record keeping. If associated with an array in static
!! storage, its use can significantly reduce the stack memory requirements
!! for the code. Its value on return is undefined.
!!
!! * `reverse` (optional): shall be a scalar of type default logical. It
!! is an `intent(in)` argument. If present with a value of `.true.` then
!! `array` will be sorted in order of non-increasing values in stable
!! order. Otherwise `array` will be sorted in order of non-decreasing
!! values in stable order.
!!
!!#### Examples
!!
!! Sorting a related rank one array:
!!
!!```Fortran
!!program example_sort_adj
!! use stdlib_sorting, only: sort_adj
!! implicit none
!! integer, allocatable :: array(:)
!! real, allocatable :: adj(:)
!!
!! array = [5, 4, 3, 1, 10, 4, 9]
!! allocate(adj, source=real(array))
!!
!! call sort_adj(array, adj)
!!
!! print *, array !print [1, 3, 4, 4, 5, 9, 10]
!! print *, adj !print [1., 3., 4., 4., 5., 9., 10.]
!!
!!end program example_sort_adj
!!```
public sort_index
Expand Down
2 changes: 2 additions & 0 deletions src/stdlib_sorting_ord_sort.fypp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ contains
integer :: stat
array_size = size( array, kind=int_index )
! If necessary allocate buffers to serve as scratch memory.
if ( present(work) ) then
if ( size(work, kind=int_index) < array_size/2 ) then
error stop "${name1}$_${sname}$_ord_sort: work array is too small."
Expand Down
8 changes: 5 additions & 3 deletions src/stdlib_sorting_sort_adj.fypp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
!! TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
!! SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
!!
!! The generic subroutine, `SORT_INDEX`, is substantially a translation to
!! The generic subroutine, `SORT_ADJ`, is substantially a translation to
!! Fortran 2008 of the `"Rust" sort` sorting routines in
!! [`slice.rs`](https://github.com/rust-lang/rust/blob/90eb44a5897c39e3dff9c7e48e3973671dcd9496/src/liballoc/slice.rs)
!! The `rust sort` implementation is distributed with the header:
Expand Down Expand Up @@ -95,7 +95,6 @@ contains
! estimation of the optimal `run size` as suggested in Tim Peters'
! original `listsort.txt`, and the optional `work` and `iwork` arrays to be
! used as scratch memory.
${t1}$, intent(inout) :: array(0:)
${ti}$, intent(inout) :: index(0:)
${t3}$, intent(out), optional :: work(0:)
Expand All @@ -104,7 +103,8 @@ contains
${t2}$, allocatable :: buf(:)
${ti}$, allocatable :: ibuf(:)
integer(int_index) :: array_size, i, stat
integer(int_index) :: array_size, i
integer(int_index) :: stat
array_size = size(array, kind=int_index)
Expand Down Expand Up @@ -136,6 +136,7 @@ contains
call merge_sort( array, index, work, ibuf )
end if
else
! Allocate a buffer to use as scratch memory.
#:if t1[0:4] == "char"
allocate( ${t3}$ :: buf(0:array_size/2-1), &
stat=stat )
Expand Down Expand Up @@ -495,3 +496,4 @@ contains
#:endfor

end submodule stdlib_sorting_sort_adj

0 comments on commit 3cf4711

Please sign in to comment.