Skip to content

Implement unique function to stdlib_sorting - #1200

Open
Mahmood-Sinan wants to merge 23 commits into
fortran-lang:masterfrom
Mahmood-Sinan:unique_function
Open

Mahmood-Sinan wants to merge 23 commits into
fortran-lang:masterfrom
Mahmood-Sinan:unique_function

Conversation

@Mahmood-Sinan

Copy link
Copy Markdown
Contributor

This PR adds a generic unique function to stdlib_sorting for extracting the distinct elements of rank-1 arrays.

  • Support integer and real arrays.
  • Support two modes of operation:
    • sorted output (sorted_output = .true.)
    • order of first occurrence (sorted_output = .false.)
  • Add an optional tolerance argument for real arrays when sorted_output = .true. to support approximate equality comparisons.

Limitations

@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 68.20%. Comparing base (9a15c77) to head (892e529).
⚠️ Report is 32 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1200   +/-   ##
=======================================
  Coverage   68.20%   68.20%           
=======================================
  Files          19       19           
  Lines        2378     2378           
=======================================
  Hits         1622     1622           
  Misses        756      756           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jalvesz
jalvesz requested a review from Copilot July 27, 2026 18:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (11)

src/sorting/stdlib_sorting.fypp:536

  • This example uses b = unique(...) as if unique were a function, but unique is a subroutine in this module. The example as written won’t compile.
!!    b = unique(a, .true.)

src/sorting/stdlib_sorting.fypp:489

  • The module-level documentation describes unique as a function (output = unique(...)), but the public API implemented below is a generic subroutine (call unique(array, sorted_output, output[, tolerance])). This mismatch will mislead users and contradicts the rest of the module’s procedure docs.
!! The generic function implementing the `UNIQUE` algorithm to return the 
!! distinct elements of a rank-1 array. The result may either preserve the
!! order of first occurrence or be returned in sorted order.
!!
!! Its use has the syntax: 

src/sorting/stdlib_sorting.fypp:526

  • This example uses b = unique(...) as if unique were a function, but unique is a subroutine in this module. The example as written won’t compile.

This issue also appears on line 536 of the same file.

!!    b = unique(a, .false.)

src/sorting/stdlib_sorting.fypp:516

  • The inline markup for real(xdp) is malformed (real(xdp)), which breaks the rendered documentation.
!! **Note:** The unsorted (`sorted_output = .false.`) implementation is
!! currently unavailable for `real(`xdp`)` because hashing is
!! performed on the underlying binary representation, which is not
!! sufficiently portable for this kind.

src/sorting/stdlib_sorting_unique.fypp:53

  • For real overloads, tolerance is validated unconditionally, even when sorted_output is .false. (where tolerance is documented as not applicable). This means callers can get a tolerance-related error stop in unsorted mode, and passing a positive tolerance is silently ignored. Consider only reading/validating tolerance when sorted_output is .true., and rejecting present(tolerance) when sorted_output is .false..
        #:if t1.startswith('real')
        tolerance_ = optval(tolerance, 0.0_${name1}$)
        if(tolerance_ < 0.0_${name1}$) error stop "tolerance must be non-negative"
        #:endif
        allocate(temp, source=A)

src/sorting/stdlib_sorting_unique.fypp:11

  • use stdlib_constants is unused in this submodule (all referenced symbols come from host association / intrinsics). Keeping it forces an extra library dependency in the sorting CMake target.
    use stdlib_constants

src/sorting/stdlib_sorting_unique.fypp:93

  • output is allocated to size(temp) and then immediately assigned pack(temp, mask), whose size is typically smaller. With allocatable assignment, this triggers a deallocate/reallocate anyway, so the manual allocation block is redundant and can add overhead.
        if (.not. allocated(output)) then
            allocate(output(size(temp)))
        else if (size(output) < size(temp)) then
            deallocate(output)
            allocate(output(size(temp)))

src/sorting/stdlib_sorting_unique.fypp:130

  • Same as in *_sort_unique: output is allocated to size(temp) and then assigned pack(temp, mask), which will generally force reallocation. This allocation block can be removed to avoid extra allocate/deallocate churn.
        if (.not. allocated(output)) then
            allocate(output(size(temp)))
        else if (size(output) < size(temp)) then
            deallocate(output)
            allocate(output(size(temp)))

doc/specs/stdlib_sorting.md:30

  • The overview still says there are "four overloaded subroutines", but the list now contains five (ORD_SORT, SORT, RADIX_SORT, SORT_INDEX, UNIQUE).
The module `stdlib_sorting` defines several public entities, two
default integer parameters, `int_index` and `int_index_low`, and four overloaded
subroutines: `ORD_SORT`, `SORT`, `RADIX_SORT`, `SORT_INDEX` and `UNIQUE`.
The overloaded subroutines also each have several specific names for

test/sorting/test_sorting_unique.fypp:128

  • The real-kind tests exclude xdp entirely (#:if name1 != 'xdp'). Since the implementation only lacks the unsorted mode for xdp, this leaves the sorted unique path untested when WITH_XDP is enabled.
        #:for t1, t2, name1, cpp1 in REAL_TYPES_ALT_NAME
        #:if name1 != 'xdp'
        block

src/sorting/stdlib_sorting.fypp:491

  • The documented syntax line still shows the function form (output = unique(...)), but the implemented interface is call unique(array, sorted_output, output[, tolerance]).
!! Its use has the syntax: 
!!     output = unique(array, sorted_output[, tolerance] )
!!

Comment thread doc/specs/stdlib_sorting.md Outdated

@jalvesz jalvesz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM @Mahmood-Sinan thanks for this contribution. Here just a couple of minor corrections.

Comment thread src/sorting/stdlib_sorting.fypp Outdated
Comment thread src/sorting/stdlib_sorting.fypp Outdated
Comment thread src/sorting/stdlib_sorting.fypp Outdated
Comment thread src/sorting/stdlib_sorting.fypp Outdated
@jalvesz
jalvesz requested a review from jvdp1 August 10, 2026 07:27
@Mahmood-Sinan

Copy link
Copy Markdown
Contributor Author

LGTM @Mahmood-Sinan thanks for this contribution. Here just a couple of minor corrections.

Thanks for the review, I have made those changes.

@jvdp1 jvdp1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @Mahmood-Sinan . Overall it sounds good.
I have 2 main comments:

  • API: could sorted_output be optional?
  • Could the implementation be extended to other types (e.g., character, string, complex,...)?

Comment thread doc/specs/stdlib_sorting.md Outdated
Comment thread doc/specs/stdlib_sorting.md Outdated
Comment thread doc/specs/stdlib_sorting.md Outdated
Comment thread doc/specs/stdlib_sorting.md
Comment thread example/sorting/example_unique.f90 Outdated
@Mahmood-Sinan

Copy link
Copy Markdown
Contributor Author

Thanks for reviewing @jvdp1

@Mahmood-Sinan

Copy link
Copy Markdown
Contributor Author

@jvdp1 I have made the changes. Please take a look.

@jvdp1

jvdp1 commented Sep 1, 2026

Copy link
Copy Markdown
Member

@Mahmood-Sinan Some tests sorting_unique failed. Could you have a look, please?

@Mahmood-Sinan

Copy link
Copy Markdown
Contributor Author

@Mahmood-Sinan Some tests sorting_unique failed. Could you have a look, please?

thanks for pointing it out, I have fixed them

@jvdp1 jvdp1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @Mahmood-Sinan . Overall LGTM. I left a few minor comments. I think it is close to be ready :)

Comment thread doc/specs/stdlib_sorting.md Outdated

##### Description

Computes the distinct elements of the input array and stores them in

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no computations, right? Maybe:

Suggested change
Computes the distinct elements of the input array and stores them in
Returns the distinct elements of the input array in

Comment thread doc/specs/stdlib_sorting.md Outdated
kind as `array`. It is an `intent(inout)` argument. On return, it
contains one copy of each distinct element of `array`.

`sorted_output` (optional): shall be a scalar of type default logical.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`sorted_output` (optional): shall be a scalar of type default logical.
`sorted_output` (optional): shall be a scalar of type default `logical`.

Comment on lines +688 to +689
available for real arrays. `tolerance` may only be supplied when `sorted_output`
is `.true.`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this the case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because sorted_output = .false. uses a hashmap, which only supports exact duplicate detection. With tolerance, we'd need to compare each value against previously accepted values, resulting in O(n²) with a brute-force approach. Since that's inefficient, I restricted tolerance to sorted output. Should I implement the brute-force approach for unsorted output as well?

Comment thread doc/specs/stdlib_sorting.md Outdated
that are effectively unordered before the sort;
* `RADIX_SORT` is intended to sort fixed width intrinsic data
types (integers and reals).
* `UNIQUE` computes the distinct elements of a rank one array,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* `UNIQUE` computes the distinct elements of a rank one array,
* `UNIQUE` computes the distinct elements of a rank-one array,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants