diff --git a/doc/specs/index.md b/doc/specs/index.md index cc804bd31..01ad4be26 100644 --- a/doc/specs/index.md +++ b/doc/specs/index.md @@ -26,6 +26,7 @@ This is and index/directory of the specifications (specs) for each new module/fe - [stats_distribution_PRNG](./stdlib_stats_distribution_PRNG.html) - Probability Distributions random number generator - [string\_type](./stdlib_string_type.html) - Basic string support - [strings](./stdlib_strings.html) - String handling and manipulation routines + - [stringlist_type](./stdlib_stringlist_type.html) - 1-Dimensional list of strings ## Released/Stable Features & Modules diff --git a/doc/specs/stdlib_stringlist_type.md b/doc/specs/stdlib_stringlist_type.md new file mode 100644 index 000000000..88dc6521a --- /dev/null +++ b/doc/specs/stdlib_stringlist_type.md @@ -0,0 +1,564 @@ +--- +title: stringlist type +--- + +# `stdlib_stringlist_type` module (1-D list of strings) + +[TOC] + +## Introduction + +The `stdlib_stringlist_type` module provides with 2 derived types to deal with lists of strings. +`stringlist_type` derived type represents one-dimensional list of variable-length strings (also referred as one-dimensional stringlist) which is compatible with Fortran intrinsic character and `stringlist_index_type` derived type represents an index to access, modify the elements of a stringlist, insert elements to a stringlist and much more. + +## Derived types provided + + +### `stringlist_type` derived type + +The `stringlist_type` derived type represents one-dimensional list of strings (also referred as one-dimensional stringlist). +The internal representation of the list is implementation dependent and is not visible to the user of the module. + +Note: _stringlist_ is an abstract concept which is expressed through the derived type `stringlist_type`. + +#### Status + +Experimental + + + +### `stringlist_index_type` derived type + +An instance of the derived type `stringlist_index_type` represents either a forward index OR a backward index. +The internal representation is implementation dependent and is not visible to the user of the module. +`list_head` and `list_tail` are 2 special instances of this type representing the head and the tail of a stringlist respectively. +An index is independent of the stringlist(or `stringlist_type`) it is used with and hence, an index can be used with multiple stringlists in the same program. + +#### Status + +Experimental + + + +### fidx/bidx + +#### Description + +`fidx`: Returns an instance which represents forward index `idx`. +`bidx`: Returns an instance which represents backward index `idx`. + +#### Syntax + +For fidx: `res = [[stdlib_stringlist_type(module):fidx()]] (idx)`. +For bidx: `res = [[stdlib_stringlist_type(module):bidx()]] (idx)`. + +#### Status + +Experimental. + +#### Class + +Pure function. + +#### Argument + +- `idx`: Shall be of kind `integer`. + This argument is `intent(in)`. + +#### Result value + +The result is of type `stringlist_index_type`. + +#### Example + +```fortran +program demo_fidx_bidx + use stdlib_stringlist_type, only: stringlist_index_type, fidx, bidx + implicit none + + type(stringlist_index_type) :: index + + index = fidx(1) + ! forward index 1 + + index = bidx(3) + ! backward index 3 + +end program demo_fidx_bidx +``` + + +### Constructor for `stringlist_type`(or stringlist) + +#### Description + +No arguments given: Initializes an empty stringlist(a stringlist containing no elements in it). + +With argument: Initializes a stringlist equivalent to the input array `array` i.e. a stringlist containing all elements of the input array `array` in the same order. + +#### Syntax + +`res = [[stdlib_stringlist_type(module):stringlist_type(interface)]] ([array])` + +#### Status + +Experimental + +#### Class + +Pure function. + +#### Argument + +- `array`: Shall be an array of `character` scalar or array of [[stdlib_string_type(module):string_type(type)]]. + This argument is `intent(in)` and `optional`. + +#### Result value + +The result is an instance of type `stringlist_type`. + +#### Example + +```fortran +program demo_constructor + use stdlib_stringlist_type, only: stringlist_type + use stdlib_string_type, only: string_type + implicit none + + type(stringlist_type) :: stringlist + + stringlist = stringlist_type() + ! stringlist <-- { } (empty stringlist) + + stringlist = stringlist_type(["#1", "#2", "#3"]) + ! stringlist <-- {"#1", "#2", "#3"} + + stringlist = stringlist_type([string_type("#1"), string_type("#2")]) + ! stringlist <-- {"#1", "#2"} + +end program demo_constructor +``` + + + +### insert_at + +#### Description + +Inserts the string `string` _AT_ the index `idx`, so that the newly added element is present at index `idx` after insertion. Inserting an element _AT_ index beyond `length + 1` inserts the element _AT_ `list_tail`, and likewise inserting _AT_ a non-positive index inserts the element _AT_ `list_head`. + +#### Syntax + +`call [[stdlib_stringlist_type(module):stringlist_type(type)]]%[[stdlib_stringlist_type(module):insert_at(generic)]] (idx, string)` + +#### Status + +Experimental. + +#### Class + +Pure subroutine. + +#### Argument + +- `idx`: [[stdlib_stringlist_type(module):stringlist_index_type(type)]]. + This argument is `intent(in)`. + +- `string`: Character scalar or [[stdlib_string_type(module):string_type(type)]]. + This argument is `intent(in)`. + +#### Example + +```fortran +program demo_insert_at + use stdlib_stringlist_type, only: stringlist_type, stringlist_index_type, fidx, bidx + use stdlib_string_type, only: string_type + implicit none + + type(stringlist_type) :: stringlist + type(stringlist_index_type) :: index + + index = fidx(1) + call stringlist%insert_at( index, "Element No. one" ) + ! stringlist <-- {"Element No. one"} + + index = bidx(1) + call stringlist%insert_at( index, string_type( "Element No. two" ) ) + ! stringlist <-- {"Element No. one", "Element No. two"} + + call stringlist%insert_at( fidx(2), string_type( "Element No. three" ) ) + ! stringlist <-- {"Element No. one", "Element No. three", "Element No. two"} + + call stringlist%insert_at( bidx(1), "Element No. four" ) + ! stringlist <-- {"Element No. one", "Element No. three", "Element No. two", "Element No. four"} + +end program demo_insert_at +``` + + + +### get + +#### Description + +Returns the string present currently at the index `idx` in a stringlist. If index `idx` is out of bounds, then an empty string is returned. + +#### Syntax + +`res = [[stdlib_stringlist_type(module):stringlist_type(type)]]%[[stdlib_stringlist_type(module):get(generic)]] (idx)` + +#### Status + +Experimental. + +#### Class + +Pure function. + +#### Argument + +- `idx`: [[stdlib_stringlist_type(module):stringlist_index_type(type)]]. + This argument is `intent(in)`. + +#### Result value + +The result is a string of type `string_type`. + +#### Example + +```fortran +program demo_get + use stdlib_stringlist_type, only: stringlist_type, fidx, bidx + use stdlib_string_type, only: string_type + implicit none + + type(stringlist_type) :: stringlist + type(string_type) :: output + + !> inserting 4 elements to the stringlist + call stringlist%insert_at( fidx(1), "Element No. one" ) + call stringlist%insert_at( fidx(1), "Element No. two" ) + call stringlist%insert_at( fidx(1), "Element No. three" ) + call stringlist%insert_at( fidx(1), "Element No. four" ) + ! stringlist <-- {"Element No. four", "Element No. three", "Element No. two", "Element No. one"} + + output = stringlist%get( fidx(1) ) + ! output <-- "Element No. four" + + output = stringlist%get( bidx(1) ) + ! output <-- "Element No. one" + + !> accessing out of bounds index + output = stringlist%get( bidx(5) ) + ! output <-- "" + output = stringlist%get( fidx(0) ) + ! output <-- "" + +end program demo_get +``` + + + +### len + +#### Description + +Returns the number of elements present currently in the stringlist. + +#### Syntax + +`res = [[stdlib_stringlist_type(module):stringlist_type(type)]]%[[stdlib_stringlist_type(module):len()]] ()` + +#### Status + +Experimental. + +#### Class + +Pure function. + +#### Argument + +No arguments. + +#### Result value + +The result is of type `integer`. + +#### Example + +```fortran +program demo_len + use stdlib_stringlist_type, only: stringlist_type, bidx + implicit none + + type(stringlist_type) :: stringlist + integer :: output + + output = stringlist%len() + ! output <-- 0 + + !> inserting 2 elements to the stringlist + call stringlist%insert_at( bidx(1), "Element No. one" ) + call stringlist%insert_at( bidx(1), "Element No. two" ) + ! stringlist <-- {"Element No. one", "Element No. two"} + + print'(a)', stringlist%len() + ! 2 + +end program demo_len +``` + + + +### clear + +#### Description + +Removes all elements from a stringlist. + +#### Syntax + +`call [[stdlib_stringlist_type(module):stringlist_type(type)]]%[[stdlib_stringlist_type(module):clear()]] ()` + +#### Status + +Experimental. + +#### Class + +Pure subroutine. + +#### Argument + +No arguments. + +#### Example + +```fortran +program demo_clear + use stdlib_stringlist_type, only: stringlist_type, fidx + implicit none + + type(stringlist_type) :: stringlist + + !> inserting 2 elements to the stringlist + call stringlist%insert_at( fidx(1), "Element No. one" ) + call stringlist%insert_at( fidx(1), "Element No. two" ) + ! stringlist <-- {"Element No. two", "Element No. one"} + + call stringlist%clear() + ! stringlist <-- { } (empty stringlist) + + !> inserting 1 element to the stringlist + call stringlist%insert_at( fidx(1), "Element No. one" ) + ! stringlist <-- {"Element No. one"} + +end program demo_clear +``` + + + +### Comparison operator equal + +#### Description + +Compares left hand side (lhs) with right hand side (rhs) for equality. + +#### Syntax + +`res = lhs == rhs` + +`res = lhs .eq. rhs` + +#### Status + +Experimental. + +#### Class + +Pure function, `operator(==)` and `operator(.eq.)`. + +#### Argument + +- `lhs`: Shall be an array of `character` scalar or of [[stdlib_string_type(module):string_type(type)]] OR +a [[stdlib_stringlist_type(module):stringlist_type(type)]]. + This argument is `intent(in)`. + +- `rhs`: Shall be an array of `character` scalar or of [[stdlib_string_type(module):string_type(type)]] OR +a [[stdlib_stringlist_type(module):stringlist_type(type)]]. + This argument is `intent(in)`. + +#### Result value + +The result is a default `logical` scalar value. + +#### Example + +```fortran +program demo_equality_operator + use stdlib_stringlist_type, only: stringlist_type, fidx, list_head, operator(==) + use stdlib_string_type, only: string_type + implicit none + + type(stringlist_type) :: stringlist + type(string_type), allocatable :: stringarray(:) + logical :: res + + !> inserting 4 elements to the stringlist + call stringlist%insert_at( fidx(1), "#1" ) + call stringlist%insert_at( list_head, "#2" ) + call stringlist%insert_at( fidx(1), "#3" ) + call stringlist%insert_at( list_head, "#4" ) + ! stringlist <-- {"#4", "#3", "#2", "#1"} + + !> creating an array of 4 string_type elements + stringarray = [string_type("#4"), string_type("#3"), string_type("#2"), string_type("#1")] + + res = ( stringarray == stringlist ) + ! res <-- .true. + + res = ( stringlist == ["#4", "#3", "#2", "#1"] ) + ! res <-- .true. + + print'(a)', stringlist == ["#4", "#3", "#1"] + ! .false. + +end program demo_equality_operator +``` + + + +### Comparison operator not equal + +#### Description + +Compares left hand side (lhs) with right hand side (rhs) for inequality. + +#### Syntax + +`res = lhs /= rhs` + +`res = lhs .ne. rhs` + +#### Status + +Experimental. + +#### Class + +Pure function, `operator(/=)` and `operator(.ne.)`. + +#### Argument + +- `lhs`: Shall be an array of `character` scalar or of [[stdlib_string_type(module):string_type(type)]] OR +a [[stdlib_stringlist_type(module):stringlist_type(type)]]. + This argument is `intent(in)`. + +- `rhs`: Shall be an array of `character` scalar or of [[stdlib_string_type(module):string_type(type)]] OR +a [[stdlib_stringlist_type(module):stringlist_type(type)]]. + This argument is `intent(in)`. + +#### Result value + +The result is a default `logical` scalar value. + +#### Example + +```fortran +program demo_inequality_operator + use stdlib_stringlist_type, only: stringlist_type, bidx, list_tail, operator(/=) + use stdlib_string_type, only: string_type + implicit none + + type(stringlist_type) :: stringlist + type(string_type), allocatable :: stringarray(:) + logical :: res + + !> inserting 4 elements to the stringlist + call stringlist%insert_at( bidx(1), "#1" ) + call stringlist%insert_at( list_tail, "#2" ) + call stringlist%insert_at( bidx(1), "#3" ) + call stringlist%insert_at( list_tail, "#4" ) + ! stringlist <-- {"#1", "#2", "#3", "#4"} + + !> creating an array of 4 string_type elements + stringarray = [string_type("#1"), string_type("#2"), string_type("#3"), string_type("#4")] + + res = ( stringarray /= stringlist ) + ! res <-- .false. + + res = ( stringlist /= ["#111", "#222", "#333", "#444"] ) + ! res <-- .true. + + print'(a)', stringlist /= ["#4", "#3", "#1"] + ! .true. + +end program demo_inequality_operator +``` + + + +### Concatenation operator(//) + +#### Description + +Returns the concatenated output of left hand side (lhs) and right hand side (rhs). + +#### Syntax + +`res = lhs // rhs` + +#### Status + +Experimental. + +#### Class + +Pure function, `operator(//)`. + +#### Argument + +- `lhs`: Shall be a `character` scalar or [[stdlib_string_type(module):string_type(type)]] OR an array of `character` scalar or of [[stdlib_string_type(module):string_type(type)]] OR +a [[stdlib_stringlist_type(module):stringlist_type(type)]]. + This argument is `intent(in)`. + +- `rhs`: Shall be a `character` scalar or [[stdlib_string_type(module):string_type(type)]] OR an array of `character` scalar or of [[stdlib_string_type(module):string_type(type)]] OR +a [[stdlib_stringlist_type(module):stringlist_type(type)]]. + This argument is `intent(in)`. + +#### Result value + +The result is an instance of `stringlist_type`. + +#### Example + +```fortran +program demo_concatenate_operator + use stdlib_stringlist_type, only: stringlist_type, operator(//) + use stdlib_string_type, only: string_type + implicit none + + type(stringlist_type) :: first_stringlist, second_stringlist + type(string_type), allocatable :: stringarray(:) + + first_stringlist = first_stringlist // "Element No. one" + ! first_stringlist <-- {"Element No. one"} + + second_stringlist = string_type("Element No. two") // first_stringlist + ! second_stringlist <-- {Element No. two, "Element No. one"} + + !> Creating an array of 2 string_type elements + stringarray = [string_type("Element No. three"), string_type("Element No. four")] + + second_stringlist = first_stringlist // stringarray + ! second_stringlist <-- {"Element No. one", "Element No. three", "Element No. four"} + + second_stringlist = ["#1", "#2"] // second_stringlist + ! second_stringlist <-- {"#1", "#2", "Element No. one", "Element No. three", "Element No. four"} + + first_stringlist = first_stringlist // second_stringlist + ! first_stringlist <-- {"Element No. one", "#1", "#2", "Element No. one", "Element No. three", "Element No. four"} + +end program demo_concatenate_operator +``` \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 80804891c..b56d75b0e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC stdlib_specialfunctions.f90 stdlib_specialfunctions_legendre.f90 stdlib_quadrature_gauss.f90 + stdlib_stringlist_type.f90 ${outFiles} ) diff --git a/src/Makefile.manual b/src/Makefile.manual index 5c1d2ba8d..7576cf3d8 100644 --- a/src/Makefile.manual +++ b/src/Makefile.manual @@ -43,6 +43,8 @@ SRC = f18estop.f90 \ stdlib_kinds.f90 \ stdlib_logger.f90 \ stdlib_quadrature_gauss.f90 \ + stdlib_strings.f90 \ + stdlib_stringlist_type.f90 \ $(SRCGEN) LIB = libstdlib.a @@ -165,3 +167,6 @@ stdlib_math_logspace.o: \ stdlib_math_arange.o: \ stdlib_math.o stdlib_linalg_outer_product.o: stdlib_linalg.o +stdlib_stringlist_type.o: stdlib_string_type.o \ + stdlib_math.o \ + stdlib_optval.o diff --git a/src/stdlib_stringlist_type.f90 b/src/stdlib_stringlist_type.f90 new file mode 100644 index 000000000..78cfb69d3 --- /dev/null +++ b/src/stdlib_stringlist_type.f90 @@ -0,0 +1,735 @@ +! stdlib_stringlist_type.f90 -- +! Module for storing and manipulating list of strings +! The strings may have arbitrary lengths, not necessarily the same +! +! insert AT: Inserts an element BEFORE the element present currently at the asked index +! for forward indexes +! Inserts an element AFTER the element present currently at the asked index +! for backward indexes +! In other words, after insertion the element will be present at the asked index +! for both forward and backward indexes +! insert BEFORE: Inserts an element BEFORE the element present currently at the asked index +! insert AFTER: Inserts an element AFTER the element present currently at the asked index +! +! Note the distinction between AT and BEFORE in the module. Care has been taken to keep it consistent +! throughout the PR +! +module stdlib_stringlist_type + use stdlib_string_type, only: string_type, operator(/=) + use stdlib_math, only: clip + implicit none + private + + public :: stringlist_type, operator(//), operator(==), operator(/=) + public :: list_head, list_tail, fidx, bidx, stringlist_index_type + + type stringlist_index_type + private + logical :: forward + integer :: offset + + end type stringlist_index_type + + type(stringlist_index_type), parameter :: list_head = stringlist_index_type( .true. , 1 ) ! fidx(1) + type(stringlist_index_type), parameter :: list_tail = stringlist_index_type( .false., 1 ) ! bidx(1) + + !> Version: experimental + !> + !> Returns an instance of type 'stringlist_index_type' representing forward index + !> [Specifications](../page/specs/stdlib_stringlist_type.html#fidx) + interface fidx + module procedure forward_index + end interface + + !> Version: experimental + !> + !> Returns an instance of type 'stringlist_index_type' representing backward index + !> [Specifications](../page/specs/stdlib_stringlist_type.html#bidx) + interface bidx + module procedure backward_index + end interface + + type stringlist_type + private + type(string_type), dimension(:), allocatable :: stringarray + + contains + private + + procedure, public :: clear => clear_list + + procedure, public :: len => length_list + + procedure :: to_future_at_idxn => convert_to_future_at_idxn + + procedure :: to_current_idxn => convert_to_current_idxn + + procedure :: insert_at_char_idx => insert_at_char_idx_wrap + procedure :: insert_at_string_idx => insert_at_string_idx_wrap + procedure :: insert_at_stringlist_idx => insert_at_stringlist_idx_wrap + procedure :: insert_at_chararray_idx => insert_at_chararray_idx_wrap + procedure :: insert_at_stringarray_idx => insert_at_stringarray_idx_wrap + generic, public :: insert_at => insert_at_char_idx, & + insert_at_string_idx, & + insert_at_stringlist_idx, & + insert_at_chararray_idx, & + insert_at_stringarray_idx + + procedure :: insert_before_string_int => insert_before_string_int_impl + procedure :: insert_before_stringlist_int => insert_before_stringlist_int_impl + procedure :: insert_before_chararray_int => insert_before_chararray_int_impl + procedure :: insert_before_stringarray_int => insert_before_stringarray_int_impl + generic :: insert_before => insert_before_string_int, & + insert_before_stringlist_int, & + insert_before_chararray_int, & + insert_before_stringarray_int + + procedure :: get_string_idx => get_string_idx_wrap + generic, public :: get => get_string_idx + + end type stringlist_type + + !> Version: experimental + !> + !> Constructor for stringlist + !> Returns an instance of type stringlist_type + !> [Specifications](../page/specs/stdlib_stringlist_type.html#stringlist_type) + interface stringlist_type + module procedure new_stringlist + module procedure new_stringlist_carray + module procedure new_stringlist_sarray + end interface + + !> Version: experimental + !> + !> Concatenates stringlist with the input entity + !> Returns a new stringlist + !> [Specifications](../page/specs/stdlib_stringlist_type.html#append-operator) + interface operator(//) + module procedure append_char + module procedure append_string + module procedure prepend_char + module procedure prepend_string + module procedure append_stringlist + module procedure append_carray + module procedure append_sarray + module procedure prepend_carray + module procedure prepend_sarray + end interface + + !> Version: experimental + !> + !> Compares stringlist for equality with the input entity + !> Returns a logical + !> [Specifications](../page/specs/stdlib_stringlist_type.html#equality-operator) + interface operator(==) + module procedure eq_stringlist + module procedure eq_stringlist_carray + module procedure eq_stringlist_sarray + module procedure eq_carray_stringlist + module procedure eq_sarray_stringlist + end interface + + !> Version: experimental + !> + !> Compares stringlist for inequality with the input entity + !> Returns a logical + !> [Specifications](../page/specs/stdlib_stringlist_type.html#inequality-operator) + interface operator(/=) + module procedure ineq_stringlist + module procedure ineq_stringlist_carray + module procedure ineq_stringlist_sarray + module procedure ineq_carray_stringlist + module procedure ineq_sarray_stringlist + end interface + +contains + + ! constructor for stringlist_type: + + !> Constructor with no argument + !> Returns a new instance of type stringlist + pure function new_stringlist() + type(stringlist_type) :: new_stringlist + + end function new_stringlist + + !> Constructor to convert chararray to stringlist + !> Returns a new instance of type stringlist + pure function new_stringlist_carray( array ) + character(len=*), dimension(:), intent(in) :: array + type(stringlist_type) :: new_stringlist_carray + type(string_type), dimension( size(array) ) :: sarray + integer :: i + + do i = 1, size(array) + sarray(i) = string_type( array(i) ) + end do + + new_stringlist_carray = stringlist_type( sarray ) + + end function new_stringlist_carray + + !> Constructor to convert stringarray to stringlist + !> Returns a new instance of type stringlist + pure function new_stringlist_sarray( array ) + type(string_type), dimension(:), intent(in) :: array + type(stringlist_type) :: new_stringlist_sarray + + new_stringlist_sarray = stringlist_type() + new_stringlist_sarray%stringarray = array + + end function new_stringlist_sarray + + ! constructor for stringlist_index_type: + + !> Returns an instance of type 'stringlist_index_type' representing forward index 'idx' + pure function forward_index( idx ) + integer, intent(in) :: idx + type(stringlist_index_type) :: forward_index + + forward_index = stringlist_index_type( .true., idx ) + + end function forward_index + + !> Returns an instance of type 'stringlist_index_type' representing backward index 'idx' + pure function backward_index( idx ) + integer, intent(in) :: idx + type(stringlist_index_type) :: backward_index + + backward_index = stringlist_index_type( .false., idx ) + + end function backward_index + + ! concatenation operator: + + !> Appends character scalar 'rhs' to the stringlist 'list' + !> Returns a new stringlist + function append_char( lhs, rhs ) + type(stringlist_type), intent(in) :: lhs + character(len=*), intent(in) :: rhs + type(stringlist_type) :: append_char + + append_char = lhs // string_type( rhs ) + + end function append_char + + !> Appends string 'rhs' to the stringlist 'list' + !> Returns a new stringlist + function append_string( lhs, rhs ) + type(stringlist_type), intent(in) :: lhs + type(string_type), intent(in) :: rhs + type(stringlist_type) :: append_string + + append_string = lhs ! Intent: creating a full, deep copy + call append_string%insert_at( list_tail, rhs ) + + end function append_string + + !> Prepends character scalar 'lhs' to the stringlist 'rhs' + !> Returns a new stringlist + function prepend_char( lhs, rhs ) + character(len=*), intent(in) :: lhs + type(stringlist_type), intent(in) :: rhs + type(stringlist_type) :: prepend_char + + prepend_char = string_type( lhs ) // rhs + + end function prepend_char + + !> Prepends string 'lhs' to the stringlist 'rhs' + !> Returns a new stringlist + function prepend_string( lhs, rhs ) + type(string_type), intent(in) :: lhs + type(stringlist_type), intent(in) :: rhs + type(stringlist_type) :: prepend_string + + prepend_string = rhs ! Intent: creating a full, deep copy + call prepend_string%insert_at( list_head, lhs ) + + end function prepend_string + + !> Appends stringlist 'rhs' to the stringlist 'lhs' + !> Returns a new stringlist + function append_stringlist( lhs, rhs ) + type(stringlist_type), intent(in) :: lhs + type(stringlist_type), intent(in) :: rhs + type(stringlist_type) :: append_stringlist + + append_stringlist = lhs ! Intent: creating a full, deep copy + call append_stringlist%insert_at( list_tail, rhs ) + + end function append_stringlist + + !> Appends chararray 'rhs' to the stringlist 'lhs' + !> Returns a new stringlist + function append_carray( lhs, rhs ) + type(stringlist_type), intent(in) :: lhs + character(len=*), dimension(:), intent(in) :: rhs + type(stringlist_type) :: append_carray + + append_carray = lhs ! Intent: creating a full, deep copy + call append_carray%insert_at( list_tail, rhs ) + + end function append_carray + + !> Appends stringarray 'rhs' to the stringlist 'lhs' + !> Returns a new stringlist + function append_sarray( lhs, rhs ) + type(stringlist_type), intent(in) :: lhs + type(string_type), dimension(:), intent(in) :: rhs + type(stringlist_type) :: append_sarray + + append_sarray = lhs ! Intent: creating a full, deep copy + call append_sarray%insert_at( list_tail, rhs ) + + end function append_sarray + + !> Prepends chararray 'lhs' to the stringlist 'rhs' + !> Returns a new stringlist + function prepend_carray( lhs, rhs ) + character(len=*), dimension(:), intent(in) :: lhs + type(stringlist_type), intent(in) :: rhs + type(stringlist_type) :: prepend_carray + + prepend_carray = rhs ! Intent: creating a full, deep copy + call prepend_carray%insert_at( list_head, lhs ) + + end function prepend_carray + + !> Prepends stringarray 'lhs' to the stringlist 'rhs' + !> Returns a new stringlist + function prepend_sarray( lhs, rhs ) + type(string_type), dimension(:), intent(in) :: lhs + type(stringlist_type), intent(in) :: rhs + type(stringlist_type) :: prepend_sarray + + prepend_sarray = rhs ! Intent: creating a full, deep copy + call prepend_sarray%insert_at( list_head, lhs ) + + end function prepend_sarray + + ! equality operator: + + !> Compares stringlist 'lhs' for equality with stringlist 'rhs' + !> Returns a logical + pure logical function eq_stringlist( lhs, rhs ) + type(stringlist_type), intent(in) :: lhs + type(stringlist_type), intent(in) :: rhs + integer :: i + + eq_stringlist = .false. + if ( lhs%len() == rhs%len() ) then + eq_stringlist = .true. + do i = 1, lhs%len() + if ( lhs%stringarray(i) /= rhs%stringarray(i) ) then + eq_stringlist = .false. + exit + end if + end do + end if + + end function eq_stringlist + + !> Compares stringlist 'lhs' for equality with chararray 'rhs' + !> Returns a logical + pure logical function eq_stringlist_carray( lhs, rhs ) + type(stringlist_type), intent(in) :: lhs + character(len=*), dimension(:), intent(in) :: rhs + integer :: i + + eq_stringlist_carray = .false. + if ( lhs%len() == size( rhs ) ) then + eq_stringlist_carray = .true. + do i = 1, lhs%len() + if ( lhs%stringarray(i) /= rhs(i) ) then + eq_stringlist_carray = .false. + exit + end if + end do + end if + + end function eq_stringlist_carray + + !> Compares stringlist 'lhs' for equality with stringarray 'rhs' + !> Returns a logical + pure logical function eq_stringlist_sarray( lhs, rhs ) + type(stringlist_type), intent(in) :: lhs + type(string_type), dimension(:), intent(in) :: rhs + integer :: i + + eq_stringlist_sarray = .false. + if ( lhs%len() == size( rhs ) ) then + eq_stringlist_sarray = .true. + do i = 1, lhs%len() + if ( lhs%stringarray(i) /= rhs(i) ) then + eq_stringlist_sarray = .false. + exit + end if + end do + end if + + end function eq_stringlist_sarray + + !> Compares chararray 'lhs' for equality with stringlist 'rhs' + !> Returns a logical + pure logical function eq_carray_stringlist( lhs, rhs ) + character(len=*), dimension(:), intent(in) :: lhs + type(stringlist_type), intent(in) :: rhs + + eq_carray_stringlist = ( rhs == lhs ) + + end function eq_carray_stringlist + + !> Compares stringarray 'lhs' for equality with stringlist 'rhs' + !> Returns a logical + pure logical function eq_sarray_stringlist( lhs, rhs ) + type(string_type), dimension(:), intent(in) :: lhs + type(stringlist_type), intent(in) :: rhs + + eq_sarray_stringlist = ( rhs == lhs ) + + end function eq_sarray_stringlist + + ! inequality operator: + + !> Compares stringlist 'lhs' for inequality with stringlist 'rhs' + !> Returns a logical + pure logical function ineq_stringlist( lhs, rhs ) + type(stringlist_type), intent(in) :: lhs + type(stringlist_type), intent(in) :: rhs + + ineq_stringlist = .not.( lhs == rhs ) + + end function ineq_stringlist + + !> Compares stringlist 'lhs' for inequality with chararray 'rhs' + !> Returns a logical + pure logical function ineq_stringlist_carray( lhs, rhs ) + type(stringlist_type), intent(in) :: lhs + character(len=*), dimension(:), intent(in) :: rhs + + ineq_stringlist_carray = .not.( lhs == rhs ) + + end function ineq_stringlist_carray + + !> Compares stringlist 'lhs' for inequality with stringarray 'rhs' + !> Returns a logical + pure logical function ineq_stringlist_sarray( lhs, rhs ) + type(stringlist_type), intent(in) :: lhs + type(string_type), dimension(:), intent(in) :: rhs + + ineq_stringlist_sarray = .not.( lhs == rhs ) + + end function ineq_stringlist_sarray + + !> Compares chararray 'lhs' for inequality with stringlist 'rhs' + !> Returns a logical + pure logical function ineq_carray_stringlist( lhs, rhs ) + character(len=*), dimension(:), intent(in) :: lhs + type(stringlist_type), intent(in) :: rhs + + ineq_carray_stringlist = .not.( lhs == rhs) + + end function ineq_carray_stringlist + + !> Compares stringarray 'lhs' for inequality with stringlist 'rhs' + !> Returns a logical + pure logical function ineq_sarray_stringlist( lhs, rhs ) + type(string_type), dimension(:), intent(in) :: lhs + type(stringlist_type), intent(in) :: rhs + + ineq_sarray_stringlist = .not.( lhs == rhs ) + + end function ineq_sarray_stringlist + + ! clear: + + !> Version: experimental + !> + !> Resets stringlist 'list' to an empy stringlist of len 0 + !> Modifies the input stringlist 'list' + subroutine clear_list( list ) + class(stringlist_type), intent(inout) :: list + + if ( allocated( list%stringarray ) ) then + deallocate( list%stringarray ) + end if + + end subroutine clear_list + + ! len: + + !> Version: experimental + !> + !> Returns the len (length) of the list + !> Returns an integer + pure integer function length_list( list ) + class(stringlist_type), intent(in) :: list + + length_list = 0 + if ( allocated( list%stringarray ) ) then + length_list = size( list%stringarray ) + end if + + end function length_list + + ! to_future_at_idxn: + + !> Version: experimental + !> + !> Converts a forward index OR a backward index to an integer index at + !> which the new element will be present post insertion (i.e. in future) + !> Returns an integer + pure integer function convert_to_future_at_idxn( list, idx ) + !> Not a part of public API + class(stringlist_type), intent(in) :: list + type(stringlist_index_type), intent(in) :: idx + + ! Formula: merge( fidx( x ) - ( list_head - 1 ), len - bidx( x ) + ( list_tail - 1 ) + 2, ... ) + convert_to_future_at_idxn = merge( idx%offset, list%len() - idx%offset + 2 , idx%forward ) + + end function convert_to_future_at_idxn + + ! to_current_idxn: + + !> Version: experimental + !> + !> Converts a forward index OR backward index to its equivalent integer index idxn + !> Returns an integer + pure integer function convert_to_current_idxn( list, idx ) + !> Not a part of public API + class(stringlist_type), intent(in) :: list + type(stringlist_index_type), intent(in) :: idx + + ! Formula: merge( fidx( x ) - ( list_head - 1 ), len + 1 - bidx( x ) + ( list_tail - 1 ), ... ) + convert_to_current_idxn = merge( idx%offset, list%len() - idx%offset + 1, idx%forward ) + + end function convert_to_current_idxn + + ! insert_at: + + !> Version: experimental + !> + !> Inserts character scalar 'string' AT stringlist_index 'idx' in stringlist 'list' + !> Modifies the input stringlist 'list' + subroutine insert_at_char_idx_wrap( list, idx, string ) + class(stringlist_type), intent(inout) :: list + type(stringlist_index_type), intent(in) :: idx + character(len=*), intent(in) :: string + + call list%insert_at( idx, string_type( string ) ) + + end subroutine insert_at_char_idx_wrap + + !> Version: experimental + !> + !> Inserts string 'string' AT stringlist_index 'idx' in stringlist 'list' + !> Modifies the input stringlist 'list' + subroutine insert_at_string_idx_wrap( list, idx, string ) + class(stringlist_type), intent(inout) :: list + type(stringlist_index_type), intent(in) :: idx + type(string_type), intent(in) :: string + + call list%insert_before( list%to_future_at_idxn( idx ), string ) + + end subroutine insert_at_string_idx_wrap + + !> Version: experimental + !> + !> Inserts stringlist 'slist' AT stringlist_index 'idx' in stringlist 'list' + !> Modifies the input stringlist 'list' + subroutine insert_at_stringlist_idx_wrap( list, idx, slist ) + class(stringlist_type), intent(inout) :: list + type(stringlist_index_type), intent(in) :: idx + type(stringlist_type), intent(in) :: slist + + call list%insert_before( list%to_future_at_idxn( idx ), slist ) + + end subroutine insert_at_stringlist_idx_wrap + + !> Version: experimental + !> + !> Inserts chararray 'carray' AT stringlist_index 'idx' in stringlist 'list' + !> Modifies the input stringlist 'list' + subroutine insert_at_chararray_idx_wrap( list, idx, carray ) + class(stringlist_type), intent(inout) :: list + type(stringlist_index_type), intent(in) :: idx + character(len=*), dimension(:), intent(in) :: carray + + call list%insert_before( list%to_future_at_idxn( idx ), carray ) + + end subroutine insert_at_chararray_idx_wrap + + !> Version: experimental + !> + !> Inserts stringarray 'sarray' AT stringlist_index 'idx' in stringlist 'list' + !> Modifies the input stringlist 'list' + subroutine insert_at_stringarray_idx_wrap( list, idx, sarray ) + class(stringlist_type), intent(inout) :: list + type(stringlist_index_type), intent(in) :: idx + type(string_type), dimension(:), intent(in) :: sarray + + call list%insert_before( list%to_future_at_idxn( idx ), sarray ) + + end subroutine insert_at_stringarray_idx_wrap + + !> Version: experimental + !> + !> Inserts 'positions' number of empty positions BEFORE integer index 'idxn' + !> Modifies the input stringlist 'list' + subroutine insert_before_empty_positions( list, idxn, positions ) + !> Not a part of public API + class(stringlist_type), intent(inout) :: list + integer, intent(inout) :: idxn + integer, intent(in) :: positions + + integer :: i, inew + integer :: new_len, old_len + type(string_type), dimension(:), allocatable :: new_stringarray + + if (positions > 0) then + + idxn = clip( idxn, 1, list%len() + 1 ) + old_len = list%len() + new_len = old_len + positions + + allocate( new_stringarray(new_len) ) + + do i = 1, idxn - 1 + ! TODO: can be improved by move + new_stringarray(i) = list%stringarray(i) + end do + do i = idxn, old_len + inew = i + positions + ! TODO: can be improved by move + new_stringarray(inew) = list%stringarray(i) + end do + + call move_alloc( new_stringarray, list%stringarray ) + + end if + + end subroutine insert_before_empty_positions + + !> Version: experimental + !> + !> Inserts string 'string' BEFORE integer index 'idxn' in the underlying stringarray + !> Modifies the input stringlist 'list' + subroutine insert_before_string_int_impl( list, idxn, string ) + !> Not a part of public API + class(stringlist_type), intent(inout) :: list + integer, intent(in) :: idxn + type(string_type), intent(in) :: string + + integer :: work_idxn + + work_idxn = idxn + call insert_before_empty_positions( list, work_idxn, 1 ) + + list%stringarray(work_idxn) = string + + end subroutine insert_before_string_int_impl + + !> Version: experimental + !> + !> Inserts stringlist 'slist' BEFORE integer index 'idxn' in the underlying stringarray + !> Modifies the input stringlist 'list' + subroutine insert_before_stringlist_int_impl( list, idxn, slist ) + !> Not a part of public API + class(stringlist_type), intent(inout) :: list + integer, intent(in) :: idxn + type(stringlist_type), intent(in) :: slist + + integer :: i + integer :: work_idxn, idxnew + integer :: pre_length, post_length + + work_idxn = idxn + pre_length = slist%len() + call insert_before_empty_positions( list, work_idxn, pre_length ) + post_length = slist%len() + + do i = 1, min( work_idxn - 1, pre_length ) + idxnew = work_idxn + i - 1 + list%stringarray(idxnew) = slist%stringarray(i) + end do + + do i = work_idxn + post_length - pre_length, post_length + idxnew = work_idxn + i - post_length + pre_length - 1 + list%stringarray(idxnew) = slist%stringarray(i) + end do + + end subroutine insert_before_stringlist_int_impl + + !> Version: experimental + !> + !> Inserts chararray 'carray' BEFORE integer index 'idxn' in the underlying stringarray + !> Modifies the input stringlist 'list' + subroutine insert_before_chararray_int_impl( list, idxn, carray ) + !> Not a part of public API + class(stringlist_type), intent(inout) :: list + integer, intent(in) :: idxn + character(len=*), dimension(:), intent(in) :: carray + + integer :: i + integer :: work_idxn, idxnew + + work_idxn = idxn + call insert_before_empty_positions( list, work_idxn, size( carray ) ) + + do i = 1, size( carray ) + idxnew = work_idxn + i - 1 + list%stringarray(idxnew) = string_type( carray(i) ) + end do + + end subroutine insert_before_chararray_int_impl + + !> Version: experimental + !> + !> Inserts stringarray 'sarray' BEFORE integer index 'idxn' in the underlying stringarray + !> Modifies the input stringlist 'list' + subroutine insert_before_stringarray_int_impl( list, idxn, sarray ) + !> Not a part of public API + class(stringlist_type), intent(inout) :: list + integer, intent(in) :: idxn + type(string_type), dimension(:), intent(in) :: sarray + + integer :: i + integer :: work_idxn, idxnew + + work_idxn = idxn + call insert_before_empty_positions( list, work_idxn, size( sarray ) ) + + do i = 1, size( sarray ) + idxnew = work_idxn + i - 1 + list%stringarray(idxnew) = sarray(i) + end do + + end subroutine insert_before_stringarray_int_impl + + ! get: + + !> Version: experimental + !> + !> Returns the string present at stringlist_index 'idx' in stringlist 'list' + !> Returns string_type instance + pure function get_string_idx_wrap( list, idx ) + class(stringlist_type), intent(in) :: list + type(stringlist_index_type), intent(in) :: idx + type(string_type) :: get_string_idx_wrap + + integer :: idxn + + idxn = list%to_current_idxn( idx ) + + ! if the index is out of bounds, return a string_type equivalent to empty string + if ( 1 <= idxn .and. idxn <= list%len() ) then + get_string_idx_wrap = list%stringarray(idxn) + + end if + + end function get_string_idx_wrap + + +end module stdlib_stringlist_type diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 7d2286fef..01df5d678 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -18,6 +18,7 @@ add_subdirectory(string) add_subdirectory(system) add_subdirectory(quadrature) add_subdirectory(math) +add_subdirectory(stringlist) ADDTEST(always_skip) set_tests_properties(always_skip PROPERTIES SKIP_RETURN_CODE 77) diff --git a/src/tests/Makefile.manual b/src/tests/Makefile.manual index c29170e24..3e801ad4b 100644 --- a/src/tests/Makefile.manual +++ b/src/tests/Makefile.manual @@ -11,4 +11,5 @@ all test clean: $(MAKE) -f Makefile.manual --directory=stats $@ $(MAKE) -f Makefile.manual --directory=string $@ $(MAKE) -f Makefile.manual --directory=math $@ + $(MAKE) -f Makefile.manual --directory=stringlist $@ $(MAKE) -f Makefile.manual --directory=linalg $@ diff --git a/src/tests/stringlist/CMakeLists.txt b/src/tests/stringlist/CMakeLists.txt new file mode 100644 index 000000000..b0a8f46e9 --- /dev/null +++ b/src/tests/stringlist/CMakeLists.txt @@ -0,0 +1,2 @@ +ADDTEST(insert_at) +ADDTEST(append_prepend) diff --git a/src/tests/stringlist/Makefile.manual b/src/tests/stringlist/Makefile.manual new file mode 100644 index 000000000..8140e758c --- /dev/null +++ b/src/tests/stringlist/Makefile.manual @@ -0,0 +1,5 @@ +PROGS_SRC = test_insert_at.f90 \ + test_append_prepend.f90 + + +include ../Makefile.manual.test.mk diff --git a/src/tests/stringlist/test_append_prepend.f90 b/src/tests/stringlist/test_append_prepend.f90 new file mode 100644 index 000000000..9b12bec08 --- /dev/null +++ b/src/tests/stringlist/test_append_prepend.f90 @@ -0,0 +1,219 @@ +! SPDX-Identifier: MIT +module test_append_prepend + use stdlib_error, only: check + use stdlib_string_type, only: string_type, operator(//), operator(==) + use stdlib_stringlist_type, only: stringlist_type, fidx, bidx, list_head, & + & list_tail, operator(//), operator(==), operator(/=) + use stdlib_strings, only: to_string + implicit none + +contains + + subroutine test_append_prepend_string + type(stringlist_type) :: work_list + type(stringlist_type) :: reference_list + integer :: i + integer, parameter :: first = -100 + integer, parameter :: last = 100 + character(len=:), allocatable :: string + + do i = first, last + string = to_string(i) + work_list = work_list // string + call check( work_list%get( fidx( i - first + 1 ) ) == string_type( string ), & + & "test_append_prepend_string: get fidx( i - first + 1 ) " // string ) + + end do + + call compare_list( work_list, first, last + 1, 1 ) + call check( work_list == [ ( string_type( to_string(i) ), i = first, last ) ], & + & "test_append_prepend_string: work_list ==& + & [ ( string_type( to_string(i) ), i = first, last ) ]" ) + call check( [ ( string_type( to_string(i) ), i = first, last ) ] == work_list, & + & "test_append_prepend_string: [ ( string_type( to_string(i) ),& + & i = first, last ) ] == work_list" ) + + do i = last, first, -1 + call check( work_list /= reference_list, "test_append_prepend_string:& + & work_list /= reference_list" ) + call check( reference_list /= work_list, "test_append_prepend_string:& + & reference_list /= work_list" ) + + string = to_string(i) + reference_list = string_type( string ) // reference_list + call check( reference_list%get( bidx( last - i + 1 ) ) == string, & + & "test_append_prepend_string: get bidx( last - i + 1 ) " // string ) + + end do + + call compare_list( reference_list, first, last + 1, 2 ) + call check( reference_list == [ ( string_type( to_string(i) ), i = first, last ) ], "test_append_prepend_string:& + & reference_list == [ ( string_type( to_string(i) ), i = first, last ) ]" ) + call check( [ ( string_type( to_string(i) ), i = first, last ) ] == reference_list, & + & "test_append_prepend_string: [ ( string_type( to_string(i) ), i = first, last ) ] == reference_list" ) + + call check( work_list == reference_list, "test_append_prepend_string:& + & work_list == reference_list" ) + call check( reference_list == work_list, "test_append_prepend_string:& + & reference_list == work_list" ) + + end subroutine test_append_prepend_string + + subroutine test_append_prepend_array + type(stringlist_type) :: work_list + type(stringlist_type) :: reference_list + integer :: i, j + integer, parameter :: first = -100 + integer, parameter :: last = 100 + integer, parameter :: stride = 10 + + do i = first, last - 1, stride + work_list = work_list // [ ( string_type( to_string(j) ), j = i, i + stride - 1) ] + call check( work_list == [ ( string_type( to_string(j) ), j = first, i + stride - 1) ], & + & "test_append_prepend_array: work_list ==& + & [ ( string_type( to_string(j) ), j = first, i + stride - 1) ]" ) + + end do + + work_list = work_list // to_string(last) + + call compare_list( work_list, first, last + 1, 3 ) + call check( work_list == [ ( string_type( to_string(i) ), i = first, last) ], & + & "test_append_prepend_array: work_list ==& + & [ ( string_type( to_string(i) ), i = first, last) ]" ) + call check( [ ( string_type( to_string(i) ), i = first, last) ] == work_list, & + & "test_append_prepend_array: [ ( string_type( to_string(i) ), i = first, last) ]& + & == work_list" ) + + do i = last, first + 1, -1 * stride + call check( work_list /= reference_list, "test_append_prepend_array:& + & work_list /= reference_list" ) + call check( reference_list /= work_list, "test_append_prepend_array:& + & reference_list /= work_list" ) + + reference_list = [ ( string_type( to_string(j) ), j = i - stride + 1, i ) ] & + & // reference_list + call check( reference_list == & + & [ ( string_type( to_string(j) ), j = i - stride + 1, last ) ], & + & "test_append_prepend_array: reference_list ==& + & [ ( string_type( to_string(j) ), j = i - stride + 1, last ) ]" ) + + end do + + reference_list = to_string(first) // reference_list + + call compare_list( reference_list, first, last + 1, 4 ) + call check( [ ( string_type( to_string(i) ), i = first, last) ] == reference_list, & + & "test_append_prepend_array:& + & [ ( string_type( to_string(i) ), i = first, last) ] == reference_list" ) + call check( [ ( string_type( to_string(i) ), i = first, last) ] == reference_list, & + & "test_append_prepend_array: [ ( string_type( to_string(i) ), i = first, last) ]& + & == reference_list" ) + + call check( work_list == reference_list, "test_append_prepend_array:& + & work_list == reference_list" ) + call check( reference_list == work_list, "test_append_prepend_array:& + & reference_list == work_list" ) + + end subroutine test_append_prepend_array + + subroutine test_append_prepend_list + type(stringlist_type) :: work_list, reference_list + type(stringlist_type) :: temp_list + integer :: i, j + integer, parameter :: first = -100 + integer, parameter :: last = 100 + integer, parameter :: stride = 10 + + do i = first, last - 1, stride + call temp_list%clear() + do j = i, i + stride - 1 + call temp_list%insert_at( list_tail, string_type( to_string(j) ) ) + end do + work_list = work_list // temp_list + + call check( work_list == [ ( string_type( to_string(j) ), j = first, i + stride - 1 ) ], & + & "test_append_prepend_list: work_list ==& + & [ ( to_string(j), j = first, i + stride - 1) ]" ) + + end do + + work_list = work_list // to_string(last) + + call compare_list( work_list, first, last + 1, 5 ) + call check( work_list == [ ( string_type( to_string(i) ), i = first, last) ], "test_append_prepend_list:& + & work_list == [ ( string_type( to_string(i) ), i = first, last) ]" ) + call check( [ ( string_type( to_string(i) ), i = first, last) ] == work_list, & + & "test_append_prepend_list: [ ( string_type( to_string(i) ), i = first, last) ]& + & == work_list" ) + + do i = last, first + 1, -1 * stride + call check( work_list /= reference_list, "test_append_prepend_list:& + & work_list /= reference_list" ) + call check( reference_list /= work_list, "test_append_prepend_list:& + & reference_list /= work_list" ) + + call temp_list%clear() + do j = i - stride + 1, i + call temp_list%insert_at( list_tail, to_string(j) ) + end do + reference_list = temp_list // reference_list + + call check( reference_list == & + & [ ( string_type( to_string(j) ), j = i - stride + 1, last ) ], & + & "test_append_prepend_list: reference_list ==& + & [ ( string_type( to_string(j) ), j = i - stride + 1, last ) ]" ) + + end do + + reference_list = to_string(first) // reference_list + + call compare_list( reference_list, first, last + 1, 6 ) + call check( [ ( string_type( to_string(i) ), i = first, last) ] == reference_list, & + & "test_append_prepend_list:& + & [ ( string_type( to_string(i) ), i = first, last) ] == reference_list" ) + call check( [ ( string_type( to_string(i) ), i = first, last) ] == reference_list, & + & "test_append_prepend_list: [ ( string_type( to_string(i) ), i = first, last) ]& + & == reference_list" ) + + call check( work_list == reference_list, "test_append_prepend_list:& + & work_list == reference_list" ) + call check( reference_list == work_list, "test_append_prepend_list:& + & reference_list == work_list" ) + + end subroutine test_append_prepend_list + + ! compares input stringlist 'list' with an array of consecutive integers + ! array is 'first' inclusive and 'last' exclusive + subroutine compare_list(list, first, last, call_number) + type(stringlist_type), intent(in) :: list + integer, intent(in) :: first, last, call_number + integer :: i, j + + call check( abs( last - first ) == list%len(), "compare_list: length mis-match& + & call_number " // to_string( call_number ) ) + + j = merge(-1, 1, last < first) + do i = 1, list%len() + call check( list%get( fidx(i) ) == to_string( first + ( ( i - 1 ) * j ) ), & + & "compare_list: call_number " // to_string( call_number ) & + & // " fidx( " // to_string( i ) // " )") + call check( list%get( bidx(i) ) == to_string( last - ( i * j ) ), & + & "compare_list: call_number " // to_string( call_number ) & + & // " bidx( " // to_string( i ) // " )") + end do + + end subroutine compare_list + +end module test_append_prepend + + +program tester + use test_append_prepend + implicit none + + call test_append_prepend_string + call test_append_prepend_array + call test_append_prepend_list + +end program tester diff --git a/src/tests/stringlist/test_insert_at.f90 b/src/tests/stringlist/test_insert_at.f90 new file mode 100644 index 000000000..231ac0e9c --- /dev/null +++ b/src/tests/stringlist/test_insert_at.f90 @@ -0,0 +1,390 @@ +! SPDX-Identifier: MIT +module test_insert_at + use stdlib_error, only: check + use stdlib_string_type, only: string_type, operator(//), operator(==) + use stdlib_stringlist_type, only: stringlist_type, fidx, bidx, list_head, list_tail, operator(==) + use stdlib_strings, only: to_string + implicit none + +contains + + subroutine test_insert_at_string_1 + type(stringlist_type) :: work_list + integer :: i, current_length + character(len=:), allocatable :: string + integer, parameter :: first = -100 + integer, parameter :: last = 1 + + work_list = stringlist_type() + call check( work_list%len() == 0, "test_insert_at_string_1: constructor" ) + + write (*,*) "test_insert_at_string_1: Starting test case 1!" + current_length = 0 + do i = first, last + string = to_string( i ) + call work_list%insert_at( fidx(i), string ) + current_length = current_length + 1 + + call check( work_list%get( fidx(1) ) == string, "test_insert_at_string_1:& + & get fidx(1) " // string ) + call check( work_list%get( list_head ) == string, "test_insert_at_string_1:& + & get list_head " // string ) + call check( work_list%get( bidx(current_length) ) == string, "test_insert_at_string_1: get& + & bidx(current_length) " // string ) + call check( work_list%get( list_tail ) == to_string(first), "test_insert_at_string_1: get& + & list_tail " // string ) + + call check( work_list%len() == current_length, "test_insert_at_string_1: length check "& + & // to_string( current_length ) ) + + end do + + ! compare work_list with [1, 0, -1, ..., ..., -99, -100] + call compare_list( work_list, last, first - 1, 1) + + call work_list%clear() + call work_list%clear() + current_length = 0 + + write (*,*) "test_insert_at_string_1: Starting test case 2!" + do i = first, last + string = to_string( i ) + call work_list%insert_at( bidx(i), string ) + current_length = current_length + 1 + + call check( work_list%get( bidx(1) ) == string, "test_insert_at_string_1:& + & get bidx(1) " // string ) + call check( work_list%get( list_tail ) == string, "test_insert_at_string_1:& + & get list_tail " // string ) + call check( work_list%get( fidx(current_length) ) == string, "test_insert_at_string_1: get& + & fidx(current_length) " // string ) + call check( work_list%get( list_head ) == to_string(first), "test_insert_at_string_1: get& + & list_head " // string ) + + call check( work_list%len() == current_length, "test_insert_at_string_1: length check "& + & // to_string( current_length ) ) + + end do + + ! compare work_list with [-100, -99, ..., ..., 0, 1] + call compare_list( work_list, first, last + 1, 2) + + end subroutine test_insert_at_string_1 + + subroutine test_insert_at_string_2 + type(stringlist_type) :: work_list + integer :: i, current_length + character(len=:), allocatable :: string + integer, parameter :: first = 2 + integer, parameter :: last = 200 + + write (*,*) "test_insert_at_string_2: Starting test case 1!" + + current_length = 0 + do i = first, last, 2 + string = to_string( i ) + call work_list%insert_at( fidx(i), string ) + current_length = current_length + 1 + + call check( work_list%get( fidx(current_length) ) == string, "test_insert_at_string_2:& + & get fidx(current_length) " // string ) + call check( work_list%get( fidx(1) ) == to_string(first), "test_insert_at_string_2:& + & get fidx(1) " // string ) + call check( work_list%get( list_head ) == to_string(first), "test_insert_at_string_2:& + & get list_head " // string ) + call check( work_list%get( bidx(1) ) == string, "test_insert_at_string_2:& + & get bidx(1) " // string ) + call check( work_list%get( bidx(current_length) ) == to_string(first), "test_insert_at_string_2: get& + & bidx(current_length) " // string ) + call check( work_list%get( list_tail ) == string, "test_insert_at_string_2: get& + & list_tail " // string ) + + call check( work_list%len() == current_length, "test_insert_at_string_2: length check "& + & // to_string( current_length ) ) + + end do + + write (*,*) "test_insert_at_string_2: Starting test case 2!" + + do i = first - 1, last - 1, 2 + string = to_string( i ) + call work_list%insert_at( fidx(i), string ) + current_length = current_length + 1 + + call check( work_list%get( fidx(i) ) == string, "test_insert_at_string_2:& + & get fidx(current_length) " // string ) + call check( work_list%get( fidx(1) ) == to_string(first - 1), "test_insert_at_string_2:& + & get fidx(1) " // string ) + call check( work_list%get( list_head ) == to_string(first - 1), "test_insert_at_string_2:& + & get list_head " // string ) + call check( work_list%get( bidx(1) ) == to_string(last), "test_insert_at_string_2:& + & get bidx(1) " // string ) + call check( work_list%get( bidx(current_length) ) == to_string(first - 1), "test_insert_at_string_2: get& + & bidx(current_length) " // string ) + call check( work_list%get( list_tail ) == to_string(last), "test_insert_at_string_2: get& + & list_tail " // string ) + + call check( work_list%len() == current_length, "test_insert_at_string_2: length check "& + & // to_string( current_length ) ) + + end do + + ! compare work_list with [1, 2, ..., ..., 199, 200] + call compare_list( work_list, first - 1, last + 1, 3 ) + + end subroutine test_insert_at_string_2 + + subroutine test_insert_at_string_3 + type(stringlist_type) :: work_list + integer :: i, current_length + character(len=:), allocatable :: string + integer, parameter :: first = 2 + integer, parameter :: last = 200 + + write (*,*) "test_insert_at_string_3: Starting test case 1!" + + current_length = 0 + do i = first, last, 2 + string = to_string( i ) + call work_list%insert_at( bidx(i), string ) + current_length = current_length + 1 + + call check( work_list%get( bidx(current_length) ) == string, "test_insert_at_string_3:& + & get bidx(current_length) " // string ) + call check( work_list%get( bidx(1) ) == to_string(first), "test_insert_at_string_3:& + & get bidx(1) " // string ) + call check( work_list%get( list_tail ) == to_string(first), "test_insert_at_string_3:& + & get list_tail " // string ) + call check( work_list%get( fidx(1) ) == string, "test_insert_at_string_3:& + & get fidx(1) " // string ) + call check( work_list%get( fidx(current_length) ) == to_string(first), "test_insert_at_string_3: get& + & fidx(current_length) " // string ) + call check( work_list%get( list_head ) == string, "test_insert_at_string_3: get& + & list_head " // string ) + + call check( work_list%len() == current_length, "test_insert_at_string_3: length check "& + & // to_string( current_length ) ) + + end do + + write (*,*) "test_insert_at_string_3: Starting test case 2!" + + do i = first - 1, last - 1, 2 + string = to_string( i ) + call work_list%insert_at( bidx(i), string ) + current_length = current_length + 1 + + call check( work_list%get( bidx(i) ) == string, "test_insert_at_string_3:& + & get bidx(current_length) " // string ) + call check( work_list%get( bidx(1) ) == to_string(first - 1), "test_insert_at_string_3:& + & get bidx(1) " // string ) + call check( work_list%get( list_tail ) == to_string(first - 1), "test_insert_at_string_3:& + & get list_tail " // string ) + call check( work_list%get( fidx(1) ) == to_string(last), "test_insert_at_string_3:& + & get fidx(1) " // string ) + call check( work_list%get( fidx(current_length) ) == to_string(first - 1), "test_insert_at_string_3: get& + & fidx(current_length) " // string ) + call check( work_list%get( list_head ) == to_string(last), "test_insert_at_string_3: get& + & list_head " // string ) + + call check( work_list%len() == current_length, "test_insert_at_string_3: length check "& + & // to_string( current_length ) ) + + end do + + ! compare work_list with [200, 199, ..., ..., 2, 1] + call compare_list( work_list, last, first - 2, 4 ) + + end subroutine test_insert_at_string_3 + + subroutine test_insert_at_array + type(stringlist_type) :: work_list + type(stringlist_type) :: reference_list + integer :: i, j + integer, parameter :: first = -100 + integer, parameter :: last = 100 + integer, parameter :: stride = 4 + + write (*,*) "test_insert_at_array: Starting work_list!" + + call work_list%insert_at( list_head, & + & [ ( string_type( to_string(j) ), j = first, first + stride - 1 ) ] ) + + call compare_list( work_list, first, first + stride, 5 ) + + call work_list%insert_at( list_tail, & + & [ ( string_type( to_string(j) ), j = last - stride, last - 1 ) ] ) + + do i = first + stride, last - stride - 1, stride + call work_list%insert_at( fidx( i - first + 1 ), & + & [ ( string_type( to_string(j) ), j = i, i + stride - 1 ) ] ) + end do + + call work_list%insert_at( list_tail, [ to_string(last) ] ) + + call compare_list( work_list, first, last + 1, 6 ) + + write (*,*) "test_insert_at_array: Starting reference_list!" + + call reference_list%insert_at( list_tail, & + & [ ( string_type( to_string(j) ), j = last - stride + 1, last ) ] ) + + call compare_list( reference_list, last - stride + 1, last + 1, 7 ) + + call reference_list%insert_at( list_head, & + & [ ( string_type( to_string(j) ), j = first + 1, first + stride ) ] ) + + do i = last - stride, first + stride + 1, -1 * stride + call reference_list%insert_at( bidx( last - i + 1 ), & + & [ ( string_type( to_string(j) ), j = i - stride + 1, i ) ] ) + end do + + call reference_list%insert_at( list_head, [ to_string(first) ] ) + + call compare_list( reference_list, first, last + 1, 8 ) + + end subroutine test_insert_at_array + + subroutine test_insert_at_list + type(stringlist_type) :: work_list, reference_list + type(stringlist_type) :: temp_list + integer :: i, j + integer, parameter :: first = -100 + integer, parameter :: last = 100 + integer, parameter :: stride = 4 + + write (*,*) "test_insert_at_list: Starting work_list!" + + call temp_list%clear() + do j = first, first + stride - 1 + call temp_list%insert_at( list_tail, string_type( to_string(j) ) ) + end do + + call work_list%insert_at(list_head, temp_list) + call compare_list( work_list, first, first + stride, 9) + + call temp_list%clear() + do j = last - 1, last - stride, -1 + call temp_list%insert_at( list_head, string_type( to_string(j) ) ) + end do + + call work_list%insert_at(list_tail, temp_list) + + do i = first + stride, last - stride - 1, stride + call temp_list%clear() + do j = i, i + stride - 1 + call temp_list%insert_at( list_tail, to_string(j) ) + end do + call work_list%insert_at( fidx( i - first + 1 ), temp_list ) + + end do + + call temp_list%clear() + call temp_list%insert_at( list_head, to_string(last) ) + call work_list%insert_at( list_tail, temp_list ) + + call compare_list( work_list, first, last + 1, 10 ) + + write (*,*) "test_insert_at_list: Starting reference_list!" + + call temp_list%clear() + do j = last - stride + 1, last + call temp_list%insert_at( list_tail, to_string(j) ) + end do + + call reference_list%insert_at( list_tail, temp_list ) + call compare_list( reference_list, last - stride + 1, last + 1, 11 ) + + call temp_list%clear() + do j = first + 1, first + stride + call temp_list%insert_at( list_tail, string_type( to_string(j) ) ) + end do + + call reference_list%insert_at( list_head, temp_list ) + + do i = last - stride, first + stride + 1, -1 * stride + call temp_list%clear() + do j = i - stride + 1, i + call temp_list%insert_at( list_tail, to_string(j) ) + end do + call reference_list%insert_at( bidx( last - i + 1 ), temp_list ) + + end do + + call temp_list%clear() + call temp_list%insert_at( list_tail, to_string(first) ) + call reference_list%insert_at( list_head, temp_list ) + + call compare_list( reference_list, first, last + 1, 12 ) + + end subroutine test_insert_at_list + + subroutine test_constructor + type(stringlist_type) :: work_list + character(len=4), allocatable :: carray(:) + type(string_type), allocatable :: sarray(:) + + write (*,*) "test_constructor: Starting test case 1!" + work_list = stringlist_type() + allocate( carray(0) ) + allocate( sarray(0) ) + call check( work_list == carray, "test_constructor:& + & test_case 1 work_list == carray" ) + call check( work_list == sarray, "test_constructor:& + & test_case 1 work_list == sarray" ) + + write (*,*) "test_constructor: Starting test case 2!" + carray = [ '#1', '#2', '#3', '#4' ] + sarray = [ string_type('#1'), string_type('#2'), string_type('#3'), string_type('#4') ] + work_list = stringlist_type( carray ) + call check( work_list == carray, "test_constructor:& + & test_case 2 work_list == carray" ) + call check( work_list == sarray, "test_constructor:& + & test_case 2 work_list == sarray" ) + + write (*,*) "test_constructor: Starting test case 3!" + work_list = stringlist_type( sarray ) + call check( work_list == carray, "test_constructor:& + & test_case 3 work_list == carray" ) + call check( work_list == sarray, "test_constructor:& + & test_case 3 work_list == sarray" ) + + end subroutine test_constructor + + ! compares input stringlist 'list' with an array of consecutive integers + ! array is 'first' inclusive and 'last' exclusive + subroutine compare_list(list, first, last, call_number) + type(stringlist_type), intent(in) :: list + integer, intent(in) :: first, last, call_number + integer :: i, j + + call check( abs( last - first ) == list%len(), "compare_list: length mis-match& + & call_number " // to_string( call_number ) ) + + j = merge(-1, 1, last < first) + do i = 1, list%len() + call check( list%get( fidx(i) ) == to_string( first + ( ( i - 1 ) * j ) ), & + & "compare_list: call_number " // to_string( call_number ) & + & // " fidx( " // to_string( i ) // " )") + call check( list%get( bidx(i) ) == to_string( last - ( i * j ) ), & + & "compare_list: call_number " // to_string( call_number ) & + & // " bidx( " // to_string( i ) // " )") + end do + + end subroutine compare_list + +end module test_insert_at + + +program tester + use test_insert_at + implicit none + + call test_insert_at_string_1 + call test_insert_at_string_2 + call test_insert_at_string_3 + call test_insert_at_array + call test_insert_at_list + call test_constructor + +end program tester