parquet_sorting Module

Sorting for plain Fortran arrays and for this library's own column types.

This module is the public face of the radix engine that orders everything this library sorts: a read-time parquet_open_reader(..., sort_by=), a post-open parquet_reader_set_sort, parquet_table%sort_by, and a raw-array pf_sort/pf_argsort alike. Sharing one engine is the point: those paths can never disagree about where nulls go, where NaNs go, or how ties are broken, because there is only one answer to disagree about.

The ordering reproduces arrow::compute::SortIndices exactly, and a second, independent C++ implementation is kept in src/parquet_wrapper.cpp purely so the tests can check this one against it -- see that file's sort-engine banner. No user-facing path reaches it.

Naming. Everything public here carries the pf_ prefix (parquet-fortran) rather than parquet_, because the subject is not a parquet file -- see CLAUDE.md's "Naming conventions". The module is parquet_sorting rather than parquet_sort because a module cannot share its name with a procedure it declares.

Four operations, over eleven element types:

  • pf_argsort(values, perm) -- the permutation that would sort values. Never modifies it.
  • pf_sort(values, sorted) -- an independent sorted copy. Never modifies its input.
  • pf_permute(values, perm) -- applies a permutation to values IN PLACE.
  • pf_is_sorted(values, answer) -- whether values is already in the stated order.

Ordering reproduces arrow::compute::SortIndices exactly. Null and NaN placement is absolute: descending reverses the values, never the tiers. Ascending gives values, then NaNs, then nulls; nulls_first=.true. gives nulls, then NaNs, then values. Ties always keep their original order -- every sort here is stable, unconditionally, so there is no stable= argument to pass.

Where nullness comes from depends on the type. The six types with no null state of their own (integer, real, logical, character) take an optional is_valid(:) mask; the temporal types and the two column types carry their own and take no such argument.

Sorting one column of a table desynchronises it. %col hands back a writable pointer into a table's live storage, so call pf_permute(p, perm) on it reorders that column and leaves every other column where it was, silently breaking row correspondence. Use parquet_table%sort_by, which reorders every column together.



Interfaces

public interface pf_argsort

Extends parquet_argsort's pf_argsort with the element types that need a parquet column, a packed string store or a temporal element, and with the multi-key form.

  • private interface argsort_date_i32()

    Arguments

    None
  • private interface argsort_date_i64()

    Arguments

    None
  • private interface argsort_time_i32()

    Arguments

    None
  • private interface argsort_time_i64()

    Arguments

    None
  • private interface argsort_ts_i32()

    Arguments

    None
  • private interface argsort_ts_i64()

    Arguments

    None
  • private interface argsort_strcol_i32()

    Arguments

    None
  • private interface argsort_strcol_i64()

    Arguments

    None
  • private interface argsort_col_i32()

    Arguments

    None
  • private interface argsort_col_i64()

    Arguments

    None
  • private interface argsort_keys_i32()

    Arguments

    None
  • private interface argsort_keys_i64()

    Arguments

    None

public interface pf_sort

An independent sorted copy of values, leaving values untouched.

Deliberately not defined for parquet_string_column or parquet_column: copying a whole column to sort it serves no purpose, and reordering one in place is pf_argsort followed by pf_permute, which says what it does at the call site.

  • private interface sort_i32()

    Arguments

    None
  • private interface sort_i64()

    Arguments

    None
  • private interface sort_f32()

    Arguments

    None
  • private interface sort_f64()

    Arguments

    None
  • private interface sort_bool()

    Arguments

    None
  • private interface sort_chr()

    Arguments

    None
  • private interface sort_date()

    Arguments

    None
  • private interface sort_time()

    Arguments

    None
  • private interface sort_ts()

    Arguments

    None

public interface pf_permute

Applies perm to values IN PLACE: afterwards element k is what was at perm(k). perm itself is not modified.

perm is validated as a true permutation of 1..n before anything is written, since an invalid one would silently duplicate some elements and drop others. Pass assume_valid=.true. to skip that check when the permutation came from pf_argsort and is known good -- it means the same thing for all eleven types, the two column ones included.

assume_valid skips the O(n) contents check only. perm's LENGTH is checked either way, because a short permutation would make the gather read past the end of values and no promise from the caller can make that defined.

  • private interface permute_i32_i32()

    Arguments

    None
  • private interface permute_i32_i64()

    Arguments

    None
  • private interface permute_i64_i32()

    Arguments

    None
  • private interface permute_i64_i64()

    Arguments

    None
  • private interface permute_f32_i32()

    Arguments

    None
  • private interface permute_f32_i64()

    Arguments

    None
  • private interface permute_f64_i32()

    Arguments

    None
  • private interface permute_f64_i64()

    Arguments

    None
  • private interface permute_bool_i32()

    Arguments

    None
  • private interface permute_bool_i64()

    Arguments

    None
  • private interface permute_chr_i32()

    Arguments

    None
  • private interface permute_chr_i64()

    Arguments

    None
  • private interface permute_date_i32()

    Arguments

    None
  • private interface permute_date_i64()

    Arguments

    None
  • private interface permute_time_i32()

    Arguments

    None
  • private interface permute_time_i64()

    Arguments

    None
  • private interface permute_ts_i32()

    Arguments

    None
  • private interface permute_ts_i64()

    Arguments

    None
  • private interface permute_strcol_i32()

    Arguments

    None
  • private interface permute_strcol_i64()

    Arguments

    None
  • private interface permute_col_i32()

    Arguments

    None
  • private interface permute_col_i64()

    Arguments

    None

public interface pf_is_sorted

Whether values is already in the stated order. O(n) with an early exit, and no copy.

Uses the same comparison pf_sort does, so the two can never disagree about nulls, NaNs or direction on one array. A run of equal values is sorted.

  • private interface is_sorted_i32()

    Arguments

    None
  • private interface is_sorted_i64()

    Arguments

    None
  • private interface is_sorted_f32()

    Arguments

    None
  • private interface is_sorted_f64()

    Arguments

    None
  • private interface is_sorted_bool()

    Arguments

    None
  • private interface is_sorted_chr()

    Arguments

    None
  • private interface is_sorted_date()

    Arguments

    None
  • private interface is_sorted_time()

    Arguments

    None
  • private interface is_sorted_ts()

    Arguments

    None
  • private interface is_sorted_strcol()

    Arguments

    None
  • private interface is_sorted_col()

    Arguments

    None
  • private interface is_sorted_keys()

    Arguments

    None

public interface pf_partial_argsort

The permutation that would sort the FIRST n elements of values, without ordering the rest. perm comes back with exactly n entries (fewer if the array is shorter).

n is CLAMPED to the array size rather than being an error, so a caller whose n is derived -- a fraction of a row count, a config value, a post-filter survivor count -- needs no min(n, size(v)) of their own. A negative n is still an error.

"The last n" is descending=.true., not a separate procedure.

  • private interface partial_argsort_i32_i32()

    Arguments

    None
  • private interface partial_argsort_i32_i64()

    Arguments

    None
  • private interface partial_argsort_i64_i32()

    Arguments

    None
  • private interface partial_argsort_i64_i64()

    Arguments

    None
  • private interface partial_argsort_f32_i32()

    Arguments

    None
  • private interface partial_argsort_f32_i64()

    Arguments

    None
  • private interface partial_argsort_f64_i32()

    Arguments

    None
  • private interface partial_argsort_f64_i64()

    Arguments

    None
  • private interface partial_argsort_bool_i32()

    Arguments

    None
  • private interface partial_argsort_bool_i64()

    Arguments

    None
  • private interface partial_argsort_chr_i32()

    Arguments

    None
  • private interface partial_argsort_chr_i64()

    Arguments

    None
  • private interface partial_argsort_date_i32()

    Arguments

    None
  • private interface partial_argsort_date_i64()

    Arguments

    None
  • private interface partial_argsort_time_i32()

    Arguments

    None
  • private interface partial_argsort_time_i64()

    Arguments

    None
  • private interface partial_argsort_ts_i32()

    Arguments

    None
  • private interface partial_argsort_ts_i64()

    Arguments

    None
  • private interface partial_argsort_strcol_i32()

    Arguments

    None
  • private interface partial_argsort_strcol_i64()

    Arguments

    None
  • private interface partial_argsort_col_i32()

    Arguments

    None
  • private interface partial_argsort_col_i64()

    Arguments

    None
  • private interface partial_argsort_keys_i32()

    Arguments

    None
  • private interface partial_argsort_keys_i64()

    Arguments

    None

public interface pf_partial_sort

The first n elements of values in order, as an independent copy of length n. Same clamping rule as pf_partial_argsort. Never modifies its input.

Cheaper than pf_sort only while n stays well below the array size -- the underlying std::partial_sort degrades past a full sort as n approaches it. At n = size this is strictly worse than calling pf_sort.

  • private interface partial_sort_i32()

    Arguments

    None
  • private interface partial_sort_i64()

    Arguments

    None
  • private interface partial_sort_f32()

    Arguments

    None
  • private interface partial_sort_f64()

    Arguments

    None
  • private interface partial_sort_bool()

    Arguments

    None
  • private interface partial_sort_chr()

    Arguments

    None
  • private interface partial_sort_date()

    Arguments

    None
  • private interface partial_sort_time()

    Arguments

    None
  • private interface partial_sort_ts()

    Arguments

    None

public interface pf_nth_element

The element a full sort would place at 1-based rank nth, without sorting -- O(n) rather than O(n log n). index optionally reports which element of values that was.

The reported index is the one a full STABLE sort would give. std::nth_element normally leaves an arbitrary member of an equal-comparing run at that position; here the comparator ends with a tiebreaker on the original index, making it a total order under which no two elements compare equal, so the answer is deterministic and agrees with pf_sort element for element.

nth counts NULLS too, placed by the same tier rules as the sort (last by default). Takes descending/nulls_first/is_valid exactly as pf_argsort does.

  • private interface nth_i32_i32()

    Arguments

    None
  • private interface nth_i32_i32_i32()

    Arguments

    None
  • private interface nth_i32_i32_i64()

    Arguments

    None
  • private interface nth_i32_i64()

    Arguments

    None
  • private interface nth_i32_i64_i32()

    Arguments

    None
  • private interface nth_i32_i64_i64()

    Arguments

    None
  • private interface nth_i64_i32()

    Arguments

    None
  • private interface nth_i64_i32_i32()

    Arguments

    None
  • private interface nth_i64_i32_i64()

    Arguments

    None
  • private interface nth_i64_i64()

    Arguments

    None
  • private interface nth_i64_i64_i32()

    Arguments

    None
  • private interface nth_i64_i64_i64()

    Arguments

    None
  • private interface nth_f32_i32()

    Arguments

    None
  • private interface nth_f32_i32_i32()

    Arguments

    None
  • private interface nth_f32_i32_i64()

    Arguments

    None
  • private interface nth_f32_i64()

    Arguments

    None
  • private interface nth_f32_i64_i32()

    Arguments

    None
  • private interface nth_f32_i64_i64()

    Arguments

    None
  • private interface nth_f64_i32()

    Arguments

    None
  • private interface nth_f64_i32_i32()

    Arguments

    None
  • private interface nth_f64_i32_i64()

    Arguments

    None
  • private interface nth_f64_i64()

    Arguments

    None
  • private interface nth_f64_i64_i32()

    Arguments

    None
  • private interface nth_f64_i64_i64()

    Arguments

    None
  • private interface nth_bool_i32()

    Arguments

    None
  • private interface nth_bool_i32_i32()

    Arguments

    None
  • private interface nth_bool_i32_i64()

    Arguments

    None
  • private interface nth_bool_i64()

    Arguments

    None
  • private interface nth_bool_i64_i32()

    Arguments

    None
  • private interface nth_bool_i64_i64()

    Arguments

    None
  • private interface nth_chr_i32()

    Arguments

    None
  • private interface nth_chr_i32_i32()

    Arguments

    None
  • private interface nth_chr_i32_i64()

    Arguments

    None
  • private interface nth_chr_i64()

    Arguments

    None
  • private interface nth_chr_i64_i32()

    Arguments

    None
  • private interface nth_chr_i64_i64()

    Arguments

    None
  • private interface nth_date_i32()

    Arguments

    None
  • private interface nth_date_i32_i32()

    Arguments

    None
  • private interface nth_date_i32_i64()

    Arguments

    None
  • private interface nth_date_i64()

    Arguments

    None
  • private interface nth_date_i64_i32()

    Arguments

    None
  • private interface nth_date_i64_i64()

    Arguments

    None
  • private interface nth_time_i32()

    Arguments

    None
  • private interface nth_time_i32_i32()

    Arguments

    None
  • private interface nth_time_i32_i64()

    Arguments

    None
  • private interface nth_time_i64()

    Arguments

    None
  • private interface nth_time_i64_i32()

    Arguments

    None
  • private interface nth_time_i64_i64()

    Arguments

    None
  • private interface nth_ts_i32()

    Arguments

    None
  • private interface nth_ts_i32_i32()

    Arguments

    None
  • private interface nth_ts_i32_i64()

    Arguments

    None
  • private interface nth_ts_i64()

    Arguments

    None
  • private interface nth_ts_i64_i32()

    Arguments

    None
  • private interface nth_ts_i64_i64()

    Arguments

    None
  • private interface nth_strcol_i32()

    Arguments

    None
  • private interface nth_strcol_i32_i32()

    Arguments

    None
  • private interface nth_strcol_i32_i64()

    Arguments

    None
  • private interface nth_strcol_i64()

    Arguments

    None
  • private interface nth_strcol_i64_i32()

    Arguments

    None
  • private interface nth_strcol_i64_i64()

    Arguments

    None

public interface pf_nth_quantile

The value at quantile (on a 0-1 scale, not 0-100) of the NON-NULL values. index optionally reports which element that was; n_null how many were excluded.

Nulls are excluded from the population, not placed in it -- unlike every other operation in this module, which is why this one takes neither descending nor nulls_first: there is no null tier to position, and a descending quantile is just 1 - quantile.

rounding= selects how a fractional position is resolved: "nearest" (the default), "down" or "up", matched case-insensitively. An unrecognized token aborts.

Aborts when EVERY value is null: there is no value to return, and no sentinel exists across all ten types. n_null is for PARTIAL nullness; the all-null case never reaches it. Guard with count(mask) (or a column's own null count) if that matters.

  • private interface quantile_i32()

    Arguments

    None
  • private interface quantile_i32_i32()

    Arguments

    None
  • private interface quantile_i32_i64()

    Arguments

    None
  • private interface quantile_i64()

    Arguments

    None
  • private interface quantile_i64_i32()

    Arguments

    None
  • private interface quantile_i64_i64()

    Arguments

    None
  • private interface quantile_f32()

    Arguments

    None
  • private interface quantile_f32_i32()

    Arguments

    None
  • private interface quantile_f32_i64()

    Arguments

    None
  • private interface quantile_f64()

    Arguments

    None
  • private interface quantile_f64_i32()

    Arguments

    None
  • private interface quantile_f64_i64()

    Arguments

    None
  • private interface quantile_bool()

    Arguments

    None
  • private interface quantile_bool_i32()

    Arguments

    None
  • private interface quantile_bool_i64()

    Arguments

    None
  • private interface quantile_chr()

    Arguments

    None
  • private interface quantile_chr_i32()

    Arguments

    None
  • private interface quantile_chr_i64()

    Arguments

    None
  • private interface quantile_date()

    Arguments

    None
  • private interface quantile_date_i32()

    Arguments

    None
  • private interface quantile_date_i64()

    Arguments

    None
  • private interface quantile_time()

    Arguments

    None
  • private interface quantile_time_i32()

    Arguments

    None
  • private interface quantile_time_i64()

    Arguments

    None
  • private interface quantile_ts()

    Arguments

    None
  • private interface quantile_ts_i32()

    Arguments

    None
  • private interface quantile_ts_i64()

    Arguments

    None
  • private interface quantile_strcol()

    Arguments

    None
  • private interface quantile_strcol_i32()

    Arguments

    None
  • private interface quantile_strcol_i64()

    Arguments

    None

public interface pf_lower_bound

The first position at which target could be inserted into an already-sorted values without breaking its order -- i.e. the first element not ordered BEFORE it.

pos lands in 1 .. size(values)+1; it is size(values)+1 when every element is ordered before the target. Together with pf_upper_bound it brackets every element equal to the target, which is what pf_equal_range returns in one call.

values is checked for sortedness first, and that check is O(n). Searching an unsorted array returns a plausible index with no symptom at all, so the check is on by default. Check once with pf_is_sorted and pass assume_sorted=.true. in a loop:

call pf_is_sorted(v, ok)                       ! O(N), once
do k = 1, m
    call pf_lower_bound(v, targets(k), pos, assume_sorted=.true.)   ! O(log N) each
end do

descending/nulls_first must describe the order values is ACTUALLY in -- they select the comparison, they do not reorder anything.

  • private interface lower_bound_i32_i32()

    Arguments

    None
  • private interface lower_bound_i32_i64()

    Arguments

    None
  • private interface lower_bound_i64_i32()

    Arguments

    None
  • private interface lower_bound_i64_i64()

    Arguments

    None
  • private interface lower_bound_f32_i32()

    Arguments

    None
  • private interface lower_bound_f32_i64()

    Arguments

    None
  • private interface lower_bound_f64_i32()

    Arguments

    None
  • private interface lower_bound_f64_i64()

    Arguments

    None
  • private interface lower_bound_bool_i32()

    Arguments

    None
  • private interface lower_bound_bool_i64()

    Arguments

    None
  • private interface lower_bound_chr_i32()

    Arguments

    None
  • private interface lower_bound_chr_i64()

    Arguments

    None
  • private interface lower_bound_date_i32()

    Arguments

    None
  • private interface lower_bound_date_i64()

    Arguments

    None
  • private interface lower_bound_time_i32()

    Arguments

    None
  • private interface lower_bound_time_i64()

    Arguments

    None
  • private interface lower_bound_ts_i32()

    Arguments

    None
  • private interface lower_bound_ts_i64()

    Arguments

    None
  • private interface lower_bound_strcol_i32()

    Arguments

    None
  • private interface lower_bound_strcol_i64()

    Arguments

    None

public interface pf_upper_bound

The first position at which target is ordered BEFORE the element there -- i.e. one past the last element equal to the target.

Same arguments, same sortedness rule and same 1 .. size(values)+1 range as pf_lower_bound; pf_upper_bound - pf_lower_bound is how many elements equal the target.

  • private interface upper_bound_i32_i32()

    Arguments

    None
  • private interface upper_bound_i32_i64()

    Arguments

    None
  • private interface upper_bound_i64_i32()

    Arguments

    None
  • private interface upper_bound_i64_i64()

    Arguments

    None
  • private interface upper_bound_f32_i32()

    Arguments

    None
  • private interface upper_bound_f32_i64()

    Arguments

    None
  • private interface upper_bound_f64_i32()

    Arguments

    None
  • private interface upper_bound_f64_i64()

    Arguments

    None
  • private interface upper_bound_bool_i32()

    Arguments

    None
  • private interface upper_bound_bool_i64()

    Arguments

    None
  • private interface upper_bound_chr_i32()

    Arguments

    None
  • private interface upper_bound_chr_i64()

    Arguments

    None
  • private interface upper_bound_date_i32()

    Arguments

    None
  • private interface upper_bound_date_i64()

    Arguments

    None
  • private interface upper_bound_time_i32()

    Arguments

    None
  • private interface upper_bound_time_i64()

    Arguments

    None
  • private interface upper_bound_ts_i32()

    Arguments

    None
  • private interface upper_bound_ts_i64()

    Arguments

    None
  • private interface upper_bound_strcol_i32()

    Arguments

    None
  • private interface upper_bound_strcol_i64()

    Arguments

    None

public interface pf_equal_range

The INCLUSIVE range first .. last of elements equal to target, from one pass.

first is pf_lower_bound's answer and last is pf_upper_bound's minus one, so a target that is absent comes back with last == first - 1 and last - first + 1 == 0. Do not read values(first) without checking that count first.

Cheaper than calling the two bounds separately: the values are extracted once.

  • private interface equal_range_i32_i32()

    Arguments

    None
  • private interface equal_range_i32_i64()

    Arguments

    None
  • private interface equal_range_i64_i32()

    Arguments

    None
  • private interface equal_range_i64_i64()

    Arguments

    None
  • private interface equal_range_f32_i32()

    Arguments

    None
  • private interface equal_range_f32_i64()

    Arguments

    None
  • private interface equal_range_f64_i32()

    Arguments

    None
  • private interface equal_range_f64_i64()

    Arguments

    None
  • private interface equal_range_bool_i32()

    Arguments

    None
  • private interface equal_range_bool_i64()

    Arguments

    None
  • private interface equal_range_chr_i32()

    Arguments

    None
  • private interface equal_range_chr_i64()

    Arguments

    None
  • private interface equal_range_date_i32()

    Arguments

    None
  • private interface equal_range_date_i64()

    Arguments

    None
  • private interface equal_range_time_i32()

    Arguments

    None
  • private interface equal_range_time_i64()

    Arguments

    None
  • private interface equal_range_ts_i32()

    Arguments

    None
  • private interface equal_range_ts_i64()

    Arguments

    None
  • private interface equal_range_strcol_i32()

    Arguments

    None
  • private interface equal_range_strcol_i64()

    Arguments

    None

public interface pf_unique_count

How many DISTINCT non-null values values holds. n_null optionally reports how many were null.

Nulls are excluded from the population, not counted as one value -- the same rule pf_nth_quantile follows, and the reason this takes neither descending (a count does not depend on direction) nor nulls_first (there is no null tier to place).

Distinctness is the sort comparator's own equality, so on a floating-point array it is EXACT: 0.1 + 0.2 and 0.3 are two distinct values. Every NaN counts as one value, collectively, since NaNs compare equal to each other here (they do not under ==).

  • private interface unique_count_i32_i32()

    Arguments

    None
  • private interface unique_count_i32_i64()

    Arguments

    None
  • private interface unique_count_i64_i32()

    Arguments

    None
  • private interface unique_count_i64_i64()

    Arguments

    None
  • private interface unique_count_f32_i32()

    Arguments

    None
  • private interface unique_count_f32_i64()

    Arguments

    None
  • private interface unique_count_f64_i32()

    Arguments

    None
  • private interface unique_count_f64_i64()

    Arguments

    None
  • private interface unique_count_bool_i32()

    Arguments

    None
  • private interface unique_count_bool_i64()

    Arguments

    None
  • private interface unique_count_chr_i32()

    Arguments

    None
  • private interface unique_count_chr_i64()

    Arguments

    None
  • private interface unique_count_date_i32()

    Arguments

    None
  • private interface unique_count_date_i64()

    Arguments

    None
  • private interface unique_count_time_i32()

    Arguments

    None
  • private interface unique_count_time_i64()

    Arguments

    None
  • private interface unique_count_ts_i32()

    Arguments

    None
  • private interface unique_count_ts_i64()

    Arguments

    None
  • private interface unique_count_strcol_i32()

    Arguments

    None
  • private interface unique_count_strcol_i64()

    Arguments

    None
  • private interface unique_count_col_i32()

    Arguments

    None
  • private interface unique_count_col_i64()

    Arguments

    None

public interface pf_unique

The distinct non-null values of values, in order, as an independent copy.

Same distinctness rule as pf_unique_count -- exact for reals, all NaNs collapsing to one. descending chooses the order the distinct values come back in; there is no nulls_first, because nulls are excluded rather than placed.

Each distinct value is taken from its FIRST occurrence in the sorted order, which for equal-comparing-but-not-identical values (a character array's trailing blanks, a parquet_string_column's empty strings) is the earliest such element of values.

  • private interface unique_i32()

    Arguments

    None
  • private interface unique_i64()

    Arguments

    None
  • private interface unique_f32()

    Arguments

    None
  • private interface unique_f64()

    Arguments

    None
  • private interface unique_bool()

    Arguments

    None
  • private interface unique_chr()

    Arguments

    None
  • private interface unique_date()

    Arguments

    None
  • private interface unique_time()

    Arguments

    None
  • private interface unique_ts()

    Arguments

    None
  • private interface unique_strcol()

    Arguments

    None

public interface pf_rank

The rank of every element of values, without reordering it. ranks(i) is the rank of values(i), so this is a per-element answer rather than a permutation.

method= chooses how ties are handled, matched case-insensitively:

token ranks of 10, 20, 20, 30
"competition" (the default) 1, 2, 2, 4
"dense" 1, 2, 2, 3
"ordinal" 1, 2, 3, 4

A null gets rank 0, which is why this takes descending but NOT nulls_first: a null has no rank at all, so there is no position for nulls_first to choose. NaNs are ranked as ordinary values (all tying with each other), unlike nulls.

"ordinal" ranks are exactly the inverse of pf_argsort's permutation.

  • private interface rank_i32_i32()

    Arguments

    None
  • private interface rank_i32_i64()

    Arguments

    None
  • private interface rank_i64_i32()

    Arguments

    None
  • private interface rank_i64_i64()

    Arguments

    None
  • private interface rank_f32_i32()

    Arguments

    None
  • private interface rank_f32_i64()

    Arguments

    None
  • private interface rank_f64_i32()

    Arguments

    None
  • private interface rank_f64_i64()

    Arguments

    None
  • private interface rank_bool_i32()

    Arguments

    None
  • private interface rank_bool_i64()

    Arguments

    None
  • private interface rank_chr_i32()

    Arguments

    None
  • private interface rank_chr_i64()

    Arguments

    None
  • private interface rank_date_i32()

    Arguments

    None
  • private interface rank_date_i64()

    Arguments

    None
  • private interface rank_time_i32()

    Arguments

    None
  • private interface rank_time_i64()

    Arguments

    None
  • private interface rank_ts_i32()

    Arguments

    None
  • private interface rank_ts_i64()

    Arguments

    None
  • private interface rank_strcol_i32()

    Arguments

    None
  • private interface rank_strcol_i64()

    Arguments

    None
  • private interface rank_col_i32()

    Arguments

    None
  • private interface rank_col_i64()

    Arguments

    None

public interface pf_minmax

The smallest and largest value in values, skipping nulls and NaNs.

Takes no descending/nulls_first: a minimum and a maximum are absolute, and reversing the order would only exchange the two answers.

Aborts when every value is null or NaN -- there is nothing to return, and no sentinel exists across all nine types. This matches pf_nth_quantile's decision for the same degenerate case; guard with count(is_valid) where that can happen.

Use pf_argminmax when the positions matter rather than the values.

  • private interface minmax_i32()

    Arguments

    None
  • private interface minmax_i64()

    Arguments

    None
  • private interface minmax_f32()

    Arguments

    None
  • private interface minmax_f64()

    Arguments

    None
  • private interface minmax_chr()

    Arguments

    None
  • private interface minmax_date()

    Arguments

    None
  • private interface minmax_time()

    Arguments

    None
  • private interface minmax_ts()

    Arguments

    None
  • private interface minmax_strcol()

    Arguments

    None

public interface pf_argminmax

WHERE the smallest and largest value of values are: imin/imax are 1-based indices into values, skipping nulls and NaNs.

The index-returning twin of pf_minmax, split off because Fortran cannot offer both answers from one generic -- optional imin/imax varying only by integer kind would make a positional call ambiguous. A caller wanting both pays one extra call.

Ties report the FIRST occurrence, which is the element a full stable sort would place at either end. Aborts on an all-null-or-NaN input, exactly as pf_minmax does. Defined for parquet_column as well, since an index needs no compile-time element type.

  • private interface argminmax_i32_i32()

    Arguments

    None
  • private interface argminmax_i32_i64()

    Arguments

    None
  • private interface argminmax_i64_i32()

    Arguments

    None
  • private interface argminmax_i64_i64()

    Arguments

    None
  • private interface argminmax_f32_i32()

    Arguments

    None
  • private interface argminmax_f32_i64()

    Arguments

    None
  • private interface argminmax_f64_i32()

    Arguments

    None
  • private interface argminmax_f64_i64()

    Arguments

    None
  • private interface argminmax_chr_i32()

    Arguments

    None
  • private interface argminmax_chr_i64()

    Arguments

    None
  • private interface argminmax_date_i32()

    Arguments

    None
  • private interface argminmax_date_i64()

    Arguments

    None
  • private interface argminmax_time_i32()

    Arguments

    None
  • private interface argminmax_time_i64()

    Arguments

    None
  • private interface argminmax_ts_i32()

    Arguments

    None
  • private interface argminmax_ts_i64()

    Arguments

    None
  • private interface argminmax_strcol_i32()

    Arguments

    None
  • private interface argminmax_strcol_i64()

    Arguments

    None
  • private interface argminmax_col_i32()

    Arguments

    None
  • private interface argminmax_col_i64()

    Arguments

    None

public interface pf_merge

Merges two ALREADY-SORTED arrays into one sorted array, in O(size(a) + size(b)) rather than the O(n log n) of sorting their concatenation.

descending/nulls_first must match the order a and b are actually in -- they select the comparison, exactly as in the searches. Both inputs are checked for sortedness unless assume_sorted=.true..

Supply is_valid_a/is_valid_b whenever either input has nulls. A sorted array containing nulls is what pf_sort(..., is_valid=) produces, and a merge that is not told which elements are null compares them as ordinary values and interleaves them into the middle of the result. The precondition cannot be checked, either: a null's stored value is indistinguishable from a real one without the mask.

merged_valid reports the result's validity and is ALWAYS allocated when asked for, all .true. when neither input mask was supplied. Ties take from a first, so the result matches pf_sort of the concatenation element for element.

  • private interface merge_i32()

    Arguments

    None
  • private interface merge_i64()

    Arguments

    None
  • private interface merge_f32()

    Arguments

    None
  • private interface merge_f64()

    Arguments

    None
  • private interface merge_bool()

    Arguments

    None
  • private interface merge_chr()

    Arguments

    None
  • private interface merge_date()

    Arguments

    None
  • private interface merge_time()

    Arguments

    None
  • private interface merge_ts()

    Arguments

    None

interface

  • public module function parquet_debug_sort_row_less(keys, a, b) result(less)

    Test-only view of what the Fortran SORT comparator says about one pair of rows.

    Public only because it has to be: sort_key_buf is private to this module, so a test cannot reach sort_row_less any other way, and the C++-side hook convention is unavailable for a decision that Stage 1 exists to move out of C++. Not called by library code. Rows are 1-based, as everywhere else in this module's public API.

    Arguments

    Type IntentOptional Attributes Name
    type(pf_sort_keys), intent(in) :: keys

    the built key set.

    integer(kind=int64), intent(in) :: a

    first row, 1-based.

    integer(kind=int64), intent(in) :: b

    second row, 1-based.

    Return Value logical

    .true. when a sorts before b.

interface

  • public module function parquet_debug_sort_keys_compare(keys, a, b, nkeys) result(c)

    Test-only view of what the Fortran TIE-FREE comparator says about one pair of rows.

    Same reasoning as parquet_debug_sort_row_less. nkeys counts ENGINE keys and is clamped to how many the set holds; note a parquet_timestamp key binds as two.

    Arguments

    Type IntentOptional Attributes Name
    type(pf_sort_keys), intent(in) :: keys

    the built key set.

    integer(kind=int64), intent(in) :: a

    first row, 1-based.

    integer(kind=int64), intent(in) :: b

    second row, 1-based.

    integer, intent(in) :: nkeys

    leading engine keys taking part.

    Return Value integer

    -1, 0 or +1.

interface

  • public module function parquet_debug_sort_sweep_less(keys, nrows, nreps) result(count)

    Test-only sweep of nreps passes of nrows comparisons, returning a checksum.

    For bench/benchmark_sort_comparator.f90, which needs the comparator's own cost rather than the cost of reaching it: at ~5 ns per comparison a per-call harness measures its own overhead. The C++ twin is parquet_debug_sort_sweep_less_cpp in src/parquet_wrapper.cpp and the two loops are deliberately identical, down to the stride walk — their checksums must agree, which is what proves they did the same work. Neither uses mod on a runtime divisor: that is an integer division, and it would cost more than the comparison being timed.

    Arguments

    Type IntentOptional Attributes Name
    type(pf_sort_keys), intent(in) :: keys

    the built key set.

    integer(kind=int64), intent(in) :: nrows

    rows to walk per pass.

    integer(kind=int64), intent(in) :: nreps

    passes.

    Return Value integer(kind=int64)

    how many pairs compared less; -1 if unusable.

interface

  • public module function parquet_debug_sort_sweep_compare(keys, nrows, nreps, nkeys) result(total)

    Test-only twin of that sweep for the tie-free comparator, summing its answers.

    Arguments

    Type IntentOptional Attributes Name
    type(pf_sort_keys), intent(in) :: keys

    the built key set.

    integer(kind=int64), intent(in) :: nrows

    rows to walk per pass.

    integer(kind=int64), intent(in) :: nreps

    passes.

    integer, intent(in) :: nkeys

    leading engine keys taking part.

    Return Value integer(kind=int64)

    sum of the answers; -1 if unusable.


Derived Types

type, public ::  pf_sort_keys

A list of sort keys, applied in the order added -- the first key added is the primary one.

Read more…

Type-Bound Procedures

generic, public :: add => add_i32, add_i64, add_f32, add_f64, add_bool, add_chr, add_date, add_time, add_ts, add_strcol, add_col

Appends one sort key. Keys apply in the order added, the first being primary.

procedure, public :: nkeys_added => keys_count

Keys added so far, one per %add call.

procedure, public :: clear => keys_clear

Drops every key, leaving the object reusable.