FIX: issue #494 (unicode/load-utf8-buffer corrupts memory)

Red/System [
	Title:   "Unicode codecs"
	Author:  "Nenad Rakocevic, Rudolf W. Meijer"
	File: 	 %unicode.reds
	Tabs:	 4
	Rights:  "Copyright (C) 2011-2012 Nenad Rakocevic. All rights reserved."
	License: {
		Distributed under the Boost Software License, Version 1.0.
		See https://github.com/dockimbel/Red/blob/master/BSL-License.txt
	}
]

#enum encoding! [
	UTF-8:  0
	Latin1: 1
	UCS-2:  2
	UCS-4:  4
]

unicode: context [
	verbose: 0

	#define U_REPLACEMENT 	FFFDh
	;	choose one of the following options
	;	FFFDh			; U+FFFD = replacement character
	;	1Ah				; U+001A = control SUB (substitute)
	;	241Ah			; U+241A = symbol for substitute
	;	2426h			; U+2426 = symbol for substitute form two
	;	3Fh				; U+003F = question mark
	;	BFh				; U+00BF = inverted question mark
	;	DC00h + b1		; U+DCxx where xx = b1 (never a Unicode codepoint)

	Latin1-to-UCS2: func [
		s		 [series!]
		return:	 [series!]
		/local
			used [integer!]
			base [byte-ptr!]
			src  [byte-ptr!]
			dst  [byte-ptr!]
	][
		#if debug? = yes [if verbose > 0 [print-line "unicode/Latin1-to-UCS2"]]

		used: as-integer s/tail - s/offset
		if used * 2 >= s/size [							;-- ensure we have enough space
			s: expand-series s (used + 1) * 2			;-- reserve one more for edge cases
		]
		base: as byte-ptr! s/offset
		src:  as byte-ptr! s/tail						;-- start from end
		dst:  (as byte-ptr! s/offset) + (used * 2)
		s/tail: as cell! dst							;-- adjust to new tail

		while [src > base][								;-- in-place conversion
			src: src - 1
			dst: dst - 2
			dst/1: src/1
			dst/2: null-byte
		]
		s/flags: s/flags and flag-unit-mask or UCS-2	;-- s/unit: UCS-2
		s
	]

	Latin1-to-UCS4: func [
		s		 [series!]
		return:	 [series!]
		/local
			used [integer!]
			base [byte-ptr!]
			src  [byte-ptr!]
			dst  [int-ptr!]
	][
		#if debug? = yes [if verbose > 0 [print-line "unicode/Latin1-to-UCS4"]]

		used: as-integer s/tail - s/offset
		if used * 4 >= s/size [							;-- ensure we have enough space
			s: expand-series s (used + 1) * 4			;-- reserve one more for edge cases
		]
		base: as byte-ptr! s/offset
		src:  as byte-ptr! s/tail						;-- start from end
		dst:  as int-ptr! (as byte-ptr! s/offset) + (used * 4)
		s/tail: as cell! dst							;-- adjust to new tail

		while [src > base][								;-- in-place conversion
			src: src - 1
			dst: dst - 1
			dst/value: as-integer src/1
		]
		s/flags: s/flags and flag-unit-mask or UCS-4	;-- s/unit: UCS-4
		s
	]

	UCS2-to-UCS4: func [
		s		 [series!]
		return:	 [series!]
		/local
			used [integer!]
			base [byte-ptr!]
			src  [byte-ptr!]
			dst  [int-ptr!]
	][
		#if debug? = yes [if verbose > 0 [print-line "unicode/UCS2-to-UCS4"]]

		used: as-integer s/tail - s/offset
		if used * 2 >= s/size [							;-- ensure we have enough space
			s: expand-series s used * 2 + 1
		]
		base: as byte-ptr! s/offset
		src:  as byte-ptr! s/tail						;-- start from end
		dst:  as int-ptr! (as byte-ptr! s/offset) + (used * 2)
		s/tail: as cell! dst							;-- adjust to new tail

		while [src > base][								;-- in-place conversion
			src: src - 2
			dst: dst - 1
			dst/value: (as-integer src/2) << 8 + src/1
		]
		s/flags: s/flags and flag-unit-mask or UCS-4	;-- s/unit: UCS-4
		s
	]

	decode-utf8-char: func [
		src		[c-string!]
		cnt		[int-ptr!]								;-- pointer to size of next char in bytes
		return: [integer!]
		/local
			b1  [integer!]								;-- up to four bytes in a UTF-8 sequence
			b2  [integer!]								;-- for computing purposes they are of integer! type
			b3  [integer!]
			b4  [integer!]
			cp  [integer!]								; computed codepoint
	][
		b1: as-integer src/1

		either b1 < 80h	[								; single byte (ASCII)
			cp: b1										; and we are done
			cnt/value: 1
		][
			cp: U_REPLACEMENT
			; assume error by default - this simplifies code greatly
			; cp is now only set if a correct sequence has been decoded

			unless b1 < C0h [							; 80h - BFh may not start a sequence
				case  [
					b1 < E0h [							; start of two-byte sequence
						if cnt/value < 2 [return -1]
						b2: as-integer src/2
						if all [
							b2 >= 80h b2 < C0h
						][
							cp:	(b1 - C0h << 6) or
								(b2 - 80h)
;							if any [
;								cp > 7Fh				; optional test for overlong
;								cp = 0					; even so, must allow U+0000
;							][
								cnt/value: 2
;							]
						]
					]
					b1 < F0h [							; start of three-byte sequence
						if cnt/value < 3 [return -1]
						b2: as-integer src/2
						b3: as-integer src/3
						if all [
							b2 >= 80h b2 < C0h
							b3 >= 80h b3 < C0h
						][
							cp:	(b1 - E0h << 12) or
								(b2 - 80h <<  6) or
								(b3 - 80h)
							if all [
								any [cp < DC00h cp > DCFFh]
;								cp > 7FFh				; optional test for overlong
							][
								cnt/value: 3
							]
						]
					]
					b1 < F8h [							; start of four-byte sequence
						if cnt/value < 4 [return -1]
						b2: as-integer src/2
						b3: as-integer src/3
						b4: as-integer src/4
						if all [
							b2 >= 80h b2 < C0h
							b3 >= 80h b3 < C0h
							b4 >= 80h b4 < C0h
						][
							cp:	(b1 - F0h << 18) or
								(b2 - 80h << 12) or
								(b3 - 80h <<  6) or
								(b4 - 80h)
							if all [
								cp <= 0010FFFFh
;								cp > FFFFh				; optional test for overlong
							][
								cnt/value: 4
							]
						]
					]
;					true [
;						error case						;@@ throw an error! value
;					]
				]
			]
		]
		cp
	]

	load-utf8-buffer: func [
		src		   [c-string!]							;-- UTF-8 input buffer (zero-terminated)
		size	   [integer!]							;-- size of src in bytes (including terminal NUL)
		dst		   [series!]							;-- optional output string! series
		remain	   [int-ptr!]							;-- number of undecoded bytes at end of buffer
		return:	   [node!]
		/local
			node   [node!]
			s 	   [series!]
			buf1   [byte-ptr!]
			buf4   [int-ptr!]
			end    [byte-ptr!]
			unit   [integer!]
			cp	   [integer!]							;-- computed codepoint
			count  [integer!]
			used   [integer!]
	][
		#if debug? = yes [if verbose > 0 [print-line "unicode/load-utf8-buffer"]]

		assert positive? size

		either null? dst [								;-- test if output buffer is provided
			node: alloc-series size 1 0
			s: as series! node/value
			unit:  Latin1									;-- start with 1 byte/codepoint
		][
			node: dst/node
			s: dst
			unit: GET_UNIT(s)
			if s/size / unit < size [
				s: expand-series s size * unit
			]
		]

		buf1:  as byte-ptr! s/offset
		buf4:  null
		end:   buf1 + s/size
		count: size

		if size = 1 [return node]						;-- terminal NUL accounted
		;assert not zero? as-integer src/1				;@@ ensure input string not empty

		;-- the first part of loop is Rudolf's code with very minor modifications
		;-- (res/value replaced by cp, 'u renamed to 'src)
		;-- original source code: https://gist.github.com/1325840

		until [
			; cycling through res is done at the end; likewise for src
			; to account for this, as soon as a multiple byte sequence is consumed
			; the pointer in src is moved one less than the number of bytes consumed

			used: count									;-- pass number of remaining bytes in input stream
			cp: decode-utf8-char src :used
			if cp = -1 [								;-- premature exit if buffer incomplete
				s/tail: as cell! either unit = UCS-4 [buf4][buf1]	;-- position s/tail at end of loaded characters (no NUL terminator)
				remain/value: count						;-- return the number of unprocessed bytes
				return node
			]

			switch unit [
				Latin1 [
					case [
						cp <= FFh [
							buf1/value: as-byte cp
							buf1: buf1 + 1
							assert buf1 <= end			;-- should not happen if we're good
						]
						cp <= FFFFh [
							s/tail: as cell! buf1
							unit: UCS-2
							s:    Latin1-to-UCS2 s		;-- upgrade to UCS-2
							buf1: as byte-ptr! s/tail
							end:  (as byte-ptr! s/offset) + s/size

							buf1/1: as-byte cp and FFh
							buf1/2: as-byte cp >> 8
							buf1: buf1 + 2
						]
						true [
							s/tail: as cell! buf1
							unit: UCS-4
							s:    Latin1-to-UCS4 s		;-- upgrade to UCS-4
							buf4: as int-ptr! s/tail
							end:  (as byte-ptr! s/offset) + s/size

							buf4/value: cp
							buf4: buf4 + 1
						]
					]
				]
				UCS-2 [
					either cp > FFFFh [
						s/tail: as cell! buf1
						unit: UCS-4
						s:    UCS2-to-UCS4 s			;-- upgrade to UCS-4
						buf4: as int-ptr! s/tail
						end:  (as byte-ptr! s/offset) + s/size

						buf4/value: cp
						buf4: buf4 + 1
					][
						if buf1 >= end [
							s/tail: as cell! buf1
							s: expand-series s s/size + (size >> 2)	;-- increase size by 50%
							buf1: as byte-ptr! s/tail
							end: (as byte-ptr! s/offset) + s/size
						]
						buf1/1: as-byte cp and FFh
						buf1/2: as-byte cp >> 8
						buf1: buf1 + 2
					]
				]
				UCS-4 [
					if buf4 >= (as int-ptr! end) [
						s/tail: as cell! buf4
						s: expand-series s s/size + size ;-- increase size by 100%
						buf4: as int-ptr! s/tail
						end: (as byte-ptr! s/offset) + s/size
					]
					buf4/value: cp
					buf4: buf4 + 1
				]
			]

			count: count - used
			src: src + used
			zero? count
		] 												;-- end until

		s/tail: as cell! switch unit [					;-- position s/tail just before the NUL character
			Latin1 [buf1 - 1]
			UCS-2  [buf1 - 2]
			UCS-4  [buf4 - 1]
		]
		assert s/size - GET_UNIT(s) >= as-integer (s/tail - s/offset) ;-- tail points before NUL

		node
	]

	load-utf8-stream: func [
		src		   [c-string!]							;-- UTF-8 input buffer (not NUL-terminated)
		size	   [integer!]							;-- size of src buffer in bytes (excluding NUL if any)
		output	   [red-string!]						;-- output buffer to append new chars to
		remain	   [int-ptr!]							;-- number of undecoded bytes at end of input buffer
		return:	   [node!]
	][
		load-utf8-buffer src size GET_BUFFER(output) remain
	]

	load-utf8: func [
		src		   [c-string!]							;-- UTF-8 input buffer (zero-terminated)
		size	   [integer!]							;-- size of src in bytes (including terminal NUL)
		return:	   [node!]
	][
		load-utf8-buffer src size null null
	]
]

Red/System [
	Title:   "Unicode codecs"
	Author:  "Nenad Rakocevic, Rudolf W. Meijer"
	File: 	 %unicode.reds
	Tabs:	 4
	Rights:  "Copyright (C) 2011-2012 Nenad Rakocevic. All rights reserved."
	License: {
		Distributed under the Boost Software License, Version 1.0.
		See https://github.com/dockimbel/Red/blob/master/BSL-License.txt
	}
]

#enum encoding! [
	UTF-8:  0
	Latin1: 1
	UCS-2:  2
	UCS-4:  4
]

unicode: context [
	verbose: 0

	#define U_REPLACEMENT 	FFFDh
	;	choose one of the following options
	;	FFFDh			; U+FFFD = replacement character
	;	1Ah				; U+001A = control SUB (substitute)
	;	241Ah			; U+241A = symbol for substitute
	;	2426h			; U+2426 = symbol for substitute form two
	;	3Fh				; U+003F = question mark
	;	BFh				; U+00BF = inverted question mark
	;	DC00h + b1		; U+DCxx where xx = b1 (never a Unicode codepoint)

	Latin1-to-UCS2: func [
		s		 [series!]
		return:	 [series!]
		/local
			used [integer!]
			base [byte-ptr!]
			src  [byte-ptr!]
			dst  [byte-ptr!]
	][
		#if debug? = yes [if verbose > 0 [print-line "unicode/Latin1-to-UCS2"]]

		used: as-integer s/tail - s/offset
		if used * 2 >= s/size [							;-- ensure we have enough space
			s: expand-series s (used + 1) * 2			;-- reserve one more for edge cases
		]
		base: as byte-ptr! s/offset
		src:  as byte-ptr! s/tail						;-- start from end
		dst:  (as byte-ptr! s/offset) + (used * 2)
		s/tail: as cell! dst							;-- adjust to new tail

		while [src > base][								;-- in-place conversion
			src: src - 1
			dst: dst - 2
			dst/1: src/1
			dst/2: null-byte
		]
		s/flags: s/flags and flag-unit-mask or UCS-2	;-- s/unit: UCS-2
		s
	]

	Latin1-to-UCS4: func [
		s		 [series!]
		return:	 [series!]
		/local
			used [integer!]
			base [byte-ptr!]
			src  [byte-ptr!]
			dst  [int-ptr!]
	][
		#if debug? = yes [if verbose > 0 [print-line "unicode/Latin1-to-UCS4"]]

		used: as-integer s/tail - s/offset
		if used * 4 >= s/size [							;-- ensure we have enough space
			s: expand-series s (used + 1) * 4			;-- reserve one more for edge cases
		]
		base: as byte-ptr! s/offset
		src:  as byte-ptr! s/tail						;-- start from end
		dst:  as int-ptr! (as byte-ptr! s/offset) + (used * 4)
		s/tail: as cell! dst							;-- adjust to new tail

		while [src > base][								;-- in-place conversion
			src: src - 1
			dst: dst - 1
			dst/value: as-integer src/1
		]
		s/flags: s/flags and flag-unit-mask or UCS-4	;-- s/unit: UCS-4
		s
	]

	UCS2-to-UCS4: func [
		s		 [series!]
		return:	 [series!]
		/local
			used [integer!]
			base [byte-ptr!]
			src  [byte-ptr!]
			dst  [int-ptr!]
	][
		#if debug? = yes [if verbose > 0 [print-line "unicode/UCS2-to-UCS4"]]

		used: as-integer s/tail - s/offset
		if used * 2 >= s/size [							;-- ensure we have enough space
			s: expand-series s used * 2 + 1
		]
		base: as byte-ptr! s/offset
		src:  as byte-ptr! s/tail						;-- start from end
		dst:  as int-ptr! (as byte-ptr! s/offset) + (used * 2)
		s/tail: as cell! dst							;-- adjust to new tail

		while [src > base][								;-- in-place conversion
			src: src - 2
			dst: dst - 1
			dst/value: (as-integer src/2) << 8 + src/1
		]
		s/flags: s/flags and flag-unit-mask or UCS-4	;-- s/unit: UCS-4
		s
	]

	decode-utf8-char: func [
		src		[c-string!]
		cnt		[int-ptr!]								;-- pointer to size of next char in bytes
		return: [integer!]
		/local
			b1  [integer!]								;-- up to four bytes in a UTF-8 sequence
			b2  [integer!]								;-- for computing purposes they are of integer! type
			b3  [integer!]
			b4  [integer!]
			cp  [integer!]								; computed codepoint
	][
		b1: as-integer src/1

		either b1 < 80h	[								; single byte (ASCII)
			cp: b1										; and we are done
			cnt/value: 1
		][
			cp: U_REPLACEMENT
			; assume error by default - this simplifies code greatly
			; cp is now only set if a correct sequence has been decoded

			unless b1 < C0h [							; 80h - BFh may not start a sequence
				case  [
					b1 < E0h [							; start of two-byte sequence
						if cnt/value < 2 [return -1]
						b2: as-integer src/2
						if all [
							b2 >= 80h b2 < C0h
						][
							cp:	(b1 - C0h << 6) or
								(b2 - 80h)
;							if any [
;								cp > 7Fh				; optional test for overlong
;								cp = 0					; even so, must allow U+0000
;							][
								cnt/value: 2
;							]
						]
					]
					b1 < F0h [							; start of three-byte sequence
						if cnt/value < 3 [return -1]
						b2: as-integer src/2
						b3: as-integer src/3
						if all [
							b2 >= 80h b2 < C0h
							b3 >= 80h b3 < C0h
						][
							cp:	(b1 - E0h << 12) or
								(b2 - 80h <<  6) or
								(b3 - 80h)
							if all [
								any [cp < DC00h cp > DCFFh]
;								cp > 7FFh				; optional test for overlong
							][
								cnt/value: 3
							]
						]
					]
					b1 < F8h [							; start of four-byte sequence
						if cnt/value < 4 [return -1]
						b2: as-integer src/2
						b3: as-integer src/3
						b4: as-integer src/4
						if all [
							b2 >= 80h b2 < C0h
							b3 >= 80h b3 < C0h
							b4 >= 80h b4 < C0h
						][
							cp:	(b1 - F0h << 18) or
								(b2 - 80h << 12) or
								(b3 - 80h <<  6) or
								(b4 - 80h)
							if all [
								cp <= 0010FFFFh
;								cp > FFFFh				; optional test for overlong
							][
								cnt/value: 4
							]
						]
					]
;					true [
;						error case						;@@ throw an error! value
;					]
				]
			]
		]
		cp
	]

	load-utf8-buffer: func [
		src		   [c-string!]							;-- UTF-8 input buffer (zero-terminated)
		size	   [integer!]							;-- size of src in bytes (including terminal NUL)
		dst		   [series!]							;-- optional output string! series
		remain	   [int-ptr!]							;-- number of undecoded bytes at end of buffer
		return:	   [node!]
		/local
			node   [node!]
			s 	   [series!]
			buf1   [byte-ptr!]
			buf4   [int-ptr!]
			end    [byte-ptr!]
			unit   [integer!]
			cp	   [integer!]							;-- computed codepoint
			count  [integer!]
			used   [integer!]
	][
		#if debug? = yes [if verbose > 0 [print-line "unicode/load-utf8-buffer"]]

		assert positive? size

		either null? dst [								;-- test if output buffer is provided
			node: alloc-series size 1 0
			s: as series! node/value
		][
			node: dst/node
			s: dst
		]

		buf1:  as byte-ptr! s/offset
		buf4:  null
		end:   buf1 + s/size
		unit:  Latin1									;-- start with 1 byte/codepoint
		count: size

		if size = 1 [return node]						;-- terminal NUL accounted
		;assert not zero? as-integer src/1				;@@ ensure input string not empty

		;-- the first part of loop is Rudolf's code with very minor modifications
		;-- (res/value replaced by cp, 'u renamed to 'src)
		;-- original source code: https://gist.github.com/1325840

		until [
			; cycling through res is done at the end; likewise for src
			; to account for this, as soon as a multiple byte sequence is consumed
			; the pointer in src is moved one less than the number of bytes consumed

			used: count									;-- pass number of remaining bytes in input stream
			cp: decode-utf8-char src :used
			if cp = -1 [								;-- premature exit if buffer incomplete
				s/tail: as cell! either unit = UCS-4 [buf4][buf1]	;-- position s/tail at end of loaded characters (no NUL terminator)
				remain/value: count						;-- return the number of unprocessed bytes
				return node
			]

			switch unit [
				Latin1 [
					case [
						cp <= FFh [
							buf1/value: as-byte cp
							buf1: buf1 + 1
							assert buf1 <= end			;-- should not happen if we're good
						]
						cp <= FFFFh [
							s/tail: as cell! buf1
							unit: UCS-2
							s:    Latin1-to-UCS2 s		;-- upgrade to UCS-2
							buf1: as byte-ptr! s/tail
							end:  (as byte-ptr! s/offset) + s/size

							buf1/1: as-byte cp and FFh
							buf1/2: as-byte cp >> 8
							buf1: buf1 + 2
						]
						true [
							s/tail: as cell! buf1
							unit: UCS-4
							s:    Latin1-to-UCS4 s		;-- upgrade to UCS-4
							buf4: as int-ptr! s/tail
							end:  (as byte-ptr! s/offset) + s/size

							buf4/value: cp
							buf4: buf4 + 1
						]
					]
				]
				UCS-2 [
					either cp > FFFFh [
						s/tail: as cell! buf1
						unit: UCS-4
						s:    UCS2-to-UCS4 s			;-- upgrade to UCS-4
						buf4: as int-ptr! s/tail
						end:  (as byte-ptr! s/offset) + s/size

						buf4/value: cp
						buf4: buf4 + 1
					][
						if buf1 >= end [
							s/tail: as cell! buf1
							s: expand-series s s/size + (size >> 2)	;-- increase size by 50%
							buf1: as byte-ptr! s/tail
							end: (as byte-ptr! s/offset) + s/size
						]
						buf1/1: as-byte cp and FFh
						buf1/2: as-byte cp >> 8
						buf1: buf1 + 2
					]
				]
				UCS-4 [
					if buf4 >= (as int-ptr! end) [
						s/tail: as cell! buf4
						s: expand-series s s/size + size ;-- increase size by 100%
						buf4: as int-ptr! s/tail
						end: (as byte-ptr! s/offset) + s/size
					]
					buf4/value: cp
					buf4: buf4 + 1
				]
			]

			count: count - used
			src: src + used
			zero? count
		] 												;-- end until

		s/tail: as cell! switch unit [					;-- position s/tail just before the NUL character
			Latin1 [buf1 - 1]
			UCS-2  [buf1 - 2]
			UCS-4  [buf4 - 1]
		]
		assert s/size - GET_UNIT(s) >= as-integer (s/tail - s/offset) ;-- tail points before NUL

		node
	]

	load-utf8-stream: func [
		src		   [c-string!]							;-- UTF-8 input buffer (not NUL-terminated)
		size	   [integer!]							;-- size of src buffer in bytes (excluding NUL if any)
		output	   [red-string!]						;-- output buffer to append new chars to
		remain	   [int-ptr!]							;-- number of undecoded bytes at end of input buffer
		return:	   [node!]
	][
		load-utf8-buffer src size GET_BUFFER(output) remain
	]

	load-utf8: func [
		src		   [c-string!]							;-- UTF-8 input buffer (zero-terminated)
		size	   [integer!]							;-- size of src in bytes (including terminal NUL)
		return:	   [node!]
	][
		load-utf8-buffer src size null null
	]
]


TESTS: adjust case-folding-test.reds

TESTS: adjust case-folding-test.reds

Red [
	Title:   "Red case folding test script"
	Author:  "Peter W A Wood"
	File: 	 %case-folding-test.red
	Tabs:	 4
	Rights:  "Copyright (C) 2011-2015 Nenad Rakocevic & Peter W A Wood. All rights reserved."
	License: "BSD-3 - https://github.com/dockimbel/Red/blob/origin/BSD-3-License.txt"
]

#include  %../../../quick-test/quick-test.red

~~~start-file~~~ "case-folding"

do-test-number: 0
dont-test-number: 0

do-fold: func [lower upper] [
	do-test-number: do-test-number + 1
	--test-- append copy "case-folding-" do-test-number
		--assert lower = upper
		--assert equal? lower upper
		--assert strict-equal? uppercase copy lower upper
		--assert upper == uppercase copy lower
		--assert strict-equal? lower lowercase copy upper
		--assert lower == lowercase copy upper
]

dont-fold: func [lower upper] [
	dont-test-number: dont-test-number + 1
	--test-- append copy "not-case-folding-" dont-test-number
		--assert lower <> upper
		--assert not equal? lower upper
		--assert not equal? uppercase copy lower upper
		--assert upper <> uppercase copy lower
		--assert not equal? lower lowercase copy upper
		--assert lower <> lowercase copy upper
]

===start-group=== "case-folding"

	do-fold "abcde" "ABCDE"
	do-fold "ba^(FB04)e" "BA^(FB04)E"
	do-fold "cant^(F9)" "CANT^(D9)"
	do-fold "cantu^(0300)" "CANTU^(0300)"
	do-fold "stra^(DF)e" "STRA^(1E9E)E"
	do-fold "i" "I"
	do-fold "^(0149)" "^(0149)"
	do-fold "^(03B0)" "^(03B0)"
	do-fold "^(0587)" "^(0587)"
	do-fold "^(1E96)" "^(1E96)"
	do-fold "^(1F80)" "^(1F88)"
	do-fold "^(1FB3)" "^(1FBC)"
	do-fold "^(03B9)" "^(1FBE)"
	do-fold "^(1FF3)" "^(1FFC)"
	do-fold "^(03C9)" "^(2126)"
	do-fold "^(2173)" "^(2163)"
	do-fold "^(EE)" "^(CE)"
	do-fold "^(0101)" "^(0100)"

===end-group===

===start-group=== "not-case-folding"

	dont-fold "ba^(FB04)e" "BAFFLE"
	dont-fold "stra^(DF)e" "STRASSE"
	dont-fold "weiss" "wie^(DF)"
	dont-fold "i" "^(0130)"
	dont-fold "^(0131)" "I"
	dont-fold "^(0149)" "^(02BC)^(6E)"
	dont-fold "^(03C5)^(0308)^(0301)" "^(03B0)"
	dont-fold "^(0565)^(0582)" "^(0587)"
	dont-fold "^(1E96)" "^(68)^(0331)"
	dont-fold "^(1F80)" "^(1F00)^(03B9)"
	dont-fold "^(03B1)^(03B9)" "^(1FBC)"
	dont-fold "^(03C9)^(03B9)" "^(1FFC)"

===end-group===

===start-group=== "manual case folding"

	--test-- "manual-case-folding-1"
		--assert "abcde" = "aBCDE"
		--assert equal? "abcde" "aBCDE"
		--assert strict-equal? lowercase "aBCDE" "abcde"
		--assert "abcde" == lowercase "aBCDE"

	--test-- "manual-case-folding-2"
		--assert "Abcde" = "ABCDE"
		--assert equal? "Abcde" "ABCDE"
		--assert strict-equal? uppercase "Abcde" "ABCDE"
		--assert "ABCDE" == uppercase "Abcde"

	--test-- "manual-case-folding-3"
		--assert "s" = "^(017F)"
		--assert "S" = "^(017F)"
		--assert "s" = "S"
		--assert "S" == uppercase "^(017F)"
		--assert "^(017F)" == lowercase "^(017F)"
		--assert "s" == lowercase "S"
		--assert "S" == uppercase "s"

===end-group===

~~~end-file~~~

Red [
	Title:   "Red case folding test script"
	Author:  "Peter W A Wood"
	File: 	 %case-folding-test.red
	Tabs:	 4
	Rights:  "Copyright (C) 2011-2015 Nenad Rakocevic & Peter W A Wood. All rights reserved."
	License: "BSD-3 - https://github.com/dockimbel/Red/blob/origin/BSD-3-License.txt"
]

#include  %../../../quick-test/quick-test.red

~~~start-file~~~ "case-folding"

do-test-number: 0
dont-test-number: 0

do-fold: func [lower upper] [
	do-test-number: do-test-number + 1
	--test-- append copy "case-folding-" do-test-number
		--assert lower = upper
		--assert equal? lower upper
		--assert strict-equal? uppercase copy lower upper
		--assert upper == uppercase copy lower
		--assert strict-equal? lower lowercase copy upper
		--assert lower == lowercase copy upper
]

dont-fold: func [lower upper] [
	dont-test-number: dont-test-number + 1
	--test-- append copy "not-case-folding-" dont-test-number
		--assert lower <> upper
		--assert not equal? lower upper
		--assert not equal? uppercase copy lower upper
		--assert upper <> uppercase copy lower
		--assert not equal? lower lowercase copy upper
		--assert lower <> lowercase copy upper
]

===start-group=== "case-folding"

	do-fold "abcde" "ABCDE"
	do-fold "ba^(FB04)e" "BA^(FB04)E"
	do-fold "cant^(F9)" "CANT^(D9)"
	do-fold "cantu^(0300)" "CANTU^(0300)"
	do-fold "stra^(DF)e" "STRA^(1E9E)E"
	do-fold "i" "I"
	do-fold "^(0149)" "^(0149)"
	do-fold "^(03B0)" "^(03B0)"
	do-fold "^(0587)" "^(0587)"
	do-fold "^(1E96)" "^(1E96)"
	do-fold "^(1F80)" "^(1F88)"
	do-fold "^(1FB3)" "^(1FBC)"
	do-fold "^(03B9)" "^(1FBE)"
	do-fold "^(1FF3)" "^(1FFC)"
	do-fold "^(03C9)" "^(2126)"
	do-fold "^(2173)" "^(2163)"
	do-fold "^(EE)" "^(CE)"
	do-fold "^(0101)" "^(0100)"
	do-fold "^(017F)" "^(0053)"

===end-group===

===start-group=== "not-case-folding"

	dont-fold "ba^(FB04)e" "BAFFLE"
	dont-fold "stra^(DF)e" "STRASSE"
	dont-fold "weiss" "wie^(DF)"
	dont-fold "i" "^(0130)"
	dont-fold "^(0131)" "I"
	dont-fold "^(0149)" "^(02BC)^(6E)"
	dont-fold "^(03C5)^(0308)^(0301)" "^(03B0)"
	dont-fold "^(0565)^(0582)" "^(0587)"
	dont-fold "^(1E96)" "^(68)^(0331)"
	dont-fold "^(1F80)" "^(1F00)^(03B9)"
	dont-fold "^(03B1)^(03B9)" "^(1FBC)"
	dont-fold "^(03C9)^(03B9)" "^(1FFC)"

===end-group===

===start-group=== "manual case folding"

	--test-- "manual-case-folding-1"
		--assert "abcde" = "aBCDE"
		--assert equal? "abcde" "aBCDE"
		--assert strict-equal? lowercase "aBCDE" "abcde"
		--assert "abcde" == lowercase "aBCDE"

	--test-- "manual-case-folding-1"
		--assert "Abcde" = "ABCDE"
		--assert equal? "Abcde" "ABCDE"
		--assert strict-equal? uppercase "Abcde" "ABCDE"
		--assert "ABCDE" == uppercase "Abcde"

===end-group===

~~~end-file~~~


TESTS: update find-test.red

TESTS: update find-test.red

Red [
	Title:   "Red find test script"
	Author:  "Nenad Rakocevic & Peter W A Wood"
	File: 	 %find-test.red
	Tabs:	 4
	Rights:  "Copyright (C) 2011-2013 Nenad Rakocevic & Peter W A Wood. All rights reserved."
	License: "BSD-3 - https://github.com/dockimbel/Red/blob/origin/BSD-3-License.txt"
]

#include  %../../../quick-test/quick-test.red

~~~start-file~~~ "find"

===start-group=== "find"
	--test-- "find-1"
		--assert [1 2 3 4] = find [1 2 3 4] 1
	--test-- "find-2"
		--assert [2 3 4] = find [1 2 3 4] 2
	--test-- "find-3"
		--assert [4] = find [1 2 3 4] 4
	--test-- "find-4"
		--assert none = find [1 2 3 4] 0
	--test-- "find-5"
		--assert none = find [1 2 3 4] 5
	--test-- "find-6"
		--assert none = find [1 2 3 4] "1"
	--test-- "find-7"
		--assert "12345" = find "12345" #"1"
	--test-- "find-8"
		--assert none = find "12345" 1
	--test-- "find-9"
		--assert "12345" = find "12345" "1"
	--test-- "find-10"
		--assert "12345" = find "12345" "12"
	--test-- "find-11"
		--assert "2345" = find "12345" #"2"
	--test-- "find-12"
		--assert "5" = find "12345" #"5"
	--test-- "find-13"
		--assert none = find "12345" #"0"
	--test-- "find-14"
		--assert none = find "12345" #"6"
	--test-- "find-15"
		--assert [2 3] = find [1 2 3] [2 3]
	--test-- "find-16"
		--assert none = find [1 2 3] [3 2]
	--test-- "find-17"
		--assert [2 3] = find [1 2 2 3] [2 3]
	--test-- "find-18"
		--assert none = find [1 2] [2 3]
	--test-- "find-19"
		--assert "é" = find "abcdeé" "é"
	--test-- "find-20"
		--assert "eé" = find "abcdeé" "eé"
	--test-- "find-21"
		--assert none = find "abcdeé" "ée"
	--test-- "find-22"
		--assert "✐" = find "abcde✐" "✐"            ;; code point 10000 (decimal)
	--test-- "find-23"
		--assert none = find "abcde✐" "ed✐"
	--test-- "find-24"
		--assert "^(010000)" = find "abcde^(010000)" "^(010000)"
	--test-- "find-25"
		--assert none = find tail "123345" 1
	--test-- "find-26 -issue #765"
		--assert none = find tail [a b c d e] 'a

===end-group===

===start-group=== "find/part"
	--test-- "find/part-1"
		--assert none = find/part "1234" "1" 0
	--test-- "find/part-2"
		--assert "1234" = find/part "1234" "1" 1
	--test-- "find/part-3"
		--assert "234" = find/part "1234" "2" 2
	--test-- "find/part-4"
		--assert "234" = find/part "1234" "2" 4
	--test-- "find/part-5"
		--assert "234" = find/part "1234" "2" 5
	--test-- "find/part-6"
		--assert none = find/part "1234" "3" 2
	--test-- "find/part-7"
		--assert none = find/part [1 2 3 4] 1 0
	--test-- "find/part-8"
		--assert [1 2 3 4] = find/part [1 2 3 4] 1 1
	--test-- "find/part-9"
		--assert [2 3 4] = find/part [1 2 3 4] 2 2
	--test-- "find/part-10"
		--assert [2 3 4] = find/part [1 2 3 4] [2 3] 4
	--test-- "find/part-11"
		--assert none = find/part [1 2 3 4] [2 3] 2
	--test-- "find/part-12"
		--assert none = find/part [1 2 3 4] 3 2
	--test-- "find/part-13"
		--assert none = find/part "Χαῖρε, κόσμε!" "Χαῖ" 2
===end-group===

===start-group=== "find/only"
	--test-- "find/only-1"
		--assert [[2 3] 4] = find/only [1 [2 3] 4] [2 3]
	--test-- "find/only-2"
		--assert none = find/only [1 2 3 4] [2 3]
===end-group===

===start-group=== "find/case"
	--test-- "find/case-1"
		--assert "abcde" = find/case "Aabcde" "a"
	--test-- "find/case-2"
		--assert "Abcde" = find/case "aAbcde" "A"
	--test-- "find/case-3"
		--assert none = find/case "è" "E"
===end-group===

===start-group=== "find/any"      ; not yet implemented
	--test-- "find/any-1"
		;--assert "12345" = find/any "12345" "*"
	--test-- "find/any-2"
		;--assert "12345" = find/any "12345" "?"
	--test-- "find/any-3"
		;--assert "2345" = find/any "12345" "2?4"
	--test-- "find/any-4"
		;--assert "2345" = find/any "12345" "2*"
	--test-- "find/any-5"
		;--assert "e✐" = find/any "abcde✐" "e?"        ;; code point 10000 (decimal)
	--test-- "find/any-6"
		;--assert "e✐f" = find/any "abcde✐f" "?f"
	--test-- "find/any-7"
		;--assert "e✐" = find/any "abcde✐" "e*"
	--test-- "find/any-8"
		;--assert "abcde✐f" = find/any "abcde✐f" "*f"
	--test-- "find/any-9"
		;--assert "e^(010000)" = find/any "abcde^(010000)" "e?"
	--test-- "find/any-10"
		;--assert "e^(010000)f" = find/any "abcde^(010000)f" "?f"
	--test-- "find/any-11"
		;--assert "e^(010000)" = find/any "abcde^(010000)" "e*"
	--test-- "find/any-12"
		;--assert "abcde^(010000)f" = find/any "abcde^(010000)f" "*f"
===end-group===

===start-group=== "find/with"      ; not yet implemented
	--test-- "find/with-1"
		;--assert "12345" = find/with "12345" "^(FFFF)" "^(FFFE)^(FFFF)"
	--test-- "find/with-2"
		;--assert "12345" = find/with "12345" "^(FFFE)" "^(FFFE)^(FFFF)"
	--test-- "find/with-3"
		;--assert "2345" = find/with "12345" "2^(FFFE)3" "^(FFFE)^(FFFF)"
	--test-- "find/with-4"
		;--assert "2345" = find/with "12345" "2^(FFFF)" "^(FFFE)^(FFFF)"
	--test-- "find/with-5"
		;--assert "e✐" = find/with "abcde✐" "e^(FFFE)" "^(FFFE)^(FFFF)"
	--test-- "find/with-6"
		;--assert "e✐f" = find/with "abcde✐f" "^(FFFE)f" "^(FFFE)^(FFFF)"
	--test-- "find/with-7"
		;--assert "e✐" = find/with "abcde✐" "e^(FFFF)" "^(FFFE)^(FFFF)"
	--test-- "find/with-8"
		;--assert "abcde✐f" = find/with "abcde✐f" "^(FFFF)f" "^(FFFE)^(FFFF)"
	--test-- "find/with-9"
		;--assert "e^(010000)" = find/with "abcde^(010000)" "e^(FFFE)" "^(FFFE)^(FFFF)"
	--test-- "find/with-10"
		;--assert "e^(010000)f" = find/with "abcde^(010000)f" "^(FFFE)f" "^(FFFE)^(FFFF)"
	--test-- "find/with-11"
		;--assert "e^(010000)" = find/with "abcde^(010000)" "e^(FFFF)" "^(FFFE)^(FFFF)"
	--test-- "find/with-12"
		;--assert "abcde^(010000)f" = find/with "abcde^(010000)f" "^(FFFF)f" "^(FFFE)^(FFFF)"
===end-group===

===start-group=== "find/skip"
	--test-- "find/skip-1"
		--assert [6 7 8 9 0] = find/skip [1 2 3 4 5 1 6 6 6 6 1 2 3 4 5 6 7 8 9 0] 6 5
	--test-- "find/skip-2"
		--assert none = find/skip [1 2 3 4 5 1 6 6 6 6 1 2 3 4 5 1 2 3 4 5] 6 5
	--test-- "find/skip-3"
		--assert [6 7 8 9 0] = find/skip [1 2 3 4 5 6 6 6 6 6 1 2 3 4 5 6 7 8 9 0] [6 7] 5
	--test-- "find/skip-4"
		--assert none = find/skip [1 2 3 4 5 1 6 6 6 6 1 2 3 4 5 1 2 3 4 5] [6 7] 5
	--test-- "find/skip-5"
		--assert "67890" = find/skip "12345166661234567890" "6" 5
	--test-- "find/skip-6"
		--assert none = find/skip "12345166661234557890" "6" 5
===end-group===

===start-group=== "find/match"
	--test-- "find/match-1"
		--assert [and now] = find/match [here and now] 'here
	--test-- "find/match-2"
		--assert none = find/match [here and now] 'her
	--test-- "find/match-3"
		--assert none = find/match [her and now] 'here
	--test-- "find/match-4"
		--assert " and now" = find/match "here and now" "here"
	--test-- "find/match-5"
		--assert "andnow" = find/match "hereandnow" "here"
	--test-- "find/match-6"
		--assert none = find/match "her and now" "here"
	--test-- "find/match-7"
		--assert " and now" = find/match "here✐ and now" "here✐"
	--test-- "find/match-8"
		--assert "✐andnow" = find/match "here✐andnow" "here"
	--test-- "find/match-9"
		--assert none = find/match "her and now" "he✐r"
	--test-- "find/match-10"
		--assert none = find/match "here and now" "✐here"
	--test-- "find/match-11"
		--assert "^(010000)andnow" = find/match "here^(010000)andnow" "here"
	--test-- "find/match-12"
		--assert none = find/match "her and now" "here^(010000)"
	--test-- "find/match-13"
		--assert " and now" = find/match "here^(010000) and now" "here^(010000)"
	--test-- "find/match-14"
		--assert "andnow" = find/match "^(010000)hereandnow" "^(010000)here"
	--test-- "find/match-15"
		--assert none = find/match "her^(010000) and now" "here^(010000)"
	--test-- "find/match-16"
		--assert [and now] = find/match [he✐re and now] 'he✐re
===end-group===

===start-group=== "find/tail"
	--test-- "find/tail-1"
		--assert [and now] = find/tail [here and now] 'here
	--test-- "find/tail-2"
		--assert none = find/tail [here and now] 'her
	--test-- "find/tail-3"
		--assert none = find/tail [her and now] 'here
	--test-- "find/tail-4"
		--assert " and now" = find/tail "here and now" "here"
	--test-- "find/tail-5"
		--assert "andnow" = find/tail "hereandnow" "here"
	--test-- "find/tail-6"
		--assert none = find/tail "her and now" "here"
	--test-- "find/tail-7"
		--assert " and now" = find/tail "here✐ and now" "here✐"
	--test-- "find/tail-8"
		--assert "✐andnow" = find/tail "here✐andnow" "here"
	--test-- "find/tail-9"
		--assert none = find/tail "her and now" "he✐r"
	--test-- "find/tail-10"
		--assert none = find/tail "here and now" "✐here"
	--test-- "find/tail-11"
		--assert "^(010000)andnow" = find/tail "here^(010000)andnow" "here"
	--test-- "find/tail-12"
		--assert none = find/tail "her and now" "here^(010000)"
	--test-- "find/tail-13"
		--assert " and now" = find/tail "here^(010000) and now" "here^(010000)"
	--test-- "find/tail-14"
		--assert "andnow" = find/tail "^(010000)hereandnow" "^(010000)here"
	--test-- "find/tail-15"
		--assert none = find/tail "her^(010000) and now" "here^(010000)"
	--test-- "find/tail-16"
		--assert [and now] = find/tail [he✐re and now] 'he✐re
	--test-- "find/tail-17 issue #457"
		--assert "de" = find/tail "abcde" #"c"
	--test-- "find-tail-18"
		--assert "de" = find/tail "abc^^de" #"^^"

===end-group===

===start-group=== "find/last"
	--test-- "find/last-1"
		--assert [1] = find/last [1 1 1 1] 1
		--assert 4 = index? find/last [1 1 1 1] 1
	--test-- "find/last-2"
		--assert [2 1] = find/last [1 2 3 4 3 2 1] 2
	--test-- "find/last-3"
		--assert [3 3 2 1] = find/last [1 2 3 3 2 1] [3 3]
	--test-- "find/last-4"
		--assert "3321" = find/last "123321" "33"
	--test-- "find/last-5"
		--assert "321" = find/last "123321" "3"
	--test-- "find/last-6"
		--assert "1" = find/last "1111" "1"
	--test-- "find/last-7"
		--assert none = find/last "123" "34"
	--test-- "find/last-8"
		--assert "✐5678" = find/last "1234✐5678" "✐"
	--test-- "find/last-9"
		--assert "^(010000)5678" = find/last "1234^(010000)5678" "^(010000)"
===end-group===

===start-group=== "find/reverse"
	--test-- "find/reverse-1"
		--assert none = find/reverse [1 1 1 1] 1
	--test-- "find/reverse-2"
		--assert [2 1] = find/reverse tail [1 2 3 4 3 2 1] 2
	--test-- "find/reverse-3"
		--assert [3 3 2 1] = find/reverse tail [1 2 3 3 2 1] [3 3]
	--test-- "find/reverse-4"
		--assert "3321" = find/reverse back back back tail "123321" "33"
	--test-- "find/reverse-5"
		--assert "3321" = find/reverse back back back tail"123321" "3"
	--test-- "find/reverse-6"
		--assert "1" = find/reverse tail "1111" "1"
	--test-- "find/reverse-7"
		--assert none = find/reverse tail "123" "34"
	--test-- "find/reverse-8"
		--assert "✐5678" = find/reverse tail "1234✐5678" "✐"
	--test-- "find/reverse-9"
		--assert "^(010000)5678" = find/reverse tail "1234^(010000)5678" "^(010000)"
	--test-- "find/reverse-10 issue #505"
		--assert "ab" = find/reverse tail "ab" #"a"
		--assert "ab" = find/reverse tail "ab" "ab"
		--assert [a b] = find/reverse tail [a b] 'a
		--assert [a b] = find/reverse tail [a b] [a b]
===end-group===

===start-group=== "find/last/tail"
	--test-- "find/last/tail-1 issue #459"
		--assert [7 8] = find/last/tail [1 2 3 4 5 6 3 7 8] 3
	--test-- "find/last/tail-2 issue #459"
		--assert "78" = find/last/tail "123456378" #"3"
	--test-- "find/last/tail-3"
		--assert "78" = find/last/tail "123456378" "3"
===end-group===

~~~end-file~~~

Red [
	Title:   "Red find test script"
	Author:  "Nenad Rakocevic & Peter W A Wood"
	File: 	 %find-test.red
	Tabs:	 4
	Rights:  "Copyright (C) 2011-2013 Nenad Rakocevic & Peter W A Wood. All rights reserved."
	License: "BSD-3 - https://github.com/dockimbel/Red/blob/origin/BSD-3-License.txt"
]

#include  %../../../quick-test/quick-test.red

~~~start-file~~~ "find"

===start-group=== "find"
	--test-- "find-1"
		--assert [1 2 3 4] = find [1 2 3 4] 1
	--test-- "find-2"
		--assert [2 3 4] = find [1 2 3 4] 2
	--test-- "find-3"
		--assert [4] = find [1 2 3 4] 4
	--test-- "find-4"
		--assert none = find [1 2 3 4] 0
	--test-- "find-5"
		--assert none = find [1 2 3 4] 5
	--test-- "find-6"
		--assert none = find [1 2 3 4] "1"
	--test-- "find-7"
		--assert "12345" = find "12345" #"1"
	;--test-- "find-8" 							;; unsupported case, it is unsure if it will be implemented or not
	;--assert "12345" = find "12345" 1			;; keeping it here commented for further reference.
	--test-- "find-9"
		--assert "12345" = find "12345" "1"
	--test-- "find-10"
		--assert "12345" = find "12345" "12"
	--test-- "find-11"
		--assert "2345" = find "12345" #"2"
	--test-- "find-12"
		--assert "5" = find "12345" #"5"
	--test-- "find-13"
		--assert none = find "12345" #"0"
	--test-- "find-14"
		--assert none = find "12345" #"6"
	--test-- "find-15"
		--assert [2 3] = find [1 2 3] [2 3]
	--test-- "find-16"
		--assert none = find [1 2 3] [3 2]
	--test-- "find-17"
		--assert [2 3] = find [1 2 2 3] [2 3]
	--test-- "find-18"
		--assert none = find [1 2] [2 3]
	--test-- "find-19"
		--assert "é" = find "abcdeé" "é"
	--test-- "find-20"
		--assert "eé" = find "abcdeé" "eé"
	--test-- "find-21"
		--assert none = find "abcdeé" "ée"
	--test-- "find-22"
		--assert "✐" = find "abcde✐" "✐"            ;; code point 10000 (decimal)
	--test-- "find-23"
		--assert none = find "abcde✐" "ed✐"
	--test-- "find-24"
		--assert "^(010000)" = find "abcde^(010000)" "^(010000)"
	--test-- "find-25"
		--assert none = find tail "123345" 1
	--test-- "find-26 -issue #765"
		--assert none = find tail [a b c d e] 'a

===end-group===

===start-group=== "find/part"
	--test-- "find/part-1"
		--assert none = find/part "1234" "1" 0
	--test-- "find/part-2"
		--assert "1234" = find/part "1234" "1" 1
	--test-- "find/part-3"
		--assert "234" = find/part "1234" "2" 2
	--test-- "find/part-4"
		--assert "234" = find/part "1234" "2" 4
	--test-- "find/part-5"
		--assert "234" = find/part "1234" "2" 5
	--test-- "find/part-6"
		--assert none = find/part "1234" "3" 2
	--test-- "find/part-7"
		--assert none = find/part [1 2 3 4] 1 0
	--test-- "find/part-8"
		--assert [1 2 3 4] = find/part [1 2 3 4] 1 1
	--test-- "find/part-9"
		--assert [2 3 4] = find/part [1 2 3 4] 2 2
	--test-- "find/part-10"
		--assert [2 3 4] = find/part [1 2 3 4] [2 3] 4
	--test-- "find/part-11"
		--assert none = find/part [1 2 3 4] [2 3] 2
	--test-- "find/part-12"
		--assert none = find/part [1 2 3 4] 3 2
	--test-- "find/part-13"
		--assert none = find/part "Χαῖρε, κόσμε!" "Χαῖ" 2
===end-group===

===start-group=== "find/only"
	--test-- "find/only-1"
		--assert [[2 3] 4] = find/only [1 [2 3] 4] [2 3]
	--test-- "find/only-2"
		--assert none = find/only [1 2 3 4] [2 3]
===end-group===

===start-group=== "find/case"
	--test-- "find/case-1"
		--assert "abcde" = find/case "Aabcde" "a"
	--test-- "find/case-2"
		--assert "Abcde" = find/case "aAbcde" "A"
	--test-- "find/case-3"
		--assert none = find/case "è" "E"
===end-group===

===start-group=== "find/any"      ; not yet implemented
	--test-- "find/any-1"
		;--assert "12345" = find/any "12345" "*"
	--test-- "find/any-2"
		;--assert "12345" = find/any "12345" "?"
	--test-- "find/any-3"
		;--assert "2345" = find/any "12345" "2?4"
	--test-- "find/any-4"
		;--assert "2345" = find/any "12345" "2*"
	--test-- "find/any-5"
		;--assert "e✐" = find/any "abcde✐" "e?"        ;; code point 10000 (decimal)
	--test-- "find/any-6"
		;--assert "e✐f" = find/any "abcde✐f" "?f"
	--test-- "find/any-7"
		;--assert "e✐" = find/any "abcde✐" "e*"
	--test-- "find/any-8"
		;--assert "abcde✐f" = find/any "abcde✐f" "*f"
	--test-- "find/any-9"
		;--assert "e^(010000)" = find/any "abcde^(010000)" "e?"
	--test-- "find/any-10"
		;--assert "e^(010000)f" = find/any "abcde^(010000)f" "?f"
	--test-- "find/any-11"
		;--assert "e^(010000)" = find/any "abcde^(010000)" "e*"
	--test-- "find/any-12"
		;--assert "abcde^(010000)f" = find/any "abcde^(010000)f" "*f"
===end-group===

===start-group=== "find/with"      ; not yet implemented
	--test-- "find/with-1"
		;--assert "12345" = find/with "12345" "^(FFFF)" "^(FFFE)^(FFFF)"
	--test-- "find/with-2"
		;--assert "12345" = find/with "12345" "^(FFFE)" "^(FFFE)^(FFFF)"
	--test-- "find/with-3"
		;--assert "2345" = find/with "12345" "2^(FFFE)3" "^(FFFE)^(FFFF)"
	--test-- "find/with-4"
		;--assert "2345" = find/with "12345" "2^(FFFF)" "^(FFFE)^(FFFF)"
	--test-- "find/with-5"
		;--assert "e✐" = find/with "abcde✐" "e^(FFFE)" "^(FFFE)^(FFFF)"
	--test-- "find/with-6"
		;--assert "e✐f" = find/with "abcde✐f" "^(FFFE)f" "^(FFFE)^(FFFF)"
	--test-- "find/with-7"
		;--assert "e✐" = find/with "abcde✐" "e^(FFFF)" "^(FFFE)^(FFFF)"
	--test-- "find/with-8"
		;--assert "abcde✐f" = find/with "abcde✐f" "^(FFFF)f" "^(FFFE)^(FFFF)"
	--test-- "find/with-9"
		;--assert "e^(010000)" = find/with "abcde^(010000)" "e^(FFFE)" "^(FFFE)^(FFFF)"
	--test-- "find/with-10"
		;--assert "e^(010000)f" = find/with "abcde^(010000)f" "^(FFFE)f" "^(FFFE)^(FFFF)"
	--test-- "find/with-11"
		;--assert "e^(010000)" = find/with "abcde^(010000)" "e^(FFFF)" "^(FFFE)^(FFFF)"
	--test-- "find/with-12"
		;--assert "abcde^(010000)f" = find/with "abcde^(010000)f" "^(FFFF)f" "^(FFFE)^(FFFF)"
===end-group===

===start-group=== "find/skip"
	--test-- "find/skip-1"
		--assert [6 7 8 9 0] = find/skip [1 2 3 4 5 1 6 6 6 6 1 2 3 4 5 6 7 8 9 0] 6 5
	--test-- "find/skip-2"
		--assert none = find/skip [1 2 3 4 5 1 6 6 6 6 1 2 3 4 5 1 2 3 4 5] 6 5
	--test-- "find/skip-3"
		--assert [6 7 8 9 0] = find/skip [1 2 3 4 5 6 6 6 6 6 1 2 3 4 5 6 7 8 9 0] [6 7] 5
	--test-- "find/skip-4"
		--assert none = find/skip [1 2 3 4 5 1 6 6 6 6 1 2 3 4 5 1 2 3 4 5] [6 7] 5
	--test-- "find/skip-5"
		--assert "67890" = find/skip "12345166661234567890" "6" 5
	--test-- "find/skip-6"
		--assert none = find/skip "12345166661234557890" "6" 5
===end-group===

===start-group=== "find/match"
	--test-- "find/match-1"
		--assert [and now] = find/match [here and now] 'here
	--test-- "find/match-2"
		--assert none = find/match [here and now] 'her
	--test-- "find/match-3"
		--assert none = find/match [her and now] 'here
	--test-- "find/match-4"
		--assert " and now" = find/match "here and now" "here"
	--test-- "find/match-5"
		--assert "andnow" = find/match "hereandnow" "here"
	--test-- "find/match-6"
		--assert none = find/match "her and now" "here"
	--test-- "find/match-7"
		--assert " and now" = find/match "here✐ and now" "here✐"
	--test-- "find/match-8"
		--assert "✐andnow" = find/match "here✐andnow" "here"
	--test-- "find/match-9"
		--assert none = find/match "her and now" "he✐r"
	--test-- "find/match-10"
		--assert none = find/match "here and now" "✐here"
	--test-- "find/match-11"
		--assert "^(010000)andnow" = find/match "here^(010000)andnow" "here"
	--test-- "find/match-12"
		--assert none = find/match "her and now" "here^(010000)"
	--test-- "find/match-13"
		--assert " and now" = find/match "here^(010000) and now" "here^(010000)"
	--test-- "find/match-14"
		--assert "andnow" = find/match "^(010000)hereandnow" "^(010000)here"
	--test-- "find/match-15"
		--assert none = find/match "her^(010000) and now" "here^(010000)"
	--test-- "find/match-16"
		--assert [and now] = find/match [he✐re and now] 'he✐re
===end-group===

===start-group=== "find/tail"
	--test-- "find/tail-1"
		--assert [and now] = find/tail [here and now] 'here
	--test-- "find/tail-2"
		--assert none = find/tail [here and now] 'her
	--test-- "find/tail-3"
		--assert none = find/tail [her and now] 'here
	--test-- "find/tail-4"
		--assert " and now" = find/tail "here and now" "here"
	--test-- "find/tail-5"
		--assert "andnow" = find/tail "hereandnow" "here"
	--test-- "find/tail-6"
		--assert none = find/tail "her and now" "here"
	--test-- "find/tail-7"
		--assert " and now" = find/tail "here✐ and now" "here✐"
	--test-- "find/tail-8"
		--assert "✐andnow" = find/tail "here✐andnow" "here"
	--test-- "find/tail-9"
		--assert none = find/tail "her and now" "he✐r"
	--test-- "find/tail-10"
		--assert none = find/tail "here and now" "✐here"
	--test-- "find/tail-11"
		--assert "^(010000)andnow" = find/tail "here^(010000)andnow" "here"
	--test-- "find/tail-12"
		--assert none = find/tail "her and now" "here^(010000)"
	--test-- "find/tail-13"
		--assert " and now" = find/tail "here^(010000) and now" "here^(010000)"
	--test-- "find/tail-14"
		--assert "andnow" = find/tail "^(010000)hereandnow" "^(010000)here"
	--test-- "find/tail-15"
		--assert none = find/tail "her^(010000) and now" "here^(010000)"
	--test-- "find/tail-16"
		--assert [and now] = find/tail [he✐re and now] 'he✐re
	--test-- "find/tail-17 issue #457"
		--assert "de" = find/tail "abcde" #"c"
	--test-- "find-tail-18"
		--assert "de" = find/tail "abc^^de" #"^^"

===end-group===

===start-group=== "find/last"
	--test-- "find/last-1"
		--assert [1] = find/last [1 1 1 1] 1
		--assert 4 = index? find/last [1 1 1 1] 1
	--test-- "find/last-2"
		--assert [2 1] = find/last [1 2 3 4 3 2 1] 2
	--test-- "find/last-3"
		--assert [3 3 2 1] = find/last [1 2 3 3 2 1] [3 3]
	--test-- "find/last-4"
		--assert "3321" = find/last "123321" "33"
	--test-- "find/last-5"
		--assert "321" = find/last "123321" "3"
	--test-- "find/last-6"
		--assert "1" = find/last "1111" "1"
	--test-- "find/last-7"
		--assert none = find/last "123" "34"
	--test-- "find/last-8"
		--assert "✐5678" = find/last "1234✐5678" "✐"
	--test-- "find/last-9"
		--assert "^(010000)5678" = find/last "1234^(010000)5678" "^(010000)"
===end-group===

===start-group=== "find/reverse"
	--test-- "find/reverse-1"
		--assert none = find/reverse [1 1 1 1] 1
	--test-- "find/reverse-2"
		--assert [2 1] = find/reverse tail [1 2 3 4 3 2 1] 2
	--test-- "find/reverse-3"
		--assert [3 3 2 1] = find/reverse tail [1 2 3 3 2 1] [3 3]
	--test-- "find/reverse-4"
		--assert "3321" = find/reverse back back back tail "123321" "33"
	--test-- "find/reverse-5"
		--assert "3321" = find/reverse back back back tail"123321" "3"
	--test-- "find/reverse-6"
		--assert "1" = find/reverse tail "1111" "1"
	--test-- "find/reverse-7"
		--assert none = find/reverse tail "123" "34"
	--test-- "find/reverse-8"
		--assert "✐5678" = find/reverse tail "1234✐5678" "✐"
	--test-- "find/reverse-9"
		--assert "^(010000)5678" = find/reverse tail "1234^(010000)5678" "^(010000)"
	--test-- "find/reverse-10 issue #505"
		--assert "ab" = find/reverse tail "ab" #"a"
		--assert "ab" = find/reverse tail "ab" "ab"
		--assert [a b] = find/reverse tail [a b] 'a
		--assert [a b] = find/reverse tail [a b] [a b]
===end-group===

===start-group=== "find/last/tail"
	--test-- "find/last/tail-1 issue #459"
		--assert [7 8] = find/last/tail [1 2 3 4 5 6 3 7 8] 3
	--test-- "find/last/tail-2 issue #459"
		--assert "78" = find/last/tail "123456378" #"3"
	--test-- "find/last/tail-3"
		--assert "78" = find/last/tail "123456378" "3"
===end-group===

~~~end-file~~~