├── .gitignore
├── LICENSE
├── README.md
├── fpm.toml
├── project
├── Makefile
├── fortran-regex.cbp
├── fortran-regex.depend
├── fortran-regex.layout
├── main.f90
├── regex_module.mod
├── regex_test_1.mod
├── regex_test_2.mod
└── regex_testdata.mod
├── src
└── regex.f90
└── test
├── results.txt
├── test_1.f90
├── test_2.f90
├── test_2_data.txt
├── test_m_regex.f90
└── tests.f90
/.gitignore:
--------------------------------------------------------------------------------
1 | *.mod
2 | project/*.mod
3 | project/regex_module.mod
4 | project/regex_test_1.mod
5 | *.mod
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Federico Perini
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### fortran-regex
2 |
3 | Fortran-regex is a Modern Fortran port of the [tiny-regex-c](https://github.com/kokke/tiny-regex-c) library for regular expressions. It is based on the original C implementation, but the API is modelled in Fortran style, which is similar to the intrinsic `index` function.
4 |
5 | ### API
6 |
7 | The main API is modelled around Fortran's `index` intrinsic function (which performs a simple search for a substring within a string):
8 |
9 | ```fortran
10 | ! Perform regex on a character string
11 | result = REGEX(string, pattern [, length] [, [back]])
12 | ```
13 | - If the pattern command is invalid, `result = -1`.
14 | - If no substrings with the given pattern are found, with a valid pattern `result = 0`. This is also returned if the string has zero length, and that is an acceptable answer for the input pattern.
15 | - Otherwise, if the pattern was found `result > 0` equal to the leftmost location inside the string where the pattern can be found. If the `back` keyword is provided and `.true.`, the location of the rightmost occurrence is returned instead. `length` returns the number of consecutive characters that match this pattern
16 |
17 | ### Arguments
18 |
19 | | Argument | Type | Intent | Description |
20 | | --- | --- | --- | --- |
21 | | `string` | `character(*)` | `in` | The input text |
22 | | `pattern` | `character(*)` | `in` | The regex command |
23 | | `length` | `integer` | `out` (optional) | Length of the matched pattern |
24 | | `back` | `logical` | `in` (optional) | If the BACK argument is present and `.true.`, the return value is the start of the last occurrence rather than the first. |
25 |
26 |
27 |
28 | ### Object-oriented interface
29 |
30 | One can also parse a regex pattern into a `type(regex_pattern)` structure, and use that instead of a string pattern. I have no idea why this should be useful, but at least it's given with a consistent interface
31 |
32 | ### Overview
33 |
34 | The original tiny-regex-c code has been significantly refactored, to:
35 |
36 | * Remove all references to `NULL` character string termination, and replace them with Fortran's string intrinsics (`len`, `len_trim`, etc.)
37 | * Remove all C escaped characters (`\n`, `\t`, etc), replace with Fortran syntax.
38 | * Even in presence of strings, use `pure` `elemental` functions wherever possible
39 | * It is a standalone module that has no external dependencies besides compiler modules.
40 |
41 | ### Example programs
42 |
43 | ```fortran
44 | ! Demonstrate use of regex
45 | program test_regex
46 | use regex_module
47 | implicit none
48 |
49 | integer :: idx,ln
50 | character(*), parameter :: text = 'table football'
51 |
52 | idx = REGEX(string=text,pattern='foo*',length=ln);
53 |
54 | ! Prints "foo"
55 | print *, text(idx:idx+ln-1)
56 |
57 | end program
58 | ```
59 |
60 |
61 | ```fortran
62 | ! Demonstrate use of object-oriented interface
63 | program test_regex
64 | use regex_module
65 | implicit none
66 |
67 | integer :: idx,ln
68 | character(*), parameter :: text = 'table football'
69 | type(regex_pattern) :: re
70 |
71 | ! Parse pattern into a regex structure
72 | re = parse_pattern('foo*')
73 |
74 | idx = REGEX(string=text,pattern=re,length=ln);
75 |
76 | ! Prints "foo"
77 | print *, text(idx:idx+ln-1)
78 |
79 | end program
80 | ```
81 |
82 | ### To do list
83 |
84 | - [X] Add a `BACK` optional keyword to return the last instance instead of the first.
85 | - [ ] Option to return ALL instances as an array, instead of the first/last one only.
86 | - [X] Replace fixed-size static storage with allocatable character strings (slower?)
87 |
88 | ### Reporting problems
89 |
90 | Please report any problems! It is appreciated. The original C library had hacks to account for the fact that several special characters are read in with escaped sequences, which partially collides with the escaped sequence options in regex. So, expect the current API to be still a bit rough around the edges.
91 |
92 | ### License
93 |
94 | fortran-regex is released under the MIT license. The code it's based upon is in the public domain.
95 |
--------------------------------------------------------------------------------
/fpm.toml:
--------------------------------------------------------------------------------
1 | name = "fortran-regex"
2 | author = "Federico Perini"
3 | copyright = "Copyright (c) 2022-2023, Federico Perini"
4 | version = "1.1.2"
5 | license = "MIT"
6 | description = "Fortran regex library"
7 | homepage = "https://github.com/perazz/fortran-regex"
8 | keywords = ["regex","regular expressions","strings"]
9 |
10 | [build]
11 | auto-executables = false
12 | auto-examples = false
13 | auto-tests = true
14 | module-naming = "regex"
15 |
16 | [library]
17 | source-dir = "src"
18 | include-dir = "src"
19 |
20 | [install]
21 | library = true
22 |
23 | [[test]]
24 | name="regex_tests"
25 | source-dir="test"
26 | main="tests.f90"
27 |
28 |
--------------------------------------------------------------------------------
/project/Makefile:
--------------------------------------------------------------------------------
1 | #
2 | # This Makefile was generated by Code::Blocks IDE.
3 | #
4 |
5 | SRCS_f90d1 = \
6 | regex.f90
7 |
8 | OBJS_f90d1 = \
9 | regex.o
10 |
11 | SRC_DIR_f90d1 = ../src/
12 | OBJS_DIR = obj/Debug/src/
13 | EXE_DIR = bin/Debug/
14 |
15 | EXE = fortran-regex
16 | FC = gcc-11
17 | LD = gcc-11
18 | IDIR =
19 | CFLAGS = -Wall -g -J$(OBJS_DIR) $(IDIR)
20 | LFLAGS =
21 | LIBS =
22 |
23 | VPATH = $(SRC_DIR_f90d1):$(OBJS_DIR)
24 | OBJS = $(addprefix $(OBJS_DIR), $(OBJS_f90d1))
25 |
26 | all : $(EXE)
27 |
28 | $(EXE) : $(OBJS_f90d1)
29 | @mkdir -p $(EXE_DIR)
30 | $(LD) -o $(EXE_DIR)$(EXE) $(OBJS) $(LFLAGS) $(LIBS)
31 |
32 | $(OBJS_f90d1):
33 | @mkdir -p $(OBJS_DIR)
34 | $(FC) $(CFLAGS) -c $(SRC_DIR_f90d1)$(@:.o=.f90) -o $(OBJS_DIR)$@
35 |
36 | clean :
37 | rm -f $(OBJS_DIR)*.*
38 | rm -f $(EXE_DIR)$(EXE)
39 |
40 | # File dependencies
41 | regex.o: \
42 | regex.f90
43 |
44 |
--------------------------------------------------------------------------------
/project/fortran-regex.cbp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/project/fortran-regex.depend:
--------------------------------------------------------------------------------
1 | # depslib dependency file v1.0
2 | 1673295739 source:/Users/federico/code/fortran-regex/src/regex.f90
3 |
4 | 1673295130 source:/Users/federico/code/fortran-regex/test/test_1.f90
5 |
6 | 1671272275 source:/Users/federico/code/fortran-regex/test/testdata.f90
7 |
8 | 1671127283 source:i:\fortran-regex\test\testdata.f90
9 |
10 | 1671525497 source:i:\fortran-regex\src\regex.f90
11 |
12 | 1671525497 source:i:\fortran-regex\test\test_1.f90
13 |
14 | 1671311161 source:/Users/federico/code/fortran-regex/test/test_2.f90
15 |
16 | 1673295146 source:/Users/federico/code/fortran-regex/test/tests.f90
17 |
18 | 1673295146 source:/Users/federico/code/fortran-regex/test/test_m_regex.f90
19 |
20 | 1671525497 source:i:\fortran-regex\test\test_2.f90
21 |
22 | 1671525497 source:i:\fortran-regex\test\test_m_regex.f90
23 |
24 |
--------------------------------------------------------------------------------
/project/fortran-regex.layout:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/project/main.f90:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/project/regex_module.mod:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/perazz/fortran-regex/96ab33fe003862a28cec91ddd170ac0e86c26c87/project/regex_module.mod
--------------------------------------------------------------------------------
/project/regex_test_1.mod:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/perazz/fortran-regex/96ab33fe003862a28cec91ddd170ac0e86c26c87/project/regex_test_1.mod
--------------------------------------------------------------------------------
/project/regex_test_2.mod:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/perazz/fortran-regex/96ab33fe003862a28cec91ddd170ac0e86c26c87/project/regex_test_2.mod
--------------------------------------------------------------------------------
/project/regex_testdata.mod:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/perazz/fortran-regex/96ab33fe003862a28cec91ddd170ac0e86c26c87/project/regex_testdata.mod
--------------------------------------------------------------------------------
/src/regex.f90:
--------------------------------------------------------------------------------
1 | ! *************************************************************************************************
2 | ! ____ ___________________ __
3 | ! / __ \/ ____/ ____/ ____/ |/ /
4 | ! / /_/ / __/ / / __/ __/ | /
5 | ! / _, _/ /___/ /_/ / /___ / |
6 | ! /_/ |_/_____/\____/_____//_/|_|
7 | !
8 | ! MIT License
9 | !
10 | ! (C) Federico Perini, 2022
11 | ! A Fortran port of the tiny-regex library.
12 | !
13 | ! https://github.com/kokke/tiny-regex-c
14 | ! Code inspired by Rob Pike's regex code described in:
15 | ! http://www.cs.princeton.edu/courses/archive/spr09/cos333/beautiful.html
16 | !
17 | ! *************************************************************************************************
18 | module regex_module
19 | use iso_fortran_env, only: output_unit
20 | implicit none
21 | private
22 |
23 | public :: parse_pattern
24 | public :: check_pattern
25 | public :: regex
26 |
27 | ! Character kind
28 | integer, parameter, public :: RCK = selected_char_kind("ascii")
29 |
30 | logical, parameter, public :: RE_DOT_MATCHES_NEWLINE = .true. ! Define .false. if you DON'T want '.' to match '\r' + '\n'
31 | integer, parameter, public :: MAX_REGEXP_OBJECTS = 512 ! Max number of regex symbols in expression.
32 | integer, parameter, public :: MAX_CHAR_CLASS_LEN = 1024 ! Max length of character-class buffer in.
33 |
34 | ! Turn on verbosity for debugging
35 | logical, parameter :: DEBUG = .false.
36 |
37 | ! Supported patterns
38 | integer, parameter :: UNUSED = 0
39 | integer, parameter :: DOT = 1 ! '.' Dot, matches any character
40 | integer, parameter :: BEGIN_WITH = 2 ! '^' Start anchor, matches beginning of string
41 | integer, parameter :: END_WITH = 3 ! '$' End anchor, matches end of string
42 | integer, parameter :: QUESTIONMARK = 4 ! '?' Question, match zero or one (non-greedy)
43 | integer, parameter :: STAR = 5 ! '*' Asterisk, match zero or more (greedy)
44 | integer, parameter :: PLUS = 6 ! '+' Plus, match one or more (greedy)
45 | integer, parameter :: ATCHAR = 7 ! '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z }
46 | integer, parameter :: AT_CHAR_CLASS = 8 ! '[abc]' Character class, match if one of {'a', 'b', 'c'}
47 | integer, parameter :: INV_CHAR_CLASS = 9 ! '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'} -- NOTE: feature is currently broken!
48 | integer, parameter :: DIGIT = 10 ! '\d' Digits, [0-9]
49 | integer, parameter :: NOT_DIGIT = 11 ! '\D' Non-digits
50 | integer, parameter :: ALPHA = 12 ! '\w' Alphanumeric, [a-zA-Z0-9_]
51 | integer, parameter :: NOT_ALPHA = 13 ! '\W' Non-alphanumeric
52 | integer, parameter :: WHITESPACE = 14 ! '\s' Whitespace, \t \f \r \n \v and spaces
53 | integer, parameter :: NOT_WHITESPACE = 15 ! '\S' Non-whitespace
54 |
55 | character(kind=RCK,len=*), parameter :: types(*) = [ character(len=14) :: "UNUSED", "DOT", "BEGIN", "END", "QUESTIONMARK", &
56 | "STAR", "PLUS", "CHAR", "CHAR_CLASS", "INV_CHAR_CLASS", "DIGIT", "NOT_DIGIT", "ALPHA", "NOT_ALPHA", &
57 | "WHITESPACE", "NOT_WHITESPACE", "BRANCH" ]
58 |
59 | ! Characters
60 | character(kind=RCK,len=*), parameter :: lowercase="abcdefghijklmnopqrstuvwxyz"
61 | character(kind=RCK,len=*), parameter :: uppercase="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
62 |
63 | character(kind=RCK), parameter, public :: UNDERSCORE = "_"
64 | character(kind=RCK), parameter, public :: SPACE = " "
65 | character(kind=RCK), parameter, public :: DASH = "-"
66 | character(kind=RCK), parameter, public :: CNULL = achar( 0,kind=RCK) ! \0 or null character
67 | character(kind=RCK), parameter, public :: NEWLINE = achar(10,kind=RCK) ! \n or line feed
68 | character(kind=RCK), parameter, public :: BACKSPCE = achar( 8,kind=RCK) ! \b or backspace character
69 | character(kind=RCK), parameter, public :: TAB = achar( 9,kind=RCK) ! \t or tabulation character
70 |
71 | ! Regex pattern element
72 | type, public :: regex_token
73 |
74 | integer :: type = UNUSED
75 |
76 | ! Single or multi-character pattern
77 | character(kind=RCK,len=:), allocatable :: ccl
78 | contains
79 |
80 | procedure :: print => print_pattern
81 | procedure :: destroy => pat_destroy
82 | procedure :: match => pat_match
83 |
84 | end type regex_token
85 |
86 | type, public :: regex_pattern
87 |
88 | integer :: n = 0
89 |
90 | type(regex_token), dimension(MAX_REGEXP_OBJECTS) :: pattern
91 |
92 | contains
93 |
94 | procedure :: new => new_from_pattern
95 | procedure :: write => write_pattern
96 | procedure :: nrules
97 | procedure :: destroy
98 | final :: finalize
99 |
100 | end type regex_pattern
101 |
102 | ! Public interface
103 | interface regex
104 | module procedure re_match
105 | module procedure re_match_noback
106 | module procedure re_match_nolength
107 | module procedure re_match_nolength_noback
108 | module procedure re_matchp
109 | module procedure re_matchp_noback
110 | module procedure re_matchp_nolength
111 | module procedure re_matchp_nolength_noback
112 | end interface regex
113 |
114 | ! Override default constructor for ifort bug
115 | interface regex_token
116 | module procedure pat_from_char
117 | end interface regex_token
118 |
119 |
120 | contains
121 |
122 | ! Construct a regex pattern from a single character
123 | elemental type(regex_token) function pat_from_char(type,ccl) result(this)
124 | integer, intent(in) :: type
125 | character(kind=RCK), intent(in) :: ccl
126 | call pat_destroy(this)
127 | this%type = type
128 | allocate(character(len=1,kind=RCK) :: this%ccl)
129 | this%ccl(1:1) = ccl
130 | end function pat_from_char
131 |
132 | ! Check that a pattern matches the expected result
133 | logical function check_pattern(string,pattern,expected) result(success)
134 | character(len=*,kind=RCK), intent(in) :: string
135 | character(len=*,kind=RCK), intent(in) :: pattern
136 | character(len=*,kind=RCK), intent(in) :: expected
137 |
138 | integer :: idx,length
139 |
140 | idx = regex(string,pattern,length)
141 |
142 | if (idx>0) then
143 | success = length==len(expected)
144 | if (success) success = string(idx:idx+length-1)==expected
145 | else
146 | success = len(expected)<=0
147 | end if
148 |
149 | if (DEBUG .and. .not.success) then
150 | print "('[regex] test FAILED: text=',a,' pattern=',a,' index=',i0,' len=',i0)", &
151 | string,pattern,idx,length
152 | stop 1
153 | endif
154 |
155 | end function check_pattern
156 |
157 | ! Clean up a pattern
158 | elemental subroutine pat_destroy(this)
159 | class(regex_token), intent(inout) :: this
160 | integer :: ierr
161 | this%type = UNUSED
162 | deallocate(this%ccl,stat=ierr)
163 | end subroutine pat_destroy
164 |
165 | ! Number of rules in the current pattern
166 | elemental integer function nrules(this)
167 | class(regex_pattern), intent(in) :: this
168 | integer :: i
169 | nrules = 0
170 | do i=1,MAX_REGEXP_OBJECTS
171 | if (this%pattern(i)%type==UNUSED) return
172 | nrules = nrules + 1
173 | end do
174 | end function nrules
175 |
176 | subroutine write_pattern(this,iunit)
177 | class(regex_pattern), intent(in) :: this
178 | integer, optional, intent(in) :: iunit
179 |
180 | integer :: i,u
181 |
182 | if (present(iunit)) then
183 | u = iunit
184 | else
185 | u = output_unit
186 | end if
187 |
188 | do i=1,this%nrules()
189 | write(u,'(a)') this%pattern(i)%print()
190 | end do
191 |
192 | end subroutine write_pattern
193 |
194 | elemental subroutine destroy(this)
195 | class(regex_pattern), intent(inout) :: this
196 | integer :: i
197 | do i=1,MAX_REGEXP_OBJECTS
198 | call this%pattern(i)%destroy()
199 | end do
200 | end subroutine destroy
201 |
202 | subroutine finalize(this)
203 | type(regex_pattern), intent(inout) :: this
204 | integer :: i
205 | do i=1,MAX_REGEXP_OBJECTS
206 | call this%pattern(i)%destroy()
207 | end do
208 | end subroutine finalize
209 |
210 | ! Check that a character matches a dot ("any character") pattern
211 | elemental logical function matchdot(c)
212 | character(kind=RCK), intent(in) :: c
213 | if (RE_DOT_MATCHES_NEWLINE) then
214 | matchdot = .true.
215 | else
216 | matchdot = c/=NEWLINE .and. c/=BACKSPCE
217 | end if
218 | end function matchdot
219 |
220 | elemental logical function ismetachar(c)
221 | character(kind=RCK), intent(in) :: c
222 | ismetachar = index("sSwWdD",c)>0
223 | end function ismetachar
224 |
225 | pure logical function matchmetachar(c, str)
226 | character(kind=RCK), intent(in) :: c
227 | character(kind=RCK,len=*), intent(in) :: str
228 |
229 | select case (str(1:1))
230 | case ('d'); matchmetachar = isdigit(c)
231 | case ('D'); matchmetachar = .not.isdigit(c)
232 | case ('w'); matchmetachar = isalphanum(c)
233 | case ('W'); matchmetachar = .not.isalphanum(c)
234 | case ('s'); matchmetachar = isspace(c)
235 | case ('S'); matchmetachar = .not.isspace(c)
236 | case default; matchmetachar = c==str(1:1)
237 | end select
238 | end function matchmetachar
239 |
240 | elemental logical function isdigit(c)
241 | character(kind=RCK), intent(in) :: c
242 | isdigit = index("1234567890",c)>0
243 | end function isdigit
244 |
245 | elemental logical function isalpha(c)
246 | character(kind=RCK), intent(in) :: c
247 | isalpha = index(lowercase,c)>0 .or. index(uppercase,c)>0
248 | end function isalpha
249 |
250 | elemental logical function isalphanum(c)
251 | character(kind=RCK), intent(in) :: c
252 | isalphanum = isalpha(c) .or. isdigit(c) .or. c==UNDERSCORE
253 | end function isalphanum
254 |
255 | elemental logical function isspace(c)
256 | character(kind=RCK), intent(in) :: c
257 | isspace = c==SPACE
258 | end function isspace
259 |
260 | ! Match range of the tye 0-9 or 5-7 etc.
261 | elemental logical function matchrange(c,str)
262 | character(kind=RCK), intent(in) :: c
263 | character(kind=RCK,len=*), intent(in) :: str ! the range pattern
264 |
265 | matchrange = len(str)>=3; if (.not.matchrange) return
266 | matchrange = c /= DASH &
267 | .and. str(1:1) /= DASH &
268 | .and. str(2:2) == DASH &
269 | .and. iachar(c)>=iachar(str(1:1)) &
270 | .and. iachar(c)<=iachar(str(3:3)) ! Range (number/letters) is in increasing order
271 |
272 | end function matchrange
273 |
274 | logical function matchcharclass(c,str) result(match)
275 | character(kind=RCK), intent(in) :: c ! The current character
276 | character(kind=RCK,len=*), intent(in) :: str ! The charclass contents
277 |
278 | integer :: i
279 |
280 | match = .false.
281 | i = 0
282 |
283 | ! All characters in the charclass contents
284 | loop: do while (ilen(str)
315 |
316 | else
317 | match = .true.
318 | end if
319 | return
320 | end if
321 |
322 | end do loop
323 |
324 | if (DEBUG) print *, 'charclass: no match on i=',i,' str=',trim(str),' c=',c
325 |
326 | end function matchcharclass
327 |
328 | recursive logical function matchquestion(p, pattern, text, matchlength)
329 | type(regex_token), intent(in) :: p, pattern(:)
330 | character(len=*,kind=RCK), intent(in) :: text
331 | integer, intent(inout) :: matchlength
332 |
333 | matchquestion = .false.
334 |
335 | if (p%type == UNUSED) then
336 | matchquestion = .true.
337 | return
338 | elseif (matchpattern(pattern, text, matchlength)) then
339 | matchquestion = .true.
340 | return
341 | elseif (len(text)>0) then
342 | if (pat_match(p,text) .and. len(text)>1) then
343 | if (matchpattern(pattern,text(2:),matchlength)) then
344 | matchlength = matchlength+1
345 | matchquestion = .true.
346 | return
347 | endif
348 | end if
349 | end if
350 |
351 | end function matchquestion
352 |
353 | recursive logical function matchstar(p, pattern, text, it0, matchlength)
354 | type(regex_token), intent(in) :: p, pattern(:)
355 | character(len=*,kind=RCK), intent(in) :: text
356 | integer, intent(in) :: it0 ! starting point
357 | integer, intent(inout) :: matchlength
358 |
359 | integer :: prelen,it
360 |
361 | if (DEBUG) print *, 'match star, length=',matchlength,' it0=',it0,' lenm=',len(text)
362 |
363 | if (len(text)<=0) then
364 | matchstar = .false.
365 | return
366 | end if
367 |
368 | ! Save input variables
369 | prelen = matchlength
370 | it = it0
371 |
372 | do while (it>0 .and. it<=len(text))
373 | if (.not.pat_match(p, text(it:))) exit
374 | it = it+1
375 | matchlength = matchlength+1
376 | end do
377 |
378 | do while (it>=it0)
379 | matchstar = matchpattern(pattern, text(it:), matchlength)
380 | it = it-1
381 | if (matchstar) return
382 | matchlength = matchlength-1
383 | end do
384 |
385 | matchlength = prelen
386 | matchstar = .false.
387 |
388 | end function matchstar
389 |
390 | recursive logical function matchplus(p, pattern, text, it0, matchlength)
391 | type(regex_token), intent(in) :: p, pattern(:)
392 | character(len=*,kind=RCK), intent(in) :: text
393 | integer, intent(in) :: it0
394 | integer, intent(inout) :: matchlength
395 |
396 | integer :: it
397 |
398 | if (DEBUG) print *, 'matching PLUS pattern'
399 |
400 | it = it0
401 | do while (it>0 .and. it<=len(text))
402 | if (.not. pat_match(p, text(it:))) exit
403 | it = it+1
404 | matchlength = matchlength+1
405 | end do
406 |
407 | do while (it>it0)
408 | matchplus = matchpattern(pattern, text(it:), matchlength)
409 | it = it-1
410 | if (matchplus) return
411 | matchlength = matchlength-1
412 | end do
413 |
414 | matchplus = .false.
415 |
416 | end function matchplus
417 |
418 | ! Find matches of the given pattern in the string
419 | integer function re_match(string, pattern, length, back) result(index)
420 | character(*,kind=RCK), intent(in) :: pattern
421 | character(*,kind=RCK), intent(in) :: string
422 | integer, intent(out) :: length
423 | logical, intent(in) :: back
424 | type (regex_pattern) :: command
425 |
426 | command = parse_pattern(pattern)
427 | index = re_matchp(string,command,length,back)
428 |
429 | end function re_match
430 |
431 | ! Find matches of the given pattern in the string
432 | integer function re_match_noback(string, pattern, length) result(index)
433 | character(*,kind=RCK), intent(in) :: pattern
434 | character(*,kind=RCK), intent(in) :: string
435 | integer, intent(out) :: length
436 | type (regex_pattern) :: command
437 |
438 | command = parse_pattern(pattern)
439 | index = re_matchp(string,command,length,.false.)
440 |
441 | end function re_match_noback
442 |
443 | ! Find matches of the given pattern in the string
444 | integer function re_match_nolength(string, pattern, back) result(index)
445 | character(*,kind=RCK), intent(in) :: pattern
446 | character(*,kind=RCK), intent(in) :: string
447 | logical , intent(in) :: back
448 |
449 | type (regex_pattern) :: command
450 | integer :: length
451 |
452 | command = parse_pattern(pattern)
453 | index = re_matchp(string,command,length,back)
454 |
455 | end function re_match_nolength
456 |
457 | ! Find matches of the given pattern in the string
458 | integer function re_match_nolength_noback(string, pattern) result(index)
459 | character(*,kind=RCK), intent(in) :: pattern
460 | character(*,kind=RCK), intent(in) :: string
461 |
462 | type (regex_pattern) :: command
463 | integer :: length
464 |
465 | command = parse_pattern(pattern)
466 | index = re_matchp(string,command,length,.false.)
467 |
468 | end function re_match_nolength_noback
469 |
470 | type(regex_pattern) function parse_pattern(pattern) result(this)
471 | character(*,kind=RCK), intent(in) :: pattern
472 |
473 | call new_from_pattern(this,pattern)
474 |
475 | end function parse_pattern
476 |
477 | subroutine new_from_pattern(this,pattern)
478 | class(regex_pattern), intent(inout) :: this
479 | character(*,kind=RCK), intent(in) :: pattern
480 |
481 | ! Local variables
482 | character(len=MAX_CHAR_CLASS_LEN,kind=RCK) :: ccl_buf ! size of buffer for chars in all char-classes in the expression. */
483 | integer :: loc,i,j,lenp,lenc
484 | character(kind=RCK) :: c
485 |
486 | ! Initialize class
487 | call this%destroy()
488 | ccl_buf = repeat(SPACE,MAX_CHAR_CLASS_LEN)
489 |
490 | if (DEBUG) print "('[regex] parsing pattern: <',a,'>')", trim(pattern)
491 |
492 | i = 1 ! index in pattern
493 | j = 1 ! index in re-compiled
494 | lenp = len_trim(pattern)
495 |
496 | ! Move along the pattern string
497 | to_the_moon: do while (i<=lenp)
498 |
499 | c = pattern(i:i)
500 | if (DEBUG) print "('[regex] at location ',i0,': <',a,'>')", i, c
501 |
502 | select case (c)
503 |
504 | ! Meta-characters are single-character patterns
505 | case ('^'); this%pattern(j) = regex_token(BEGIN_WITH,c)
506 | case ('$'); this%pattern(j) = regex_token(END_WITH,c)
507 | case ('.'); this%pattern(j) = regex_token(DOT,c)
508 | case ('*'); this%pattern(j) = regex_token(STAR,c)
509 | case ('+'); this%pattern(j) = regex_token(PLUS,c)
510 | case ('?'); this%pattern(j) = regex_token(QUESTIONMARK,c)
511 |
512 | ! Escaped character-classes (\s, \w, ...)
513 | case ('\');
514 |
515 | ! Parse an escaped character class
516 | if (i=lenp) then
553 | call this%destroy()
554 | return
555 | end if
556 |
557 | else
558 | this%pattern(j)%type = AT_CHAR_CLASS
559 | end if
560 |
561 | ! Remove any escape characters
562 | loc = index(pattern(i+1:),']')
563 | lenc = loc-1
564 | if (loc>0) then
565 | ccl_buf = pattern(i+1:i+loc-1)
566 | i = i+loc
567 | if (DEBUG) print "('[regex] at end of multi-character pattern: ',a)", trim(ccl_buf)
568 | else
569 | ! Incomplete [] pattern
570 | call this%destroy()
571 | return
572 | end if
573 |
574 | ! If there is any escape character(s), just check that the next is nonempty
575 | loc = index(ccl_buf,'\')
576 | if (loc>0) then
577 | if (loc>=len(ccl_buf)) then
578 | ! stop 'incomplete escaped character inside [] pattern'
579 | call this%destroy()
580 | return
581 | end if
582 | if (ccl_buf(loc+1:loc+1)==SPACE) then
583 | ! stop 'empty escaped character inside [] pattern'
584 | call this%destroy()
585 | return
586 | end if
587 | end if
588 |
589 | ! Ensure there are no spaces
590 |
591 | allocate(character(len=lenc,kind=RCK) :: this%pattern(j)%ccl)
592 | this%pattern(j)%ccl = ccl_buf(:lenc)
593 |
594 | case default
595 |
596 | ! Single character
597 | this%pattern(j) = regex_token(ATCHAR,c)
598 |
599 | end select
600 |
601 | if (DEBUG) print "('[regex] added pattern ',i0,': ',a)",j,this%pattern(j)%print()
602 |
603 | ! A pattern was added: move to next
604 | i = i+1
605 | j = j+1
606 | if (j>MAX_REGEXP_OBJECTS) stop 'max regexp reached!'
607 |
608 | end do to_the_moon
609 |
610 | ! Save number of patterns
611 | this%n = j-1
612 | return
613 |
614 | end subroutine new_from_pattern
615 |
616 | function print_pattern(pattern) result(msg)
617 | class(regex_token), intent(in) :: pattern
618 | character(:,kind=RCK), allocatable :: msg
619 |
620 | character(len=MAX_CHAR_CLASS_LEN,kind=RCK) :: buffer
621 | integer :: lt
622 |
623 | write(buffer,1) trim(types(pattern%type+1)),trim(pattern%ccl)
624 |
625 | lt = len_trim(buffer)
626 | allocate(character(len=lt,kind=RCK) :: msg)
627 | if (lt>0) msg(1:lt) = buffer(1:lt)
628 |
629 | 1 format('type=',a,:,1x,'char=',a)
630 |
631 | end function print_pattern
632 |
633 | ! Match a single pattern at the g
634 | recursive logical function pat_match(p, c) result(match)
635 | class(regex_token), intent(in) :: p
636 | character(kind=RCK), intent(in) :: c
637 |
638 | select case (p%type)
639 | case (DOT); match = matchdot(c)
640 | case (AT_CHAR_CLASS); match = matchcharclass(c,p%ccl)
641 | case (INV_CHAR_CLASS); match = .not.matchcharclass(c,p%ccl)
642 | case (DIGIT); match = isdigit(c)
643 | case (NOT_DIGIT); match = .not.isdigit(c)
644 | case (ALPHA); match = isalphanum(c)
645 | case (NOT_ALPHA); match = .not.isalphanum(c)
646 | case (WHITESPACE); match = isspace(c)
647 | case (NOT_WHITESPACE); match = .not.isspace(c)
648 | case default; match = c==p%ccl(1:1)
649 | end select
650 |
651 | if (DEBUG) print "('[regex] current pattern=',a,' at char=',a,' match? ',l1)", p%print(),c,match
652 |
653 | end function pat_match
654 |
655 | integer function re_matchp_nolength(string, pattern, back) result(index)
656 | type(regex_pattern), intent(in) :: pattern
657 | character(len=*,kind=RCK), intent(in) :: string
658 | logical, intent(in) :: back
659 | integer :: matchlength
660 | index = re_matchp(string, pattern, matchlength, back)
661 | end function re_matchp_nolength
662 |
663 | integer function re_matchp_nolength_noback(string, pattern) result(index)
664 | type(regex_pattern), intent(in) :: pattern
665 | character(len=*,kind=RCK), intent(in) :: string
666 | integer :: matchlength
667 | index = re_matchp(string, pattern, matchlength, .false.)
668 | end function re_matchp_nolength_noback
669 |
670 | integer function re_matchp_noback(string, pattern, length) result(index)
671 | type(regex_pattern), intent(in) :: pattern
672 | character(len=*,kind=RCK), intent(in) :: string
673 | integer, intent(out) :: length
674 | index = re_matchp(string, pattern, length, .false.)
675 | end function re_matchp_noback
676 |
677 |
678 | integer function re_matchp(string, pattern, length, back) result(index)
679 | type(regex_pattern), intent(in) :: pattern
680 | character(len=*,kind=RCK), intent(in) :: string
681 | integer, intent(out) :: length
682 | logical, intent(in) :: back
683 |
684 | integer :: first,last,step
685 |
686 | if (pattern%n>0) then
687 |
688 | if (pattern%pattern(1)%type == BEGIN_WITH) then
689 |
690 | ! String must begin with this pattern
691 | length = 0
692 | index = merge(1,0,matchpattern(pattern%pattern(2:), string, length) .and. len(string)>0)
693 |
694 | else
695 |
696 | first = merge(1,len(string),.not.back)
697 | last = merge(1,len(string),back)
698 | step = sign(1,last-first)
699 |
700 | do index=first,last,step
701 | length = 0
702 | if (matchpattern(pattern%pattern,string(index:),length)) goto 1
703 | end do
704 |
705 | index = 0
706 |
707 | end if
708 |
709 | else
710 |
711 | ! On an empty/invalid pattern, return -1
712 | index = -1
713 |
714 | end if
715 |
716 | 1 if (DEBUG) then
717 | if (index==-1) then
718 | print "('[regex] end: empty/invalid regex pattern. ')"
719 | elseif (index==0) then
720 | print "('[regex] end: pattern not found. ')"
721 | else
722 | print "('[regex] end: pattern found at ',i0,': ',a)", index,string(index:)
723 | end if
724 | end if
725 |
726 | end function re_matchp
727 |
728 |
729 | ! Iterative matching
730 | recursive logical function matchpattern(pattern, text, matchlength) result(match)
731 | type(regex_token), intent(in) :: pattern(:)
732 | character(kind=RCK,len=*), intent(in) :: text
733 | integer, intent(inout) :: matchlength
734 |
735 | integer :: pre,ip,it
736 |
737 | pre = matchlength
738 | ip = 1
739 | it = 1
740 |
741 | iterate: do while (ip<=size(pattern))
742 |
743 | if (pattern(ip)%type == UNUSED .or. pattern(ip+1)%type == QUESTIONMARK) then
744 |
745 | match = matchquestion(pattern(ip),pattern(ip+2:),text(it:),matchlength)
746 | return
747 |
748 | elseif (pattern(ip+1)%type == STAR) then
749 |
750 | match = matchstar(pattern(ip),pattern(ip+2:), text, it, matchlength)
751 | return
752 |
753 | elseif (pattern(ip+1)%type == PLUS) then
754 |
755 | match = matchplus(pattern(ip),pattern(ip+2:), text, it, matchlength)
756 | return
757 |
758 | elseif (pattern(ip)%type == END_WITH .and. pattern(ip+1)%type == UNUSED) then
759 |
760 | if (DEBUG .and. len(text(it:))>0) print *, '[regex] at end: remaining = ',text(it:),' len=',matchlength
761 |
762 | match = it>len(text)
763 | return
764 |
765 | end if
766 |
767 | if (it>len(text)) exit iterate
768 |
769 | matchlength = matchlength+1
770 |
771 | if (DEBUG) print "('[regex] matching ',i0,'-th pattern on chunk <',i0,':',i0,'>')", ip,it,len(text)
772 | if (.not. pat_match(pattern(ip), text(it:it))) exit iterate
773 | ip = ip+1
774 | it = it+1
775 |
776 | end do iterate
777 |
778 | matchlength = pre
779 | match = .false.
780 | return
781 |
782 | end function matchpattern
783 |
784 |
785 |
786 |
787 | end module regex_module
788 |
--------------------------------------------------------------------------------
/test/results.txt:
--------------------------------------------------------------------------------
1 | { OK, "\\d", "5", (char*) 1 },
2 | { OK, "\\w+", "hej", (char*) 3 },
3 | { OK, "\\s", "\t \n", (char*) 1 },
4 | { NOK, "\\S", "\t \n", (char*) 0 },
5 | { OK, "[\\s]", "\t \n", (char*) 1 },
6 | { NOK, "[\\S]", "\t \n", (char*) 0 },
7 | { NOK, "\\D", "5", (char*) 0 },
8 | { NOK, "\\W+", "hej", (char*) 0 },
9 | { OK, "[0-9]+", "12345", (char*) 5 },
10 | { OK, "\\D", "hej", (char*) 1 },
11 | { NOK, "\\d", "hej", (char*) 0 },
12 | { OK, "[^\\w]", "\\", (char*) 1 },
13 | { OK, "[\\W]", "\\", (char*) 1 },
14 | { NOK, "[\\w]", "\\", (char*) 0 },
15 | { OK, "[^\\d]", "d", (char*) 1 },
16 | { NOK, "[\\d]", "d", (char*) 0 },
17 | { NOK, "[^\\D]", "d", (char*) 0 },
18 | { OK, "[\\D]", "d", (char*) 1 },
19 | { OK, "^.*\\\\.*$", "c:\\Tools", (char*) 8 },
20 | { OK, "^[\\+-]*[\\d]+$", "+27", (char*) 3 },
21 | { OK, "[abc]", "1c2", (char*) 1 },
22 | { NOK, "[abc]", "1C2", (char*) 0 },
23 | { OK, "[1-5]+", "0123456789", (char*) 5 },
24 | { OK, "[.2]", "1C2", (char*) 1 },
25 | { OK, "a*$", "Xaa", (char*) 2 },
26 | { OK, "a*$", "Xaa", (char*) 2 },
27 | { OK, "[a-h]+", "abcdefghxxx", (char*) 8 },
28 | { NOK, "[a-h]+", "ABCDEFGH", (char*) 0 },
29 | { OK, "[A-H]+", "ABCDEFGH", (char*) 8 },
30 | { NOK, "[A-H]+", "abcdefgh", (char*) 0 },
31 | { OK, "[^\\s]+", "abc def", (char*) 3 },
32 | { OK, "[^fc]+", "abc def", (char*) 2 },
33 | { OK, "[^d\\sf]+", "abc def", (char*) 3 },
34 | { OK, "\n", "abc\ndef", (char*) 1 },
35 | { OK, "b.\\s*\n", "aa\r\nbb\r\ncc\r\n\r\n",(char*) 4 },
36 | { OK, ".*c", "abcabc", (char*) 6 },
37 | { OK, ".+c", "abcabc", (char*) 6 },
38 | { OK, "[b-z].*", "ab", (char*) 1 },
39 | { OK, "b[k-z]*", "ab", (char*) 1 },
40 | { NOK, "[0-9]", " - ", (char*) 0 },
41 | { OK, "[^0-9]", " - ", (char*) 1 },
42 | { OK, "0|", "0|", (char*) 2 },
43 | { NOK, "\\d\\d:\\d\\d:\\d\\d", "0s:00:00", (char*) 0 },
44 | { NOK, "\\d\\d:\\d\\d:\\d\\d", "000:00", (char*) 0 },
45 | { NOK, "\\d\\d:\\d\\d:\\d\\d", "00:0000", (char*) 0 },
46 | { NOK, "\\d\\d:\\d\\d:\\d\\d", "100:0:00", (char*) 0 },
47 | { NOK, "\\d\\d:\\d\\d:\\d\\d", "00:100:00", (char*) 0 },
48 | { NOK, "\\d\\d:\\d\\d:\\d\\d", "0:00:100", (char*) 0 },
49 | { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:0:0", (char*) 5 },
50 | { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:00:0", (char*) 6 },
51 | { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:0:00", (char*) 5 },
52 | { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:0:0", (char*) 6 },
53 | { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:00:0", (char*) 7 },
54 | { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:0:00", (char*) 6 },
55 | { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "0:00:00", (char*) 6 },
56 | { OK, "\\d\\d?:\\d\\d?:\\d\\d?", "00:00:00", (char*) 7 },
57 | { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello world !", (char*) 12 },
58 | { OK, "[Hh]ello [Ww]orld\\s*[!]?", "hello world !", (char*) 12 },
59 | { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello World !", (char*) 12 },
60 | { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello world! ", (char*) 11 },
61 | { OK, "[Hh]ello [Ww]orld\\s*[!]?", "Hello world !", (char*) 13 },
62 | { OK, "[Hh]ello [Ww]orld\\s*[!]?", "hello World !", (char*) 15 },
63 | { OK, ".?bar", "real_bar", (char*) 4 },
64 | { NOK, ".?bar", "real_foo", (char*) 0 },
65 | { NOK, "X?Y", "Z", (char*) 0 },
66 | { OK, "[a-z]+\nbreak", "blahblah\nbreak", (char*) 14 },
67 | { OK, "[a-z\\s]+\nbreak", "bla bla \nbreak", (char*) 14 },
68 |
--------------------------------------------------------------------------------
/test/test_1.f90:
--------------------------------------------------------------------------------
1 | module regex_test_1
2 | use regex_module
3 | implicit none
4 | public
5 |
6 | ! Some tests from tiny-regex-c
7 | ! valid, pattern, string, length
8 | character(len=*,kind=RCK), parameter :: test1data(4,66) = reshape([ character(len=30) :: &
9 | "YES", "\d ", "5 ", "1 ", &
10 | "YES", "\w+ ", "hej ", "3 ", &
11 | "YES", "\s ", "\t \n ", "1 ", &
12 | "NO ", "\S ", "\t \n ", "0 ", &
13 | "YES", "[\s] ", "\t \n ", "1 ", &
14 | "NO ", "[\S] ", "\t \n ", "0 ", &
15 | "NO ", "\D ", "5 ", "0 ", &
16 | "NO ", "\W+ ", "hej ", "0 ", &
17 | "YES", "[0-9]+ ", "12345 ", "5 ", &
18 | "YES", "\D ", "hej ", "1 ", &
19 | "NO ", "\d ", "hej ", "0 ", &
20 | "YES", "[^\w] ", "\ ", "1 ", &
21 | "YES", "[\W] ", "\ ", "1 ", &
22 | "NO ", "[\w] ", "\ ", "0 ", &
23 | "YES", "[^\d] ", "d ", "1 ", &
24 | "NO ", "[\d] ", "d ", "0 ", &
25 | "NO ", "[^\D] ", "d ", "0 ", &
26 | "YES", "[\D] ", "d ", "1 ", &
27 | "YES", "^.*\\.*$ ", "c:\Tools ", "8 ", &
28 | "YES", "^[\+-]*[\d]+$ ", "+27 ", "3 ", &
29 | "YES", "[abc] ", "1c2 ", "1 ", &
30 | "NO ", "[abc] ", "1C2 ", "0 ", &
31 | "YES", "[1-5]+ ", "0123456789 ", "5 ", &
32 | "YES", "[.2] ", "1C2 ", "1 ", &
33 | "YES", "a*$ ", "Xaa ", "2 ", &
34 | "YES", "a*$ ", "Xaa ", "2 ", &
35 | "YES", "[a-h]+ ", "abcdefghxxx ", "8 ", &
36 | "NO ", "[a-h]+ ", "ABCDEFGH ", "0 ", &
37 | "YES", "[A-H]+ ", "ABCDEFGH ", "8 ", &
38 | "NO ", "[A-H]+ ", "abcdefgh ", "0 ", &
39 | "YES", "[^\s]+ ", "abc def ", "3 ", &
40 | "YES", "[^fc]+ ", "abc def ", "2 ", &
41 | "YES", "[^d\sf]+ ", "abc def ", "3 ", &
42 | "YES", "\n ", "abc\ndef ", "1 ", &
43 | "YES", ".*c ", "abcabc ", "6 ", &
44 | "YES", ".+c ", "abcabc ", "6 ", &
45 | "YES", "[b-z].* ", "ab ", "1 ", &
46 | "YES", "b[k-z]* ", "ab ", "1 ", &
47 | "NO ", "[0-9] ", "- ", "0 ", &
48 | "YES", "[^0-9] ", "- ", "1 ", &
49 | "YES", "0| ", "0| ", "2 ", &
50 | "NO ", "\d\d:\d\d:\d\d ", "0s:00:00 ", "0 ", &
51 | "NO ", "\d\d:\d\d:\d\d ", "000:00 ", "0 ", &
52 | "NO ", "\d\d:\d\d:\d\d ", "00:0000 ", "0 ", &
53 | "NO ", "\d\d:\d\d:\d\d ", "100:0:00 ", "0 ", &
54 | "NO ", "\d\d:\d\d:\d\d ", "00:100:00 ", "0 ", &
55 | "NO ", "\d\d:\d\d:\d\d ", "0:00:100 ", "0 ", &
56 | "YES", "\d\d?:\d\d?:\d\d? ", "0:0:0 ", "5 ", &
57 | "YES", "\d\d?:\d\d?:\d\d? ", "0:00:0 ", "6 ", &
58 | "YES", "\d\d?:\d\d?:\d\d? ", "0:0:00 ", "5 ", &
59 | "YES", "\d\d?:\d\d?:\d\d? ", "00:0:0 ", "6 ", &
60 | "YES", "\d\d?:\d\d?:\d\d? ", "00:00:0 ", "7 ", &
61 | "YES", "\d\d?:\d\d?:\d\d? ", "00:0:00 ", "6 ", &
62 | "YES", "\d\d?:\d\d?:\d\d? ", "0:00:00 ", "6 ", &
63 | "YES", "\d\d?:\d\d?:\d\d? ", "00:00:00 ", "7 ", &
64 | "YES", "[Hh]ello [Ww]orld\s*[!]? ", "Hello world ! ", "12", &
65 | "YES", "[Hh]ello [Ww]orld\s*[!]? ", "hello world ! ", "12", &
66 | "YES", "[Hh]ello [Ww]orld\s*[!]? ", "Hello World ! ", "12", &
67 | "YES", "[Hh]ello [Ww]orld\s*[!]? ", "Hello world! ", "11", &
68 | "YES", "[Hh]ello [Ww]orld\s*[!]? ", "Hello world ! ", "13", &
69 | "YES", "[Hh]ello [Ww]orld\s*[!]? ", "hello World ! ", "15", &
70 | "YES", ".?bar ", "real_bar ", "4 ", &
71 | "NO ", ".?bar ", "real_foo ", "0 ", &
72 | "NO ", "X?Y ", "Z ", "0 ", &
73 | "YES", "[a-z]+"//NEWLINE//"break ", "blahblah"//NEWLINE//"break ", "14", &
74 | "YES", "[a-z\s]+"//NEWLINE//"break ", "bla bla "//NEWLINE//"break ", "14"],[4,66])
75 |
76 | ! These cases have C-specific characters and need be defined
77 | ! "YES", "b.\s*\n ", "aa\r\nbb\r\ncc\r\n\r\n ", "4 ", &
78 |
79 | contains
80 |
81 | logical function run_test1(valid,pattern,string,correct_len) result(success)
82 | logical, intent(in) :: valid
83 | character(*), intent(in) :: pattern
84 | character(*), intent(in) :: string
85 | integer, intent(in) :: correct_len
86 |
87 | integer :: idx,length
88 | type(regex_pattern) :: re
89 |
90 | print "('regex test: pattern=',a,' string=',a,'....')",trim(pattern),trim(string)
91 |
92 | idx = regex(string, pattern, length)
93 |
94 | success = valid .eqv. (idx>0) .and. (length == correct_len)
95 |
96 | if (.not.success) then
97 | write(*,*) 'pattern = ',trim(pattern),' string = ',trim(string),' idx = ',idx,' len = ',length
98 | write(*,*) 'should be found? ',valid,' with length = ',correct_len
99 | re = parse_pattern(pattern)
100 | print *, 'pattern breakdown: '
101 | call re%write()
102 | endif
103 |
104 | end function run_test1
105 |
106 | subroutine get_test1(itest,valid,pattern,string,length)
107 | integer, intent(in) :: itest
108 | logical, intent(out) :: valid
109 | character(*), intent(out) :: pattern,string
110 | integer, intent(out) :: length
111 |
112 | character(len(test1data)) :: buffer
113 |
114 | if (.not.(itest>0 .and. itest<=size(test1data,2))) return
115 |
116 | valid = trim(test1data(1,itest))=='YES'
117 | pattern = test1data(2,itest)
118 | string = test1data(3,itest)
119 | buffer = test1data(4,itest); read(buffer,*) length
120 |
121 | end subroutine get_test1
122 |
123 | ! Read in results from test data in tiny-regex-c
124 | subroutine read_results(fileName)
125 | character(*), intent(in) :: fileName
126 |
127 | integer :: iunit,ierr,j,i,ntest
128 | character(len=30) :: openb,isok,pattern,text,chart,length,closeb,test_param(4,100)
129 |
130 | open(newunit=iunit,file=fileName,form='formatted',action='read',iostat=ierr)
131 | ntest = 0
132 |
133 | do while (.not.is_iostat_end(ierr))
134 |
135 | read(iunit,*,iostat=ierr) openb,isok,pattern,text,chart,length,closeb
136 |
137 | if (ierr==0) then
138 | ntest = ntest + 1
139 | test_param(1,ntest) = merge('YES','NO ',trim(isok)=='OK')
140 | test_param(2,ntest) = pattern
141 | test_param(3,ntest) = text
142 | test_param(4,ntest) = length
143 | end if
144 |
145 | end do
146 |
147 | close(iunit)
148 |
149 | ! Print to output
150 | open(newunit=iunit,file='testdata.f90',form='formatted',action='write',iostat=ierr)
151 |
152 | write(iunit,1) ntest,len(test_param)
153 | write(iunit,2) ((adjustl(test_param(i,j)),i=1,4),j=1,ntest-1)
154 | write(iunit,3) ((adjustl(test_param(i,j)),i=1,4),j=ntest,ntest),4,ntest
155 |
156 | close(iunit)
157 |
158 | 1 format(4x,'character(len=*,kind=RCK), parameter :: testdata(4,',i0,') = reshape([ character(len=',i0,') :: &')
159 | 2 format(8x,'"',a3,'", "',a30,'", "',a30,'", "',a2,'", &')
160 | 3 format(8x,'"',a3,'", "',a30,'", "',a30,'", "',a2,'"],[',i0,',',i0,'])')
161 |
162 | end subroutine read_results
163 |
164 | end module regex_test_1
165 |
--------------------------------------------------------------------------------
/test/test_2.f90:
--------------------------------------------------------------------------------
1 | ! A small performance test, to see how the library performs on a few MBs of text.
2 | module regex_test_2
3 | use iso_fortran_env
4 | use regex_module
5 |
6 | implicit none
7 | private
8 |
9 | public :: read_test2
10 | public :: run_test2
11 |
12 | character(len=*), parameter :: test2data = &
13 | "yfufcoubtwfoirqxficrlajyhmgfguiebmrcgtdbgdlfxhuoskhhdhqfyjjgpnownhyevasnwqtdwvtm" // &
14 | "nvyqyyjctuotmfgdwauyryisoigejkficydqhlgflnydgvdjsdjaymqrhghgfdcwosgmctwrkoochofg" // &
15 | "sfouwbqkwrhmfqxujtulibdaxxfqamvvoehxpyehvbsodvcdtsnjjarbfvcqmtgujkjnursmeiybmgcc" // &
16 | "llgnkvnayjbmpighptkptnbonnavhxwowvmmuksnyiorjxgekqakruiekpirpvyvrvivgfarlqfytxln" // &
17 | "dhafovngcxauseiwguwbukpwuipbvsqpxqwxfnuevkymtahaxntwcynmwqofoeeravddgwixjugfcyem" // &
18 | "bhrnxmqlnjbbdulmaqgmhogqituxileynevaujeshcbmkmomliyinvtfoclnjuppbxqcmngnferbbtxw" // &
19 | "oifuvylsbnlpjvlxnfohelupbbxabkanaxqqsnlojgaeaktxpgsexytvotsocvcpfmjqbxhtqpiewewe" // &
20 | "xjrqneydksycdkxdfiwboqlslhxpqyryoklwcjhgiphnaqwapevlrqffngqlwclekrfcsapqdinqxlta" // &
21 | "weqadfhgoufisnvnfcftlkhbnlxespvxapfpygighxpvsckhqgflxwlrvlshhahllyjftwebnqkaqugb" // &
22 | "vbkqqkeituywledgpjqslsaiphsvtiwikkphxygybcthicwpkjtjfuusehcjdvdrywmtijfegwgsstju" // &
23 | "enracngtmmevveadpwxrwpoifqnluhhocombaprwlvkphnmxheloescietwfdthfqbgrjdhwmrkxrkrx" // &
24 | "peiercjiburkijxehimjurfvyfgtyivmwweksaslptovyjfdgmrffnhcdslclqmlcieksdkvhsqargjy" // &
25 | "xjoeqerfbmcsdwxwogpmofyytueunrvrjwxnngtmofojgelucxlgxyrakqvwmmivonfaabbyafmxtxio" // &
26 | "cyptuyccpmpfgeapyymjmwjubkidqgdcbhlhrjgmrsxrdewrrqjdpqvseumvokfskikfhydxkgvntpgi" // &
27 | "bdrjnrrksomllfxuffnkitlyiplrntnngobpebbjhfvjtjiqlvwkksodbgjkheaiwxtbpsphkurphkum" // &
28 | "btweqwmepfplihuubbuimhcnmkxpwawixijhdtnrlmrunffgosnoxrrlfpfijqngltvxtwsfsupggbuw" // &
29 | "gxvmjxiucjbnmfakshrxymihosmbhpxonccoxetjwxyjbxpieywmklkbnusncglccjqbaxlmidoqbobr" // &
30 | "kaewbuynsygvcpijlbpbawinbqnkrvvdwhfxwdkjtfgipejhqvqiudkqqagmgbikafbcvdfscxtwncha" // &
31 | "lqpiewmirknouxmtjhrkirkxrhehvmgqeoarocpgxsobkedkbvvwcjwxnycubuggbjjtojlupeaohkgn" // &
32 | "jliqcujrqmusxbxfyypnulfnkksqmwhmvulwoqlgpovprdtwcxisowojddwkyreotaqqmmhfcmsvovhl" // &
33 | "akvjisvrlcxtsxbtphbjpftfafrtogvgldsimijtgsxiurnavjisjwqgfnftqjodclgpwxdokivuflmm" // &
34 | "eueuwjrkwgeconmjmfxffvaotocuytajdbuywjhvgatdmduedlpuhlptpqjmaygviljckiwcwcarfvix" // &
35 | "rhrutcdjsypoieksqpkhahybueyheogpwxowreusjdlunevxsjimamjmqmqkdleagjldxddxuspvifnt" // &
36 | "rsyxrtkxqcbrumhcixmqhmdeupkscriuwvgrulvenpcofqebsdpcxtosuoxsqnwupvtuaphrqymriyit" // &
37 | "aruhrybemaqlwgsfginbltvpqdqypsclgcsohxsflkbxphblfgtwalqpjolunayhsayiqnmflhtrddju" // &
38 | "pliihrfuaaidfihcgmvqjnjxawgwiwqmkjrqrnaeoqodmyafkwvdgofnydwumvdixkroutqlwamiaura" // &
39 | "mxlqntlnqxwodvfbbukufusjvupshlwmuqxsdsxivdanbjyopjmunucfeujdkefdbfxgwutbganghkls" // &
40 | "miiunliswicdulkxixgsrjdxjrxoavqfvcwguesddjwhoaoxnmxpvkggumnbptfjphpltmbfisjpfiks" // &
41 | "bpoaccqopjudmthvanrqbpdfpqaihdaeyxkrdcmlgajgdspvtplfwqfrlgifphgnpigqsrmqtdkqspxp" // &
42 | "uvtnscmdnsyhknjwnbgmrhlvdosmbgihdergufhdagjuulrqutsxtmbyocobgspprnhyvwvnnnxeecda" // &
43 | "bxqrchqcsppaqvdgtqtgtwnysgypwokytygjnjpvbwtprcltedcxdmsiepmqnlbdglrekcwmcpremtmc" // &
44 | "xuhpscnwqaskunvtcprebuajvfusdgmwdjfkklfdirpvgtljrgtfjbfsywkbwjusaixkfgbpedvywlut" // &
45 | "wcsbcsoldenljrwkaxuwladcdjahnehfqksobnirycoyyupwuvcwvuphehclmbshphkpcpyfhprwenet" // &
46 | "bdtxmpyttonmxsmjtmuoyflkbhwguqnmkcjjxxphtrteowlrtlfxwxrsoxmfcacnisyrorbvuqpaxwvp" // &
47 | "xiweucqokjtacadrdbgryktpnkcyftlnxlpslifmqwyvqngxtkkdocwauljmnrfjvuqomqwupwvtpqkc" // &
48 | "ldinghaqjqueikcvykvbwvsxmvfoidrmlbqarllpdivnbbiudskvvgkxvpxphgrnkhosimqktoysexlg" // &
49 | "tsobasacbttlhpvwjjjowofhxgdsuimibkwcioaraoawjcipbyaafquruailxcxltxoxjvraihlgeyum" // &
50 | "vebgktjbtjcfblujxaoybdadwiwwuxwirigeseqmleshnhikrtvxbjxrwvxhttbjlsvsyqrweojncdse" // &
51 | "itrvhyggxwuxrrhdvsdadxoglnajkyqkbtqkvnnnntkvangesryfaeetnaftpieggvbalfgvevjbddkm" // &
52 | "ppctcgxsrmcxmpnuvtykaimssidfnvggbwththeduhpisogktyhlwshadhvabtqoykmacedtgusmejvi" // &
53 | "ucrlipspbwaveibpompwxdxbwneqcmytrxnktbohtqfnmsvvovsffbbhfnrrsjsricsyikfnlqfftxbn" // &
54 | "uatbhovluljigwakvvofbofbgbunrmqmosilpiijmnhpccihhesjgetywgmvuiqhblrfvevqxthblhhf" // &
55 | "dbmowhixeqhymgwkdjykhqehemtcrgfvlaxtlgbsuspfidarknfsmqvujajpxalcumyefayhtrkwtcwg" // &
56 | "askygcsmgyesdrqehfkdsvcpxpvxmfycjjluwdbgwdtvimrdsuqntfenvklppnhxuwebhovcrpqggpdf" // &
57 | "ghypcgohpohokqybvjunweporoafekscabfegwwyrrknmukehnjneeaxfhdbyfcrdtqjrxvdlfmukdmx" // &
58 | "htcilmeccipvmnpgmdmoimvuekosxpclsnwomypwcwieqmtmxxapeedeuqgcdtlhxemohrerhkethxfd" // &
59 | "iwyrwsqxqvgtytkwcmuvogbvgrrramstxeddgwgoxtvkuiuhbrgsousyjkpyvdkqdyhvjqcepxatreoh" // &
60 | "vujujbeabktdpmnttrkrqfrrukkjcslvwqnpeqarhyytxbjcrxmarsiaaxickdyufrcgwoweuojrajlf" // &
61 | "rnpihjbllpqnuivvfnwvfixthxdyfuthogyxsmxyfctuobbbhxldvdrneqfiwvbfqctaxbtrkgixcjnv" // &
62 | "baohjgrrytxgerjhmoaqerdaysjdvwjkqluulxgwvxitbtuqwwpilaqflxjgwhecwrylodxwhxchfpxo" // &
63 | "pxfyvcjeyfxcnkuaplbhyckmbwhvbohbxxrrltytkhnttpsgsqkoijqxjwpvrdjhnjiysxqwqhmkhycb" // &
64 | "ckgoihnmgejtgfsbgxmibsgvqsymjwegpndvfkpsnaiqqbuprcihnmenjsbpjigiovoptofoddvfkbsl" // &
65 | "jnskuofnxkdlmsnijoxbkxtegrffiicsuyuswrwwtthxvqyxalknavkmvcpynxrauldioxaqhlawiitu" // &
66 | "ofunblmgyoiqvqxgufmiguokcaocpcvuctpxdwnixlcpfmhdjudqkdgxscnutwvjbtsvgpoxocudaykp" // &
67 | "suedtpaowmtnxerucqifyetwomfjwoxvfgedfabaqydlykduylgmvgwqiefqkpbdsnmggoupeycbakbj" // &
68 | "fhguxhkwfyglojjsoscxjgtmohkbpfjnngletolghmgyajnpdohkjddojdfgsjvxlgfulyieqlkcamax" // &
69 | "jtjxeubkkuwxswdaakqoolgnmwdmdmdvwkivrpwbuishevpvvljiwsifgjtjpolwpjvjahxsprxbvkfk" // &
70 | "dqmbmpyjflyvfuiqfyxwuukwmggkuudubsljlditnecynmlwjimxbtijfunonxsmttblhkpallcdgehs" // &
71 | "tmdxhqafwfjfoaeootjijuhlskruiccbndvuyqdajeeaanfetwabhhgqihkskvhsbceesrjqnajcabsn" // &
72 | "npgocpdfbadjxpaoiyfwtibjsumvtmawggroyuarelgicwqabcaqmmdjjpcqrkjfbvlarouruekusdvk" // &
73 | "lmstjpadtqlcolhuipssvxkgstopraulffioukmdtykfkeelkalscfgvmtaenbarcmljugmbnpvkdwkh" // &
74 | "utwasblyywofcsgdcbfqsyfsirqprghrmmyhwohbdpdpnrifkrdecwaaksswxwldaibrrmnvpurcwieb" // &
75 | "sskppdgqrjoyuuduosxgyyxnvclsjlenlpivcjojjokfusfqauxhkgqrkegmhtbxeoitqycgywkianki" // &
76 | "pxturfodwbysysxebkokvbnhjraasoxahvclwlcnmlqoprcuhagiojnaqrkfsldyyuymnlgejvfvwlkw" // &
77 | "udrswidtrifkomvtddstioqyedqimbmplvarnedwjgrxtriboqqjipsyjppmoomyonjnugvxerwdioci" // &
78 | "shryljawkrxcrcyijrnybgorckmavnsdbjxtrtdykqrhgccvowvadepqraccfusbppghpcldmvmgsrjj" // &
79 | "xkdxqqfhykkigpnfssxrcvyecgydiquhylkaxjlvcapyqlwlofcjidooudvpecrpbqlsttkonyrdfmys" // &
80 | "mdgkimvecsljfddprgilccqcisxgwjmtcchxaskksisrsqghevwenhrjcmgnesxsjmtaastbjjbcecpi" // &
81 | "fqphjfgxacluyjckrxeewwsqhokdnfdnkxgsludmfkwstphflpjbkxneajhcjduiqwngrppvpnktuvkb" // &
82 | "ycxlcoihvobjafkruvgtxsiccruaduipchrgiombahbvnobvpjgktuupplvmqkorpgcyrdgomwdaohca" // &
83 | "dagbnejnwfnuyduyyacocywdstjqcakvcnsrubuughqonaeunjvqhtyhepenlbxnfdcwhuxnqmyqhhus" // &
84 | "lgsumtrmweggogfumcummtrodtovsugesxwgfwfisuaejakrljqqsnopenrjtorvaitgromtutrcexqk" // &
85 | "ysclousbppseymovdvgidjriuiynmxibqnkfusktnalivcfmiabstjydogwhkcuctmvhrnimtoyxbvbo" // &
86 | "plobexcerwdveftccjmkndsbfmbfkvchleirubndjjmutymgenkgjncccmkdonjuwqtesqbavgmktxlf" // &
87 | "etohasfcydjytggkxpoldhivkwgmbrqvivnvqlttquklpnixqmbmielwhhmmbputwoeixbxtkvrvexwt" // &
88 | "cuqfnlmbvkwpvykxvnmfmkikdywbeaglfopssfhqhpeetienehvyfvgfhhmsxmiycoddyhqusmcgudke" // &
89 | "ivpejcphafsshtqlcabsusxmfttsctdukljrradtqpkipuanqwsorwgggassancplihgbvqrrrreempm" // &
90 | "ssihtvqfggpfixbxrmxvyjoyveaaollynbcviiwvmfiyyouqduklmslqxnmtnqkfmgikwfimmmjtwocv" // &
91 | "mgeedvhghfhoacxmvdtnmifqbpqfwwckvxrfpvxerobbgebvfnyidihgfpwnvdulqhjqqpehaqxulvhm" // &
92 | "deeyoxfwpffotsqneeuhndngrvqkeqmocscnfshmkrgmojwpdwbynelbfwnswdttwdefnsxmyvsqvafn" // &
93 | "vwoslnmxsqdfxescisibdctoniyfuqpylmgrgeruxhsovbbxhxyrevatiogymxsxhgwqvxtalvxexjvd" // &
94 | "omqojowyinfqnbfoahsrihhscgitlvyfoulisrtlobxhkmwcmgdwodrwkollnlcwswlqssoadeixcfof" // &
95 | "rwidlrkmrlmdqkiaufebnohujmqtxqujuohmhhxauqubylusrkfbqqyiomimyjfijfyaceigasqerskk" // &
96 | "poxixlxafkiidltpxuosyojntppfldygxnlxqltbnwlfqovypqvgsvvwdalhaaxtrxmmdmrejjyclbfu" // &
97 | "yrhqqdoycrlkdcuhqrwrlkumnlgufehmyplsyklmropvxmoahvojfwrnbqkqceqempenmwafvnyqvcpv" // &
98 | "ukteognyoarnjamcflcjnbbwcuqxeynyjexsscityfsqtcbvsjgxdwixkbyxggvrupfhvnnnudwjejpw" // &
99 | "eygnaykwwxncrhygvwhlxresqoxxwqpdjcvohprocrggvwermutckskslknpuvgnhfupxiilkfwgxfik" // &
100 | "raddxbhrvsxqeocyptwxuyuferpxlypvasdyvtywfeacvudunpcklybggtsrhogbnkhbdbpqatmlxkku" // &
101 | "hinmfvjchrmcmklxsrkldsqnvsilwrsrtxaeknobulicwmxvreumopckbdumbdwmkpprwdetbiceiqqo" // &
102 | "bggjwyuiwnbwagbtyvxqxpeaevvpismujvjrjstjvtmpytkvdhbvlywqdujwvnmteqvjgbpqdkqckrqp" // &
103 | "hdrppmnawdxgpkqcftdurrgfutcbqvoienobprtjlrxrfxmuycgltfyqghugxxqjtobvccevdhqoeakr" // &
104 | "gdfcnhtfmgpmlehobechbfvagabotqxehjlfyakllmdbrgwumipkbkpbuynxoujfshqkwvfrnlrmhnkl" // &
105 | "rxpbkgekjahjagcsbsmbprwglhgktyeyxrjuvkfnnpqakdawrccadheulapuvhidqhsxqqjmytqcxtno" // &
106 | "potspntsvuivckigpxcggooterhiqomlrhvxvsmejivfbcqssbmfghaugkpnmfatvgjmikuhjeoodpvg" // &
107 | "icpcdulgmvgjdbkerqlhmxfsuorcwkwqhbolwlikkkvfvrogcpfxkvjtbgaluchvlnuvcjodueamrcbi" // &
108 | "vfnjlchhqikbficomopvwcibvpnvxiqsukmxadxdnvrfdrfgnfgximbnrcqlgbjcpqlqffgvijerdngl" // &
109 | "hklekpkjfspwqaxtclujbwimmneuqvfwxxrrhyfhenmhkepfreyeysxbbiuctlrtsqdluwjjhdhvqmto" // &
110 | "fupqbddtkrpqmyhadlaurywimgdxlkcxjqcgmhdasjshtijrdlwprkhlmmccloxcayjdjprqptfsmymj" // &
111 | "bqoyfiexasakxpxdnmykwuxxhaomxyjwkwidtncvgesarsxfkldnxsshnsvbnmfmdybjnrqdpyjbdeas" // &
112 | "tnckjofhnghwshactsbjiurmwlnropmvqnvfnnupirfaomfuehidxlokfrjwdrraogadlvplfvywknng" // &
113 | "vvujdpqcgjncsvnxbncotedoobyuriaxrunkdgnroshyhakmrdoqrdkqlbhkjmdkqvlcrasbooliudcp" // &
114 | "knooleoxjtdybdiyclwotjhajhtjphrhcoqwxvjnvsgioshqilssygyadpaqhskeduvisngwjrbtvcos" // &
115 | "ddbirkqcssdinsdmcpgvaymstlymthewcfwndijbenyklnynevknbjrsrviqbvcilcdpmkclawqtaqle" // &
116 | "eppglwntdybdcwyssfhpeagrajhfbrmnexkkxtrkqgjiqemvyiaephndjwilkkcbmcvwgnkmgqixnmkx" // &
117 | "elawodybcakfryievwemliboywmxmboxhwmprmcqyeafxfcnihstmovqduwclcpamwvxrqoatbdhdjgu" // &
118 | "qyfpdrbkeefymupjaugepleawrgsthwpeltustmadoyxybukfyxusquvjcsheqnbuwthpwlfmyedfipf" // &
119 | "tflwqomxmmpswuyecnvfcmqegtskopbtxhmgrfuwihbptuochpynwahmmhtxvxvteuopeyoscakvltuu" // &
120 | "hoemthcqgdrcgkhtielstcuiqeugkyyoucmfuommscmspqvtqrwqpveyrmmvxewrxrluvvrrwkhjslom" // &
121 | "yhublmihxbeyrxmnhimtcefpwbkdunrjvnuonaqrtvaatffaxrpbesydugufqsspismlpdiotxprxqsc" // &
122 | "bpvlkbavsgwujredbynvmelbgcbwrxewqrduiwlowaimivcryyjocrtbxwmublkotcsptmjpojofafqu" // &
123 | "tsjlmyhqxumoptpvqjabgimbtkgsfomlyiimgmqfsfneluyhjtyprskemjnjvmmjkhejhqkcsjxhovpu" // &
124 | "srxotlomltksqfjdjhkgrxvhpinumvkbkaxowruvniolmhnkmuluruxksqkvccokujjtlxstuqnvwfna" // &
125 | "fbcercgwbjqujocqwrtnqoritefwahtpdvbedhchwukywpackdjyrosbeiqmfasnkcewsrwbcugbebkv" // &
126 | "aqxvampeohwdajogkghuqfsiubjlrqhiajjyqlmvohtwjcbmjotaokidrkguvwxalpioibagrbgegnbl" // &
127 | "fiikjmjpmsowculielymyqmatkxuklscmllxtoogqgxqdeckitmtkkxatfuwfhijfeyxejnqvyiqdvwg" // &
128 | "amlqflgdebfwletrbdalnlundtxcbbhsagyoglfburvobafstulbijhkuwmrnbwjdrtgvyleyxyhvuxi" // &
129 | "axnwdiaitpfxudjhoglntputujamxvcgofkqmtlfcbsltclmrslwujwfuusqhmmamkcclmwtliehmgcp" // &
130 | "cajqrgoxsbpooeoosumjupqaarjfrrbagcsynqbjcfvlddhwfklnkasnscxrbukcnknejbbwjwlbpghr" // &
131 | "smqcithlxixeumckduevlrncymcmgpfkvjkbuivlbvfrajgqdttplcsnqtkjdbiheqjkbslgmhxmsiet" // &
132 | "lafteebjjmqaitplheekiyrqqtonlbtcdifpvmkrljwatrbehnubqnccddbitlnpxicejoxljenllegn" // &
133 | "hbcelbonmalaviqwtjttmjjwpvpuloluialubohgigamrrckcplpiqnvowtdvgowvwnarvteccsemgie" // &
134 | "tnwcxwcvhevsdjhjerupakqhtoxmtbaqdkjvyalwjhxbpxxedtoqiupxqxfetmjufvpuilsvycbhtnir" // &
135 | "vkbmgeypymepyjbavdeuijvplitkemiuwhkorfhuscwiikhgfoqicwtakpbknownxnfixodwalpubpmv" // &
136 | "yrfdvuooqsuswyqcvxsnmpixulnvvymhqvcwgmjdosdphgoagmvoltrtvbpadoscdthhvyjfxjchjase" // &
137 | "eloaaqkrnqbofwrfcusebftiqfrgalhdcfsxtcdqquwjqjnkwdfhiwwqbsdpqecqqpnjuopcgervwyrk" // &
138 | "hwuhjfolyvfebrubiewrwtiovnvjhvouwoulgtmuybllfnvbjqefwbkqafqvklrlcchhjijcuqhkmavv" // &
139 | "njjghoegroppkcducgeqidfelrrfrtfuewmihnrwjargsvkgglugasxhrkdimlnhxvpglohcrvaihqio" // &
140 | "gkaimsfldvqxytbqkbqcitajcuojkdawfaltquainijkaexhbxulkhywjvoacqefngstbssvxkcxrljg" // &
141 | "icxdcmjommctpdmtrwjuslchqqbumypowrbjinvwasqyxpxndyxnnexshvyueselrmdvsvngstjbyypd" // &
142 | "qsmdjbfgpgtfnxxndndodosmhyhgoafhxsbkackjvbfxyavldqrufildbkcmsrawiqsxcttfqsclpfeh" // &
143 | "jrbpuefpsrbipoxxfxqcsjppjtgnyguritasthuporcupsdirutcquodtfcehvtwuiuceaywgwiontuu" // &
144 | "tlbokxqfxbddkiwdfptsjhtuenspxotirvprihpmfgtsmfhsklfbfvfjpvgrexaoxhbasdeksjdahgdx" // &
145 | "ooaexhdoqrifdqsaxtiwjttyopxkvmyrnetboggnktwsiuadjyppbxlqarleyawjykoffyjylbgodjga" // &
146 | "rjdrrwyaxwqpvgxxckxdwlsflctnlectehdlemdvqrkftqmktiusgelsclrfxigsuojuknodjfsicfnk" // &
147 | "hqiajcxnljxfqinbxurdsoexpsrrpxvjkcuxvglfajqqmjjbevsqwicetdyfmfthbmmdxtvuxyfqmmox" // &
148 | "acdtqcechdxkymqoayrndqccoqsmtqcdtoqxxkhtocboxnovjklhhbbjqgocfrccotmrwqrmelkvptra" // &
149 | "trqvyjwreaktmagussigwfsrckfvjigatdfxdrbjdeabcrafhrxwsjrfympwxdfcshauswcyyxfomygo" // &
150 | "tafxowkwfjcauiwbrwtfgkcaqwcugrggvsarraujhgudyxjaslobdsiyqyerylloqpfrwsbffxyrqsdh" // &
151 | "knongsljttjjtdpbyuamkppljsbrrvrfxfxxupmjpfeorvlgvistvcvupvioeprjkhmjqyrjthdrvenc" // &
152 | "fjafqibivfqgsolabciodpnfphpmwftsddodhcyrplvdftpkxjcjaitakjbcqknogwadysldddtqjsdk" // &
153 | "ejyxpnrqfyxwvaxiovqpkuiurofefiygqvvyqktowawcqurscmrnsfbpfywsaijjliiibccvtmnrimgv" // &
154 | "jrsaaoogxtvwbsdtivaiepvsopwsurhvsbxtxtiqiwlhmjeupylvbdhbprmepgesowfctwhntjvpywrg" // &
155 | "ehqfpvgnehwalvvuoxwwlesvkdpojymsndbmycrqhatbdcrsbysffnyapkjnbykdhefbtcvqswmsqfdm" // &
156 | "qhhwtayptfeqtfcslhdurbrrnqniurqcueysebgbpabtikmlejxcrofbwbuioplhjxsafyonsgvydbof" // &
157 | "cklutwjfucwvkmiwqaerfmpshstwnstvcurcfmgraiakitwhtnswfebjmfqlxyefbhranjynpyphqwio" // &
158 | "cfgwqkkbqgfkeexuutuqhrdknmibdhscatnwjowtcuopuaghhrkssfoydwhtqmisqbvwhhyuluynxofq" // &
159 | "ejbgutodxjcsrchkiplsemictwlkxeukjwswwdaiuvwxanqxewvqpmmeiqeftlaflbvswfjeyqkghnrq" // &
160 | "sqbqtmunnousvlhnynytyyytfwmunghhlvhehovqiholftmspvaesbyvlivvyjwehmauolfipsbsnrwm" // &
161 | "hgmutginiercduccjbgtktwswseiqccodcylutkpxbiijrcdbxntmfsuwljwxbgxpdwkkwhubgsvaaqc" // &
162 | "upjxmjvrvcflnwyoieeeinqydqruopaeuopkxyjlxbmmdbnhydytaejhobbesqnwvcbtttkmyxiuiobk" // &
163 | "juwgqmgpmekpmptxahrmxymwfhssdqddnrftreuyllhrlqklpayvrjeqrvqntubdglexugoydifgodbr" // &
164 | "syphcadjomuncatnotanlbodbvlthypjhqqklfgnahjoewdplecfkecbyoycwpagesgntlgndquapplh" // &
165 | "declnhlgpdcevxxxheaywmrroygxmmgquprihuopcfeiudurkgukgomemcsyaneidlsuifyeahvpbvxr" // &
166 | "ktskfclyhrxjbpsjhsggdpwiqeolhaodlhmdytxchbdapxntkqgsqsbadjlnwpfekdfmmjieeqfbotln" // &
167 | "esuoxbhrelojpwdkmsqhkakisemapyovbgxamcopfneebbksdlxqbvenqsnumqnusjtumhoijskupuij" // &
168 | "qjsqlfpcumoequwacxoxlivkeygtwedotyotntfnipwhxsfjeokdlsuddkhbwruvuicketdwbefejqiq" // &
169 | "xewwyfgrmlhjcyyrsbbwnlyuoxhjdlbgeledepuhdrqbftpwcmvcxdvurnehqxsbwloctdqbyxpjilsh" // &
170 | "ljkosjnbsfogvtlxclvwlltqkffcahlvhrrlkaolrkkxwxahcfjhxtqhdimurmxljhatidgqbgkoncds" // &
171 | "rllxdpvyiavmredudwpdqswuakvnbpuinstfmlkpgfoueixghxgkjhrqrqwltdytsmsoybyqlssjgnri" // &
172 | "uqbreckcyvtyygwyfdrsaobrvfbvshgcrdktlkxodjivguspxsugqqteempntdwhxrnqspojjoipckqn" // &
173 | "wylnsocqgewmumdgdessdtafsiiehfngimfexyrakhduggqvvuyqbvawqvvgpibnneegujppqhqkaltj" // &
174 | "kcprlsywtgqreargfmieuamiwlvqdoyiwjhsejuoujcglmxtturjdtrucorokedvymxqaaymwyhtingr" // &
175 | "tpdrjxlamqkjdpbjgidnkbajtaixuxrimnycphxmydcnbneolyqpjhpundrygwchryexdwnmbqyukcrq" // &
176 | "eilhfbaylgaxvlmusmjlnfpaonkpikaumskacrsejwkxhnoxikgcsvogcqmgbnvqqxxhxcerokujvmyh" // &
177 | "uxwumpnqvwvvpwrajbqggsqujsfndvjevfnhgxyfemmqsfcylrjoxvmxckgpxvabnvuvtpuaweuketvx" // &
178 | "fojrrpfpcsxntwxhajautukbljrysykowkutvtdydtdwqkurfuayxjijhjlwhgwsvnemojnanltkppuf" // &
179 | "prviwkvhnkwdvakmwojjbkysjjvbdinjnjkuoarmyfqwnrhtimkpewedfwkqlfpuccbedeujkgidwqep" // &
180 | "xgcbytusrpcrwfosfnaibuyqhwoygvbgxcpgbikuxwmqxcwxaautkwbqptmnbqsqxvyhasxawwbgwkmi" // &
181 | "wjjtidbplnuqnfrvccaktbhapiudjkhsokgcjyyexxxgsmcyluneyfmcvjkgqgiawquvvptmigafdksf" // &
182 | "fpewmpoaqnvijhpqpfnoprdxoufishqwkisfymditfsdnckuppdrqytqsojrmrxcsapmivsdlsiaxaxx" // &
183 | "gujxunoanbutwjhpgerlnuyjrkkcywtsjljikfkajrfgslgmlijmodxolutlttdrvobcwfitpnrulway" // &
184 | "xrbuuljlipvcjtulwlurhykbjdckpnmkxnwlamfnbwuvlrslacwudpvrjcbmrqbnvpfhchiflrtlxtdd" // &
185 | "tcjmijtxfhcfvctgntifiucedboqlbyaqmvncbfametnjxasnbxrekjvkopjerladadiaoofjpcnwkwc" // &
186 | "tbynwkpmgrfqrjoswqwbuxkjckxgffawyefdsqrwswwuysueuemtuetgiplmtshoalvemlmddasohwnl" // &
187 | "jwswdiymueojrhuojwilapidxsnptucqwxsifssjnfegpckovwutmwtltixmluufviqisfxnixiibqpi" // &
188 | "pxdmgpbgdeqwkrjqphpbqqtpsmpdgvitpdrjpmfitbfjribtqwnrksfqtjlhkjhllimgvxaxkjetumtq" // &
189 | "yajuultoroteuvbtodbjgbhokujlwixfnhpfhwtgkhcivbnidmpcjxgdipelagjdgabviwppjxphxeda" // &
190 | "invbdfjycxkqxwmkuroguirvfycwtmglpuqxomsudxkwkwoyxosebayutfqtnkhatbuevxkukiptutnk" // &
191 | "hbaekwcfabjshcayhptpsmgqkscmakiireanaxgjsgjlftkujkfihcsekbwhsrjgjcjpwriimkpbytct" // &
192 | "mlyyvntykjpkoxorwiqbajrknxneuappqafmeluqpncjiabrwylepmuxoxoslxauscekxamgiqnvjppk" // &
193 | "hxsutprpagcxxahvwdmfqiqajollbjwwwpkxhagmrksupgpsvjtvygmagsljydavhuviurncbclvxkyx" // &
194 | "yyhtjqlsiilaptfeqgukfiarrnnldeyhmfcqksbmhrplhdvawjalkicwoxcdbctbiyoujosjfbjjveim" // &
195 | "xhmgxvxsnorsrwldntbeadrujdcgaovvhisukmeimjwnwgjkmwsbcslbqlosmawvdfyjovgllovrwrmd" // &
196 | "kvtibyckdjcumvvbikqelylvdphjmunshwfjsargmcjwdakhljdshwgqtrrjgfmiwrguvenykqlyjdfj" // &
197 | "oluqqnuyhycqigippghqhvhxbnyswsflctqfpkumydwdlhbyxbovdbibwapgysfmmbwpfayfgexsepyq" // &
198 | "uqwwhnjkrdhgdflbuhdohsvanawoqbbeohkvlgelteyfrvtocimmxlmejcrebkfdmyrvtmjfggagodvy" // &
199 | "vinfysievjofhdrlglypnraisneygcmtgullqpbtaadvitkvvhbbfqjrjklrnttwawicnnhlhavaauun" // &
200 | "ipmlhsqcsaruougpeiewrywsvowjiriqyccukiubcumakjcowqiknwuhxtpwhgsrvafhbajnswyaftja" // &
201 | "pofuejvfgulymysyfdauymfjxjsvjjmogcfqgishknnjpahjwmeikgnocrypsmuvwkkrpatlvvvuprwl" // &
202 | "kdfodnoudgcqdpygapbrqmfeofcwkxpweqxfpdvctmbwkpmjdreanubjfmontjygsndtrydjyaadbfor" // &
203 | "prufwnblsdfqiyftdapgbirqaxtwkobwlugxgadvhrhqdcpiounvxmihxbcreicrquqitekqynntcpqj" // &
204 | "jnsjnfcffcovixuhetekhhlfymdxjiqkkaisgxmsxkbuqvykasmufcolejoxcxukxnlqpuhhkxwislgk" // &
205 | "mokrwxshchibwrwwsfwnhjhsllmsgcdmhsifxdntiqobhovqecpahgryqnihtalklocpiqmcdkygruek" // &
206 | "uavcamnmpjwfufhmqfmndcelroobxmgbjmugccaxfxfxmfyxreeyaypsrswibbqavfsffbcbocdcmmum" // &
207 | "ohbbnvekjipcuhonmqfudytimnxlsisygkjncoyfbicqlssclwdmgrdkuriphpbbdxixognlwoxeppjh" // &
208 | "nqdamcuunyfcnpotwucnqxgtyfqkdtlybxmgkjgacansakfitnpfjfdsftjtcrcsqvlditkslmufbymb" // &
209 | "pwockilxixfehbogeaqoiqutubiyvxjfynnsbcwtuqmmmtsjxtdmylofmaopqvypgqmibfuyqaikhfbf" // &
210 | "lsnqblnmcfsmaiujdarqusmqyhawtdimmarrvdfpdlslutsfugwmownhgfhokhbsdysuiowqhnfxofsh" // &
211 | "tvmmgmmsefkmiprpppdqutaxwdoycikdgxmecfsmpdwufawpcfuexlwsbwugdrakljhyjkkpgtkqrexq" // &
212 | "tdyyaihlhdgtdgdsmjlapnrbmfbwnntybmwvelrvyuvxowauluceywisbqjnkrlvnbdubnulexrljfon" // &
213 | "thwdhmkkknqvoqfuhpsumlnktuyfiayamfqgcscuvhpgxolhnuiiuxokgvysartbdacdcwnkxepskdjd" // &
214 | "vktnkgdxeqhhtbyusjirduafpfqmkcbqfehxwebxfrgxmumujrhmqhcnvcyfsinopfpbfcjariyiitva" // &
215 | "aghrbadkfmnyyghyvdeeakjomnchnvqgktphswlvgjwywthjpnoboblmcownuljahqvdxffgentnwelg" // &
216 | "bnbmkilgtnceptmqlemtbbodaapjybbwmpypgivufpjlbqakwhmksshbpqqkphucwcvbjfawovdurdjp" // &
217 | "uvehhcloxkfhhqytvhwvyukyadionhmucqkwehrcddjbnmrwtqgmhacelddqubgimvhbqtebaopxoexq" // &
218 | "gwpferiyrfbyfbmkkkxjhfowpupuxmctaxwechootysjkkgptwvsujtfjsdolsnuwmqqcobubrrfqcqo" // &
219 | "gqevqpoysigdqhsythpmntnyybeotuakinpnpkprxclfgqnreirnuqandkjicbvboatrfkgcmfsderes" // &
220 | "vpbbpqpfxbkocntcacxtynwdjsdrjhjhdbghjuxtspmeuqaolxgyfqduyuwgxipptmbphqumgsydtrgt" // &
221 | "pkgxflwqxlufigigylpqklgtyyxycsylbbxbtuamsmadxmakpwgcegbxplkcxcfyblselmpgigmaykvf" // &
222 | "fvnbfawglaoabudrwvmewegsdswhsitjebwsjadsoeuvacxdiymhiekikbmmsoapygmlarpyfxafqtls" // &
223 | "bparnxwiwjbuomgyqcavwukerrceedqtxwopqcwcuffmgavwidrosimiqtdqcrmuwcwgwdmoromkbcpo" // &
224 | "rbaqegdvxcffqoiskrgnwwysiyjmrtyhrnpamgftgmsrybmodcogpiyqhrycnwkfgseqrhdsxodfxhfe" // &
225 | "dxrahtyoojcjfkdqofkglllsaoxsqbnetgfolwjgmawwssalyjxbtbbbpdnnbcybaltngwcuqacgoqcg" // &
226 | "vscfthclfjqivlrbrgaorqdtdqkhckgbhkyvpqvbrrtaogoytibreasakrmsyxcbphcsweaqypqjgplb" // &
227 | "oybrsauwtypvdlqaghvfquriqfjxfmoumjauloqumpgyiaerllqhrueawiscmugivknxpdjlgnniwggn" // &
228 | "fvarkaxtfvnhkfdfrkhtokwswompavxvlqouyghhhwhhbmmtoxnbnewtybgyearxasnnwljwbphdbyes" // &
229 | "mwwxxienuqyksnuiwieuoyylkttcgguqtsvihmggtbimkxwhfajgoebigdxkujgrvdorccvfklbdpchp" // &
230 | "tqawkxhlwcvqlijlpuxhbwacwlfwydyyvuwnmrfktkbgvfttmhnjwlluketmblapugogmdvrkjenlcqk" // &
231 | "jdotgpinbrfefentdjxqabafjcsffyiltnxiklboleateefptqdgviyojtcbvqjnxkuglhcvcskmibrr" // &
232 | "pdyyclprjyvtgojghciixewdnoxrhyhojbrupkuvlikmmesbihvxalnarruupxqytnsjusvgmkhuwryb" // &
233 | "hmwkkasujtrfhcpyiflgqrgvscgvhncnucipnxffukgymmpnnnjivclvuyayteqgbdbgoklhamstpkbc" // &
234 | "ufjjjjdvkgsuisjnjhmrsqsujmiufqguspqaiqtqekqowhjwtyotibppemmwmrbgbtavykymuqnnvxrp" // &
235 | "bdplnylhpegmeuvbcpkufhhopfhsnxuhjnaxnkxnqfuhbyolfggllubykiqiurqjpvpmsprakwxxnsuu" // &
236 | "ohcluumrpuccckjymasbyukmjogabjnqiqjjahhvajrkvtoapwkgsupbmegnbyttrakvdiiilhxephyp" // &
237 | "erdhhlaotbxlrlbuldrdykrrbmtrkxluxjqgryvqumugjcfocfyttpujnddnhnnxkhrtlmsterlhxrfp" // &
238 | "ymaavwdnwtgotokwiyysrqcbixurbqonfglrfkanppqehqnyuuugdgutjkjequsmwxrnqascuxllxjuh" // &
239 | "eqwjvmmnjnaedrltvciewpaietmthgoebicwwauviiwofkwewlkijjlgvfturlfjdgbseunujjijpcol" // &
240 | "ikinyipqfkqqnsfwfsrwptfqiktdayenmokxapvtcxvcugwbcjbypanlroljcyuerrnvdswkmugtuosg" // &
241 | "kajhifodonjhauyvipikoqvjwchondqbmgvxpgogyxhsjokgtjwqwrplixsrrqgmcljrwakfclqxpopv" // &
242 | "yayomaksojdxmngntgtjtnyylcykoliqguabkmfqeyckynvlwuafcewujxuojaamihwsydwowvvmigxo" // &
243 | "vjsewnpjxuwybnvcavijhtxixqomvomdmslehlqmafixnkdybpmfmtnpglnhljqyfghsorsjwsegbuim" // &
244 | "qgssrknpohbrltfrwrmgutwbrshnpedkoevloirrwbcannrsbmkivmqbakjbmymxjphaevfqdlewrfqo" // &
245 | "iqyprmlipirdaytjayrlthfrrgchmwdqquimnirvnmrxusfrkwkwwrdwtvnfbniwbobsgyicycsdxvpk" // &
246 | "owraoxyghvaocxnlislytygyywwxeukeeiwlfeuhxfbluggpilyqwioymimyqfcuiivruikdbpdxyidr" // &
247 | "hsmhlrfjjofgmoftbtpaqpphwsxeaonlaqofhvvfyplmdmvumnsmgbxuljktxcgebcfgmwyfwryjdvxx" // &
248 | "kfolnmksqmhhtjukcwfknifunulgfnosnsswuqrvhdjlafkmncyisodraejgylmabngmwjwrhhovwsvo" // &
249 | "lfiuctptunfmfikpptfjlnekgqyvedjpcqohvwewxxehicftpenjhlpgobfyuqckdntercckxfcoosgs" // &
250 | "rgafwoxcodmhptpplaccxrhivytlldrbesncptjthmbctkboarhblnrqyvtgtptgwnypofrkuodceogf" // &
251 | "heuwdvafijgbrqdyrasruaecwwwpeebvehpwlccbjhydcsofxfxcogtuiswikxeplfpwbiftvundqoyb" // &
252 | "gxwytbsajgvjobeuuthjhkeysulifhlyoxurralhvdyjoibuksqttymmamrxrneaueewbbilmjvxdvac" // &
253 | "iqkmbxjmjxghyyfqksrvxtcrtvpyhyibatkyylxbexqdushbhtqpwajeymahaunnmssewfpxiedqoebn" // &
254 | "rianuvyhidnicmhebchgyggfcqiyijwvdlsqkkwcyxbvushedkpxuqndpdkowcwgtfxslwbhvfpnqfso" // &
255 | "pewcaqdlrjqhsfgbpwignxcujkpilvdovcubwgnggmcfmgekfsfufsskkxueuvpvanxflqfbquyptyep" // &
256 | "fyoupcvublxhowunympdqciuvhqugyiiwmijhjmjplsmlloliqsxmaasoffcqkmougqwgsvvjvlagfji" // &
257 | "sqirrxckvokxkrojddstudouwbdjgjsijycwurubhslitdmqvvnggeqpjytuvtgybchllpgedvrevkiq" // &
258 | "syxdpyavaftyjjjxkntofgbvqebdsocfflcwnrfrtpplorkyhgcbyosebvgewdlabadhyrpawmjipoww" // &
259 | "womxviixthhtlnfggdbmcqueuqlubffaigypnkoqbwlugwxynwjqqwhrswiuabeqjxnllpvtivaxfvnb" // &
260 | "dqqlyeekqjsnkvpywccgunftcvumflckymriguexplqiivpukjdfqqkjfdyiotblxawenobpvnsstmmj" // &
261 | "hfoxpvfojvjklodwxahcraixitxlyaagrjovmmlxcceigxsvximpsvxquewiswofekajxppqqygyyndl" // &
262 | "ljmjyscrjrwmtpwnwclqtuayiqjdbdhtuudmofditslfwdsncnsmgsvkskwlopioobclppnaxmctgfyi" // &
263 | "lhqhhuyabkkpjiujxetbmrltytfcmqhpltyniufmiothlkeriphrefnrqfonhbpmnuwhtwtavtycocld" // &
264 | "xiomfxllojwmtgcdydmafdyjahcmaipnawjdcdpmubmtdmtgmrwyovoubkrwfklsjvihowbtqftdcymr" // &
265 | "veiotsskhfdrpbsvdmplmcssrvivbdruvjbxiashjgbjdgwvliwtrowfgbnnfprdqyforeudkowsyuxj" // &
266 | "dmywkbwbfgshrbbgmmlxiluwmilkqdgescmmcejqymcnwdcqgwhmcagncqjruatilvbextcgunqihwik" // &
267 | "igplwmmlwwmfamfvibfkdhuxgdakxlxpwahemfowxoannwgkbpkiagjvhtampipjhqictjjsidduyjem" // &
268 | "qbhwtehkxinsefndkyfaxcjkhvtormslgjrxuomnamfkerpbgxdtwlgxquqbamyptqnjpatjojxmragv" // &
269 | "qquabmbgjasspditnbrrmxrvjkpmrwacydknrkanwponhlqbtdcgeggnmfbrqtvyproocyrcsdhcslol" // &
270 | "fqjuingrtjashimqvseocbqmfqiavmvodlltltvkcbypnddnqsqmqgjifvnsowgoeqspakgikuhmjlap" // &
271 | "qtfcagwwhaajtrmuudipdbkulshekttnlaltqlyievtwvifxhydyavhpxshmjgevsfkkphdyvqwcejno" // &
272 | "ounejdlinpomfvvfblxqqscnrmjtgwxtefmrsodnfqalrwgqmtirakxgxspkcvoekptqugxlkyvqckjn" // &
273 | "afoiodmnavioxhatmhrdcyxvlnojrkkpeelcfsfhntmdboogymiaxkkanvkpnmhgrritxjxuhqpkkdqc" // &
274 | "ellirvpopnstjaucaxoygeaimhlbuyhyejgohxpibkkdwxkviduprngsesqspxfknkjoskpqicosipok" // &
275 | "iuaprejuaguvemlsqkseyhpioilkrdlykjankktoydhhlobpnybbkfnopdfusybumgsskqnmubrjkcml" // &
276 | "olteqekwafxlxmnmjqbdhgajhrrbnkqmfujxdfutojtsubqkmfebdhytouvfypwexrenjbateshnrubd" // &
277 | "isekaonqandnvseclkmwfkrrxrkliiyvlxusemnthevpdkwlwmfyddotbmkaxjscnjbsrnlbuybfirlg" // &
278 | "xavbnfcihsxxtaxqnpuwcdaouhvqdrtulsrucatawtdethtxceyunmxwgentfvlxfvbvheosxvilcvpy" // &
279 | "muniafexmhbsfsklwdlrxulcjdhyotemepmpheeeltkitjqdrtntujrskilqxwulibnjhswwcfjxmwnb" // &
280 | "lvffueniyurmpqegtujmfdrfwpvusulsukqoyjbxibrajigqfqjcbyxiacjbjchyhicsegptroyuesjv" // &
281 | "odfrmqfmbjhtghnkglnrynkjfjeiujnjabyletcvcjhvmifhgqfhfuvntpcgshgatfenkldkppuxvhti" // &
282 | "vwnlwvfjajcjnsgsknujjpsfxyayjnvtetymnyuvrojgafjreyakekgwjisdnjhhsvpxylqxkwkrpdud" // &
283 | "jciypdrxrpyjaosdvywqiuijwewrkaexedfbortrsqdwvplsnhjbmopxdneomedqpllmnqefctbqjlkp" // &
284 | "jenllinaysxreixboynctlefrmjlkbklpedpgqlwkfbwrmjbabeqxgbfipykfoblasaldbapacsmvyhf" // &
285 | "fofojcafuuarajqkbelbbtiuiowesrcrbxuumplunrolaamyeqhjnnmihuejrawuisvkdwuahtecinbo" // &
286 | "cyeenfolaxihsbaoeupjhrnfbjtacrneegqyacrowwwtgwhlrdawdbocgwjkqxukjwvunmagtlbuxcjq" // &
287 | "oowotlfsxuxbfbpxejwqlqeyypkpykximelfxsnjrwivhxkqpvcabyodnreriidekqhkmllbrqreaieq" // &
288 | "lmfigqmmqioihmatoelhgykcixaoelydhfrjmdmqldkmdtdbrdgqhavpiasjrbfudwpnrovohsljaxrs" // &
289 | "pvvkuqemjgsdbhytqpfwnreurtvrxdshqetgkgdadcnqntvgkllcnprverlodqfxflbvuiepuvwnxjon" // &
290 | "gskakjjroiwaauoqvvpqucumnthcdletwdrgsreadpqvjyonaqcvxqcwfuxgnflamxyaglynhmfgauvs" // &
291 | "llvloaqasrindxsydciaqetekmutadwexqdekqmkplbhdbcblpkrxbyhnyjemidymjimyinvmtqytjpo" // &
292 | "ylnqcwvdfltdheursjegsikodbatpsojjkjgjfrnsbewjpeijpabocbwwkeuceaehfguagyqjfesldpe" // &
293 | "hnqktgwujytpymbnqurbkmhqxfgujoogkbgccqbyhnwphsdveunfjuacksbaqikplvywlldnmyqbponm" // &
294 | "stivotqsxlapxxoghprdqmjguaxqtscleflrmwobwphhrrfstyiyeyhgiqtuelbacdtcvwyescslmkjx" // &
295 | "lgvdhscxfsxgupccawohmmtjfbavbclohotijklpxjucgmnnocidfrimmjttbjagyoxbncdtqumsrgor" // &
296 | "mpolkfiycyycfplajabvpqsqrcwxbkuhiukwqnanbymancjtwmxdukyysbgbuorgovshfoxridafiljq" // &
297 | "sujiqdlargqvmdtrxgmpeyamyjhgldptbplmrnrhpxonkrfwradhkajmwlcxlectxvhaokdfsrsfegmp" // &
298 | "icujynmycqauvysfvalqiqkfexxcmxlnnyijoudleqhxgsvuplmoldgstskkxhwbtyroovstvgmlquaj" // &
299 | "iwmhrrkkillchbvlxiybyhqbprnfqqgcigpkviyxstigjxcfeedermgrdpsfnspenahffjujefpefkwc" // &
300 | "blaswperaivbkrdiabmydvavirdsvfgkqukuvhwcyvligivmylbefvsxsunhnayuowqmbtcgfhcyrujx" // &
301 | "yufunajkruooouxpqhvgecxxmscctgqluxtnfbncxudxigkdmoytaodgqwbcmdvgcyutohswivnhjjyu" // &
302 | "ehinmjihelwbakmwxpjsykcnymeruflyonwimrptuburlewfxbpplnixsyqkmubvcemglftmkrfnaoma" // &
303 | "gkfqqjnrxdqscytrtpwfsyqejsffgceoqlsysgxtaojutpvxikuxohleeaqefgftylbtifyaywdipgsk" // &
304 | "utlhtbdfvjirhsectanlcdgmhucjhdyakwjfuykeowubrwusdiyasgkhbuiojdwheefaahrjikwqxlkt" // &
305 | "vljpcoqeeoiqbslujtaxmbnnyihiwljlubnfgfbquiflhuqaxbidlfpadapckmxboeqsufjikdcocmis" // &
306 | "ywihfpvnodrypfocydvipnegslcowgcjijotahkclcqhappdbkhucnxhaqnwokxxtspwfakwlcveysvy" // &
307 | "hxksaqwpakysklwcibwdhlhybrjbrbakqccfbjueynecjucytiivxfhlgweovuacqsvdidudqbtotcfc" // &
308 | "tpmfookytapvmopdthjnkphdrrucniiaodvjinypadvimkgjngcoxysdwwrsaejembkixmxtbuykfesd" // &
309 | "gradldaotdhsimiooqpwuwqsucytoyocdrxpcebnedpjocpkgsxawpnpxqacmhjctsbutulklvkejwwd" // &
310 | "woopatetmohwkcebjtkxmvcslhomlxykttfivpooxamhwutyhocdkrehplaysmrutumooehdjtdhbpmh" // &
311 | "smevudefwiqjdksoplnljfgxayhcpyhlplvdhvgsebeqqyrwewyxrunqhrdfdchyfpgdubyfbtkrjpxa" // &
312 | "pnvcjbvihwlsbheoolenfjqiqfcncmgxrrfyfursivuuidonggxrrqqiafomghcbkklacebvrtlxhdqx" // &
313 | "vlrqsyfgrvluhkpncbmgdphhlewauqammglicjmbjwsmeosviopfdsirqjytnejkcvffrnymkmuvhpjx" // &
314 | "jlxhwfubluyijnxtlnexdoxhvlusbbyhmwumglhrrebhgatcrlcetxjhqqkdywshivdbwuxvgeuguhnd" // &
315 | "ygwohpidabivdxqnniahuvlthcmysiadlituipistlblnlosacnfrceqbhwtmbbuiucckhpqcoepjxsa" // &
316 | "huqfjwkjvyadjohrxduoddmeunsecuemmrebpddikjcxltahanlusbbnqbjxecaeusfaktkeoicnoikb" // &
317 | "esddmrvfvvrmgjemsegyycbjnhqeijncvrlgomdtavmvmudjdcupdjhredyyrjmwudorcetrngfpcmde" // &
318 | "crijthwtrywqlmyobyrabjwvamtesiotvhqvgvbocolwijbdbtjiqjlxgxgeepyequlnkhnbxevimfjp" // &
319 | "lchujlsuhmcshndoaeujubvymdqvxbjulkwyjjwtjrsfrciubpwnbeqqlvksndtubyqwusovmjsrbmis" // &
320 | "jjmoscrullhgptasxmclmtqegtujojndmhcwyjekbjfkvstukjyimlyjyyyxquvgpditpabyenenwcgs" // &
321 | "ywjsxabvtokoukogkgvekpmjaplgadtccksthmmiyegqnfkriuoeauqgcpkcvsdjnaurhmbhegycofhh" // &
322 | "fayhbtpinjxelwysibqcbseekooqkbohufxbsmbqdjlcvffibljixxehspeogsyigqpbnecbipnopwdb" // &
323 | "nnifaaybhrjulosujsbnoeebmjqllwqlijltjksxrnhdicsxwkjkxramkdqumeqqkarqnvwnafmfhjdj" // &
324 | "fuflvdsjlcmqgstvtfpullctphnxasjogxmybhofdxadmatrawjyufolyxvvrioyebesnanqchnvdoib" // &
325 | "nlnyqysrqekeevemriwqpxxkdpwysutbykypqpvoqqfbmktslxwhmmeutdqrldohcuxhoqahxxsbtpan" // &
326 | "hrqlrqpwhkdclvypvojegtsjrdjwkabipulonnkvfqtbqulpqpsajayxtwwavoluhcrwddwngjltsxrj" // &
327 | "nipydbcvevmokycxhpnudwdnlndxrsptrivtawdregnhhgdlonovqloguwjrmrpyurpxonemtrjbcvih" // &
328 | "wxlrwcujktsddptannbwvqxjqvtaduucaqxgwqfnlsanwbqckollmwjxwdmpjlsxhvnmlpoedtaasfvj" // &
329 | "bqooshykkpsbujtsuekkntuupkpxwdeuousdfrgewhfkbmbgmpfgbqhoxhvgmacgkueegcxanxfqumfs" // &
330 | "iubxppsuggyccbgribnxswxydpdfrfcntpbwnawdyitpcbnmrqlkomgihoyjsgpebjuogghmoddpipdb" // &
331 | "gvdbnqcfephxjfohpxgpnclgabgiwsawomkoghfwlqcrtywefmwwtqcxdiytqttquxbkxmlpqqxpiukk" // &
332 | "cnrhglqilupjamfvsjtegfqohonqxrkuywlfakuembhlprsrcdgigrdkawboyjhdenvrhnnwbhqbfisd" // &
333 | "oxljwkbvqpqpnnvhjlkcqyhjxntfsemwjgygdqybuhetljrkysqtkcppieivyikpjvjecspijlqcrjsy" // &
334 | "beiycluemnscfquaryuoaalhnwftopueffexrbgmpidehcgrylgdmhpjskbnbfvwijnatmsroykhqbyn" // &
335 | "bkgywndmfdsowpekeqemiglurfwavnitartbhgrjewnuuyjmouelojqttonqqybtwjauttmaowwvqdys" // &
336 | "aifjaadbtwjmnohxumlpwtqxwulbrscltcddshtdkcrvmhtljyphklhawiqdfdavajhusnphwigmgnsy" // &
337 | "jpweohgjfdlnnrgpvxhmifvwwfctthbeteteossibdjrurlyawfbqcqfoembafsqwubucuhjyhhwdygg" // &
338 | "dvixkkdadavdouqkrprkflhsfbsufyyqiskpegyfeiejdsytedvwhtryhbftgxpmhpmkstuvdooyvhgl" // &
339 | "xrdgcepoawbkadwlptdpmnqhysrlrpwbhamhjsuntaaougywgmapdvigpyoxpfvjnicfsixnfdbbfuaa" // &
340 | "lcykclndnjhfdcauyegkjvutlcxcxapfdvqssoedosuyrpeoedpbosirbvggbwpgjxepdsmywqhkyvie" // &
341 | "ufuvkkqmkngkoqwoapabduclwejwbidjqwefloglbtyibyloqnlhfnfvbjuslghswsvwoytluqxncbrd" // &
342 | "ttsphfvuwnkvsspcjxwwpnclwxnrjmbnmurcdxbfqgrkbsuptmgpciivhtpbujruynwhceqhffykkkbt" // &
343 | "hcimoqxfndpxngfbfoymjwnkxdedndhsurpqgcvajafoythjpwupivprvaxkbihwljxeqfajtrfrnlts" // &
344 | "ihouqkkipqstaqiwdmlwovvobctsstbgoqopsejqubcyeaduyitdjajwclginbshedrbohsfkvyjskpi" // &
345 | "bbqvomnoqfopusgrdxjyvhseeqofnabrpbekfssvniyfiosnwryeisxahnlypdukpswueywsarkiytcj" // &
346 | "dhciajvtcjyumpgmddxammrxtnnewecrtppqjrgiunlatfycfguuyuycrloswqxtruokqgecwxklimjx" // &
347 | "climjoabdgakkmqadkomremfewxxlcevqgsaihqvlrrcikxjnybdlclrborhybpgdsngliwvhpofcgrr" // &
348 | "liauabnuiawwejktaamowyrxswkcmkfqxddvyocbtieoebtotwxddcaevpcqbffuvxcehmnartgwqqgf" // &
349 | "lgsevrtghuvpfhxnmbkwlaejmlolfovsvnrcbcrtocjrkcmmbxrqqpjmennytftdsdstyfwajqhrsswq" // &
350 | "ganhuiafgssnkpaiacngbxnotgathtchdhjcyexlxjbsqeefjbukpfhsrmqynqoefkrxultjbbtxabue" // &
351 | "mgdgxehnumxwajyrcjkfkuisdsedjlbnfocxgcvvoxagwsrwcnbyyslefumhrapovohbeopkleifsqfq" // &
352 | "txveufxkcxpktgxuelluuukhocddgacameyviptqbhgeowtcdegxbfemjlbljuagqnnprlxwimfinkcv" // &
353 | "rpgydnhsusmbkmipkoeoxmiycqhrumgqmvooibxdioeffcblktyooypsyugryrcsldrobkbowvlblpfg" // &
354 | "ocyfigeyndnimqgoitgercodhsyymmigwmdptluytbbjceaybeheksxshjyqqnbaksjekthtfkovsnem" // &
355 | "nxriivnrqaalsctndqmkbbclaaexdqxhkvrypenjewcadfqavruhicuqqouwvaksnwpqdbaveeoqvwrs" // &
356 | "dsatuulotdmljrfhoucqssnkugtnkossjapffahpvlpdrcanwtsmsgncascsmvjmnmjfqnflfrwjyggb" // &
357 | "irqffiamuwftnmgutceumosatjfdpianadwucvlfywtydoefbqjljgihafuxselakthuppjgsqchedwf" // &
358 | "fjqwrbsmbseluogjaykpaqadocjucnyxqishjkfohfbeupphahvxmdldcsrvebfkfxbukiwvdwqrqmxj" // &
359 | "ksydkjvsvlxmljxyuvuygwmvryjboxissapfgyagbmejrwfebcqmmbbmujndypghpeqwuwngnalnddpp" // &
360 | "ojbpbimbpkclhykhqnqcnblqvjkgfjsyjsgwlalljcvkdxapcmjuoteckhgsscqcshhtvnnlfahqlnvy" // &
361 | "wvafimejpjyyjteefkwwhrbymxqgxtcyteiddqhtemjdxlavsluscdhvgnxxhuiejeuxwiqjpjpkprrc" // &
362 | "dtyechybcaynwyaefqnyxislgofuccexfjpgbciwcvmyhqwtolkqlgnpqxfisnriwqeookifnamsappk" // &
363 | "gvrfdqeqdaydkjlliebianntyccmekhlqvbxcwuiyovfwnlxnxsjtbhfehamwriokoeqkytwatitjupv" // &
364 | "jwpswruqvhssklcikugtnyvgqxdjyhneebvomutsjhqgkdbtdbweiaxhrpmikpnbitbkmdxpvtjjcewx" // &
365 | "rqcmlichqbboyuusqlwmbugfanssbgcrfncqxmhgyyvtmttsyvmyqrpeifstwmtnorqbgtbwoklfecun" // &
366 | "fkuiayltwksxpoirrtiobfalrkclsgnktppsmjsgqpwqijyqupgbraydssfxdviyobjmvifkiljiuqfw" // &
367 | "iuvksimfakytafddefsdvtocllfuijyrpdatutbarjsowegqdfiicjfsvpciudxbygykxocmiagowdpf" // &
368 | "ksvljdiohurgldsqgoilcjbqpxsffnyblweonpavgwmqspubbhxobvaeajpeppsubsardlvnktgjkfel" // &
369 | "beykixarsblpdxqxhlcrfxsyhsbhhogcsmmudcythtyikccgjvukcwydvthlvwhpiosfvbvdsedapgnk" // &
370 | "rpnpyunolgmxablnyuufteatimxdchpasgenptgfibhmsdogljgqlrnjkosspwfakpennvjncuworbxj" // &
371 | "jnnfifhvatovlchferqeciuskdkyhbyetapvyptuybnlpmirwqkcqalrgmktnqufkkewesrcgkjaisad" // &
372 | "mwtvecanbhnpbdnyiyhulyqxbdbxyfpephgmgkbechgckxjcscblakhhnapsitchdfibnlcrcbbtggab" // &
373 | "grujtxbfgcisuhvlfeifnnhrrebqcrqicftwegkplcmhhqprvrwarsuhlsoanvnfpaglkaugdvmowxrp" // &
374 | "nnmwgquxtqsvgaypmqsvlomweevhfjvrvqdgtmbkdmtxosbvxytkuwjwfettyndmngdbkwriapgddwkc" // &
375 | "ptgsiryednqhqynncdjpbsvrqqtgjbpciaomcgttprlmjidywmwukhnpqdforxxinukteleysseforui" // &
376 | "wautchslsxmkfduvooyxhmkojrnbcgyxkwuqinpsahfcjfutcmrqdwwihpylrbxijamumgaxanskltga" // &
377 | "qhblbwuelvsdhshyxjsjrewhdvummduxxvacoxivafbdrlpyncjposcvmvwoxcglkwqypjxqiivlmfof" // &
378 | "hdaqathrunmsiqqkyvtgcdpieurnlvcrmnsmulsuadirufqkwkdbhangmhfrikaxpmtmlucrqqpxbjip" // &
379 | "phjnntibdifbxkldfyjwwypnvxjpicbrodereumgtewlnrdmbhrfgcitilurlcsqfoygskolerxrvjon" // &
380 | "fnulkurtjmgggtalddqybremblovcqhynxlcyaiehhdffmjybqxpwxsianfnwrchunudgnijsqoqbske" // &
381 | "tsspffunpvwqxmqxknbmoifaocnmywesbswaeayvaxbwwpwwwkcnpvnyrhpobtexmvjcjfmwlsflxnmk" // &
382 | "ogjgqyuwvtmnhfyihudpltwpotqognmovbdtetcfgmucyieslsebmxhfwftqkemrcsafnqgdsdwjxofc" // &
383 | "aecvcmqmyyeayfodktlupcfwtepuxcfonbobyserfjcyfsscbivcnktvusqtnnshealhgeylyckwbums" // &
384 | "opihhwlccnxvivutpdjplgoblsnnxftekpyrsilralxncrrvdhxxpyavposlircrrxbiasgfdkgfylak" // &
385 | "rmjtprikqtnjyixsswhhskxrfjhagghvjqbbhkhfvknxewbprvvkvdlhyohijfgscakitdbjvtkyweie" // &
386 | "kwrtotbdxobskiydvxqjyawdfwcwwkihnmgjtlvhhrawqdaekreaodmqvhfxqlnhutuosppfvmsfjucw" // &
387 | "ifgvkhechsoudymonyjxdiqpmxpacwfylyfpswekrmpetahfcyykvvyxovgywwbwixteeojmvmufmnpy" // &
388 | "nlygrkldvlneidvmhyktcjcfetitqnbfxubtsfawmlbwahnvfjhscntdjuhcoswjhuriyiedmjgpyhol" // &
389 | "muoshhosplegknticrochkytwyvcqkmmoilwllbkqltgldkibsxfhwdtssqtdkjitlartnjlhxwkgooj" // &
390 | "aejxgawrnbmlngifnjviwryocingrjviuxmnedgpjmtxysrpraowayorkaauwicmhxtetgcwcqrrqldy" // &
391 | "xkcupdoolvoqumctqcvnuceigvdamrjkdphuglwqapngspnhssiejfhgrsrijwkxjasffekekahkjsyo" // &
392 | "dxrvlgapywwligqvyarbtdidoxmogglxtbdrcpdqyoctlilmwkicrppqushhlylgieyvktomayyjxnxb" // &
393 | "xeaphuxmwwpnugefolewakihsatbljemtwwdgovxbkqmgbuayxdeerbduafnflpsphmycqdpmuxakabg" // &
394 | "ebdfdjvghxnmeysdchrqjoqawjkibsciscdlgqiuqcdkanslefdnjtkxijdjwppgmrkeydaodpscrmhs" // &
395 | "tnorasarqrauwgphpfargsigkqsdwagpiqrwwhqcjapcpbqhcxqbpytvvbtcvhbsrddfqemrcwjwjvbl" // &
396 | "weackbyqukhtoftervtoqqnecxmttfqxwhutrbsleqflmufbqxhceorcithtrdbhcetxgtatuiokxlry" // &
397 | "wicjlubbwwdwrbytsnuihdqhoedwcoutjtudbhbvuufcibybmpuqtvqchoiwkkakytwlegkqavfwiryf" // &
398 | "ncupsolrpinqawkalpitrjutxnjqkjgqxamwkmhwyfwjuvpbgddsnrqienfdpesgntnnbxhqdbhnwllt" // &
399 | "fvrebymryxcbdwjgkkyemgjryxcdhfuncmpfiddvtbifoumgcytarpjseqqbuueimmrdbdrctntroygy" // &
400 | "xldcktvxpdldrfbqibajosmwmekqdlcalqelxtvgtidvrtuhhcwedgpppakehgmrnjdhbiavkqdtfglq" // &
401 | "kvawyuvlqbnakmnmwrufrkbkbsuuqagfypllkbcfjtvmpqjbhurubtpkfagnaoohwjrxysqxuaxcjuss" // &
402 | "bpqucnfrtyoosjubeyyyuoyxnwubfngschethujxiopbxehcuxbmsjhjvovjlsbndhoccgrxdqtvqbfy" // &
403 | "paxawdiyndxfhuevpjgjcgatgwlxxusdidiorbpewttfvntktwvgbuhbfxwrjfjdklpxcwkcbwwwefsd" // &
404 | "bvksujpcrkqsfaplvdativyjvuengcnuetluxjkxfhqnvhhlplfderostuoklkkjtqfcpinamdkstuoo" // &
405 | "aaemsgqwepsgmmmbtlhehtevboplthvnlhkvhhxryjgdqqjwjttdrittfaqpdhikknkjapshpbewwbds" // &
406 | "jnyvxhmjlvaqssixilesomgjdknsirdewqyrgqhjctupyusxmdvcgdikwsjmkmwhblgvjodjvdajtwhi" // &
407 | "jmmkgovsnopgewhcdudcybwxdpkppiubsvvrgosnptpmxywnuwpvobsdxofdgihypqrrjsmxadgprwye" // &
408 | "nrbdtbrrxvyvhcdbgpnvmotrvadrtygdotihxyeiidlcibcadhxedgmnngqnevvdjfepwygttgliernp" // &
409 | "bkxehtpjdmnyvsprogbucdttqxsyiybfduovhurufqmachigaikiwhrikprkufdykeqiagludpurhesi" // &
410 | "gemqfyadpesjvvyqqnvdckspjmestcjosuvrggarfpwyxjvdaloysbibdnbigmnkxfdamycwfadrjntx" // &
411 | "bcoofmkcdvmguehkhcwjpwkgaykbythsqxuucdojkasunqchqjxkhphsresdpudxxxfvoqecavtdxkra" // &
412 | "ndnexwcelmwlyketxbsfvbyvaypidfkhrbtmkusghxanbouerexuigpnsaqaewiljsbsfkoqsqbsxmwg" // &
413 | "xuflwnelcywopoeurogcliupipngtrrrniptqhpvkbhowfkdufgyeexapgwvgmdeibsegptngxuijhme" // &
414 | "xbmmbhelngkaxobydxoetrsmlavwhdqwqywilfueyrpukadlgnnruhhqberrshqjxttjjomgbakjfjfn" // &
415 | "uddtndboqmtlkrnhjpmrfdmqkfenitihxlafynqbucoykxeospditmmgjuyqrgoralywrgfvwfqyywts" // &
416 | "acwyokpxybrcxfteyfcjildbbxdhrsdbgvjutpynndcycusgwwjivtuivqbohpxiutvflwifkkpkmkyv" // &
417 | "isditdkrlxdhfcurmohnavryigvipnjldyheyctkdrbnwnmbxrwlgtjuxiecnrppiibwacwbnayfwsuq" // &
418 | "kmhtiejglqwmhxfhhulennwmyblvbliavhhxmhmugmhdchnmhcvtxxfadjintfcbhxtsrxkvyaeahmsv" // &
419 | "tilpjkwrgyeljwfsfgqogahlsnrgunxyrbxcmiybcpdpgxymvenhpelsqcjuuofwrmkqrjkqtuddnnoc" // &
420 | "trvvwgejixcjhhctopasnadbkeatwbtmfbnvnlgxgkwdgisygwnohslihuelwtwxbbhmowthxgqybtmf" // &
421 | "xdioepkbhlbicunhysxokxeyqnjgsbskohnenxbabxnqrjqgpygniutqpxfpaojpavtvgtyegkdxpcfy" // &
422 | "tetobywtwqcasxlwlqgbyigjolhgdvfswmvebhhvahmunav"
423 |
424 | contains
425 |
426 | ! Convert a C hex string to an integer
427 | integer(8) function c_strtol(string)
428 | use iso_c_binding
429 | character(kind=c_char, len=*), intent(in), target :: string
430 | interface
431 | integer(c_long) function strtol(str, endptr, base) bind(C,name="strtol")
432 | use iso_c_binding
433 | type(c_ptr), intent(in), value :: str ! address of a string
434 | type(c_ptr), intent(inout) :: endptr
435 | integer(c_int), intent(in), value :: base
436 | end function strtol
437 | end interface
438 |
439 | type(c_ptr) :: strend,str1
440 |
441 | strend = c_null_ptr
442 | str1 = c_loc(string)
443 | c_strtol = strtol(str1,strend,base=16_c_int)
444 |
445 | end function c_strtol
446 |
447 | ! Read in results from test data in tiny-regex-c, convert to an array
448 | subroutine read_test2(fileName)
449 | use iso_c_binding
450 | character(*), intent(in) :: fileName
451 |
452 | integer :: iunit,ierr,i,last
453 | character(len=4) :: line(16)
454 | character(kind=c_char,len=5) :: forc
455 | character(kind=RCK,len=:), allocatable :: whole
456 |
457 |
458 | open(newunit=iunit,file=fileName,form='formatted',action='read',iostat=ierr)
459 |
460 | allocate(character(len=4*1000000) :: whole)
461 |
462 |
463 | last = 0
464 | do while (.not.is_iostat_end(ierr))
465 | read(iunit,2,iostat=ierr) line
466 | do i=1,16
467 | if (len_trim(line(i))<=0) exit
468 | forc = line(i)(1:4)//c_null_char
469 | last = last+1
470 |
471 | ! Now turn to chars
472 | whole(last:last) = achar(c_strtol(forc))
473 |
474 | end do
475 |
476 | end do
477 |
478 | close(iunit)
479 |
480 | whole = whole(1:last)
481 |
482 | ! Print to output
483 | open(newunit=iunit,file='testdata.f90',form='formatted',action='write',iostat=ierr)
484 |
485 | write(iunit,1)
486 |
487 | last = 0
488 | do while (last0
7 | character(len=*,kind=RCK), parameter :: testMdata(3,91) = reshape([ character(len=30) :: &
8 | "Foo", "FooBar", "YES", &
9 | "Poo", "FooBar", "NO " , &
10 | "Bar", "FooBar", "YES", &
11 | "Par", "FooBar", "NO " , &
12 | "Foo", "Foo", "YES", &
13 | "Fo", "Foo", "YES", &
14 | "Foo", "Fo", "NO " , &
15 | "ooB", "FooBar", "YES", &
16 | "ooP", "FooBar", "NO " , &
17 | ".", "FooBar", "YES", &
18 | "P.", "FooBar", "NO " , &
19 | "^Foo", "FooBar", "YES", &
20 | "^Bar", "FooBar", "NO " , &
21 | "Foo$", "FooBar", "NO " , &
22 | "Bar$", "FooBar", "YES", &
23 | ".*o", "FooBar", "YES", &
24 | "o*o", "FooBar", "YES", &
25 | "P*o", "FooBar", "YES", &
26 | "Fo*o", "FooBar", "YES", &
27 | "Po*o", "FooBar", "NO " , &
28 | "F[po]o", "FooBar", "YES", &
29 | "F[op]o", "FooBar", "YES", &
30 | "F[qp]o", "FooBar", "NO " , &
31 | "F[^po]o", "FooBar", "NO " , &
32 | "F[^op]o", "FooBar", "NO " , &
33 | "F[^qp]o", "FooBar", "YES", &
34 | "F[po]*o", "FooBar", "YES", &
35 | "F[56]*o", "F5oBar", "YES", &
36 | "F[46]*o", "F5oBar", "NO " , &
37 | "F[46]*5", "F5oBar", "YES", &
38 | "F[46]*5o", "F5oBar", "YES", &
39 | "F[op]*o", "FooBar", "YES", &
40 | "F[qp]*o", "FooBar", "YES", &
41 | "P[qp]*o", "FooBar", "NO " , &
42 | "F[^po]*o", "FooBar", "YES", &
43 | "F[^op]*o", "FooBar", "YES", &
44 | "F[^qp]*o", "FooBar", "YES", &
45 | "P[^qp]*o", "FooBar", "NO " , &
46 | "[0-9][0-9]*$", "0123456789", "YES" , &
47 | "[0-9][0-9]*$", "A0123456789", "YES" , &
48 | "^[0-9][0-9]*$", "A0123456789", "NO ", &
49 | "^[0-9][0-9]*$", "", "NO ", &
50 | "^[0-9]$", "", "NO ", &
51 | "^[0-9]*$", "", "YES" , &
52 | "^$", "", "YES", &
53 | "^$", " ", "NO ", &
54 | "^[A-Z ][A-Z ]*$", "", "NO ", &
55 | "^[ ]*[A-Z][A-Z ]*$", " THIS IS ALL UPPERCASE", "YES", &
56 | "^[ ]*[a-z][a-z ]*$", " this is all lowercase", "YES", &
57 | "^[ ]*[A-Z][A-Z ]*$", " THIS IS not ALL UPPERCASE", "NO " , &
58 | "^[ ]*[a-z][a-z ]*$", " this is NOT all lowercase", "NO " , &
59 | "X[-+]Y", "X-Y", "YES", &
60 | "X[-+]Y", "X+Y", "YES", &
61 | "X[+-]Y", "X-Y", "YES", &
62 | "X[+-]Y", "X+Y", "YES", &
63 | "X[-+]Y", "Y-X", "NO ", &
64 | "X[-+]Y", "Y+X", "NO ", &
65 | "X[+-]Y", "Y-X", "NO ", &
66 | "X[+-]Y", "Y+X", "NO ", &
67 | "X"//TAB//"Y", "X"//TAB//"Y", "YES", &
68 | "X["//TAB//"ab]Y", "X"//TAB//"Y", "YES", &
69 | "X["//TAB//"ab]Y", "XtY", "NO ", &
70 | "X["//TAB//"ab]Y", "XaY", "YES", &
71 | "[0-9][0-9]*\.[0-9]*", "1.9", "YES", &
72 | "[0-9][0-9]*\.[0-9]*", "1.99", "YES", &
73 | "[0-9][0-9]*\.[0-9]*", "1.999", "YES", &
74 | "[0-9][0-9]*\.[0-9]*", "1.9999", "YES", &
75 | "[0-9][0-9]*\.[0-9]*", "1.99999", "YES", &
76 | "[0-9][0-9]*\.[0-9]*", "11.99999", "YES", &
77 | "[0-9][0-9]*\.[0-9]*", "111.99999", "YES", &
78 | "[0-9][0-9]*\.[0-9]*", "1111.99999", "YES", &
79 | "[0-9][0-9]*\.[0-9]*", "11111.99999", "YES", &
80 | "[0-9][0-9]*\.[0-9]*", "123456.99999", "YES", &
81 | "^[0-9][0-9]*\.[0-9]*", "1.9", "YES", &
82 | "^[0-9][0-9]*\.[0-9]*", "1.99", "YES", &
83 | "^[0-9][0-9]*\.[0-9]*", "1.999", "YES", &
84 | "^[0-9][0-9]*\.[0-9]*", "1.9999", "YES", &
85 | "^[0-9][0-9]*\.[0-9]*", "1.99999", "YES", &
86 | "^[0-9][0-9]*\.[0-9]*", "11.99999", "YES", &
87 | "^[0-9][0-9]*\.[0-9]*", "111.99999", "YES", &
88 | "^[0-9][0-9]*\.[0-9]*", "1111.99999", "YES", &
89 | "^[0-9][0-9]*\.[0-9]*", "11111.99999", "YES", &
90 | "^[0-9][0-9]*\.[0-9]*", "111111.99999", "YES", &
91 | "a[0-9][0-9]*\.[0-9]*", "a1.9", "YES", &
92 | "a[0-9][0-9]*\.", "a1.9", "YES", &
93 | "a[0-9][0-9]*", "a1.9", "YES", &
94 | "a", "a1.9", "YES", &
95 | "\\", "\", "YES", &
96 | "\.", "\", "NO " , &
97 | ".", "\", "YES", &
98 | "F[qpo", "FooBar", "NO "],[3,91])
99 |
100 | ! These cases have C-specific characters and need be defined
101 |
102 | contains
103 |
104 | subroutine get_m_test(itest,valid,pattern,string)
105 | integer, intent(in) :: itest
106 | logical, intent(out) :: valid
107 | character(*), intent(out) :: pattern,string
108 |
109 | if (.not.(itest>0 .and. itest<=size(testMdata,2))) return
110 |
111 | valid = trim(testMdata(3,itest))=='YES'
112 | pattern = testMdata(1,itest)
113 | string = testMdata(2,itest)
114 |
115 | end subroutine get_m_test
116 |
117 | logical function run_m_test(valid,pattern,string) result(success)
118 | logical, intent(in) :: valid
119 | character(*), intent(in) :: pattern
120 | character(*), intent(in) :: string
121 |
122 | integer :: idx,length
123 | type(regex_pattern) :: re
124 |
125 | print "('regex test: pattern=',a,' string=',a,'....')",trim(pattern),trim(string)
126 |
127 | idx = regex(string, pattern, length)
128 |
129 | ! This test does not check the length of the match
130 | if (.not.valid) then
131 | success = idx<=0
132 | else
133 | success = idx>0 .or. (idx==0 .and. len(string)==0)
134 | end if
135 |
136 | if (.not.success) then
137 | write(*,*) 'FAILED: regex result: idx=',idx,' length=',length,' expected valid = ',valid
138 | re = parse_pattern(pattern)
139 | print *, ' ...pattern breakdown: '
140 | call re%write()
141 | endif
142 |
143 | end function run_m_test
144 |
145 |
146 | end module regex_test_m_regex
147 |
--------------------------------------------------------------------------------
/test/tests.f90:
--------------------------------------------------------------------------------
1 | program tests
2 | use regex_module
3 | use regex_test_1
4 | use regex_test_2
5 | use regex_test_m_regex
6 | use iso_fortran_env, only: output_unit
7 | implicit none
8 |
9 | integer :: nfailed = 0
10 | integer :: npassed = 0
11 |
12 | integer :: i,length
13 | logical :: valid
14 | character(len=30) :: pattern,str
15 |
16 |
17 | ! Test #1
18 | do i=1,size(test1data,2)
19 | call get_test1(i,valid,pattern,str,length)
20 | call add_test(run_test1(valid,pattern,trim(str),length))
21 | end do
22 |
23 | ! Test m_regex
24 | do i=1,size(testMdata,2)
25 | call get_m_test(i,valid,pattern,str)
26 | call add_test(run_m_test(valid,trim(pattern),trim(str)))
27 | end do
28 |
29 | ! Test #3
30 | call add_test(test_invalid())
31 | call add_test(test_main())
32 | call add_test(test_bracket_space())
33 | call add_test(test_end_anchor())
34 | call add_test(test_end_anchor2())
35 | call add_test(test_read_version())
36 |
37 | ! Test #2
38 | call add_test(run_test2())
39 |
40 | if (nfailed<=0) then
41 | print "(*(a,:,i0))", 'SUCCESS! all ',npassed,' tests passed.'
42 | stop 0
43 | else
44 | print "(*(a,:,i0))", 'ERROR: ',nfailed,' tests failed, ',npassed,' passed.'
45 | stop 1
46 | end if
47 |
48 |
49 | contains
50 |
51 | subroutine add_test(successful_test)
52 | logical, intent(in) :: successful_test
53 | if (successful_test) then
54 | npassed = npassed+1
55 | else
56 | nfailed = nfailed+1
57 | end if
58 | end subroutine add_test
59 |
60 | ! Test two bug patterns reported by @DavidKorczynski in https://github.com/kokke/tiny-regex-c/issues/44
61 | logical function test_invalid() result(success)
62 |
63 | type(regex_pattern) :: re
64 |
65 | ! Test 1: inverted set without a closing ']'
66 | re = parse_pattern("\\\x01[^\\\xff][^")
67 | success = re%n==0; if (.not.success) return
68 |
69 | ! Test 1: inverted set without a closing ']'
70 | re = parse_pattern("\\\x01[^\\\xff][\\")
71 | success = re%n==0; if (.not.success) return
72 |
73 | end function test_invalid
74 |
75 | logical function test_main() result(success)
76 | use regex_module
77 | implicit none
78 |
79 | character(*), parameter :: text = 'table football'
80 |
81 | success = check_pattern(text,'foo*',expected="foo")
82 | if (.not.success) return
83 |
84 | end function test_main
85 |
86 | logical function test_bracket_space() result(success)
87 | use regex_module
88 | implicit none
89 |
90 | character(*), parameter :: text = 'table football'
91 |
92 | success = check_pattern(text,'e[ ]f',expected="e f")
93 | if (.not.success) return
94 |
95 | success = check_pattern(text,'e[ ]+f',expected="e f")
96 | if (.not.success) return
97 |
98 |
99 | end function test_bracket_space
100 |
101 | logical function test_end_anchor() result(success)
102 | use regex_module
103 | implicit none
104 |
105 | character(*), parameter :: text = 'table football'
106 |
107 | success = check_pattern(text,'ll$',expected="ll")
108 | if (.not.success) return
109 |
110 | success = check_pattern(text,'l$',expected="l")
111 | if (.not.success) return
112 |
113 | end function test_end_anchor
114 |
115 | logical function test_end_anchor2() result(success)
116 | use regex_module
117 | implicit none
118 |
119 | character(*), parameter :: text = 'Avida Dollar$'
120 |
121 | success = check_pattern(text,'[A-Z][a-z]+$',expected="")
122 | if (.not.success) return
123 |
124 | end function test_end_anchor2
125 |
126 | logical function test_read_version() result(success)
127 | character(*), parameter :: &
128 | text = 'Intel(R) MPI Library 2021.8 for Linux*Copyright Intel Corporation.ifort version 2021.8.0'
129 |
130 | success = check_pattern(text,'\d+\.\d+\.\d+',expected="2021.8.0")
131 |
132 | end function test_read_version
133 |
134 | end program tests
135 |
--------------------------------------------------------------------------------